业务需求期望多选的下拉框带有全部选择选项,并且全部选择选项和其他的选项为互斥关系,而element中的下拉多选框是没有这种全选功能的,所以需要自己实现。下面我们看一下完成的效果动图:
实现的基本思路就是判断选中项中是否有全部选项,从而对选中项进行过滤。下面是这个多选框组件multi-select.vue的代码:
<template>
<el-select :placeholder="placeholder" v-model="curOption" filterable :clearable="clearable" multiple :collapse-tags="collapse" :disabled="disabled" :style="{width: width}" @remove-tag="removeTag" @visible-change="$emit('visible-change', $event)" @clear="clear">
<el-option @click.native="selectOption(item)" v-for="(item, index) in selectList" :key="index" :label="item[opFormat[0]]" :value="item[opFormat[1]]"/>
</el-select>
</template>
<script>
export default {
name: 'multi-select',
props: {
options: {
type: Array,
default: () => []
},
opFormat: {
type: Array,
default: () => ['label', 'value']
},
placeholder: {
type: String,
default: '请选择'
},
clearable: {
type: Boolean,
default: true
},
disabled: Boolean,
width: String,
collapse: {
type: Boolean,
default: true
},
alllabel: {
type: String,
default: '全部'
}
},
data () {
return {
curOption: this.$attrs.value,
selectList: [],
allOptions: []
}
},
methods: {
removeOption (value) {
let tSet = new Set(this.curOption)
tSet.delete(value)
this.curOption = [...tSet]
},
removeTag (value) {
this.removeOption(value)
this.emitChange(this.curOption)
},
clear () {
this.curOption = []
this.emitChange([])
},
selectOption (value) {
let v = value[this.opFormat[1]]
if (!this.curOption.includes(this.alllabel)) return this.emitChange(this.curOption)
if (v === this.alllabel) {
this.curOption = [this.alllabel]
this.emitChange(this.allOptions)
} else {
this.removeOption(this.alllabel)
this.emitChange(this.curOption)
}
},
emitChange ($event) {
this.$emit('input', [...$event])
this.$emit('change', [...$event])
}
},
mounted () {
this.allOptions = this.options.map(i => i[this.opFormat[1]])
this.selectList = [...this.options]
this.selectList.unshift({
[this.opFormat[0]]: this.alllabel,
[this.opFormat[1]]: '全部'
})
}
}
</script>
下拉框数据和组件使用代码分别如下:
data () {
return {
point: [],
pointList: [
{
name: '浙江大学',
value: '45'
},
{
name: '浙理工学',
value: '4'
},
{
name: '青海矿业',
value: '34'
},
{
name: '南海美院',
value: '77'
}
]
}
}
<multi-select v-model="point" :options="pointList" :opFormat="['name', 'value']" multiple :collapse="false" width="350px"/>
这里对组件的两个属性做一下说明:
1: alllable, 可以指定下拉选项中的全部选项会显示的文案。
2:opFormat 数组的值依次指定了el-select中的label,value。这样做的意义就是你无法确定数据的key,value对应的属性名时,在封装组件的时候通过一个指定属性去完成原始数据key-value 和组件lable-vlaue的映射关系,也是二次封装的好处,因为我不期望单独去指定key,value,这样的写法比较麻烦。(这就是仁者见仁智者见智了,不习惯这种opFormat的朋友可以自行换成label, value的属性的形式)