树形控件的勾选设计,先确定“最终选的是哪些对象”,再定父子联动:可选子项全部选中时父级打勾,部分选中时显示半选,一个也没选时为空。展开、收起和移动焦点都不应悄悄改变勾选结果。禁用节点是否计入全选、搜索后选中哪些节点,也要写进同一套规则。
以“给华东门店安排巡检”为例,把整棵树的未选、半选和全选状态并排画出来,比只画一个默认组件更容易发现误选。可以用 Pixso 的界面设计功能整理这些状态,让产品、设计和研发对着同一组门店确认选择结果。

先把巡检对象和组织层级分开
这棵树有三级:华东区,下面是上海、杭州、南京;上海有徐汇店和浦东店,杭州有西湖店和滨江店,南京有鼓楼店。鼓楼店当前停用,不能被加入巡检。业务最终需要的是门店清单,不是“华东区”这个分组名称,因此本例提交的是选中的可用门店,区域和城市只负责组织及批量选择。
这个前提决定了计数方式。选中上海和杭州全部门店时,摘要应是“已选 4 家门店”,不能把华东区、两个城市也算进去变成 7 个对象。停用的鼓楼店仍可留在树里显示,并标明“停用,无法加入”,帮助用户理解为什么原来熟悉的门店不能选择。
另一类树可能允许独立选择部门本身,例如为某个部门建立负责人档案。此时“选中父级”等于选中一个业务对象,不一定包含下级。不要因为两种界面长得相似就共用联动规则。Ant Design 的 Tree 文档也把关联勾选与父子独立勾选区分为不同配置;产品方案应先说明业务语义,再选择相符的实现。
用四次点击推演父子联动
先从全部未选开始。用户勾选徐汇店,上海显示半选,华东区也显示半选;再勾选浦东店,上海变为全选,但华东区仍是半选,因为杭州两家店尚未加入。接着勾选杭州,西湖店、滨江店一起选中,华东区达到“所有可选门店已选中”。最后取消滨江店,杭州和华东区都退回半选,上海保持全选。
| 操作后 | 城市状态 | 华东区状态 | 实际巡检门店 |
|---|---|---|---|
| 仅勾选徐汇店 | 上海半选;杭州未选 | 半选 | 1 家 |
| 再勾选浦东店 | 上海全选;杭州未选 | 半选 | 2 家 |
| 勾选杭州 | 上海、杭州全选 | 全选可用门店 | 4 家 |
| 取消滨江店 | 上海全选;杭州半选 | 半选 | 3 家 |
本例将停用门店排除在“可选集合”之外,因此鼓楼店未选不会让华东区永远处于半选。南京没有可选门店,它的批量勾选也不可用,但仍能展开查看停用原因。若业务需要保留历史已选对象,就应单独显示“已选但已失效”,要求处理后再提交,不能直接套用本例的新增任务规则。
半选图标是集合状态,不是第三种可以单独提交的值。用户点击半选的上海时,本例约定补选该分支内剩余可选门店;再点已全选的上海才取消整个分支。把这条行为写在交付注释里,否则有人会做成“半选一律清空”,有人会做成“半选补齐”,两者都能画出相同静态稿,却会产生不同结果。
别让行高亮同时承担三种含义
箭头表示分支是否展开,复选框表示门店是否加入巡检,焦点边框表示键盘正在操作哪一行。三者各司其职:展开上海只显示下面两家门店;用方向键经过杭州不代表选中杭州;点击复选框才改变任务范围。焦点移开后,已经勾选的门店应继续保留选中标记。
如果点击城市名称会在右侧打开城市信息,名称就是详情入口,不能同时执行全选;展开箭头、文字入口和复选框需要清楚的点击区域。若本页根本没有详情功能,可以让文字辅助切换勾选,但箭头仍只负责展开。不要为了扩大点击面积,让用户点展开图标时意外给整个城市分配任务。
W3C 的树视图模式强调多选树中焦点与选中状态相互独立。设计交付时至少画出“有焦点但未选”“已选但焦点在别处”两种状态;键盘方案可用上下方向键移动、左右方向键展开或收起,空格切换当前节点的选择。焦点提示不能只依赖与选中色相近的一层浅背景。
在同一张画布上检查三份门店清单
在 Pixso 中建立“巡检范围”页面,先画一行包含展开箭头、复选框、门店名称和状态说明的基础结构,再分别整理未选、已选、半选、不可选以及键盘焦点的外观。不要把“停用”仅做成整行透明度降低;停用原因要有可读文字,也不要让它看上去像网络尚未加载完成。
复制成三个画板,保留完全相同的门店和顺序:第一个仅选徐汇店,第二个选中全部 4 家可用门店,第三个从全选中取消滨江店。每个画板下方都写出实际提交清单和数量。这样既能检查父级图标,也能发现“勾选看起来正确、实际提交却少一项”这种单看组件外观无法暴露的问题。

评审时请同事从第一张画板出发,指出“再点哪里能选中杭州”“取消滨江店后哪两个父级应变半选”。有争议的规则写在对应行旁边,例如“半选时点击父级补齐剩余可选项”。图标尺寸、缩进和文本样式可以统一,业务规则则要跟着具体状态保留,不能只写一句笼统的“父子联动”。
搜索和折叠只能改变可见范围
用户在搜索框输入“西湖”,树里可能只剩西湖店及它的祖先路径。此时若仍显示“杭州”的父级复选框,就必须说明点击它是只选搜索结果,还是选杭州全部门店。本例采用更容易辨认的做法:搜索状态提供“选择当前结果”,明确只加入命中的可用门店,不把隐藏的滨江店一起选中;完整城市批量选择仍回到原树操作。
搜索结果不要只显示一行“西湖店”。当不同城市存在同名门店时,补上“华东区 / 杭州”的路径,并用稳定的门店标识确定对象,不能按显示名称去重。取消搜索后,保留先前勾选,同时恢复原来的展开位置,避免用户以为刚才的选择丢失。
收起华东区后,可显示“已选 3 家门店”的摘要,并提供查看已选清单的入口。摘要中的移除动作要与树同步:从清单移除徐汇店,上海从全选变半选,华东区仍为半选,数量从 3 变 2。只在右侧标签中删掉一个名字、左边复选框不更新,会让用户无法判断最终以哪一边为准。
还没加载的分支不能冒充已经全选
大规模门店库可能在展开城市后才读取门店。若杭州尚未加载,客户端只看见上海两家店,便把华东区显示为全选,会给用户错误的覆盖范围。解决办法取决于产品到底支持哪种选择承诺,不能仅靠一个旋转图标补救。
如果接口只提供已加载节点,就在加载完成前阻止区域级全选,给出“加载门店后可选择全部”,失败时保留已选门店并提供重试。如果业务支持服务端按整个区域选取,则要明确显示“选择华东区全部可用门店”,由服务端确认范围、数量和排除项;不能把“选中当前已加载项”与“选中整个区域”用同一状态混在一起。
还要明确选择是否包含之后新增的门店。本例巡检任务保存的是提交时的门店清单,新增门店不会自动加入;如果另一个产品做的是持续生效的组织权限规则,可能需要相反的行为。两者在今天都能显示“华东区全选”,但一个保存具体对象,一个保存动态范围,设计说明必须把这一区别写清楚。
提交前若门店停用或操作者权限变化,展示具体受影响对象,让用户移除无效门店或重新选择;不要无声删项后仍显示“提交成功”。沿着“勾选徐汇店—全选可用门店—取消滨江店—搜索西湖—移除已选门店”这条实际操作顺序检查,树形控件的重点就从画好一组复选框,落到了用户能否准确知道这次会影响谁。