el-tree是一个经常用到的组件,但是它不支持v-model,使用起来很麻烦,这篇教程封装了el-tree,使得它使用起来很简单,并且支持搜索,支持叶子节点横向排列,这样就算数据多了,也会显的很紧凑,同时它支持提交halfCheck节点,这点在做菜单管理的时候很有用,如果数据中不保存halfCheck,你需要向上遍历父节点,但是保存了父节点在回显的时候会有问题,因为只要父节点选中子节点都会选中,这些在组件封装中都做了处理
组件的封装
<template><div><el-input v-model="filterText" placeholder="请输入搜索条件" v-if="filter" clearable/><el-tree v-bind="allProps" ref="treeRef" :filter-node-method="filterNode"@check="handleCheck"/></div>
</template><script lang="ts">
// @ts-nocheck
export default {name: "ui-tree",props: { // 参考 https://element-plus.org/zh-CN/component/tree.html#%E5%B1%9E%E6%80%A7modelValue: {default: () => []}, //要提交的表单值nodeKey: {default: 'id'}, //每个树节点用来作为唯一标识的属性,整棵树应该是唯一的defaultExpandAll: {default: true}, //是否默认展开所有节点showCheckbox: {default: true}, //是否显示选择框data: {default: null}, //树的数据filter: {default: true}, //是否显示过滤框leafInline: {default: true}, //叶子节点是否显示成一行props: { //参考:https://element-plus.org/zh-CN/component/tree.html#propstype: Object, default: () => {return {}}}},data() {return {textValue: '', //view模式显示的内容userChecked: false, //是否用户引起的变化filterText: '',allProps: {...this.$attrs,...this.$props}}},created() {if (this.leafInline) {this.props.class = this.customNodeClass}},mounted() {this.setChecked()},watch: {'modelValue': {handler(val) {this.setChecked()},},// 过滤filterText(val) {this.$refs.treeRef.filter(val)},},computed: {labelField() {if (!this.props || !this.props.label) return 'label'return this.props.label},childrenField() {if (!this.props || !this.props.children) return 'children'return this.props.children},},methods: {// 设置选中的节点setChecked() {let val = this.modelValue//如果是用户点击则不设置if (this.userChecked) {this.userChecked = falsereturn}if (!val || !this.$refs.treeRef) returnlet leafNode = []// 如果后台保存了half节点,需要过滤掉this.filterLeafNode(leafNode, this.data, val)this.$refs.treeRef.setCheckedKeys(leafNode)},//用户选择后回调handleCheck(data, check) {this.updateModelValue(check.halfCheckedKeys, check.checkedKeys)},updateModelValue(halfCheckedKeys, checkedKeys) {this.userChecked = truelet checkIds = []checkIds.push(...halfCheckedKeys)checkIds.push(...checkedKeys)this.$emit('update:modelValue', checkIds)},//根据搜索框过滤节点filterNode(value: string, data) {// 该方法会遍历所有节点,显示返回为true的节点if (!value) return truereturn data[this.labelField].includes(value)},// 过滤父节点,只返回叶子节点filterLeafNode(leafNode, children, checkedArray) {if (!children) return []children.forEach(item => {if (!item[this.childrenField] || item[this.childrenField].length == 0) {if (checkedArray.indexOf(item[this.nodeKey]) > -1) {leafNode.push(item[this.nodeKey])}} else {this.filterLeafNode(leafNode, item[this.childrenField], checkedArray)}})},customNodeClass(data, node) {if (node.isLeaf) return ''let addClass = truefor (const key in node.childNodes) {if (!node.childNodes[key].isLeaf) {addClass = false}}let levelClass = 'level-' + node.levelreturn addClass ? `penultimate-node ${levelClass}` : ''},}
}
</script><style>
.penultimate-node .el-tree-node__children {line-height: 12px;
}.el-tree-node.penultimate-node > .el-tree-node__children {display: flex;flex-direction: row;flex-wrap: wrap;
}.el-tree-node.penultimate-node.level-1 > .el-tree-node__children {padding-left: 30px;
}
.el-tree-node.penultimate-node.level-2 > .el-tree-node__children {padding-left: 48px;
}
.el-tree-node.penultimate-node.level-3 > .el-tree-node__children {padding-left: 64px;
}
.el-tree-node.penultimate-node.level-4 > .el-tree-node__children {padding-left: 84px;
}.penultimate-node .el-tree-node__children > .el-tree-node .el-tree-node__content {padding-left: 12px !important;
}.penultimate-node .el-tree-node__children .el-tree-node__content .el-tree-node__expand-icon {display: none;
}
</style>
组件的使用
node-key就是绑定值,如果要form绑定id就传id,组件默认显示label,子节点保存在children里面,如果要变更可以通过:props="{label:'title',children:'children'}"来实现
<template><div style="width: 400px"><ui-tree :data="data" node-key="label" v-model="form"></ui-tree></div>
</template><script>
import UiTree from "@/components/ui-tree.vue";
export default {name: "tree",components: {UiTree},data() {return {form: ['菜单管理'],data: [{label: '系统管理',children:[{label:'用户管理', children:[{label:'菜单管理'},{label:'按钮管理'},{label:'权限管理'}]},{label:'角色管理'}]},{label:'文档管理',children:[{label:'目录管理'},{label:'图片管理'},{label:'文件管理'}]}]}},
}
</script>