後端返回兩組數據:
allMenus:完整菜單樹 (含 id、parentId、name、children...)userRights:用戶擁有的權限菜單 ID 集合 (如 [101, 103, 105])前端需渲染樹形組件 (如 Element UI el-tree、zTree、Ant Design Tree),並自動勾選用戶擁有的節點。
// 方案 A:兩個獨立列表 (原始回答思路)
{
"allMenus": [{ "id": 1, "parentId": 0, "name": "系統管理", "children": [...] }],
"userRightIds": [101, 103, 105]
}
// 方案 B:單樹節點內嵌權限標記 (前端更省事)
{
"menuTree": [{
"id": 1,
"parentId": 0,
"name": "系統管理",
"checked": false, // 當前用戶是否擁有
"halfChecked": false, // 子節點部分擁有
"children": [...]
}]
}
// 假設使用 Vue + Element UI
export default {
props: {
allMenus: Array,
userRightIds: Array
},
data() {
return {
defaultCheckedKeys: [], // 完全勾選的節點 id
defaultExpandedKeys: [] // 默認展開的節點 id
};
},
mounted() {
this.initCheckedKeys(this.allMenus);
},
methods: {
// 遞歸遍歷樹,收集用戶擁有的節點 id
initCheckedKeys(nodes) {
nodes.forEach(node => {
if (this.userRightIds.includes(node.id)) {
this.defaultCheckedKeys.push(node.id);
}
if (node.children?.length) {
this.initCheckedKeys(node.children);
}
});
}
}
};
若樹組件不支持原生聯動,需自行實現:
// 1. 父節點變化 → 遞歸設置所有後代
onParentCheck(node, checked) {
this.setDescendantsChecked(node, checked);
},
// 2. 子節點變化 → 向上更新父節點狀態 (全選/半選/未選)
onChildCheck(node, checked) {
this.updateAncestorsStatus(node.parentId);
},
// 3. 計算父節點狀態
updateAncestorsStatus(parentId) {
const parent = this.findNode(parentId);
if (!parent) return;
const childIds = parent.children.map(c => c.id);
const checkedCount = childIds.filter(id => this.defaultCheckedKeys.includes(id)).length;
if (checkedCount === 0) {
this.uncheckParent(parentId);
} else if (checkedCount === childIds.length) {
this.checkParent(parentId); // 遞歸向上
} else {
this.halfCheckParent(parentId); // 半選狀態
}
}
Set<Long> 存儲,O(1) 判斷包含Map<Long, Node> + 一次遍歷掛載 children,避免遞歸查庫load(currentNode, resolve) 動態請求子節點「後端返回樹 + 權限 ID 扁平列表,前端遞歸匹配生成 checkedKeys」是最通用、解耦的方案。掌握樹形數據的遞歸遍歷與狀態同步,是前端必修課。