前言
今天小谭要给大家分享的是基于element ui 的下拉框和树型控件二次分装的树型下拉框,element plus新增了这一组件,但是对于还在使用vue2的我来说,就很不友好。组件可以实现单选和多选,以及其他常用功能,废话不多说,直接上效果图:
代码实现
效果图如上,接下来实现代码:
使用时,如果想实现回显效果node-key必须要传!!!
HTMl:
<!-- * @description: 通用树型下拉框 * @fileName: treeSelect.vue * @author: t * @date: 2023-03-07 17:15:03 * @Attributes: data 展示数据 array props 配置选项,具体配置可以参照element ui库中el-tree的配置 object show-checkbox 节点是否可被选择 boolean check-strictly 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false boolean icon-class 自定义树节点的图标 string load 加载子树数据的方法,仅当 lazy 属性为true 时生效 function(node, resolve) lazy 是否懒加载子节点,需与 load 方法结合使用 boolean disabled 下拉框是否禁用 getCheckedKeys 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点的 key 所组成的数组 getCurrentNode 获取当前被选中节点的 data,若没有节点被选中则返回 null collapse-tags 多选时是否将选中值按文字的形式展示 select-last-node 单选时是否只能选择最后一个节点 Scoped Slot 自定义树节点的内容,参数为 { node, data } show-count 若节点中存在children 则在父节点展示所属children的数量,注意但设置插槽时 show-count将失效! clearable 单选时是否可以清空选项 filterable 属性即可启用搜索功 !!!!!必传!!!!!! props-value 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的 !!!!!! !--> <!--:clearable="$attrs['show-checkbox']==undefined?$attrs['clearable']:false"--> <template> <el-select :value="valueFilter(value)" :placeholder="$attrs['placeholder']" :multiple="$attrs['show-checkbox']" :disabled="$attrs['disabled']" :filterable="$attrs['filterable']" :clearable="$attrs['clearable']" :collapse-tags="$attrs['collapse-tags']" @change="selectChange" @clear="selectClear" ref="mySelect" :filter-method="remoteMethod" > <template slot="empty"> <div class="selecTree"> <el-tree :data="data" :props="props" @node-click="handleNodeClick" :show-checkbox="$attrs['show-checkbox']" :check-strictly="$attrs['check-strictly']" :icon-class="$attrs['icon-class']" :lazy="$attrs['lazy']" :load="$attrs['load']" :node-key="props.value" :filter-node-method="filterNode" @check-change="handleCheckChange" :default-expanded-keys="defaultExpandedKeys" ref="myTree" > <template slot-scope="{ node, data }"> <slot :node="node" :data="data"> <span class="slotSpan"> <span> {{ data[props.label] }} <b v-if="$attrs['show-count'] != undefined && data[props.children]">({{ data[props.children].length }})</b> </span> </span> </slot> </template> </el-tree> </div> </template> </el-select> </template>
复制
JS:
export default { props: { value: { type: undefined, default: null, }, data: { type: Array, default: null, }, props: { type: Object, default: null, }, }, data() { return { defaultExpandedKeys: [], }; }, created() { this.propsInit(); }, mounted() { setTimeout(this.initData, 10); }, beforeUpdate() { this.propsInit(); this.initData(); }, methods: { initData() { if (this.$attrs['show-checkbox'] === undefined) { let newItem = this.recurrenceQuery(this.data, this.props.value, this.value); if (newItem.length) { if (this.props.value && newItem[0][this.props.value]) { this.defaultExpandedKeys = [newItem[0][this.props.value]]; } this.$nextTick(() => { this.$refs.myTree.setCurrentNode(newItem[0]); }); } } else { let newValue = JSON.parse(JSON.stringify(this.value)); if (!(newValue instanceof Array)) { newValue = [newValue]; } if (newValue?.length) { let checkList = newValue.map(key => { if (key) { let newItem = this.recurrenceQuery(this.data, this.props.value, key); return newItem[0] || ''; } }); if (checkList?.length) { let defaultExpandedKeys = checkList.map(item => item?.[this.props.value || '']); if (defaultExpandedKeys.length) this.defaultExpandedKeys = defaultExpandedKeys; this.$nextTick(() => { this.$refs.myTree.setCheckedNodes(checkList); }); } } } this.$forceUpdate(); }, // 多选 handleCheckChange(data, e, ev) { let checkList = this.$refs.myTree.getCheckedNodes(); let setList = null; if (checkList.length) { setList = checkList.map(item => item[this.props.value]); } this.$emit('input', setList); // 共三个参数,依次为:传递给 data 属性的数组中该节点所对应的对象、节点本身是否被选中、节点的子树中是否有被选中的节点 this.$emit('change', data, e, ev); }, // 单选事件 handleNodeClick(data, e) { if (!(this.$attrs['select-last-node'] === undefined)) { if (data[this.props.children] && data[this.props.children]?.length) { return false; } } if (this.$attrs['show-checkbox'] === undefined) { this.$emit('input', data[this.props.value]); this.$refs.mySelect.blur(); } this.$emit('change', data, e); }, // 递归查找通用方法 recurrenceQuery(list, key, value) { if (!list || !key || !value) return []; let queryData = []; list.map(item => { if (item[this.props.children] && item[this.props.children].length) { queryData.push(...this.recurrenceQuery(item[this.props.children], key, value)); } if (item[key] == value) { queryData.push(item); } return item; }); return queryData; }, selectChange(e) { if (this.$attrs['show-checkbox'] !== undefined) { let checkList = e.map(key => { let newItem = this.recurrenceQuery(this.data, this.props.label, key); return newItem[0] || ''; }); this.$refs.myTree.setCheckedNodes(checkList); this.$emit('input', e); } }, selectClear(flag) { if (this.$attrs['show-checkbox'] === undefined) { if (!flag) this.$emit('input', ''); this.$refs.myTree.setCurrentKey(null); } else { if (!flag) this.$emit('input', []); this.$refs.myTree.setCheckedKeys([]); } this.remoteMethod(''); }, getCheckedNodes() { if (this.value !== null && this.value !== undefined && this.value !== '') { return this.$refs.myTree.getCheckedNodes(); } return []; }, getCurrentNode() { if (this.value !== null && this.value !== undefined && this.value !== '') { return this.$refs.myTree.getCurrentNode(); } return null; }, valueFilter(val) { if (this.$attrs['show-checkbox'] === undefined) { let res = ''; [res] = this.recurrenceQuery(this.data, this.props.value, val); return res?.[this.props.label] || ''; } else { if (!val?.length) return []; let res = val.map(item => { let [newItem] = this.recurrenceQuery(this.data, this.props.value, item); return newItem?.[this.props.label] || ''; }); if (!res?.length) return []; res = res.filter(item => item); return res; } }, propsInit() { this.props.label = this.props.label || 'label'; this.props.value = this.props.value || 'value'; this.props.children = this.props.children || 'children'; if (this.$attrs['select-last-node'] !== undefined && !this.props.disabled) { this.props.disabled = data => data?.[this.props.children]?.length; this.$attrs['check-strictly'] = true; } }, remoteMethod(query) { this.$refs.myTree.filter(query); }, filterNode(value, data) { if (!value) return true; return data[this.props.label].indexOf(value) !== -1; }, }, watch: { value: { deep: true, handler(val) { if (!val || !val?.length) { this.selectClear(true); } }, }, }, };
复制
CSS:
.selecTree { max-height: 50vh; overflow: auto; padding: 5px; } .el-select { width: 100%; } .slotSpan { font-size: 14px; b { font-weight: normal; font-size: 12px; color: #999; } } .selecTree ::v-deep .el-tree-node__content { font-size: 14px; }
复制