基于el-tree实现选中数据重组为树形结构的完整方案

基于el-tree实现选中数据重组为树形结构的完整方案

在复杂业务系统中,树形结构数据的选择与重组是常见需求。本文将以el-tree组件为例,详细介绍如何实现选中数据重组为树形结构的技术方案,涵盖数据初始化、组件配置、选中节点获取及树形重组等完整流程。

一、数据结构初始化

树形数据结构需要满足特定格式要求,通常包含节点标识、显示文本和子节点数组三个核心字段。以下是标准树形数据示例:

  1. const treeData = [
  2. {
  3. id: 1,
  4. label: '根节点1',
  5. children: [
  6. { id: 2, label: '子节点1-1' },
  7. {
  8. id: 3,
  9. label: '子节点1-2',
  10. children: [
  11. { id: 4, label: '孙节点1-2-1' }
  12. ]
  13. }
  14. ]
  15. },
  16. {
  17. id: 5,
  18. label: '根节点2',
  19. children: [
  20. { id: 6, label: '子节点2-1' }
  21. ]
  22. }
  23. ];

数据结构要求:

  1. 每个节点必须包含唯一标识字段(如id)
  2. 父节点通过children数组包含子节点
  3. 节点文本通过label字段显示
  4. 层级深度不限制,但建议不超过5级

二、el-tree组件配置

在Vue组件中配置el-tree需要设置多个关键属性:

  1. <template>
  2. <el-tree
  3. :data="treeData"
  4. show-checkbox
  5. node-key="id"
  6. ref="treeRef"
  7. @check-change="handleCheckChange"
  8. :props="treeProps"
  9. ></el-tree>
  10. </template>
  11. <script>
  12. export default {
  13. data() {
  14. return {
  15. treeData: [], // 初始化空数据
  16. treeProps: {
  17. children: 'children',
  18. label: 'label'
  19. }
  20. };
  21. },
  22. mounted() {
  23. this.initTreeData(); // 组件挂载后初始化数据
  24. },
  25. methods: {
  26. initTreeData() {
  27. // 这里可以添加异步数据加载逻辑
  28. this.treeData = treeData; // 加载准备好的树形数据
  29. },
  30. handleCheckChange(data, checked) {
  31. // 选中状态变化处理
  32. }
  33. }
  34. };
  35. </script>

关键配置说明:

  1. show-checkbox:启用复选框功能
  2. node-key:指定节点唯一标识字段
  3. ref:设置组件引用,便于后续操作
  4. props:配置子节点字段名和显示文本字段名
  5. @check-change:绑定选中状态变化事件

三、选中节点获取与处理

通过组件引用可以获取完整的选中节点信息:

1. 获取选中节点

  1. methods: {
  2. getSelectedNodes() {
  3. const tree = this.$refs.treeRef;
  4. // 获取所有选中节点(包含半选状态)
  5. const checkedNodes = tree.getCheckedNodes();
  6. // 获取严格选中节点(不包含半选状态)
  7. const strictlyCheckedNodes = tree.getCheckedNodes(true);
  8. return {
  9. allSelected: checkedNodes,
  10. strictSelected: strictlyCheckedNodes
  11. };
  12. }
  13. }

2. 节点数据处理

获取的原始节点数据包含完整结构,通常需要根据业务需求进行过滤和转换:

  1. processSelectedNodes(nodes) {
  2. return nodes.map(node => ({
  3. id: node.id,
  4. label: node.label,
  5. // 可以添加业务需要的额外字段
  6. customField: node.someProperty || 'default'
  7. })).filter(node => node.id !== undefined);
  8. }

四、选中数据重组为树形结构

重组选中数据为树形结构是核心需求,需要处理父子节点关系:

1. 基础重组方法

  1. buildSelectedTree(nodes) {
  2. // 创建id到节点的映射
  3. const nodeMap = {};
  4. nodes.forEach(node => {
  5. nodeMap[node.id] = { ...node, children: [] };
  6. });
  7. // 构建树形结构
  8. const tree = [];
  9. nodes.forEach(node => {
  10. if (node.parentId) { // 假设原始数据有parentId字段
  11. if (nodeMap[node.parentId]) {
  12. nodeMap[node.parentId].children.push(nodeMap[node.id]);
  13. }
  14. } else {
  15. tree.push(nodeMap[node.id]);
  16. }
  17. });
  18. return tree;
  19. }

2. 优化后的重组方案

