vue项目-封装树形控件公用组件

发布时间 2023-09-20 11:42:29作者: 听见海棠花未眠

vue项目中,如h5端,第三方的树形选择器无法满足项目开发时,原生封装tree控件,通过判断是否存在子节点,循环递归组件完成树形封装,通过vue指令实现跨级传递数据或方法

封装树形组件如下:

  1 <template>
  2   <div class="tree-select-page">
  3     <div class="tree-item" v-for="item in treeData" :key="item.key">
  4       <div class="tree-item-options">
  5         <div class="tree-item-left">
  6           <!-- 有子级,出现箭头 -->
  7           <div class="tree-item-icon" @click="hanlderOpen(item)" v-if="item.children">
  8             <img
  9               src="../../../assets/images/common/icon-up.png"
 10               v-if="expandedKeys.includes(item.key)"
 11             />
 12             <img src="../../../assets/images/common/icon-down.png" v-else />
 13           </div>
 14           <div class="tree-item-img" v-if="$slots.treeIcon" :class="{ 'ml-16': !item.children }">
 15             <slot name="treeIcon"></slot>
 16           </div>
 17           <div class="tree-item-content" :class="{ 'ml-8': !item.children }">
 18             <div class="tree-item-title" :title="item.title">
 19               {{ item.title }}
 20             </div>
 21             <div class="tree-item-desc">
 22               <slot name="desc"></slot>
 23               <span v-if="!$slots.desc && item.desc">{{ item.desc }}</span>
 24             </div>
 25           </div>
 26         </div>
 27         <div class="tree-item-right" @click="selectItem(item)">
 28           <slot name="right"></slot>
 29           <span
 30             v-if="!$slots.right"
 31             class="ok-checkbox"
 32             :class="{ 'ok-checkbox-checked': selectedKeys.includes(item.key) }"
 33           >
 34             <span class="ok-checkbox-inner"></span>
 35           </span>
 36         </div>
 37         <div class="tree-item-line"></div>
 38       </div>
 39         <!-- 调用自己生成子级 -->
 40         <!-- v-bind="$props",v-on="$listeners":多级嵌套的中间件,用于跨级传递 -->
 41       <TreeSelect
 42         :list="item.children"
 43         v-bind="$props"
 44         :selectIds.sync="selectedKeys"
 45         v-if="item.children && expandedKeys.includes(item.key)"
 46         style="padding-left: 16px"
 47         v-on="$listeners"
 48       >
 49         <!-- 自定义内容 -->
 50         <template slot="treeIcon">
 51           <slot name="treeIcon"></slot>
 52         </template>
 53         <template slot="desc">
 54           <slot name="desc"></slot>
 55         </template>
 56         <template slot="right">
 57           <slot name="right"></slot>
 58         </template>
 59       </TreeSelect>
 60     </div>
 61   </div>
 62 </template>
 63 
 64 <script>
 65 export default {
 66   name: 'TreeSelect',
 67   props: {
 68      // 传入的树形结构数据
 69     list: {
 70       type: Array,
 71       default() {
 72         return [];
 73       }
 74     },
 75     // 选中的选项
 76     selectedIds: {
 77       type: Array,
 78       default() {
 79         return [];
 80       }
 81     }
 82   },
 83   data() {
 84     return {
 85       listMap: {}, // 数组对象
 86       treeData: [], // 数据
 87       selectedKeys: [], // 选中的keys
 88       expandedKeys: [] // 展开的keys
 89     };
 90   },
 91   watch: {
 92     // 监听选中项改变
 93     selectedIds(val) {
 94       this.selectedKeys = val;
 95     }
 96   },
 97   created() {
 98     this.treeData = this.list;
 99     this.selectedKeys = this.selectedIds;
100   },
101   methods: {
102     hanlderOpen(data) {
103       if (!data.children) return;
104       // 展开/收起 树形控件,请求数据
105       const targ = this.expandedKeys.includes(data.key);
106       if (!targ) {
107         // 展开项数组中无对应数据,则为展开操作
108         this.expandedKeys = [...this.expandedKeys, data.key];
109       } else {
110         //反之,收起操作
111         this.expandedKeys = this.expandedKeys.filter((item) => item !== data.key);
112       }
113         // 展开/收起抛出方法,用于父级操作数据
114       this.$emit('expand', targ, this.expandedKeys, data);
115     },
116     selectItem(data) {
117       // 选择操作,与展开/收起逻辑相同
118       const targ = this.selectedKeys.includes(data.key);
119       if (!targ) {
120         this.selectedKeys.push(data.key);
121       } else {
122         this.selectedKeys = this.selectedKeys.filter((item) => item !== data.key);
123       }
124         // 抛出选择方法
125       this.$emit('selectKeys', this.selectedKeys);
126     }
127   }
128 };
129 </script>
130 
131 <style lang="less" scoped>
132 .tree-select-page {
133   background: #fff;
134   .tree-item-line {
135     height: 1px;
136     width: calc(100% - 8px);
137     margin-left: 8px;
138     background: #eaeaea;
139     position: absolute;
140     bottom: 0;
141     left: 0;
142     right: 0;
143   }
144   .tree-item-options {
145     padding: 8px 15px 8px 8px;
146     display: flex;
147     align-items: center;
148     justify-content: space-between;
149     position: relative;
150 
151     .tree-item-left {
152       display: flex;
153       align-items: center;
154       width: calc(100% - 24px);
155       .tree-item-icon {
156         margin-right: 8px;
157         width: 24px;
158         img {
159           width: 24px;
160           height: 24px;
161         }
162       }
163       .tree-item-img {
164         height: 40px;
165         width: 40px;
166         margin-right: 16px;
167       }
168       .ml-8 {
169         margin-left: 8px;
170       }
171       .ml-16 {
172         margin-left: 16px;
173       }
174       .tree-item-content {
175         text-align: left;
176         flex-grow: 1;
177         overflow: hidden;
178         .tree-item-title {
179           font-size: 18px;
180           color: #2f2f2e;
181           line-height: 24px;
182           font-weight: 500;
183           overflow: hidden;
184           width: 100%;
185           text-overflow: ellipsis;
186           white-space: nowrap;
187         }
188         .tree-item-desc {
189           margin-top: 4px;
190           font-size: 14px;
191           color: #999999;
192           line-height: 16px;
193           font-weight: 400;
194           overflow: hidden;
195           text-overflow: ellipsis;
196           display: -webkit-box;
197           -webkit-line-clamp: 3;
198           -webkit-box-orient: vertical;
199         }
200       }
201     }
202   }
203 }
204 :deep(.ok-checkbox.ok-checkbox-checked .ok-checkbox-inner) {
205   // border-color: #07c160;
206   // background: #07c160;
207   border-color: var(--feature-color-primary);
208   background: var(--feature-color-primary);
209 }
210 </style>

