1. 使用场景
- 权限系统中独立选择菜单和按钮
- 商品分类中选择部分类别和具体商品
- 组织架构中选择部分部门和个别成员
2. 特点
- 独立选择:
可以单独选中父节点而不选中其子节点
可以单独选中子节点而不选中其父节点 - 独立删除:
删除父节点不会影响其子节点的选中状态
删除子节点不会影响其父节点的选中状态 - 展示清晰:
已选择列表明确显示每个节点的类型
每个节点有独立的删除按钮
3. 完成代码
<template>
<div class="container">
<div class="tree-container">
<Tree
v-model:checkedKeys="checkedKeys"
checkable
:tree-data="treeData"
:field-names="fieldNames"
:checkStrictly="true"
@check="handleTreeCheck"
/>
</div>
<div class="selected-container">
<div
v-for="item in selectedItems"
:key="item.value"
class="selected-item"
>
<span>{{ item.title }}</span>
<span class="node-type">{{ item.isLeaf ? '叶子节点' : '父节点' }}</span>
<Button type="text" danger @click="removeItem(item)">
<template
</Button>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { Tree, Button } from 'ant-design-vue';
import { DeleteOutlined } from '@ant-design/icons-vue';
// 树形数据示例
const treeData = ref([
{
title: '父节点1',
value: '0-0',
children: [
{
title: '子节点1',
value: '0-0-0',
},
{
title: '子节点2',
value: '0-0-1',
},
],
},
{
title: '父节点2',
value: '0-1',
children: [
{
title: '子节点3',
value: '0-1-0',
},
{
title: '子节点4',
value: '0-1-1',
},
],
},
]);
const fieldNames = ref({
children: 'children',
title: 'title',
key: 'value',
});
// 选中的keys(父子节点解耦)
const checkedKeys = ref([]);
// 已选中的项目(包含父节点和子节点)
const selectedItems = ref([]);
// 处理树节点选中事件
const handleTreeCheck = (checkedKeysValue, { checkedNodes, node }) => {
// 更新已选中的项目(包含父节点和子节点)
selectedItems.value = checkedNodes.map((node) => ({
value: node.value,
title: node.title,
isLeaf: !node.children || node.children.length === 0,
}));
};
// 删除已选中的项目
const removeItem = (item) => {
// 从已选列表中移除
selectedItems.value = selectedItems.value.filter(
(i) => i.value !== item.value,
);
// 从树形组件的选中状态中移除
checkedKeys.value = checkedKeys.value.filter((key) => key !== item.value);
// 如果是父节点被删除,不需要自动删除子节点(因为父子解耦)
// 如果是子节点被删除,也不需要处理父节点(因为父子解耦)
};
// 监听selectedItems变化,同步到checkedKeys
watch(
selectedItems,
(newVal) => {
checkedKeys.value = newVal.map((item) => item.value);
},
{ deep: true },
);
// 初始化示例数据(可选)
checkedKeys.value = ['0-0', '0-0-1']; // 可以独立选中父节点和子节点
</script>
<style scoped>
.container {
display: flex;
height: 100%;
}
.tree-container {
width: 50%;
padding: 16px;
overflow: auto;
border-right: 1px solid
}
.selected-container {
width: 50%;
padding: 16px;
overflow: auto;
}
.selected-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px;
margin-bottom: 8px;
border: 1px solid
border-radius: 4px;
}
.node-type {
margin: 0 8px;
font-size: 12px;
color:
}
</style>