需求:
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 }
