若依框架DictTag改造为render函数

作者 Hpyer 日期 2023-09-06
若依框架DictTag改造为render函数

问题起因

最近在做了一个基于 RuoYi框架 前后端分离版的项目,项目上线前需要通过某SAST平台的代码漏洞扫描。其中,框架提供的DictTag字典标签组件爆出v-for指令未添加key属性的漏洞,但是该组件中这个指令是加在template标签上的,而vue2中template标签又不支持加key属性,就导致了这个问题。

简单解决

由于vue2中template标签又不支持加key属性,最直接的办法,就是把template替换为span标签,然后加上key就可以了,不过多个标签并排时的间距的问题需要另外通过样式去控制下。

另一种解决

改变标签固然是一种简单的方式,但是一想到要额外套一层标签,总觉得不太舒服(强迫症?🤔)。这种情况下,改造为 render 函数 可以说是再合适不过了。

移除最外层整个 template 标签,再script部分加上如下代码即可:

render(h) {
let items = [];
if (this.values.length > 0 && this.options.length > 0) {
for (let index=0,length=this.options.length; index<length; index++) {
let item = this.options[index];
if (!this.values.includes(item.value)) continue;
if (item.raw.listClass == 'default' || item.raw.listClass == '') {
items.push(h('span', {
index,
class: item.raw.cssClass,
}, [item.label + ' ']));
}
else {
items.push(h('el-tag', {
index,
'disable-transitions': true,
type: item.raw.listClass == 'primary' ? '' : item.raw.listClass,
class: item.raw.cssClass,
}, [item.label + ' ']));
}
}
}
if (this.unmatch && this.showValue) {
items.push(this.unmatchArray | this.handleArray);
}
return h("div", items);
}

完整代码:

<script>
export default {
name: "DictTag",
props: {
options: {
type: Array,
default: null,
},
value: [Number, String, Array],
// 当未找到匹配的数据时,显示value
showValue: {
type: Boolean,
default: true,
}
},
data() {
return {
unmatchArray: [], // 记录未匹配的项
}
},
computed: {
values() {
if (this.value !== null && typeof this.value !== 'undefined') {
return Array.isArray(this.value) ? this.value : [String(this.value)];
} else {
return [];
}
},
unmatch(){
this.unmatchArray = [];
if (this.value !== null && typeof this.value !== 'undefined') {
// 传入值为非数组
if(!Array.isArray(this.value)){
if(this.options.some(v=> v.value == this.value )) return false;
this.unmatchArray.push(this.value);
return true;
}
// 传入值为Array
this.value.forEach(item => {
if (!this.options.some(v=> v.value == item )) this.unmatchArray.push(item)
});
return true;
}
// 没有value不显示
return false;
},

},
filters: {
handleArray(array) {
if(array.length===0) return '';
return array.reduce((pre, cur) => {
return pre + ' ' + cur;
})
},
},
render(h) {
let items = [];
if (this.values.length > 0 && this.options.length > 0) {
for (let index=0,length=this.options.length; index<length; index++) {
let item = this.options[index];
if (!this.values.includes(item.value)) continue;
if (item.raw.listClass == 'default' || item.raw.listClass == '') {
items.push(h('span', {
index,
class: item.raw.cssClass,
}, [item.label + ' ']));
}
else {
items.push(h('el-tag', {
index,
'disable-transitions': true,
type: item.raw.listClass == 'primary' ? '' : item.raw.listClass,
class: item.raw.cssClass,
}, [item.label + ' ']));
}
}
}
if (this.unmatch && this.showValue) {
items.push(this.unmatchArray | this.handleArray);
}
return h("div", items);
}
};
</script>
<style scoped>
.el-tag + .el-tag {
margin-left: 10px;
}
</style>