用 computed 实现多层级联选择器的关键是响应式推导节点状态:checkedStatus 根据后代选中情况动态计算全选/半选/未选;点击父节点时同步批量更新整棵子树的 isSelected;路由激活时通过 pathNodes 回溯自动展开高亮;面包屑与全选状态共享同一数据源,结构清晰易扩展。

用 computed 实现多层级联选择器的选中状态,关键是把“当前选中了什么”和“某个节点该显示什么状态”这两件事交给响应式推导,而不是手动维护一堆 isXxx 标志位。核心在于:节点本身不存 isChecked 或 isIndeterminate,而是靠计算属性实时判断。
节点状态由后代决定(父级自动同步)
每个节点的选中状态不是独立设置的,而是根据其所有后代节点的选中情况动态计算:
- 全选(true):所有直接子节点及其全部后代都已选中
- 未选(false):所有后代节点均未选中
- 半选(null):部分后代被选中,但非全部
实现时为每个节点定义 checkedStatus 计算属性,递归遍历 children,统计已选数量与总节点数。注意避免在 computed 中直接修改响应式数据,只做推导。
点击父节点时联动更新后代(受控更新)
用户点击父节点复选框时,并不直接改它自己的状态,而是触发一个方法,批量设置整棵子树的选中值:
- 若父节点当前是未选或半选,点击后设为全选 → 所有后代节点 isSelected 设为 true
- 若已是全选,点击后设为未选 → 所有后代节点 isSelected 设为 false
- v-model 仍绑定到每个节点的 isSelected 字段,保持模板简洁
这个操作必须是同步、可预测的,不能依赖事件冒泡或异步更新,否则 computed 推导会滞后。
路径回溯生成激活态与展开态
当用户通过 URL 进入某叶子节点(如 /category/1005),需自动高亮并展开它的所有父级:
- 用 computed 查找匹配 $route.params.id 的节点,得到 activeNode
- 基于 activeNode 向上遍历 parent(建议预先建好 id → parent 映射表),生成 pathNodes 数组
- 再为每个节点计算 isExpanded:只要其任意后代在 pathNodes 中,就应展开
这样展开逻辑完全由路由驱动,无需额外维护 expandedKeys 数组,也不用监听点击事件去 toggle。
面包屑与全选状态共用同一套数据源
面包屑展示「首页 > 电脑 > 笔记本」和权限管理中的「全选/半选」看似无关,实则共享底层结构:
- 面包屑依赖 activeNode 向上回溯,提取 name 和 link
- 全选状态依赖 checkedStatus 推导,而 checkedStatus 又依赖所有后代的 isSelected
- 二者都基于同一份分类树数据和当前路由/选中状态,天然一致、无冗余
不需要为不同用途复制多份节点数据,也不用写多个 findNode 方法——结构清晰,后续加搜索、过滤、禁用态都容易扩展。


