父级组件:(部分代码)

 1 <TreeSelect
 2       :list="treeData"
 3       :selectedIds="selectedKeys"
 4       @selectKeys="treeSelectKeys"
 5       @expand="expandTree"
 6     >
 7       <div slot="desc">自定义说明书</div>
 8       <div class="tree-left-icon" slot="treeIcon">
 9         <img src="../../assets/images/broadcast/icon-group.png" />
10       </div>
11     </TreeSelect>
12     <div class="click-btn-box" @click="submitTreeSelect">提交树形控件选中</div>

父组件传给子组件的数据

 1 // 树形控件选择器
 2       selectedIds: [],
 3       selectedKeys: [],
 4       treeData: [
 5         {
 6           title: '一一级',
 7           key: 'one-one-key',
 8           desc: '说明书',
 9           children: [
10             {
11               title: '一二级',
12               key: 'one-two-key',
13               children: [
14                 {
15                   title: '一三级',
16                   key: 'one-three-key'
17                 }
18               ]
19             }
20           ]
21         },
22         {
23           title: '二一级',
24           key: 'two-one-key',
25           desc: '说明书',
26           children: [
27             {
28               title: '二二级',
29               key: 'two-two-key',
30               children: [
31                 {
32                   title: '二三级',
33                   key: 'two-three-key'
34                 }
35               ]
36             }
37           ]
38         }
39       ],

方法的调用,数据回传显示

 1 treeSelectKeys(ids) {
 2       this.selectedKeys = ids;
 3       console.log('this.selectedKeys--', this.selectedKeys);
 4       console.log('tree调用---keys---回传', this.selectedKeys);
 5     },
 6     expandTree(targ, keys, data) {
 7       console.log('expandTree---', targ, keys, data);
 8       if (targ) {
 9         // 收起
10       } else {
11         // 展开
12       }
13     },
14     submitTreeSelect() {
15       // 树形控件提交
16       console.log('submitTreeSelect---', this.selectedKeys);
17     },

实现的效果如下: