ScopePick 的条目、勾选状态、界面与日志均为可复核的虚拟样本。配图不是 DevEco 或真机测试截图示例不代表真实上架审核或实际平台审批结果。一、先决定“全选”到底在选什么素材上架工具里一个“全选”按钮经常被认为很简单给所有复选框设成true就行。问题是用户通常先做了筛选。假设当前只展示“待校验”的五张素材而页面背后还有三张已归档记录其中两张之前就被选中过。用户点下“当前筛选全选”合理预期是把眼前的五张选中而不是悄悄覆盖那些看不见的记录。我把这个情境做成ScopePickDemo页面是AssetReviewPage演示任务编号SP-1910。数据总共八项AS-01至AS-08。当前可见项为AS-02、AS-03、AS-05、AS-07、AS-08其中AS-03原本已经选中隐藏项是AS-01、AS-04、AS-06其中AS-01与AS-06已选。这样初始全局选中数为三当前可见选中数为一。按钮被点击后当前五项全部选中隐藏的两项继续保留所以全局已选是七而不是五也不是八。AS-04仍然保持未选。把这组具体 ID 写清楚很有必要因为只看“全选成功”四个字无法证明程序没有误操作隐藏数据。接下来所有 UI 示意和 HiLog 演示都围绕这份固定快照展开。封面以两层清单和一个可见窗口表达筛选作用域仅为机制示意。这个案例讨论的是客户端选择集语义不涉及审核通过率也不会替任何真实平台做自动提交。我的建议是把“全选”按钮改成“当前筛选全选”让动作范围成为用户可见的产品契约而不是把危险操作藏进默认行为。二、让原始数据、可见列表、选择集各自独立第一层是原始八条素材第二层是当前筛选后得出的可见五条第三层是跨筛选条件保存的已选 ID 集合。三层可以互相推导但不能互相替代。特别要避免把visibleItems当作选择数据源一旦切换过滤条件隐藏项将从屏幕消失错误实现往往顺手把对应选择状态也清空。这里先用一个固定数组把演示边界钉住。status只是本地业务标签不是 HarmonyOS 提供的审核状态常量同理AS-*ID 也是为了便于验收而编造的示例主键。上线系统应使用数据库或服务端的稳定主键不能依赖列表位置更不能把ForEach当前索引当成唯一身份。interface ReviewAsset { id: string; label: string; status: string; } const ASSETS: ReviewAsset[] [ { id: AS-01, label: 启动封面, status: 已归档 }, { id: AS-02, label: 商店主图, status: 待校验 }, { id: AS-03, label: 预览截图A, status: 待校验 }, { id: AS-04, label: 旧版图标, status: 已归档 }, { id: AS-05, label: 预览截图B, status: 待校验 }, { id: AS-06, label: 授权声明, status: 已归档 }, { id: AS-07, label: 功能介绍图, status: 待校验 }, { id: AS-08, label: 适配说明图, status: 待校验 } ];有了这种数据区分可以清楚回答三个经常混淆的问题“列表里有几项”是五“当前可见已选几项”起初是一“全局共有几项被选中”起初是三。这三个数字不是冗余而是帮助用户判断有没有隐藏选择项。若产品没有把隐藏选择数展示出来用户可能以为点了“取消全选”就清除了全部勾选结果后台提交却仍附带之前藏起来的两项。三、不要把 CheckboxGroup 当成筛选作用域的保证CheckboxGroup适合定义一个明确的组件分组官方文档也说明相同组名会联系到组内复选框。但业务上的“当前筛选结果”不一定等于组件树中的组成员分页、条件过滤、虚拟化渲染都可能改变屏幕上实际存在的节点。如果希望隐藏项继续保持选中最稳妥的方式是把选择逻辑放在 ID 集合上而不是让组件实例数量决定业务意义。这段代码把筛选与勾选写在AssetReviewPage中。State selectedIds每次整体赋新数组确保 UI 能观察到变更setVisibleAll只修改当前筛选所得的 ID不碰其他状态。逻辑选择集合使用数组是为了示例易读数据量大时可在计算中使用临时集合但最终仍需要映射到框架可观察的状态模型。Entry Component struct AssetReviewPage { State filter: string 待校验; State selectedIds: string[] [AS-01, AS-03, AS-06]; visibleItems(): ReviewAsset[] { return ASSETS.filter((item: ReviewAsset) item.status this.filter); } checked(id: string): boolean { return this.selectedIds.includes(id); } changeOne(id: string, yes: boolean): void { const exists: boolean ASSETS.some((item: ReviewAsset) item.id id); if (!exists) return; let next: string[] this.selectedIds.filter((key: string) key ! id); if (yes) next.push(id); this.selectedIds next; } setVisibleAll(yes: boolean): void { const visibleIds: string[] this.visibleItems().map((item: ReviewAsset) item.id); let next: string[] this.selectedIds.filter((id: string) !visibleIds.includes(id)); if (yes) next next.concat(visibleIds); this.selectedIds next; } visibleAllChecked(): boolean { const now: ReviewAsset[] this.visibleItems(); return now.length 0 now.every((item: ReviewAsset) this.checked(item.id)); } hiddenSelectedCount(): number { const visibleIds: string[] this.visibleItems().map((item: ReviewAsset) item.id); return this.selectedIds.filter((id: string) !visibleIds.includes(id)).length; } // 下一节的 build() 方法放在此组件中。 }需要注意setVisibleAll(false)的含义是取消当前筛选范围的选择而不是清空全局。对于这个演示数据从点击后全局七项再点“取消当前筛选全选”会回到只保留隐藏AS-01、AS-06的两项AS-03原本的勾选也会被取消这是按钮的明确业务约定不是恢复操作历史。如果产品需要撤销必须额外保存操作前快照不能混用这两个概念。四、UI 的职责是暴露范围与后果渲染时我选择每行一个受控Checkbox通过.select(this.checked(id))显示业务真值通过.onChange((yes) this.changeOne(id, yes))把交互送回选择集合。ForEach的键使用稳定 ID而不是筛选结果的顺序号。按钮明确写“当前筛选全选/取消当前筛选全选”同时展示全局已选以及隐藏已选数。这样即使换成其他筛选条件也不会因为显示数量变化而误导用户。下面是上一节AssetReviewPage的build()成员需与前两段代码合并到同一 ETS 文件。这里没有调用审核 API也没有上传任何文件确认按钮仅展示本地状态不会对外发送请求。// 放入 AssetReviewPage 组件体内。 build() { Column({ space: 16 }) { Text(ScopePick · 上架素材选择).fontSize(22).fontWeight(FontWeight.Bold) Text(任务 SP-1910 | 当前筛选 this.filter) Text(可见 this.visibleItems().length 项 | 全局已选 this.selectedIds.length 项) Text(隐藏已选 this.hiddenSelectedCount() 项) Button(this.visibleAllChecked() ? 取消当前筛选全选 : 当前筛选全选) .onClick(() this.setVisibleAll(!this.visibleAllChecked())) ForEach(this.visibleItems(), (item: ReviewAsset) { Row({ space: 12 }) { Checkbox({ name: item.id }).select(this.checked(item.id)) .onChange((yes: boolean) this.changeOne(item.id, yes)) Text(item.id · item.label).fontSize(16) }.width(100%).height(54) }, (item: ReviewAsset) item.id) }.padding(20).width(100%) }DevEco 风格示意图左侧是ScopePick项目树中间展示setVisibleAll对可见 ID 做集合运算右侧预览为按下按钮之后的五行勾选状态可见5/5、全局已选7、隐藏已选2。底部 HiLog 的字符串是按示例状态人工编写的并非系统实际打印。将右侧模拟器放在固定位置是为了检查 IDE 图和纯手机图是否传达同一业务快照。五、筛选变化、失败提交与生命周期细节纯手机示意图显示ScopePick、任务SP-1910屏幕上五条待校验素材全部勾选页脚再次提示隐藏已选2项。顶部的时间、5G、Wi-Fi、信号和电量都只是视觉规范的一部分不能被视作实际测量证据。它不显示后台“审核已通过”因为示例根本没有提交审核。这里要给一个容易漏掉的逆向用例若用户切换到“已归档”筛选原来五条待校验素材立刻变成隐藏全局已选数仍然是七只有可见集及隐藏已选统计发生变化。若此时再点“当前筛选全选”应只针对三条归档素材操作不应反过来改变待校验的五条。这一点是作用域模型成立的关键不能靠在按钮上多加一句提示来替代。真正提交前还需要二次校验。选择集合里的 ID 可能已经过期、素材可能被他人删除、权限可能变化尤其在分页场景里用户看见的是某一时刻的快照。可以在确认时先做去重再与服务端允许操作的资源集求交集并让用户核对“将操作七项其中隐藏两项”。服务端必须重新授权并执行幂等校验客户端勾选并不意味着具备审核权限。提交失败时保留选择集并给出逐项失败原因比静默清空勾选更利于复查。生命周期方面页面退出时无需释放Checkbox的底层资源组件会随页面节点管理但是订阅了外部素材更新流的业务代码必须解除订阅挂起的提交请求应隔离旧页面回调。本文没有创建定时器、网络会话、数据库游标或系统文件句柄因此不存在可展示的destroy()调用。凭空补一个释放 API 反而会制造错误知识。若后续接入服务端响应建议使用独立请求标识并在页面重建时重新读取经过授权的选择集。六、按 ID 验收比看勾选动画可靠这份演示可以用四个明确断言验收。初始selectedIds为AS-01, AS-03, AS-06选择当前筛选全部之后应得到七项绝不能包含AS-04取消当前筛选全部之后只留下AS-01, AS-06在“已归档”筛选下操作时待校验素材的勾选集合应保持原样。这些判断可以在纯业务函数层做单元测试毋须先上真机看勾选框是否变色。最值得留下的工程结论是业务选择集的作用域必须用数据来定义组件分组只负责渲染和交互隐藏不等于取消选择撤销不等于取消全选客户端勾选更不等于后台有权提交。把这几对概念拆开才有可能安全地扩展到分页、多窗口和折叠屏多栏列表。华为官方资料及边界华为 ArkUI Checkbox APIselect、onChange华为 CheckboxGroup 官方行为说明华为动态生成 CheckboxGroup 常见问题本文仅核对基本组件 API 和业务行为设计没有在目标 SDK 编译整段 ETS、跑自动化、验证真实上架审核。要上线还需按应用实际 UI 版本确认组件表现、权限和提交协议。