效果图

用途:一般用于系统管理中多选角色
涉及关键知识点:
1、css样式:flex布局,postion之absolute,relative,画三角形
2、input checkbox类型的 v-model 双向绑定 onchange事件
3、数组过滤及循环遍历
4、子组件向父组件传值
样式类代码
/* 多选显示区 */
.ui-xselect .content{
min-height: 30px;
border-radius: 2px;
border: 1px solid rgba(0, 0, 0, .3);
}
/* 选中区 */
.ui-xselect .content .selected {
width: calc(100% - 30px);
}
/* 选中区单项 */
.ui-xselect .content .selected .item {
border-radius: 2px;
border: 1px solid rgba(0, 0, 0, .3);
}
/* 待选列表 */
.ui-xselect .list {
background-color: #fff;
position: absolute;
border: 1px solid rgba(0, 0, 0, .3);
left:0;
top:0;
right: 0;
height: 100px;
width: 300px;
z-index: 99;
}
/* 待选列表行高 */
.ui-xselect .list .item {
line-height: 24px;
}
/* 选中框大小 */
.list input {
height: 16px;
width: 16px;
border: 1px solid rgba(0, 0, 0, .3);
}
组件代码解析
<template>
<div class = "ui-xselect flex flex-col">
<div class="content font-s flex flex-center-cz padding-left-m padding-right-m flex-space-between ">
<div class="selected flex flex-wrap padding-bottom-s ">
<div v-for="item in selectData" class="item margin-right-m margin-top-s padding-left-s padding-right-s flex flex-center-cz">
<div>{{item.text}}</div>
<img src="@/assets/images/guanbi.png" class="logo-18 pointer" @click="close(item.value)"/>
</div>
</div>
<div class="menu-down margin-top-s pointer" @click="select"></div>
</div>
<div v-if="show" class="position-relative" @click="listClick">
<div class="list flex flex-col font-s ">
<div class="flex flex-center-cz item cannotselect" v-for="item in data" >
<input @change="inputChange" :value="item.value" type="checkbox" v-model="checks">{{item.text}}</div>
</div>
</div>
</div>
</template>
<script>
export default {
name:"XSelect",
props:{
data:{},
values:{}
},
watch:{
values:{
handler(newValue) {
console.log("newValue", newValue);
this.checks = newValue;
this.selectData = [];
newValue.forEach(item => {
let find = this.data.filter(b=>b.value == item);
if (find.length > 0) {
this.selectData.push(find[0]);
}
});
},
immediate: true,
deep: true //深度监听
}
},
data() {
return {
checks:[],
selectData:[],
show: false,
}
},
methods:{
close(value) {
this.selectData = this.selectData.filter(item=>item.value != value);//数组过滤得到一个匹配的一个新数组
this.checks = this.checks.filter(item=>item != value);//数组过滤得到一个匹配的一个新数组
this.$emit("change", this.checks);//触发父组件 change事件
},
inputChange() {
console.log("change", this.checks)
this.selectData = [];
this.checks.forEach(item => {
let find = this.data.filter(b=>b.value == item);//数组过滤得到一个匹配的一个新数组
if (find.length > 0) {
this.selectData.push(find[0]);//往数组中添加一项
}
});
this.$emit("change", this.checks);//子组件向父组件传值
},
listClick(event) {
event.stopPropagation();//阻止冒泡,防止触发下层点击事件
},
select(event) {
event.stopPropagation();//阻止冒泡,防止触发下层点击事件
document.addEventListener("click", this.closeList, false);// 添加监听点击事件
this.show = !this.show;
},
closeList() {
document.removeEventListener("click", this.closeList, false);//关闭监听点击事件
this.show = false;
}
}
}
</script>
父组件代码
<template>
<div class="body">
<div class="table">
<div class="filter font-bold">组件库(五) ComboBox下拉多选组件</div>
<div class="margin-top-l margin-left-l flex flex-col ">
<x-select class="x-select-demo" :data="selectData" :values="values" @change="valueChange"></x-select>
<div class="margin-top-l">选中值为{{lastVal}}</div>
</div>
</div>
</div>
</template>
<script>
/*
名称:组件库(五) ComboBox下拉多选组件
作者:唐赢
时间:2023-4-6
*/
import XSelect from '@/components/xSelect/XSelect'
export default {
name: 'XSelectDemo',
components: {
XSelect
},
data () {
return {
selectData:[
{"value":"1", "text": "管理员"},
{"value":"2", "text": "销售顾问"},
{"value":"3", "text": "服务顾问"},
],
values:["1","2"],
lastVal:["1","2"]
}
},
methods: {
valueChange(values) {
this.lastVal = values;
}
}
}
</script>