Element ui树选择
TreeSelect.vue
<template><div class="tree-select"><el-popoverplacement="bottom-start"width="300"trigger="click"v-model="visible"popper-class="tree-select-popper"><!-- 搜索框 --><el-inputv-model="filterText"placeholder="搜索..."size="small"clearablestyle="margin-bottom: 8px;"/><!-- 树形结构 --><el-treeref="tree":data="data":props="defaultProps"node-key="id":show-checkbox="multiple"highlight-current:filter-node-method="filterNode"@node-click="handleNodeClick"@check="handleCheck"default-expand-all/><!-- 输入框触发器 --><el-inputslot="reference"v-model="selectedLabels":placeholder="placeholder"readonlysuffix-icon="el-icon-caret-bottom"/></el-popover></div>
</template><script>
export default {name: "TreeSelect",props: {value: [String, Number, Array], // 绑定值data: { type: Array, required: true }, // 树数据multiple: { type: Boolean, default: false }, // 是否多选placeholder: { type: String, default: "请选择" }},data() {return {visible: false,filterText: "",selectedLabels: "",defaultProps: { children: "children", label: "label" }};},watch: {filterText(val) {this.$refs.tree.filter(val);},value: {immediate: true,handler(val) {if (!val) {this.selectedLabels = "";return;}if (this.multiple && Array.isArray(val)) {const nodes = this.$refs.tree? this.$refs.tree.getCheckedNodes(): [];this.selectedLabels = this.formatGroupedLabels(nodes);} else {const node = this.findNodeById(this.data, val);this.selectedLabels = node ? node.label : "";}}}},methods: {filterNode(value, data) {if (!value) return true;return data.label.indexOf(value) !== -1;},handleNodeClick(node) {if (!this.multiple) {this.selectedLabels = node.label;this.$emit("input", node.id);this.visible = false; // 单选时选完收起}},handleCheck() {if (this.multiple) {const nodes = this.$refs.tree.getCheckedNodes();this.selectedLabels = this.formatGroupedLabels(nodes);this.$emit("input", nodes.map(n => n.id));}},// 格式化多选显示:按顶级父节点分组formatGroupedLabels(nodes) {const grouped = {};nodes.forEach(node => {const topParent = this.findTopParent(this.data, node.id);if (topParent) {// 跳过顶级父节点本身if (node.id === topParent.id) return;if (!grouped[topParent.label]) {grouped[topParent.label] = [];}grouped[topParent.label].push(node.label);}});return Object.keys(grouped).map(parent => `${parent}: ${grouped[parent].join(", ")}`).join("; ");},// 找到某个节点的顶级父节点findTopParent(list, id, parent = null) {for (let item of list) {if (item.id === id) return parent || item;if (item.children) {const found = this.findTopParent(item.children, id, parent || item);if (found) return found;}}return null;},findNodeById(list, id) {for (let item of list) {if (item.id === id) return item;if (item.children) {const found = this.findNodeById(item.children, id);if (found) return found;}}return null;}}
};
</script><style scoped>
.tree-select {width: 300px;
}
.tree-select-popper {max-height: 300px;overflow: auto;
}
</style>
使用示例
<template><div><h3>单选</h3><tree-select v-model="singleValue" :data="treeData" /><h3 style="margin-top:20px;">多选</h3><tree-select v-model="multiValue" :data="treeData" multiple /></div>
</template><script>
import TreeSelect from "./TreeSelect.vue";export default {components: { TreeSelect },data() {return {singleValue: null,multiValue: [],treeData: [{id: 1,label: "水果",children: [{id: 11,label: "苹果",children: [{ id: 111, label: "红富士" },{ id: 112, label: "奶苹果" }]},{ id: 12, label: "香蕉" }]},{id: 2,label: "蔬菜",children: [{ id: 21, label: "西红柿" },{ id: 22, label: "黄瓜" }]}]};}
};
</script>
效果
