Element-UI 实现下拉树

组件调用

 1 <template>
 2   <!-- 行模式 -->
 3   <el-form inline>
 4     <el-form-item label="inline 默认:">
 5       <select-tree :options="options" v-model="selected" />
 6     </el-form-item>
 7     <el-form-item label="inline 定义宽度:">
 8       <select-tree width="200" :options="options" v-model="selected" />
 9     </el-form-item>
10   </el-form>
11   <!-- 块模式 -->
12   <el-form>
13     <el-form-item label="自适应:">
14       <select-tree v-model="selected" :options="options" :props="defaultProps" />
15     </el-form-item>
16   </el-form>
17 </template>
18 
19 <script>
20 import SelectTree from '@/components/widget/SelectTree.vue';
21 
22 export default {
23   name: 'about',
24   components: {
25     SelectTree,
26   },
27   data() {
28     return {
29       // 默认选中值
30       selected: 'A',
31       // 数据默认字段
32       defaultProps: {
33         parent: 'parentId',   // 父级唯一标识
34         value: 'id',          // 唯一标识
35         label: 'label',       // 标签显示
36         children: 'children', // 子级
37       },
38       // 数据列表
39       options: [
40         {
41           parentId: '0',
42           id: 'A',
43           label: 'label-A',
44           children: [
45             {
46               parentId: 'A',
47               id: 'A-1',
48               label: 'label-A-1',
49             },
50           ],
51         },
52         {
53           parentId: '0',
54           value: 'B',
55           label: 'label-B',
56           children: [],
57         },
58       ],
59     };
60   },
61 };
62 </script>

