vue3项目封装支持搜索,选中不可选,选中的数据项支持上下移动,删除的上下穿梭的树状穿梭框
1,vue3代码
1 // 这个是返回的所有的数据 2 const sourceItems = ref([]) 3 // 这是穿梭到下面的数据 4 const targetItems = ref([]) 5 // 这是搜索字段 6 const searchName = ref('') 7 // 这两个要是后端返回,可不写 8 const expanded = ref(false) 9 const selected = ref(false) 10 11 watch(sourceItems, (newVal,oldVal)=> { 12 newVal.forEach((item)=>{ 13 if (!item.expanded && !item.selected) { 14 item.expanded = expanded.value 15 item.selected = selected.value 16 } 17 sourceItems.value = newVal 18 }) 19 }) 20 // 搜索 21 const searchFn = () => { 22 updateItemsVisibility(sourceItems.value); 23 }; 24 // 清除搜索框里面的值 25 const clearSearch = () => { 26 searchName.value = '' 27 } 28 const updateItemsVisibility = (items) => { 29 for (const item of items) { 30 const isShown = item.messageName.toLowerCase().includes(searchName.value.toLowerCase()); 31 item.isShown = isShown; 32 if (item.children) { 33 updateItemsVisibility(item.children); 34 if (item.children.some((child) => child.isShown)) { 35 item.expanded = true; 36 } else { 37 item.expanded = false; 38 } 39 } 40 } 41 } 42 const moveToTarget = (item) => { 43 // 将 item 从 sourceItems 移动到 targetItems 44 sourceItems.value = sourceItems.value.filter((el) => el.id !== item.id); 45 item.selected = false; // 取消选中状态 46 targetItems.value.push(item); 47 } 48 const moveToSource = (item) => { 49 // 将 item 从 targetItems 移动到 sourceItems 50 targetItems.value = targetItems.value.filter((el) => el.id !== item.id); 51 // 在 sourceItems 中查找 item 的父级,并将 item 添加到找到的父级的 children 中 52 const parent = findParentItem(item, sourceItems.value); 53 if (parent) { 54 parent.children.push(item); 55 } else { 56 sourceItems.value.push(item); 57 } 58 } 59 const findParentItem = (item, items) => { 60 // 递归查找 item 的父级 61 for (let i = 0; i < items.length; i++) { 62 const currentItem = items[i]; 63 if (currentItem.children && currentItem.children.some((el) => el.id === item.id)) { 64 return currentItem; 65 } else if (currentItem.children) { 66 const parent = findParentItem(item, currentItem.children); 67 if (parent) return parent; 68 } 69 } 70 return null; 71 } 72 const isChecked = (item) => { 73 return item.selected; 74 } 75 const toggleSelection = (item) => { 76 item.selected = !item.selected; 77 if (item.selected) { 78 item.expanded = false; // 选中状态下,不展开子项 79 } 80 } 81 // 上移 82 const moveUp = (index) => { 83 if (index > 0 ) { 84 [targetItems.value[index - 1], targetItems.value[index]] = [ 85 targetItems.value[index], 86 targetItems.value[index - 1], 87 ]; 88 } 89 } 90 // 下移 91 const moveDown = (index) => { 92 if (index < targetItems.value.length - 1) { 93 [targetItems.value[index], targetItems.value[index + 1]] = [ 94 targetItems.value[index + 1], 95 targetItems.value[index], 96 ]; 97 } 98 } 99 // 删除 100 const removeItem = (index) => { 101 targetItems.value.splice(index, 1); 102 } 103 // 取消 104 const cancel = () => { 105 console.log('取消') 106 open.value = false 107 } 108 // 确定 109 const confirm = () => { 110 let targetItemObj={} 111 sourceItems.value.forEach(item=>{ 112 let targetItemArr=[] 113 targetItems.value.forEach(it=>{ 114 if (item.children.length && it.parentId == item.id) { 115 targetItemArr.push(it.messageCode) 116 } 117 }) 118 let iten=item.messageCode 119 targetItemObj[iten]=targetItemArr 120 }) 121 let oldObj ={ 122 trend:targetItemObj 123 } 124 let obj={} 125 let arr=Object.keys(oldObj.trend) 126 arr.forEach(item=>{ 127 if(oldObj.trend[item].length>0){ 128 obj[item]=oldObj.trend[item] 129 } 130 }) 131 emit('confirm',obj) 132 cancel(); 133 };
template代码
1 <template> 2 <DrawerBox 3 v-model:open="open" 4 title="可选择输入数据信息" 5 width='448px' 6 > 7 <!-- 8 可选择输入数据信息 9 --> 10 <Form> 11 <div class="transfer-view"> 12 <div class="checked-box"> 13 <div class="search-box"> 14 <Input 15 v-model:value="searchName" 16 placeholder="请输入" 17 @input="searchFn" 18 style="width:288px" 19 > 20 <template #prefix> 21 <SearchOutlined style="color:#D8D8D8"/> 22 </template> 23 <template #suffix> 24 <CloseCircleOutlined @click="clearSearch"/> 25 </template> 26 </Input> 27 <div @click="searchFn" class="inquire">查询</div> 28 </div> 29 <ul style="marginLeft:-25px"> 30 <li v-for="item in sourceItems" :key="item.id" style="list-style:none"> 31 <span @click="item.expanded = !item.expanded" :class="{ expanded: item.expanded }"> 32 <input 33 type="checkbox" 34 :checked="isChecked(item)" 35 @change="toggleSelection(item)" 36 :disabled="item.selected" 37 > 38 {{ item.messageName }} 39 </span> 40 <ul v-show="item.expanded"> 41 <template v-for="child in item.children" :key="child.id"> 42 <li style="list-style:none"> 43 <input 44 type="checkbox" 45 :checked="isChecked(child)" 46 @change="toggleSelection(child)" 47 :disabled="child.selected" 48 @click="moveToTarget(child)" 49 > 50 {{ child.messageName }} 51 </li> 52 <li v-for="subChild in child.children" :key="subChild.id" style="list-style:none"> 53 <input 54 type="checkbox" 55 :checked="isChecked(subChild)" 56 @change="toggleSelection(subChild)" 57 :disabled="subChild.selected" 58 @click="moveToTarget(subChild)" 59 > 60 {{ subChild.messageName }} 61 </li> 62 </template> 63 </ul> 64 </li> 65 </ul> 66 </div> 67 <div class="title">已选择验证数据信息</div> 68 <div class="select-checked-box"> 69 <div class="search-box"> 70 <span class="selected">已选:{{ targetItems.length }}个</span> 71 </div> 72 <ul style="marginLeft:-25px"> 73 <li v-for="(item, index) in targetItems" :key="item.id" style="list-style:none"> 74 <div @click="item.expanded = !item.expanded" class="selected"> 75 <div> 76 <input type="checkbox" :checked="isChecked(item)" @change="toggleSelection(item)" :disabled="item.selected"/> 77 {{ item.messageName }} 78 </div> 79 <div class="operate"> 80 <UpSquareOutlined @click="moveUp(index)" v-if="index !== 0" class="activate"></UpSquareOutlined> 81 <UpSquareOutlined v-else /> 82 <DownSquareOutlined @click="moveDown(index)" v-if="index !== targetItems.length - 1" class="activate"></DownSquareOutlined> 83 <DownSquareOutlined v-else /> 84 <DeleteOutlined @click="removeItem(index)" class="delete activate"></DeleteOutlined> 85 </div> 86 </div> 87 <ul v-show="item.expanded"> 88 <template v-for="child in item.children" :key="child.id"> 89 <li style="list-style:none"> 90 <input type="checkbox" :checked="isChecked(child)" @change="toggleSelection(child)" :disabled="child.selected"/> 91 {{ child.messageName }} 92 </li> 93 <li v-for="subChild in child.children" :key="subChild.id" style="list-style:none"> 94 <input type="checkbox" :checked="isChecked(subChild)" @change="toggleSelection(subChild)" :disabled="subChild.selected"/> 95 {{ subChild.messageName }} 96 </li> 97 </template> 98 </ul> 99 </li> 100 </ul> 101 </div> 102 </div> 103 </Form> 104 <template #footer> 105 <Button type="primary" @click="confirm">确定</Button> 106 <Button style="margin-left:12px;" @click="cancel">取消</Button> 107 </template> 108 </DrawerBox> 109 </template>
css代码
1 <style lang="less" scoped> 2 .transfer-view { 3 width: 100%; 4 display: flex; 5 flex-wrap: wrap; 6 .checked-box, 7 .select-checked-box { 8 width: 400px; 9 border: 1px solid #eeeeee; 10 border-radius: 4px; 11 padding: 1.31rem; 12 box-sizing: border-box; 13 } 14 .select-checked-box { 15 min-height: 152px; 16 } 17 .selected{ 18 display: flex; 19 justify-content: space-between; 20 button{ 21 margin: 5px; 22 } 23 } 24 .title{ 25 height: 40px; 26 width: 100%; 27 background: #F5F6FB; 28 padding: 14px 0 14px 24px; 29 font-size: 14px; 30 } 31 .search-box { 32 font-family: Source Han Sans CN; 33 font-weight: 400; 34 display: flex; 35 height: 2.4rem; 36 justify-content: space-between; 37 align-items: center; 38 width: 100%; 39 margin-bottom: 0.8rem; 40 .selected { 41 color: #888ea2; 42 } 43 .inquire{ 44 color: #0058FB; 45 cursor: pointer; 46 } 47 } 48 .checked-box { 49 min-height: 600px; 50 } 51 .item-label { 52 width: 60px; 53 margin-right: 12px; 54 height: 100%; 55 text-align: right; 56 font-weight: 400; 57 } 58 .operate { 59 font-size: 16px; 60 margin-left: 40px; 61 color: #D0CFE0; 62 cursor: not-allowed; 63 64 .activate { 65 color: #0859FE; 66 cursor: pointer; 67 68 &.delete { 69 color: #B30000; 70 } 71 } 72 73 & > span { 74 margin-right: 8px; 75 76 &:last-child { 77 margin-right: 0; 78 } 79 } 80 } 81 } 82 </style>