实现了完整的父子节点联动逻辑功能:
- 选择父节点时自动选中所有子节点
- 取消父节点时自动取消所有子节点
- 选择子节点时自动选中所有父节点
- 取消子节点时保留父节点的勾选状态
如图效果:


代码部分:
<template><div><el-drawerv-model="dialogVisible"title="菜单权限设置 - 静态案例"size="400"@close="handleClose"class="pl-8 pr-8"><div class="mb-4 text-sm text-gray-500">这是一个静态案例,展示父子节点联动选择功能</div><el-treeref="treeRef":data="menuData"highlight-currentshow-checkbox:check-strictly="true"node-key="id":default-checked-keys="defaultCheckedKeys":props="defaultProps":default-expand-all="true"@check="hanleCheck"@check-change="checkChange"/><template #footer><div class="dialog-footer"><el-button @click="handleClose">取消</el-button><el-button type="primary" @click="submitForm">确认</el-button></div></template></el-drawer></div>
</template>
逻辑部分:
<script lang="ts" setup>
import { ElMessage } from "element-plus";
import { ref } from "vue";// 类型定义
interface MenuItem {id: number;title: string;children?: MenuItem[];
}interface FormInfo {menuIds: number[];
}interface RowList {id?: number;menus_ids?: number[];
}// 静态菜单数据
const menuData: MenuItem[] = [{id: 1,title: "系统管理",children: [{ id: 11, title: "用户管理" },{ id: 12, title: "角色管理" },{ id: 13, title: "权限设置" }]},{id: 2,title: "商品管理",children: [{ id: 21, title: "商品列表" },{ id: 22, title: "商品分类" },{ id: 23, title: "库存管理" }]},{id: 3,title: "订单管理",children: [{ id: 31, title: "订单列表" },{ id: 32, title: "退款管理" },{ id: 33, title: "物流跟踪" }]},{id: 4,title: "数据统计",children: [{ id: 41, title: "销售统计" },{ id: 42, title: "用户分析" },{ id: 43, title: "商品分析" }]}
];// 默认选中的菜单ID
const defaultCheckedKeys = [11, 21, 31];// 响应式数据
const dialogVisible = ref<boolean>(true);
const treeRef = ref();
const selectedMenuIds = ref<number[]>(defaultCheckedKeys);const defaultProps = {children: "children",label: "title",
};// 递归设置子节点的选择状态
const setChildrenChecked = (children: any[], isChecked: boolean) => {children.forEach((child: any) => {treeRef.value!.setChecked(child.id, isChecked, false);if (child.children && child.children.length > 0) {setChildrenChecked(child.children, isChecked);}});
};// 递归选中所有父节点
const setParentChecked = (node: any) => {if (!node.parent) return;// 选中父节点treeRef.value!.setChecked(node.parent, true, false);// 继续递归选中父节点的父节点setParentChecked(node.parent);
};// 处理节点选择
const hanleCheck = (data: MenuItem) => {const isChecked = treeRef.value!.getNode(data).checked;const currentNode = treeRef.value!.getNode(data);if (isChecked) {setParentChecked(currentNode);if (data.children?.length) {setChildrenChecked(data.children, true);}} else {if (data.children?.length && !currentNode.checked) {setChildrenChecked(data.children, false);}}updateSelectedMenuIds();
};const checkChange = () => {updateSelectedMenuIds();
};const updateSelectedMenuIds = () => {const checkedNodes = treeRef.value!.getCheckedNodes(false, false);selectedMenuIds.value = checkedNodes.map(node => node.id);
};const handleClose = () => {dialogVisible.value = false;ElMessage.info("已关闭菜单权限设置");
};const submitForm = () => {updateSelectedMenuIds();ElMessage.success(`权限设置成功!选中的菜单ID: [${selectedMenuIds.value.join(', ')}]`);console.log("选中的菜单ID:", selectedMenuIds.value);
};
</script><style scoped>
.dialog-footer {display: flex;justify-content: flex-end;gap: 12px;margin-top: 16px;
}
</style>