SelectTree.vue

  1 <!-- 树状选择器 -->
  2 <template>
  3   <el-popover
  4     ref="popover"
  5     placement="bottom-start"
  6     trigger="click"
  7     @show="onShowPopover"
  8     @hide="onHidePopover">
  9     <el-tree
 10       ref="tree"
 11       class="select-tree"
 12       highlight-current
 13       :style="`min-width: ${treeWidth}`"
 14       :data="data"
 15       :props="props"
 16       :expand-on-click-node="false"
 17       :filter-node-method="filterNode"
 18       :default-expand-all="false"
 19       @node-click="onClickNode">
 20     </el-tree>
 21     <el-input
 22       slot="reference"
 23       ref="input"
 24       v-model="labelModel"
 25       clearable
 26       :style="`width: ${width}px`"
 27       :class="{ 'rotate': showStatus }"
 28       suffix-icon="el-icon-arrow-down"
 29       :placeholder="placeholder">
 30     </el-input>
 31   </el-popover>
 32 </template>
 33 
 34 <script>
 35 export default {
 36   name: 'Pagination',
 37   props: {
 38     // 接收绑定参数
 39     value: String,
 40     // 输入框宽度
 41     width: String,
 42     // 选项数据
 43     options: {
 44       type: Array,
 45       required: true,
 46     },
 47     // 输入框占位符
 48     placeholder: {
 49       type: String,
 50       required: false,
 51       default: '请选择',
 52     },
 53     // 树节点配置选项
 54     props: {
 55       type: Object,
 56       required: false,
 57       default: () => ({
 58         parent: 'parentId',
 59         value: 'rowGuid',
 60         label: 'areaName',
 61         children: 'children',
 62       }),
 63     },
 64   },
 65   // 设置绑定参数
 66   model: {
 67     prop: 'value',
 68     event: 'selected',
 69   },
 70   computed: {
 71     // 是否为树状结构数据
 72     dataType() {
 73       const jsonStr = JSON.stringify(this.options);
 74       return jsonStr.indexOf(this.props.children) !== -1;
 75     },
 76     // 若非树状结构,则转化为树状结构数据
 77     data() {
 78       return this.dataType ? this.options : this.switchTree();
 79     },
 80   },
 81   watch: {
 82     labelModel(val) {
 83       if (!val) {
 84         this.valueModel = '';
 85       }
 86       this.$refs.tree.filter(val);
 87     },
 88     value(val) {
 89       this.labelModel = this.queryTree(this.data, val);
 90     },
 91   },
 92   data() {
 93     return {
 94       // 树状菜单显示状态
 95       showStatus: false,
 96       // 菜单宽度
 97       treeWidth: 'auto',
 98       // 输入框显示值
 99       labelModel: '',
100       // 实际请求传值
101       valueModel: '0',
102     };
103   },
104   created() {
105     // 检测输入框原有值并显示对应 label
106     if (this.value) {
107       this.labelModel = this.queryTree(this.data, this.value);
108     }
109     // 获取输入框宽度同步至树状菜单宽度
110     this.$nextTick(() => {
111       this.treeWidth = `${(this.width || this.$refs.input.$refs.input.clientWidth) - 24}px`;
112     });
113   },
114   methods: {
115     // 单击节点
116     onClickNode(node) {
117       this.labelModel = node[this.props.label];
118       this.valueModel = node[this.props.value];
119       this.onCloseTree();
120     },
121     // 偏平数组转化为树状层级结构
122     switchTree() {
123       return this.cleanChildren(this.buildTree(this.options, '0'));
124     },
125     // 隐藏树状菜单
126     onCloseTree() {
127       this.$refs.popover.showPopper = false;
128     },
129     // 显示时触发
130     onShowPopover() {
131       this.showStatus = true;
132       this.$refs.tree.filter(false);
133     },
134     // 隐藏时触发
135     onHidePopover() {
136       this.showStatus = false;
137       this.$emit('selected', this.valueModel);
138     },
139     // 树节点过滤方法
140     filterNode(query, data) {
141       if (!query) return true;
142       return data[this.props.label].indexOf(query) !== -1;
143     },
144     // 搜索树状数据中的 ID
145     queryTree(tree, id) {
146       let stark = [];
147       stark = stark.concat(tree);
148       while (stark.length) {
149         const temp = stark.shift();
150         if (temp[this.props.children]) {
151           stark = stark.concat(temp[this.props.children]);
152         }
153         if (temp[this.props.value] === id) {
154           return temp[this.props.label];
155         }
156       }
157       return '';
158     },
159     // 将一维的扁平数组转换为多层级对象
160     buildTree(data, id = '0') {
161       const fa = (parentId) => {
162         const temp = [];
163         for (let i = 0; i < data.length; i++) {
164           const n = data[i];
165           if (n[this.props.parent] === parentId) {
166             n.children = fa(n.rowGuid);
167             temp.push(n);
168           }
169         }
170         return temp;
171       };
172       return fa(id);
173     },
174     // 清除空 children项
175     cleanChildren(data) {
176       const fa = (list) => {
177         list.map((e) => {
178           if (e.children.length) {
179             fa(e.children);
180           } else {
181             delete e.children;
182           }
183           return e;
184         });
185         return list;
186       };
187       return fa(data);
188     },
189   },
190 };
191 </script>
192 
193 <style>
194   .el-input.el-input--suffix {
195     cursor: pointer;
196     overflow: hidden;
197   }
198   .el-input.el-input--suffix.rotate .el-input__suffix {
199     transform: rotate(180deg);
200   }
201   .select-tree {
202     max-height: 350px;
203     overflow-y: scroll;
204   }
205   /* 菜单滚动条 */
206   .select-tree::-webkit-scrollbar {
207     z-index: 11;
208     width: 6px;
209   }
210   .select-tree::-webkit-scrollbar-track,
211   .select-tree::-webkit-scrollbar-corner {
212     background: #fff;
213   }
214   .select-tree::-webkit-scrollbar-thumb {
215     border-radius: 5px;
216     width: 6px;
217     background: #b4bccc;
218   }
219   .select-tree::-webkit-scrollbar-track-piece {
220     background: #fff;
221     width: 6px;
222   }
223 </style>

猜你喜欢

转载自www.cnblogs.com/yuwenjing0727/p/10214490.html