前端vue2实现数据的分级自动排列,展示树状图

1.先看效果图

2.数据结构

直接将样式封装成一个组件后在父页面调用

下面是数据结构

name:是部门名称,id:是部门id,parent:是父级id,通过父级id对数组进行处理,当parent为null时为最高级

[
        {
            "name": "测试总部",
            "id": 1,
            "parent": null
        },
        {
            "name": "测试部门2-2",
            "id": 38,
            "parent": 33
        },
        
    ]

3.数据处理

对数据结构进行处理并存储在treeData数组里面,调用buildTree方法将扁平数组转换成树状结构

data() {
    return {
        treeData: [],
        rawData:[
        {
            "name": "测试部门2-4",
            "id": 40,
            "parent": 35
        },
        {
            "name": "新组织",
            "id": 46,
            "parent": 36
        },
        {
            "name": "测试部门1-6",
            "id": 45,
            "parent": 1
        },
        {
            "name": "测试部门2-8",
            "id": 44,
            "parent": 34
        },
        {
            "name": "测试部门2-7",
            "id": 43,
            "parent": 35
        },
        {
            "name": "测试部门2-6",
            "id": 42,
            "parent": 34
        },
        {
            "name": "测试部门2-5",
            "id": 41,
            "parent": 34
        },
        {
            "name": "测试总部",
            "id": 1,
            "parent": null
        },
        {
            "name": "测试部门2-3",
            "id": 39,
            "parent": 33
        },
        {
            "name": "测试部门2-2",
            "id": 38,
            "parent": 33
        },
        {
            "name": "测试部门2-1",
            "id": 37,
            "parent": 33
        },
        {
            "name": "测试部门1-5",
            "id": 36,
            "parent": 1
        },
        {
            "name": "测试部门1-4",
            "id": 35,
            "parent": 1
        },
        {
            "name": "测试部门1-3",
            "id": 34,
            "parent": 1
        },
        {
            "name": "测试部门1-2",
            "id": 33,
            "parent": 1
        },
        {
            "name": "测试部门1-1",
            "id": 32,
            "parent": 1
        }
    ]
    };
  },


mounted() {
    this.treeData = this.buildTree(this.rawData);
  },



methods: {
// 转换数组 -> 树
    buildTree(data, parentId = null) {
  // 筛选出当前层级的所有节点,这些节点的 parent 属性等于 parentId
  return data
    .filter((item) => item.parent === parentId) // 筛选出当前层级的节点
    .map((item) => ({
      ...item, // 复制当前节点的所有属性
      children: this.buildTree(data, item.id) // 递归调用 buildTree 方法,构建当前节点的子节点
    }));
}
}

转换后的treeData数组是这个样子

[
    {
        "name": "测试总部",
        "id": 1,
        "parent": null,
        "children": [
            {
                "name": "测试部门1-6",
                "id": 45,
                "parent": 1,
                "children": []
            },
            {
                "name": "测试部门1-5",
                "id": 36,
                "parent": 1,
                "children": []
            },
            {
                "name": "测试部门1-4",
                "id": 35,
                "parent": 1,
                "children": [
                    {
                        "name": "测试部门2-4",
                        "id": 40,
                        "parent": 35,
                        "children": []
                    },
                    {
                        "name": "测试部门2-7",
                        "id": 43,
                        "parent": 35,
                        "children": []
                    }
                ]
            },
            {
                "name": "测试部门1-3",
                "id": 34,
                "parent": 1,
                "children": [
                    {
                        "name": "测试部门2-8",
                        "id": 44,
                        "parent": 34,
                        "children": []
                    },
                    {
                        "name": "测试部门2-6",
                        "id": 42,
                        "parent": 34,
                        "children": []
                    },
                    {
                        "name": "测试部门2-5",
                        "id": 41,
                        "parent": 34,
                        "children": []
                    }
                ]
            },
            {
                "name": "测试部门1-2",
                "id": 33,
                "parent": 1,
                "children": [
                    {
                        "name": "测试部门2-3",
                        "id": 39,
                        "parent": 33,
                        "children": []
                    },
                    {
                        "name": "测试部门2-2",
                        "id": 38,
                        "parent": 33,
                        "children": []
                    },
                    {
                        "name": "测试部门2-1",
                        "id": 37,
                        "parent": 33,
                        "children": []
                    }
                ]
            },
            {
                "name": "测试部门1-1",
                "id": 32,
                "parent": 1,
                "children": []
            }
        ]
    }
]

