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>