需求:
1、自定义多选样式
2、多选值为对象数组
3、已选项侧边显示,可删除,并关联“备选”的选择状态
4、此示例为组件,数据为父组件传递而来
难点:多选项的值为对象数组,如果直接使用对象值进行匹配,由于对象的指引不同,所以即使完全相同的值,匹配也不相同。也就导致多选项的值匹配失败,显示为空。
思路:
1、备选:多选框的显示使用对象数组,值使用id数组匹配
2、已选:选择“备选”生成的值为id数组,“已选”根据此id数组匹配“备选”里的所有对象数组生成一个对象数组用以展示
3、已选:当点击删除时,用对象数组里的id匹配id数组,进行删除,从而改变右侧“备选里的值”
效果图如下:
主要代码如下:
1 <div class="transfer-card-box"> 2 备选: 3 <div class="transfer-card-body"> 4 <div> 5 <slot name="search"></slot> 6 </div> 7 <div style="height: 86%"> 此处控制全选,checkAll为是否全选的值,indeterminate控制全选的样式 8 <a-checkbox v-model:checked="checkAll" :indeterminate="indeterminate" 9 @change="onCheckAllChange">全选</a-checkbox> 10 <div class="selector-item-box"> checkedDataId2为选中的值,此处只记录id 11 <a-checkbox-group v-model:value="checkedDataId2" > optionAll为所有备选的数据,是个对象数组。利用循环栅格实现自定义布局 12 <div v-for="item in optionAll" :key="item.id"> 13 <a-row :gutter="[0,16]"> 14 <a-col :span="24"> 为每一个多选项赋值,值为id 15 <a-checkbox :value="item.id"> 16 {{item.name}} 17 </a-checkbox> 18 </a-col> 19 </a-row> 20 </div> 21 </a-checkbox-group> 22 </div> 23 </div> 24 </div> 25 </div> 26 27 <div class="transfer-card-box"> 28 已选: 29 <div class="transfer-card-body"> 30 <div class="item-flex-between" style="padding-bottom: 4px" 31 v-for="item in checkedData2" :key="item.id"> 32 <div>{{item.name}}</div> 33 <div class="remove-icon" @click="removeItem(item.id)"><CloseCircleOutlined /></div> 34 </div> 35 </div> 36 </div> 37 </div>
1 import { reactive, toRefs, watch } from 'vue' 2 3 export default { 父组件传来的数据 4 props: { 5 optionAll: { type: Array, default() { return [] } },//所有备选项(对象数组) 6 checkedDataId: { type: Array, default() { return [] } },//已选项(id数组) 7 }, 8 setup(props) { 9 const state = reactive({ 10 indeterminate: true, 11 checkAll: false, 12 checkedData2: [],//已选项(对象数组)页面展示使用 13 checkedDataId2: [],//已选项(id数组)后续改变值要用,所以不能直接使用props.checkedDataId 14 }) 监听props 15 watch( 16 props, 17 newProps => { 将已选项id数组转为字符串,方便后续匹配数据 18 const valStr = newProps.checkedDataId.join() 初始化数组,避免重复push数据 19 state.checkedData2 = [] 20 state.checkedDataId2 = [] 循环所有备选项。由于已选项数组里只有id,所有此处将id进行匹配,将匹配的值push进checkData2,也就是页面显示的已选项的值。 21 newProps.optionAll.forEach((item) => { 22 if (valStr.indexOf(item.id) > -1) { 23 state.checkedData2.push(item) 24 } 25 }) 为checkedDataId2赋值,为后续删除操作做准备 26 state.checkedDataId2 = newProps.checkedDataId 27 }, immediate必须加,表示立即执行,否则监听无效 28 { deep: true, immediate: true }, 29 ) 监听checkedDataId2,当此字段值改变时,对应的改变checkData2的值,也就是页面页面‘已选’的展示数据 30 watch( 31 () => state.checkedDataId2, 32 val => { 33 state.indeterminate = !!val.length && val.length < props.optionAll.length 34 state.checkAll = val.length === props.optionAll.length 35 const valStr = val.join() 36 state.checkedData2 = [] 37 props.optionAll.forEach((item) => { 38 if (valStr.indexOf(item.id) > -1) { 39 state.checkedData2.push(item) 40 } 41 }) 42 }, 43 { deep: true, immediate: true }, 44 ) 45 用户点击全选按钮 46 const onCheckAllChange = (e) => { 47 /* 实现数组浅拷贝,否则删除已选项也会删除备选项optionAll */ 48 const newArr = props.optionAll.concat() 49 Object.assign(state, { 50 checkedData2: e.target.checked ? newArr : [], 51 indeterminate: false, 52 }) 53 54 state.checkedDataId2 = [] 55 if (e.target.checked) { 全选 56 props.optionAll.forEach(item => { 57 state.checkedDataId2.push(item.id) 58 }) 59 } else { 60 state.checkedDataId2 = [] 61 } 62 } 63 删除已选项 64 const removeItem = val => { 匹配删除id的位置,将其从checkedDataId2中移除,由于有监听checkedDataId2,所以后续操作都在监听中完成 65 const index = state.checkedDataId2.indexOf(val) 66 state.checkedDataId2.splice(index, 1) 67 } 68 69 return { 70 ...toRefs(state), 71 onCheckAllChange, 72 removeItem, 73 } 74 }, 75 }