实现下拉选择器支持全选
运行效果
输入2后,模糊查询后,点击全选,选中的是当前筛选后的全部,并不是实际所有下拉选项的全部
此修改时所处情况:当时该系统已经处于迭代更新状态,需求要求将所有多选的下拉选择器都改成支持 模糊查询 全选(是根据模糊查询后 现有匹配项的当前全选) 反选 有编码的还有支持输入编码可以自动匹配选中功能;功能不复杂但是涉及页面很多,所以在能够兼容全局页面的情况下编写了一下代码。
封装组件
components/selectCheckBox/index.vue
template部分
// template部分
<el-select
v-if="filterableFlag === 'filterable'"
v-model="selectValue"
:placeholder="placeholder"
popper-class="smallSelectDropdown pop_select_down"
:class="[defaultClass !== 'no' ? 'selectCheckBox' : '', className]"
:style="style"
multiple
clearable
collapse-tags
filterable
reserve-keyword
:filter-method="filterableHandler"
@visible-change="selectVisibleChange"
@change="(v) => selectChange(v, showAll ? options : notAllOptions)"
>
<el-checkbox
v-model="isSelectAll"
:indeterminate="isIndeterminate"
class="selectbox"
style="padding-left:18px"
@change="selectAllnotAllOptions"
>全选</el-checkbox
>
<el-option
v-for="item in showAll ? options : notAllOptions"
:key="item[opv] + '0' + Math.random()"
:label="item[opl]"
:value="item[opv]"
>
<span class="check" />
<span>{{ item[opl] }}</span>
</el-option>
</el-select>
props从外部接收的参数
props: {
// 父组件的样式名称
defaultClass: {
type: String,
default: ''
},
// 产品名称等支持根据编码自动选中的 不需要传该属性
// 其他需要传 filterableFlag === 'filterable'
filterableFlag: {
type: String,
default: ''
},
placeholder: {
type: String,
default: ''
},
// 也是央视名称(因为是多人协作定义时候出现了冲突 导致同功能变量定义重复)
className: {
type: String,
default: ''
},
// 因为是兼容的全局 有的页面是单独写的是style
style: {
type: Object,
default: () => {}
},
// 下拉选择器的 备选项
options: {
type: Array,
default: () => []
},
// 对应的el-options的value绑定的字段
opv: {
type: String,
default: 'id'
},
// label绑定的字段
opl: {
type: String,
default: 'label'
},
// 父组件传参过来的下拉备选项的初始数据(options用于子组件绑定)
optionsOri: {
type: Array,
default: () => []
},
// 父组件原来选择器选中的的绑定值 其实和子组件的v-model保持一致
modelOri: {
type: Array,
default: () => []
}
},
data watch methods部分
mounted(离开页面后再返回保留原来的查询条件)
data() {
return {
selectValue: [], // 当前子组件 v-model值
isSelectAll: false, // 全选
isIndeterminate: false, //
notAllOptions: [], // 模糊查询后产生的新数据
showAll: true // 模糊查询后 是否显示全部备选项 默认为true
};
},
watch: {
isSelectAll: {
handler(val) {
this.$emit('selectChange', this.selectValue);
}
}
},
mounted() {
if (this.modelOri.length > 0) {
this.selectValue = this.modelOri;
this.selectCheck(this.selectValue, this.options);
}
},
methods: {
// 根据模糊查询结果 全选
selectAllnotAllOptions(v) {
let opArr = this.showAll ? this.options : this.notAllOptions;
this.selectValue = v ? opArr.map((item) => item[this.opv]) : [];
this.isIndeterminate = false;
},
// filterableFlag === 'filterable' 自定义模糊查询
filterableHandler(value) {
if (value) {
this.showAll = false;
this.notAllOptions = [];
this.notAllOptions = this.optionsOri.filter((item) => {
return (
item[this.opv].indexOf(value) > -1 ||
item[this.opl].indexOf(value) > -1
);
});
} else {
this.showAll = true;
}
this.selectCheck(
this.selectValue,
this.showAll ? this.options : this.notAllOptions
);
},
// visible-change事件
selectVisibleChange(v) {
if (v === false) {
this.options = this.optionsOri;
this.showAll = true;
}
this.$emit('selectVisibleChange', v);
this.selectCheck(
this.selectValue,
this.showAll ? this.options : this.notAllOptions
);
},
// change事件
selectChange(v, arr) {
this.$emit('selectChange', v);
// 选中 对应的复选框更改状态,全选根据当前更改状态
this.selectCheck(v, arr);
},
// 根据选中数据 更新全选框状态
selectCheck(v, arr) {
if (arr.length !== 0) {
if (v && v.length === arr.length) {
this.selectValue = arr.map((item) => item[this.opv]);
this.isSelectAll = true;
} else {
this.isSelectAll = false;
}
this.isIndeterminate = v.length > 0 && v.length < arr.length;
}
}
}
样式补充
<style lang="scss">
.selectCheckBox .el-tag:first-child {
max-width: calc(100% - 72px);
.el-select__tags-text {
display: inline-block;
width: calc(100% - 18px);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.el-icon-close {
top: -7px;
}
}
</style>
<style lang="scss">
.pop_select_down.is-multiple .el-select-dropdown__item {
padding: 0 18px;
}
.pop_select_down.is-multiple .el-select-dropdown__list {
padding-top: 0;
}
.pop_select_down.is-multiple .selectbox {
display: block;
height: 34px;
line-height: 34px;
}
.pop_select_down.is-multiple .el-select-dropdown__item.selected::after {
top: 0.05rem;
left: 1.17rem;
z-index: 1;
font-weight: normal;
color: #fff;
}
.pop_select_down.is-multiple .el-select-dropdown__item .check {
position: relative;
box-sizing: border-box;
display: inline-block;
width: 0.875rem;
height: 0.875rem;
margin-right: 10px;
vertical-align: middle;
border: 1px solid #656c7e;
border-radius: 2px;
transition: border-color 0.25s cubic-bezier(0.71, -0.49, 0.26, 1.46),
background-color 0.25s cubic-bezier(0.71, -0.49, 0.26, 1.46);
}
.pop_select_down.is-multiple .el-select-dropdown__item.selected .check {
background-color: #656c7e;
}
</style>
使用组件
<span>产品名称:</span>
<selectCheckBox
ref="selectCheckBox"
placeholder="请选择产品代码或名称"
class-name="filterWidth"
:model-ori="productCode"
:options="productCodeList"
:options-ori="productCodeList"
opl="label"
opv="id"
@selectChange="productNameChange"
></selectCheckBox>
// 单独写个change事件 实现子组件的值回传给父组件 给父组件对应值赋值
productNameChange(v) {
this.productCode = v;
}
根据编码自动选中功能
主要是遍历options,根据输入的值 做匹配,代码写的很繁琐,为了适用多用情况加了很多参数和判断,故不附代码;
测试过程中出现的问题:
发现名称不一致,但是编码却相同,所以就导致filter-methods方法报错,(报错内容是关于key未定义),所以在el-option上:key时加了个随机数。