更完善的实现需要考虑:

  1. 原始数据可能没有parentId字段
  2. 需要处理选中节点的完整路径
  3. 保持原始树形结构的层级关系
  1. buildOptimizedSelectedTree(nodes, originalTree) {
  2. // 创建原始节点的完整路径映射
  3. const pathMap = {};
  4. const buildPaths = (tree, path = []) => {
  5. tree.forEach(node => {
  6. const currentPath = [...path, node.id];
  7. pathMap[node.id] = currentPath;
  8. if (node.children) {
  9. buildPaths(node.children, currentPath);
  10. }
  11. });
  12. };
  13. buildPaths(originalTree);
  14. // 构建选中节点的树形结构
  15. const selectedMap = {};
  16. nodes.forEach(node => {
  17. selectedMap[node.id] = { ...node, children: [] };
  18. });
  19. const result = [];
  20. nodes.forEach(node => {
  21. const path = pathMap[node.id];
  22. if (path.length > 1) {
  23. // 找到父节点
  24. const parentId = path[path.length - 2];
  25. if (selectedMap[parentId] && nodes.some(n => n.id === parentId)) {
  26. selectedMap[parentId].children.push(selectedMap[node.id]);
  27. } else {
  28. result.push(selectedMap[node.id]);
  29. }
  30. } else {
  31. result.push(selectedMap[node.id]);
  32. }
  33. });
  34. return result;
  35. }

五、完整实现示例

综合以上内容,完整的实现方案如下:

  1. <template>
  2. <div>
  3. <el-tree
  4. :data="treeData"
  5. show-checkbox
  6. node-key="id"
  7. ref="treeRef"
  8. @check-change="handleCheckChange"
  9. :props="treeProps"
  10. ></el-tree>
  11. <div class="result-area">
  12. <h3>重组后的树结构:</h3>
  13. <pre>{{ formattedSelectedTree }}</pre>
  14. </div>
  15. </div>
  16. </template>
  17. <script>
  18. export default {
  19. data() {
  20. return {
  21. treeData: [],
  22. selectedTree: [],
  23. treeProps: {
  24. children: 'children',
  25. label: 'label'
  26. }
  27. };
  28. },
  29. computed: {
  30. formattedSelectedTree() {
  31. return JSON.stringify(this.selectedTree, null, 2);
  32. }
  33. },
  34. mounted() {
  35. this.initTreeData();
  36. },
  37. methods: {
  38. initTreeData() {
  39. // 模拟异步加载数据
  40. setTimeout(() => {
  41. this.treeData = [
  42. {
  43. id: 1,
  44. label: '根节点1',
  45. children: [
  46. { id: 2, label: '子节点1-1' },
  47. {
  48. id: 3,
  49. label: '子节点1-2',
  50. children: [
  51. { id: 4, label: '孙节点1-2-1' }
  52. ]
  53. }
  54. ]
  55. },
  56. {
  57. id: 5,
  58. label: '根节点2',
  59. children: [
  60. { id: 6, label: '子节点2-1' }
  61. ]
  62. }
  63. ];
  64. }, 300);
  65. },
  66. handleCheckChange() {
  67. this.rebuildSelectedTree();
  68. },
  69. rebuildSelectedTree() {
  70. const checkedNodes = this.$refs.treeRef.getCheckedNodes();
  71. if (checkedNodes.length === 0) {
  72. this.selectedTree = [];
  73. return;
  74. }
  75. // 创建原始节点的路径映射
  76. const pathMap = {};
  77. const buildPaths = (tree, path = []) => {
  78. tree.forEach(node => {
  79. const currentPath = [...path, node.id];
  80. pathMap[node.id] = currentPath;
  81. if (node.children) {
  82. buildPaths(node.children, currentPath);
  83. }
  84. });
  85. };
  86. buildPaths(this.treeData);
  87. // 构建选中节点的映射
  88. const selectedMap = {};
  89. checkedNodes.forEach(node => {
  90. selectedMap[node.id] = { ...node, children: [] };
  91. });
  92. // 构建树形结构
  93. const result = [];
  94. checkedNodes.forEach(node => {
  95. const path = pathMap[node.id];
  96. if (path.length > 1) {
  97. const parentId = path[path.length - 2];
  98. if (selectedMap[parentId]) {
  99. selectedMap[parentId].children.push(selectedMap[node.id]);
  100. } else {
  101. result.push(selectedMap[node.id]);
  102. }
  103. } else {
  104. result.push(selectedMap[node.id]);
  105. }
  106. });
  107. this.selectedTree = result;
  108. }
  109. }
  110. };
  111. </script>
  112. <style>
  113. .result-area {
  114. margin-top: 20px;
  115. padding: 10px;
  116. border: 1px solid #eee;
  117. max-height: 300px;
  118. overflow-y: auto;
  119. }
  120. </style>

六、性能优化建议

  1. 大数据量处理:当树节点超过1000个时,建议使用虚拟滚动技术
  2. 防抖处理:对check-change事件添加防抖,避免频繁重组
  3. 记忆化缓存:对重组结果进行缓存,相同选中状态不重复计算
  4. Web Worker:将重组计算放在Web Worker中执行,避免阻塞UI

七、常见问题解决方案

  1. 选中状态不同步:确保node-key设置为唯一标识字段
  2. 重组后树结构错乱:检查原始数据是否包含循环引用
  3. 性能卡顿:对大数据量使用分页加载或懒加载
  4. 样式异常:检查是否正确引入了element-ui的样式文件

通过以上完整方案,开发者可以轻松实现el-tree选中数据的树形重组功能,满足复杂业务场景下的数据展示和处理需求。