4.将处理好的数据传递给组件

<div ref="treeContainer" class="tree-container">
                <TreeNode v-for="root in treeData" :key="root.id" :node="root" :level="1" />
              </div>

5.设置层级样式

组件页面接受数据,显示数据,想设置后面的层级可以自己加level-X样式

<template>
  <div class="node">
    <!-- 节点主体 -->
    <div class="box" :class="`level-${level}`">
      {{ node.name }}
    </div>

    <!-- 子节点 -->
    <div v-if="node.children && node.children.length" class="children">
      <TreeNode v-for="child in node.children" :key="child.id" :node="child" :level="level + 1" />
    </div>
  </div>
</template>

<script>
export default {
  name: "TreeNode",
  props: {
    node: Object,
    level: { type: Number, default: 1 },
  },
};
</script>

<style scoped>
.node {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* 父节点与子节点整体居中 */
}

.box {
  padding: 10px 10px;
  border-radius: 6px;
  color: white;
  font-weight: bold;
  white-space: nowrap;
  /* width: 100px; */
  text-align: center;
}

/* 各层节点样式(可自行换颜色) */
.level-1 {
  background: #4a90e2;
  /* 蓝色 */
  margin: 10px 0;
}

.level-2 {
  background: #27ae60;
  /* 绿色 */
  margin: 5px;
}

.level-3 {
  background: #16a085;
  /* 灰色 */
  margin: 5px;
}

.level-4 {
  background: #f39c12;
  /* 橙色 */
  margin: 5px;
}

.level-5 {
  background: #9b59b6;
  /* 紫色 */
  margin: 5px;
}

/* 子节点容器 */
.children {
  display: flex;
  justify-content: center;
  flex-wrap: nowrap;
  gap: 10px;
  margin-top: 20px;
  padding: 0 20px;
  /* 防止两边节点贴边 */
}
</style>

6.居中显示

数据过多我们得在页面居中展示,需要设置样式和居中计算

样式

//样式
.tree-container {
  height: 400px;
  overflow-x: auto;
  overflow-y: hidden;
  border: 1px solid #e4e7ed;
  border-radius: 4px;
  /* padding: 10px; */

  display: flex;
  justify-content: flex-start;
  /* ✅ 让根节点组在容器中心 */
  align-items: flex-start;
  box-sizing: border-box;
  padding: 10px 40px;
  /* ⚠️ 关键修复 */
  position: relative;
}

方法

centerTree() {
      const container = this.$refs.treeContainer;
      if (!container) return;

      this.$nextTick(() => {
        const scrollWidth = container.scrollWidth;
        const clientWidth = container.clientWidth;

        // 居中整个内容区域
        container.scrollLeft = (scrollWidth - clientWidth) / 2;
      });
    },

6.完整代码

有父页面和组件

父页面

<template>
  <div ref="treeContainer" class="tree-container">
    <TreeNode v-for="root in treeData" :key="root.id" :node="root" :level="1" />
  </div>
</template>

