Vue3+TS+Element-Plus+el-tree创建树节点

发布于:2025-08-30 ⋅ 阅读:(16) ⋅ 点赞:(0)

1、一级树

应用效果:

代码:MaterialCategory.vue

<script setup lang="ts" name="MaterialCategory">
......
// 创建树(一级树)
const createTree = (dataList: IMaterialCategory[]) => {
  // 将原始数据转换成树节点数据
  let data: IMaterialCategoryTree[] = dataList.map((item) => {
    return {
      label: item.categoryName,
      value: item.categoryNo
    };
  });

  // 添加根节点
  treeData.value.push({
    label: "物资分类",
    value: "",
    children: data
  });
};
......
</script>

<template>
  <el-container class="layout-container">
    <el-aside class="aside">
      <el-scrollbar>
        <el-tree
          class="tree-horizontal-scrollbar"
          :data="treeData"
          :default-expand-all="true"
          :highlight-current="true"
          :expand-on-click-node="false"
          :indent="15"
          @node-click="onTreeNodeClick">
          <!-- 自定义节点内容,点击的节点字体变色加粗 -->
          <!-- 动态样式:通过<template #default>插槽自定义节点内容,使用:style绑定根据当前选择的节点值currentNode.value动态设置color和fontWeight -->
          <template #default="{ node, data }">
            <span
              :style="{
                color: currentNode?.value === data.value ? `#409EFF` : `#606266`,
                fontWeight: currentNode?.value === data.value ? `bold` : `normal`
              }">
              {{ node.label }}
            </span>
          </template>
        </el-tree>
      </el-scrollbar>
    </el-aside>
    ......
  </el-container>
</template>

2、二级树

应用效果1:

代码2:MaterialInfo.vue

<script setup lang="ts" name="MaterialInfo">
......
// 创建树
const createTree = (dataList: IMaterialCategory[]) => {
  // 将原始数据转换成树节点数据
  let data: IMaterialCategoryTree[] = dataList.map((item) => {
    return {
      label: item.categoryName,
      value: item.categoryNo
    };
  });

  // 遍历物资类别列表,过滤出一级节点数据
  let data1 = data.filter((item) => {
    if (item.value.length === 2) return item;
  });
  // 遍历物资类别列表,过滤出二级节点数据
  let data2 = data.filter((item) => {
    if (item.value.length === 4) return item;
  });

  // 将一级节点数据与二级节点数据组合成树节点数据
  let dataChild = data1.map((item1) => {
    // 遍历二级节点数据,过滤出对应一级节点的数据
    let dataChild2 = data2.filter((item2) => {
      if (item2.value.substring(0, 2) === item1.value) return item2;
    });
    return {
      label: item1.label,
      value: item1.value,
      children: dataChild2
    };
  });

  // 添加根节点
  treeData.value.push({
    label: "物资分类",
    value: "",
    children: dataChild
  });
};
......
</script>

<template>
  <el-container class="layout-container">
    <el-aside class="aside">
      <el-scrollbar>
        <el-tree
          class="tree-horizontal-scrollbar"
          :data="treeData"
          :default-expand-all="true"
          :highlight-current="true"
          :expand-on-click-node="false"
          :indent="15"
          @node-click="onTreeNodeClick">
          <!-- 自定义节点内容,点击的节点字体变色加粗 -->
          <!-- 动态样式:通过<template #default>插槽自定义节点内容,使用:style绑定根据当前选择的节点值currentNode.value动态设置color和fontWeight -->
          <template #default="{ node, data }">
            <span
              :style="{
                color: currentNode?.value === data.value ? `#409EFF` : `#606266`,
                fontWeight: currentNode?.value === data.value ? `bold` : `normal`
              }">
              {{ node.label }}
            </span>
          </template>
        </el-tree>
      </el-scrollbar>
    </el-aside>
    ......
  </el-container>
</template>

<style scoped lang="scss">
// 树节点
:deep(.el-tree-node__content) {
  height: 32px;
}
// 树节点展开图标
:deep(.el-tree-node__content > .el-tree-node__expand-icon) {
  padding: 0;
}
</style>


应用效果2:

代码2:LedgerDetail.vue

<script setup lang="ts" name="Ledger">
......
import { useMaterialCategoryTree } from "@/hooks/useMaterialCategoryTree";

// 物资分类树 hook
const { materialCategoryTreeData, defaultProps } = useMaterialCategoryTree();
......
</script>

<template>
  <el-container class="layout-container">
    <el-aside class="aside">
      <el-scrollbar>
        <el-tree
          class="tree-horizontal-scrollbar"
          ref="materialCategoryTreeRef"
          :data="materialCategoryTreeData"
          node-key="value"
          :show-checkbox="true"
          :highlight-current="true"
          :default-expand-all="true"
          :check-on-click-node="true"
          :expand-on-click-node="false"
          :props="defaultProps"
          @check="handleCheck" />
      </el-scrollbar>
    </el-aside>
    ......
  </el-container>
</template>

