Element UI 自定义下拉框组件
效果图
支持全选反选,及模糊搜索
组件源码
<template>
<div>
<el-select
ref="subjectSelect"
v-model="subjectNameIds"
:disabled="hideSelect"
style="width:100%"
multiple
:key="selectKeys"
value-key="id"
@click.native="subjectSelectClick"
@change="subjectChange"
>
<el-option
v-for="item in copy_subjectTypeList"
:key="item.id"
:label="item.name"
:value="item.id"
>
{{ item.name }}
</el-option>
</el-select>
<el-popover
ref="popover"
placement="bottom"
trigger="manual"
v-model="popShow"
>
<div class="close-btn">
<el-button
size="mini"
type="primary"
@click="subjectSelectClick"
>关闭
</el-button
>
</div>
<div class="poper__head">
<div class="checkBoxTitle">
<el-checkbox
:disabled="hideCheckBox"
:indeterminate="isIndeterminate"
v-model="checkAll"
@change="handleCheckAllChange"
>全选
</el-checkbox
>
</div>
<div>
<el-input
placeholder="请输入关键字模糊搜索"
v-model.trim="subject_name"
class="input-with-select"
clearable
>
<el-button
slot="append"
icon="el-icon-search"
@click="searchSubjectName"
></el-button>
</el-input>
</div>
</div>
<br/>
<div class="popChild">
<el-checkbox-group
:disabled="hideCheckBox"
v-model="checkedSubjects"
@change="handleCheckedCitiesChange"
>
<el-checkbox
v-for="item in copy_subjectTypeList"
:label="item.id"
:key="item.id"
>{{ item.name }}
</el-checkbox
>
</el-checkbox-group>
</div>
</el-popover>
</div>
</template>
<script>
export default {
props: {
subjectTypeList: { //传入的可筛选数组
type: Array,
default: []
},
hideSelect: {
type: Boolean,
default: false
},
hideCheckBox: {
type: Boolean,
default: false
},
panelTitle: {
type: String,
default: "请选择数据"
}
},
data() {
return {
alreadySelectList: [],//已经选中的数据,编辑时展示
copy_subjectTypeList: [],//复制的可筛选数组,子组件不可直接更改父组件传来的值
subjectNameIds: [],//当前选中的科目
primeSubjectTypeList: [],
selectKeys: 1,
checkAll: false,
isIndeterminate: false,
checkedSubjects: [], //当前勾选的复选框
subject_name: "",
popShow: false
};
},
watch: {
subjectTypeList() {
this.copy_subjectTypeList = this.subjectTypeList.concat();
this.primeSubjectTypeList = this.subjectTypeList.concat();
}
},
created() {
},
mounted() {
this.initSelectAndPanel();
this.$refs.popover.$refs.popper.style.width = this.$el.clientWidth + "px";
},
methods: {
//子组件需要每次操作后向父级传递选中科目
emitParent() {
this.$emit("selectData", this.subjectNameIds);
},
//初始化当前下拉框和复选框
initSelectAndPanel() {
this.subject_name = "";
this.checkAll = false;
this.isIndeterminate = false;
this.popShow = false;
this.checkedSubjects = [];
this.subjectNameIds = [];
this.copy_subjectTypeList = [];
this.primeSubjectTypeList = [];
this.updateSelectCompoment([]);
},
//设置当前已经选中的数据,仅作展示
// (当前逻辑编辑时不可修改,如需编辑时可修改需要变动当前页面逻辑和调用逻辑)
setSelfAlreadySelect(list) {
this.subjectNameIds = list;
let test = this.copy_subjectTypeList.filter(item => {
return item.id == list[0];
});
this.$forceUpdate();
},
subjectChange(val) {
this.checkedSubjects = val;
this.handleCheckedCitiesChange(this.subjectNameIds);
},
//点击下拉框时打开pop框
subjectSelectClick() {
if (!this.hideSelect) {
this.subject_name = "";
//只有新增时才可显示
this.$refs.subjectSelect.blur();
this.popShow = !this.popShow;
}
},
//每此点击复选框的时候判断:全选按钮显示逻辑和下拉框选项合并逻辑
handleCheckedCitiesChange(value) {
this.subjectNameIds = value;
//如果当前下拉框里的列表选项已被全部选择: 全选为true,否则为false
let ids = this.subjectNameIds.sort();
let list = this.copy_subjectTypeList.sort((a, b) => {
return a.id > b.id;
});
if (
value.length > 0 &&
(value.length > this.copy_subjectTypeList.length ||
value.length == this.copy_subjectTypeList.length)
) {
let temp = 0;
for (let i = 0; i < ids.length; i++) {
let Index = list.findIndex((item) => {
return item.id === ids[i];
});
temp = Index >= 0 ? (temp += 1) : temp;
}
// console.log("相同的数量", temp);
if (temp > 0) {
if (list.length == temp) {
this.checkAll = true;
this.isIndeterminate = false;
}
else {
this.checkAll = false;
this.isIndeterminate = true;
}
}
else {
this.checkAll = false;
}
}
else if (
value.length > 0 &&
value.length < this.copy_subjectTypeList.length
) {
//判断剩余的选项里有咩有列表里的元素
let temp = 0;
for (let i = 0; i < ids.length; i++) {
let Index = list.findIndex((item) => {
return item.id === ids[i];
});
temp = Index >= 0 ? (temp += 1) : temp;
}
this.isIndeterminate = temp > 0 ? true : false;
}
else {
// console.log("选项里没有数据");
this.isIndeterminate = false;
this.checkAll = false;
}
this.emitParent();
this.$forceUpdate();
},
//模糊搜索科目,每次都从primeSubjectTypeList列表里搜
async searchSubjectName() {
if (this.subject_name != "") {
this.copy_subjectTypeList = this.fuzzySearch(
this.subject_name,
this.primeSubjectTypeList
);
if (this.copy_subjectTypeList.length == 0) {
this.isIndeterminate = false;
this.checkAll = false;
}
else {
this.checkSubjectList();
}
}
else {
this.copy_subjectTypeList = this.primeSubjectTypeList; //直接显示原始数据,不需要再次向服务器请求
this.checkSubjectList();
}
},
//每次搜索出结果进行比对:检测两个数组显示反选全选的显示
checkSubjectList() {
//需要判断当前已选择的和下拉框里的数组进行比较,用来显示全选按钮的状态
let ids = this.subjectNameIds.sort();
let list = this.copy_subjectTypeList.sort();
let temp = 0;
for (let i = 0; i < ids.length; i++) {
let Index = list.findIndex((item) => {
return item.id === ids[i];
});
temp = Index >= 0 ? (temp += 1) : temp;
}
//默认将两个bool设置成false,根据结果设置不同显示
this.isIndeterminate = false;
this.checkAll = false;
if (ids.length > list.length || ids.length == list.length) {
if (temp > 0 && list.length == temp) {
// console.log("完全包含列表里所有选项");
this.checkAll = true;
}
else if (temp > 0 && list.length != temp) {
// console.log("不完全包含列表里的选项");
this.isIndeterminate = true;
}
}
else {
if (temp > 0) {
this.isIndeterminate = true;
}
}
},
fuzzySearch(str, container) {
var newList = [];
var startChar = str.charAt(0);
var strLen = str.length;
for (var i = 0; i < container.length; i++) {
var obj = container[i];
var isMatch = false;
for (var p in obj) {
var curItem = "";
if (obj[p] != null) {
curItem = obj[p];
}
for (var j = 0; j < curItem.length; j++) {
if (curItem.charAt(j) == startChar) {
if (curItem.substring(j).substring(0, strLen) == str) {
isMatch = true;
break;
}
}
}
}
if (isMatch) {
newList.push(obj);
}
}
return newList;
},
handleCheckAllChange(val) {
let ids = this.copy_subjectTypeList.map((item) => {
return item.id;
});
//如果是全选需要将已保存的和下拉框里的合并去重
//如果是反选,需要将已选的数组进行判断是否有下拉框里的数组,有的话直接移除
// console.log("当前是否是全选", this.checkAll);
// console.log("当前下拉框里的数组", ids);
if (this.checkAll) {
//去重显示合并后的数组
let newArr = [];
let arr = this.subjectNameIds.concat(ids).sort();
for (let i in arr) {
if (newArr.indexOf(arr[i]) < 0) {
newArr.push(arr[i]);
}
}
// console.log("去重显示合并后的数组", newArr);
this.subjectNameIds = newArr;
this.checkedSubjects = newArr;
this.isIndeterminate = false;
}
else {
let temp = [];
for (let i in this.subjectNameIds) {
if (ids.indexOf(this.subjectNameIds[i]) < 0) {
temp.push(this.subjectNameIds[i]);
}
}
// console.log("不相同的元素", temp);
this.subjectNameIds = temp;
//如果是取消全选,清空下拉框的数据,再重新渲染下拉组件,否则下拉框不显示选项文字
this.updateSelectCompoment(temp);
if (this.subject_name != "") {
this.copy_subjectTypeList = this.primeSubjectTypeList;
this.subject_name = "";
this.handleCheckedCitiesChange(this.subjectNameIds);
}
if (temp.length == 0) {
this.isIndeterminate = false;
}
this.checkedSubjects = temp;
}
this.emitParent();
this.$forceUpdate();
// console.log('当前选中的科目提交给父级',this.subjectNameIds)
},
updateSelectCompoment(temp) {
if (this.$refs.subjectSelect) {
this.$refs.subjectSelect.cachedOptions = temp;
this.$refs.subjectSelect.selected = temp;
this.selectKeys += 1;
this.$forceUpdate();
}
}
}
};
</script>
<style scoped>
.close-btn {
direction: rtl;
padding-bottom: 15px;
}
.poper__head {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
flex-direction: row;
}
.popChild {
max-height: 300px;
overflow-y: scroll;
}
/deep/ .el-select__tags {
max-height: 400px;
overflow-y: auto;
}
/deep/ .el-checkbox {
line-height: 26px;
}
/deep/ .el-checkbox-group {
display: flex;
flex-direction: column;
}
/deep/ .el-tag.el-tag--info {
max-width: 540px;
height: auto;
}
/deep/ .el-select__tags-text {
max-width: 540px;
white-space: normal;
}
/deep/ .el-input__inner {
height: auto;
}
.checkBoxTitle {
font-size: 20px;
}
</style>
使用方式
调用该组件的脚本:需要手动修改下拉框展示样式,否则下拉框和pop框宽度不一致。
如果是编辑时需要展示数据,则调用this.$refs.xxxx.setSelfAlreadySelect(list) ,传入要展示的数据id数组
<template>
<div class="app-container">
<el-form inline>
<el-form-item label-width="80" label="测试">
<SelectComponent style="width: 380px" :subjectTypeList="list" @selectData="selectData"/>
</el-form-item>
</el-form>
</div>
</template>
<script>
import SelectComponent from './select-pop.vue'
export default {
name: 'index2',
components: { SelectComponent },
data(){
return{
list:[]
}
},
created() {},
mounted() {
this.list = [
{id:1,name:'测试'},
{id:2,name:'测试1'},
{id:3,name:'测试2'},
{id:4,name:'测试3'},
{id:5,name:'测试4'},
{id:6,name:'测试44'},
{id:7,name:'测试45'},
{id:8,name:'测试46'},
{id:9,name:'测试47'},
{id:10,name:'测试48'},
{id:11,name:'试测试测试测试测试测试49'},
]
},
methods:{
selectData(list){
console.log('子组件返回的选项',list)
}
}
}
</script>
<style scoped>
</style>
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!