vue3项目封装支持搜索,选中不可选,选中的数据项支持上下移动,删除的上下穿梭的树状穿梭框

发布时间 2023-09-27 16:33:15作者: 程序员肉包子

 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>