javascript
后端数据
├─ 平铺数组 → listToTree → deptTree
└─ 树形数组 → 直接使用
<el-tree-select>
├─ v-model = 选中id
├─ :data = deptTree
├─ :props = 字段映射
└─ @change = 递归拿节点 → 回显名称
1. 完整 mini Demo(可运行)
javascript
<template>
<el-form :model="form" label-width="80">
<el-form-item label="部门" prop="deptId" :rules="[{ required: true }]">
<el-tree-select
v-model="form.deptId"
:data="deptTree"
placeholder="请选择部门"
check-strictly
:props="{ label: 'name', value: 'id' }"
clearable
@change="onChange" />
</el-form-item>
</el-form>
</template>
<script setup lang="ts">
import { ref } from 'vue';
// 模拟平铺数据
const flat = [
{ id: 1, name: '总部', parentId: 0 },
{ id: 11, name: '技术部', parentId: 1 },
{ id: 12, name: '财务部', parentId: 1 }
];
// 工具:转树
const listToTree = (list: any[], root = 0, key = 'id', parentKey = 'parentId') => {
const map: any = {};
list.forEach(item => map[item[key]] = item);
const tree: any[] = [];
list.forEach(item => {
const parent = map[item[parentKey]];
if (parent) (parent.children ||= []).push(item);
else if (item[parentKey] === root) tree.push(item);
});
return tree;
};
const deptTree = ref(listToTree(flat));
const form = ref({ deptId: null as number | null, deptName: '' });
const findNodeDeep = (tree: any[] | undefined, id: number): any | undefined => {
if (!tree) return;
for (const node of tree) {
if (node.id === id) return node;
const found = findNodeDeep(node.children, id);
if (found) return found;
}
};
const onChange = (id: number) => {
const node = findNodeDeep(deptTree.value, id);
form.value.deptName = node?.name ?? '';
};
</script>
1. 第 1 步:先看清后端数据长啥样
① 平铺数组(最常见)
javascript
[
{ id: 1, name: '总部', parentId: 0 },
{ id: 11, name: '技术部', parentId: 1 },
{ id: 12, name: '财务部', parentId: 1 }
]
→ 需要 转树。
② 已经是树
javascript
[
{ id: 1, name: '总部', children: [
{ id: 11, name: '技术部', children: [] },
{ id: 12, name: '财务部', children: [] }
]}
]
→ 直接可用。
2. 第 2 步:平铺转树工具函数
javascript
function listToTree(list: any[], root = 0, key = 'id', parentKey = 'parentId', childKey = 'children') {
const map: any = {};
list.forEach(item => map[item[key]] = item);
const tree: any[] = [];
list.forEach(item => {
const parent = map[item[parentKey]];
if (parent) {
(parent[childKey] ||= []).push(item);
} else if (item[parentKey] === root) {
tree.push(item);
}
});
return tree;
}
用法:
javascript
const deptTree = listToTree(flatList); // 得到 ElementPlus 直接能用的数组
3. 第 3 步:选对组件 & 记住 4 个核心属性
组件 | 属性 | 含义 |
---|---|---|
<el-tree-select> |
v-model |
绑定 选中值 (一般是 id ) |
:data |
树形数组 | |
:props |
字段映射 { label: 'name', value: 'id', children: 'children' } |
|
@change |
选中变化回调,参数就是 value |
html
<el-tree-select
v-model="form.deptId"
:data="deptTree"
placeholder="请选择部门"
check-strictly
:props="{ label: 'name', value: 'id' }"
clearable
@change="onChange" />
4. 第 4 步:选中后拿"名称"或其他字段
组件只返回 id
,要名称 → 递归找节点。
javascript
function findNodeDeep(tree: any[] | undefined, id: number): any | undefined {
if (!tree) return;
for (const node of tree) {
if (node.id === id) return node;
const found = findNodeDeep(node.children, id);
if (found) return found;
}
}
const onChange = (id: number) => {
const node = findNodeDeep(deptTree, id);
form.deptName = node?.name ?? ''; // 回显名称
form.deptId = id; // 保存 id
};
5. 第 5 步:默认值 / 回显(编辑场景)
javascript
// 新增:空值
form.deptId = null;
// 编辑:把后端返回的 id 丢进去即可
form.deptId = row.deptId; // 组件会自动高亮对应节点
6. 第 6 步:校验 & 清空
场景 | 说明 |
---|---|
必填 | <el-form-item label="部门" prop="deptId" :rules="[{ required: true, message: '请选择' }]"> |
清空 | 组件自带 clearable ,清空后 form.deptId = null ,无需额外处理 |