<style scoped lang="scss">
// 树节点
:deep(.el-tree-node__content) {
  height: 32px;
}
// 树节点展开图标
:deep(.el-tree-node__content > .el-tree-node__expand-icon) {
  padding: 0;
}
</style>


物资分类树 hook:useMaterialCategoryTree.ts

import { onMounted, ref } from "vue";
import type { IMaterialCategory, IMaterialCategoryTree } from "@/views/warehouse/types";
import { materialCategoryService } from "@/api/warehouse";

/**
 * 物资分类树 hook
 * @returns
 */
export const useMaterialCategoryTree = () => {
  // 物资分类列表数据
  const materialCategoryListData = ref<IMaterialCategory[]>([]);
  // 物资分类树数据
  const materialCategoryTreeData = ref<IMaterialCategoryTree[]>([]);
  const defaultProps = {
    children: "children",
    label: "label"
  };

  // 获取物资分类列表数据
  const fetchMaterialCategoryListData = async () => {
    // 获取物资分类列表数据
    let result = await materialCategoryService();
    materialCategoryListData.value = result.data;
  };

  // 创建物资分类树数据
  const createMaterialCategoryTreeData = (dataList: IMaterialCategory[]) => {
    // 将原始数据转换成树节点数据
    let data: IMaterialCategoryTree[] = dataList.map((item) => {
      return {
        label: item.categoryName,
        value: item.categoryNo
      };
    });

    // 遍历物资分类列表数据,过滤出一级节点数据
    let data1 = data.filter((item) => {
      if (item.value.length === 2) return item;
    });
    // 遍历物资分类列表数据,过滤出二级节点数据
    let data2 = data.filter((item) => {
      if (item.value.length === 4) return item;
    });

    // 将一级节点数据与二级节点数据组合成树节点数据
    let dataChild = data1.map((item1) => {
      // 遍历二级节点数据,过滤出对应一级节点的数据
      let dataChild2 = data2.filter((item2) => {
        if (item2.value.substring(0, 2) === item1.value) return item2;
      });
      return {
        label: item1.label,
        value: item1.value,
        children: dataChild2
      };
    });

    // 添加根节点
    materialCategoryTreeData.value.push({
      label: "物资分类",
      value: "",
      children: dataChild
    });
  };

  onMounted(async () => {
    // 获取物资分类列表数据
    await fetchMaterialCategoryListData();
    // 创建物资分类树数据
    createMaterialCategoryTreeData(materialCategoryListData.value);
  });

  return { materialCategoryTreeData, defaultProps };
};

3、并级树

应用效果:

代码:QualityFileCategoryTree.vue

<script setup lang="ts" name="QualityFileCategoryTree">
......
// 创建树
const createTree = async (dataList: IFileCategory[]) => {
  // 文件小类列表(对象数组),数据可能会有重复
  let smallCategoryList = dataList
    .filter((item) => item.smallCategoryNo && item.smallCategoryName)
    .map((item) => {
      return {
        smallCategoryNo: item.smallCategoryNo!,
        smallCategoryName: item.smallCategoryName!
      };
    });

  // 对象数组去重,通过对象所有属性值都相同去重,使用 JSON 序列化与 Set(适用扁平对象,简单高效)
  smallCategoryList = deduplicateObjectArray(smallCategoryList);

  // 遍历文件小类列表,将原始数据转换成树节点数据
  treeData.value = smallCategoryList.map((item1) => {
    // 链式调用,.filter().map().filter()
    return {
      value: item1.smallCategoryNo,
      label: item1.smallCategoryName,
      children: dataList
        // 过滤出对应一级节点的二级节点数据(文件细类)
        .filter((item2) => item2.smallCategoryNo === item1.smallCategoryNo)
        // 将文件细类数据转换为树节点数据
        .map((item) => ({
          value: item.detailCategoryNo as string,
          label: item.detailCategoryName as string
        }))
        // 过滤出有效的数据(value 和 label 值不为空)
        .filter((item) => item.value && item.label)
    };
  });
};
......
</script setup lang="ts">

<template>
  <el-scrollbar>
    <!-- el-scrollbar 显示横向滚动条 -->
    <div class="scroll-container">
      <el-tree
        ref="treeRef"
        :data="treeData"
        node-key="value"
        :default-expand-all="true"
        :highlight-current="true"
        :expand-on-click-node="false"
        :indent="15"
        @node-click="onTreeNodeClick">
        <!-- 自定义节点内容,点击的节点字体变色加粗 -->
        <!-- 动态样式:通过<template #default>插槽自定义节点内容,使用:style绑定根据当前选择的节点值currentNode.value动态设置color和fontWeight -->
        <template #default="{ node, data }">
          <span
            :style="{
              color: currentNode?.value === data.value ? `#409EFF` : `#606266`,
              fontWeight: currentNode?.value === data.value ? `bold` : `normal`
            }">
            {{ node.label }}
          </span>
        </template>
      </el-tree>
    </div>
  </el-scrollbar>
</template>

<style scoped lang="scss">
// el-scrollbar 显示横向滚动条
.scroll-container {
  display: flex;
  width: max-content;
}
</style>

网站公告

今日签到

点亮在社区的每一天
去签到