<script>
export default {
  data() {
    return {
      treeData: [],
      rawData: [
        {
          "name": "测试部门2-4",
          "id": 40,
          "parent": 35
        },
        {
          "name": "新组织",
          "id": 46,
          "parent": 36
        },
        {
          "name": "测试部门1-6",
          "id": 45,
          "parent": 1
        },
        {
          "name": "测试部门2-8",
          "id": 44,
          "parent": 34
        },
        {
          "name": "测试部门2-7",
          "id": 43,
          "parent": 35
        },
        {
          "name": "测试部门2-6",
          "id": 42,
          "parent": 34
        },
        {
          "name": "测试部门2-5",
          "id": 41,
          "parent": 34
        },
        {
          "name": "测试总部",
          "id": 1,
          "parent": null
        },
        {
          "name": "测试部门2-3",
          "id": 39,
          "parent": 33
        },
        {
          "name": "测试部门2-2",
          "id": 38,
          "parent": 33
        },
        {
          "name": "测试部门2-1",
          "id": 37,
          "parent": 33
        },
        {
          "name": "测试部门1-5",
          "id": 36,
          "parent": 1
        },
        {
          "name": "测试部门1-4",
          "id": 35,
          "parent": 1
        },
        {
          "name": "测试部门1-3",
          "id": 34,
          "parent": 1
        },
        {
          "name": "测试部门1-2",
          "id": 33,
          "parent": 1
        },
        {
          "name": "测试部门1-1",
          "id": 32,
          "parent": 1
        }
      ]
    }
  },

  mounted() {
    this.treeData = this.buildTree(this.rawData);
  },

  methods: {
    // 转换数组 -> 树
    buildTree(data, parentId = null) {
      // 筛选出当前层级的所有节点,这些节点的 parent 属性等于 parentId
      return data
        .filter((item) => item.parent === parentId) // 筛选出当前层级的节点
        .map((item) => ({
          ...item, // 复制当前节点的所有属性
          children: this.buildTree(data, item.id) // 递归调用 buildTree 方法,构建当前节点的子节点
        }));
    },
    centerTree() {
      const container = this.$refs.treeContainer;
      if (!container) return;

      this.$nextTick(() => {
        const scrollWidth = container.scrollWidth;
        const clientWidth = container.clientWidth;

        // 居中整个内容区域
        container.scrollLeft = (scrollWidth - clientWidth) / 2;
      });
    },
  }
};
</script>

<style scoped>
.tree-container {
  height: 400px;
  overflow-x: auto;
  overflow-y: hidden;
  border: 1px solid #e4e7ed;
  border-radius: 4px;
  /* padding: 10px; */

  display: flex;
  justify-content: flex-start;
  /* 让根节点组在容器中心 */
  align-items: flex-start;
  box-sizing: border-box;
  padding: 10px 40px;
  /* 关键修复 */
  position: relative;
}
</style>

组件

<template>
  <div class="node">
    <!-- 节点主体 -->
    <div class="box" :class="`level-${level}`">
      {{ node.name }}
    </div>

    <!-- 子节点 -->
    <div v-if="node.children && node.children.length" class="children">
      <TreeNode v-for="child in node.children" :key="child.id" :node="child" :level="level + 1" />
    </div>
  </div>
</template>

<script>
export default {
  name: "TreeNode",
  props: {
    node: Object,
    level: { type: Number, default: 1 },
  },
};
</script>

<style scoped>
.node {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* 父节点与子节点整体居中 */
}

.box {
  padding: 10px 10px;
  border-radius: 6px;
  color: white;
  font-weight: bold;
  white-space: nowrap;
  /* width: 100px; */
  text-align: center;
}

/* 各层节点样式(可自行换颜色) */
.level-1 {
  background: #4a90e2;
  /* 蓝色 */
  margin: 10px 0;
}

.level-2 {
  background: #27ae60;
  /* 绿色 */
  margin: 5px;
}

.level-3 {
  background: #16a085;
  /* 灰色 */
  margin: 5px;
}

.level-4 {
  background: #f39c12;
  /* 橙色 */
  margin: 5px;
}

.level-5 {
  background: #9b59b6;
  /* 紫色 */
  margin: 5px;
}

/* 子节点容器 */
.children {
  display: flex;
  justify-content: center;
  flex-wrap: nowrap;
  gap: 10px;
  margin-top: 20px;
  padding: 0 20px;
  /* 防止两边节点贴边 */
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值