Vue3树形表格实现求助:修复parent_id关联children的递归组件
树形表格递归组件实现修复方案
问题描述
需要实现树形表格,现有数据包含parent_id和待构建的children数组,数据库返回的数据结构为:{name: string, employee_id: number, subordinates: number[], boss_id: number}(数组形式对应该字段集合)。当前递归组件实现存在逻辑问题,需修复基于children数组的树形渲染逻辑。
现有代码问题分析
- Tree组件中
treeData为模拟固定结构,未对接服务端扁平数据的树形转换逻辑;模板中绑定的items未定义,数据指向错误。 - TreeItem组件中,判断是否含子节点用了
subordinates字段,但递归渲染时遍历的是model.children,两者未关联;直接修改props.model.children违反Vue单向数据流原则。
修复步骤
1. 数据转换:扁平数据转树形结构
先把服务端返回的扁平员工数据,通过boss_id与employee_id的关联,转换成带children的树形结构:
function convertToTree(flatEmployees) { const employeeMap = new Map(); // 先将所有员工存入Map,方便快速查找 flatEmployees.forEach(emp => { employeeMap.set(emp.employee_id, { ...emp, children: [] }); }); const tree = []; // 遍历匹配父节点,构建树形结构 flatEmployees.forEach(emp => { if (emp.boss_id) { const boss = employeeMap.get(emp.boss_id); boss?.children.push(employeeMap.get(emp.employee_id)); } else { // 无boss_id的为根节点 tree.push(employeeMap.get(emp.employee_id)); } }); return tree; }
2. 修复Tree组件
修改Tree组件,对接服务端数据并完成树形转换,修正模板数据绑定:
<script setup> import { ref, watchEffect } from 'vue'; import TreeItem from './TreeItem.vue'; const treeData = ref([]); // 模拟服务端数据请求,实际替换为接口调用 async function fetchDataFromServer() { const flatEmployees = [ { name: '总经理', employee_id: 1, subordinates: [2,3], boss_id: null }, { name: '部门经理A', employee_id: 2, subordinates: [4], boss_id: 1 }, { name: '部门经理B', employee_id: 3, subordinates: [], boss_id: 1 }, { name: '员工小甲', employee_id: 4, subordinates: [], boss_id: 2 }, ]; treeData.value = convertToTree(flatEmployees); } // 数据转换函数 function convertToTree(flatEmployees) { const employeeMap = new Map(); flatEmployees.forEach(emp => { employeeMap.set(emp.employee_id, { ...emp, children: [] }); }); const tree = []; flatEmployees.forEach(emp => { if (emp.boss_id) { const boss = employeeMap.get(emp.boss_id); boss?.children.push(employeeMap.get(emp.employee_id)); } else { tree.push(employeeMap.get(emp.employee_id)); } }); return tree; } watchEffect(fetchDataFromServer); </script> <template> <ul> <TreeItem class="item" v-for="item in treeData" :key="item.employee_id" :model="item" ></TreeItem> </ul> </template>
3. 修复TreeItem组件
统一子节点判断逻辑,遵循单向数据流,通过事件通知父组件修改数据:
<script setup> import { ref, computed } from 'vue'; const props = defineProps({ model: { type: Object, required: true } }); const isOpen = ref(false); // 基于转换后的children数组判断是否有子节点 const hasChildren = computed(() => { return props.model.children?.length > 0; }); function toggle() { isOpen.value = !isOpen.value; } // 通过emit通知父组件添加子节点,避免直接修改props function addChildren() { if (!hasChildren.value) { emit('add-children', props.model.employee_id); } } const emit = defineEmits(['add-children']); </script> <template> <li> <div :class="{ bold: hasChildren }" @click="toggle" @dblclick="addChildren" > {{ model.name }} <span v-if="hasChildren">[{{ isOpen ? '-' : '+' }}]</span> </div> <ul v-show="isOpen" v-if="hasChildren"> <TreeItem class="item" v-for="child in model.children" :key="child.employee_id" :model="child" @add-children="addChildren" ></TreeItem> </ul> </li> </template> <style scoped> .bold { font-weight: bold; cursor: pointer; } .item { margin-left: 16px; } </style>
关键修复点说明
- 数据结构统一:通过Map快速匹配父节点,将扁平数据转换为带
children的树形结构,解决parent_id与子节点的关联问题。 - 单向数据流遵循:TreeItem组件不再直接修改props数据,通过emit事件通知父组件处理数据变更,符合Vue设计规范。
- 渲染逻辑一致:判断子节点存在性与递归渲染的数据源统一为
children数组,避免逻辑混乱。
内容的提问来源于stack exchange,提问作者sow cantli
相关产品推荐
相关产品推荐

