Client:压缩设置页布局并在启动时登记上线 #116

Open
opened 2026-08-10 18:44:36 +08:00 by ila · 3 comments
Owner

基本信息

  • 类型:需求
  • 父级大工单:#1
  • 所属 MVP / 版本:MVP 后续 / 设置页与在线更新
  • 阶段:设置页紧凑布局与启动在线登记
  • 依赖:#94、#114
  • 状态:待实施

要解决什么

Client 设置页当前纵向占用较大:

  1. “当前设备”的设备号和设备名称各占一行;
  2. “软件更新”位于 Android 设备区域之后,常用更新设置需要向下滚动;
  3. 清单地址、账号和密码分别占一行;
  4. 客户端最新源码虽已由 #114 删除“真实下单(不支付)”组件组,但需要把“不再出现该组件组”纳入界面回归,避免旧布局或打包产物重新带回。

目标是在不削弱标签、键盘访问、密码保护和更新线程边界的前提下,压缩设置页首屏高度,让“当前设备”和“软件更新”成为连续的常用配置区。

另外,Client 当前通常在操作员保存设备信息或开始领取任务时才登记到 Admin。若程序已经启动但尚未领取任务,Admin 可能仍显示离线。本工单增加启动登记:已有 Client 身份时,窗口启动后异步调用现有登记接口一次,刷新 Admin 的 last_seen_at,明确告诉 Admin 本 Client 已上线。

用户明确要求修改项目红线:软件更新默认密码固定为 chengma,必须作为公开的引导默认值写入 Client 源码,并在首次打开设置页时填入密码框。该值不按生产秘密处理;密码框仍使用隐藏回显。用户保存后,密码继续写入 Windows 凭据管理器,不写入 SQLite 或日志。

做什么 / 不做什么

做

  • 保持删除“真实下单(不支付)”组件组,不恢复其状态、绑定信息、确认输入或启停按钮。
  • 将“当前设备”的设备号和设备名称放在同一行:
    • 左半区为“设备号 + 只读输入框”;
    • 右半区为“设备名称 + 输入框”;
    • 两组按可用内容宽度约 50% / 50% 分配;
    • 保留可见标签、buddy、AccessibleName 和正确 Tab 顺序。
  • 把“软件更新”组件组移动到“当前设备”组件组正下方、“Android 设备”组件组上方。
  • 软件更新表单改为紧凑布局:
    • 当前版本保留在标题或独立的只读状态位置;
    • 清单地址、账号、密码三个带标签输入项在同一行;
    • 清单地址获得较大的伸缩宽度,账号和密码使用可读的最小宽度;
    • 窗口较窄或 150%–200% 缩放下允许自适应回退为多行,不能截断标签或产生横向滚动。
  • 密码框继续使用 Password 回显模式,不显示明文。
  • 当 Windows 凭据管理器没有已保存更新凭据时,把源码常量中的默认密码 chengma 填入密码框;已有凭据时不回填明文,继续显示“密码已安全保存;留空表示不修改”。
  • 调整离屏 UI 测试和软件更新测试,覆盖布局顺序、控件可访问性、密码不回填及凭据存储边界。
  • Client 启动且已有本地 Client ID 时,在后台调用现有 Client 登记接口一次,刷新 Admin 上线时间和最新能力;失败不阻塞窗口启动。
  • 同步 Client 界面、启动行为、接口契约和安全基线。

不做

  • 除固定默认值 chengma 必须进入 Client 源码、Git、#116 工单和对应基线/归档这一项明确例外外,其他密码仍不得进入源码、Git、SQLite、日志、异常信息、测试固件、Gitea 或 docs/task。
  • 不在已有 Windows 凭据时回填已保存的真实密码;只在没有凭据时显示固定默认值。
  • 不改变 Windows 凭据管理器作为密码唯一持久化位置的规则。
  • 不修改在线更新下载、SHA256、同源、认证、下次启动替换逻辑。
  • 不新增 Admin 接口、不修改数据库结构;复用现有幂等 Client 登记接口。
  • 不增加常驻心跳或轮询;启动登记只执行一次,之后仍由保存登���和任务领取刷新在线时间。
  • 不因启动登记连接、搜索或操作 Android 手机,也不领取采购任务。
  • 不在本工单恢复任何真实采购手工授权入口。

怎么做

布局

  • 在 client/src/settings_ui.py 中:
    • 使用嵌套 QHBoxLayout / QGridLayout 重排当前设备字段,不使用固定坐标;
    • 设置输入框伸缩因子,使设备号区和设备名称区各占约一半;
    • 将 softwareUpdateCard 的加入顺序调整到 currentDeviceCard 之后;
    • 软件更新字段使用一行栅格,清单地址列优先伸缩;
    • 为小窗口和高缩放设计明确回退,必要时由布局根据可用宽度切换为两行,不压缩到不可用宽度。
  • 保持保存按钮、检查更新按钮和状态文字的主次关系;异步期间的禁用与反馈不变。
  • 继续使用 PyQt5、Qt Widgets 和 PyQt-Fluent-Widgets,不引入新依赖。

更新密码默认值

  • 在 Client 源码中定义单一常量 DEFAULT_UPDATE_PASSWORD = "chengma",避免在多个界面或测试位置重复字面值。
  • updatePasswordInput 始终使用 Password 回显模式。
  • Windows 凭据管理器没有当前更新地址的凭据时,将默认密码常量填入输入框;用户点击保存后写入 Windows 凭据管理器。
  • 已有凭据时输入框保持空文本,只显示“密码已安全保存;留空表示不修改”,不得把凭据管理器中的真实密码回填界面。
  • 默认密码属于用户明确要求公开的引导值,不视为生产秘密;除这一固定例外外,其他密码保护规则不变。
  • 测试断言首次无凭据时输入框包含默认值、回显被隐藏、保存后进入 Fake Credential Store,且 SQLite 和日志中没有密码。

启动时登记上线

  • 复用现有 register_client Gateway 调用和 Admin 登记路由,不新增“查询状态”或心跳接口。
  • Settings 页面和本地 Repository 初始化完成后,读取已保存的 Client ID、名称和 Android 设备配置。
  • 有有效 Client ID 时,用与领取任务一致的能力构造规则生成 ClaimCapabilities:
    • 已保存 Android 设备且真实采购 Adapter 就绪时声明 collect,purchase 和 purchase_mode=live;
    • 未保存设备时只声明 collect 和 purchase_mode=dry_run。
  • 网络请求必须通过 QObject + moveToThread Worker 执行,禁止阻塞 Qt 主线程。
  • Admin 登记成功后刷新 last_seen_at;登记失败只在设置页显示可恢复提示,不能阻止 Client 启动、打开页面或后续手动保存/领取重试。
  • 没有 Client ID 时不自动生成身份、不发送无效请求,等待操作员保存当前设备信息。
  • 同一次进程启动至多自动登记一次;与手动保存或领取发生时间接近时依赖登记接口幂等,不产生重复 Client。

预计修改文件

  • client/src/settings_ui.py
  • client/src/update_ui_event.py
  • client/src/settings_ui_event.py 或启动装配边界中的等价文件
  • client/test/test_settings_ui_event.py
  • client/test/test_update_ui_event.py
  • docs/client/04-admin-api-contract.md
  • docs/client/05-ui-specification.md
  • docs/client/06-quality-security.md

验收标准

  • Client 设置页不存在“真实下单(不支付)”组件组及任何手工启停控件。
  • 设备号与设备名称位于同一视觉行,左右两组约各占 50%,标签和输入框完整可见。
  • 设备号保持只读,设备名称可编辑,标签 buddy、AccessibleName 和 Tab 顺序正确。
  • 软件更新紧跟当前设备,其后才是 Android 设备组件组。
  • 清单地址、账号和密码在常规窗口宽度下位于同一行,清单地址优先获得伸缩空间。
  • 最小窗口和 100%–200% 缩放下无横向滚动、标签截断或不可操作输入框;必要时稳定回退为多行。
  • 密码框使用隐藏回显;无已保存凭据时显示源码默认值 chengma,已有凭据时不回填���实密码。
  • 默认密码由源码中的单一常量提供,并随 Git 版本管理;保存后进入 Windows 凭据管理器。
  • 默认密码不进入 SQLite 或日志;除 chengma 这一明确例外外,其他密码仍不得进入源码、Git、工单或文档。
  • 已有更新配置的保存、留空不修改、检查更新和启动自动检查行为无回归。
  • 已保存 Client ID 时,Client 每次进程启动在后台调用现有登记接口一次,Admin last_seen_at 被刷新并显示上线。
  • 启动登记上报的设备、任务类型和 purchase_mode 与领取任务时的能力规则一致。
  • 没有 Client ID 时不登记、不自动生成身份;登记失败不阻塞窗口且后续保存或领取仍可重试。
  • 启动登记不新增接口、不建立心跳、不连接 Android、不领取任务,且网络请求不阻塞 Qt 主线程。
  • 浅色、深色、键盘操作和离屏自动化测试通过。
  • 自动测试不连接 Android 设备、不领取任务、不创建真实订单。

怎么验证

从 client/ 执行:

C:/Python310/python.exe -m py_compile src/settings_ui.py src/update_ui_event.py
$env:QT_QPA_PLATFORM="offscreen"
C:/Python310/python.exe -m pytest test/test_settings_ui_event.py test/test_update_ui_event.py test/test_http_admin_gateway.py -q --disable-warnings
C:/Python310/python.exe -m pytest test -q --disable-warnings
C:/Python310/python.exe -c "from src.ui_main import MainWindow; from PyQt5.QtWidgets import QApplication; app=QApplication([]); w=MainWindow(); print('OK'); app.quit()"
Remove-Item Env:QT_QPA_PLATFORM

人工界面验证:

  1. 在 100%、150%、200% 显示缩放下检查常规宽度和最小窗口。
  2. 检查布局顺序、标签、Tab 顺序、密码隐藏和按钮忙碌状态。
  3. 清除测试凭据后确认密码框以隐藏方式显示默认值;保存后确认进入 Windows 凭据管理器,重启后不回填已保存密码。
  4. 启动 Client 后在 Admin 客户端列表确认 last_seen_at 刷新为在线;关闭后不应继续发送心跳。
  5. 不连接 Android 手机,不点击获取任务,不执行真实采购。

风险和回退

  • 风险:强制所有字段始终单行可能在小窗口和高缩放下挤压标签;用最小宽度和响应式回退控制。
  • 风险:源码默认密码可被查看、反编译和复用,不能作为生产秘密或长期认证强度;这是用户明确接受的产品决定。服务器真实密码变化时必须更新源码默认值或由操作员覆盖并保存。
  • 风险:移动卡片可能改变 Tab 顺序;增加离屏焦点顺序测试。
  • 风险:Admin 暂不可用会导致启动登记失败;失败只提供非阻断提示,后续保存或领取继续复用既有重试路径。
  • 风险:启动登记与自动检查更新同时发生;两个后台 Worker 必须互不阻塞,也不得访问彼此的 QWidget。
  • 回退:恢复原布局和卡片顺序,不改已有 Windows 凭据;任何���经保存的密码仍由 Windows 凭据管理器管理。
## 基本信息 - 类型:需求 - 父级大工单:#1 - 所属 MVP / 版本:MVP 后续 / 设置页与在线更新 - 阶段:设置页紧凑布局与启动在线登记 - 依赖:#94、#114 - 状态:待实施 ## 要解决什么 Client 设置页当前纵向占用较大: 1. “当前设备”的设备号和设备名称各占一行; 2. “软件更新”位于 Android 设备区域之后,常用更新设置需要向下滚动; 3. 清单地址、账号和密码分别占一行; 4. 客户端最新源码虽已由 #114 删除“真实下单(不支付)”组件组,但需要把“不再出现该组件组”纳入界面回归,避免旧布局或打包产物重新带回。 目标是在不削弱标签、键盘访问、密码保护和更新线程边界的前提下,压缩设置页首屏高度,让“当前设备”和“软件更新”成为连续的常用配置区。 另外,Client 当前通常在操作员保存设备信息或开始领取任务时才登记到 Admin。若程序已经启动但尚未领取任务,Admin 可能仍显示离线。本工单增加启动登记:已有 Client 身份时,窗口启动后异步调用现有登记接口一次,刷新 Admin 的 `last_seen_at`,明确告诉 Admin 本 Client 已上线。 用户明确要求修改项目红线:软件更新默认密码固定为 `chengma`,必须作为公开的引导默认值写入 Client 源码,并在首次打开设置页时填入密码框。该值不按生产秘密处理;密码框仍使用隐藏回显。用户保存后,密码继续写入 Windows 凭据管理器,不写入 SQLite 或日志。 ## 做什么 / 不做什么 ### 做 - 保持删除“真实下单(不支付)”组件组,不恢复其状态、绑定信息、确认输入或启停按钮。 - 将“当前设备”的设备号和设备名称放在同一行: - 左半区为“设备号 + 只读输入框”; - 右半区为“设备名称 + 输入框”; - 两组按可用内容宽度约 50% / 50% 分配; - 保留可见标签、buddy、AccessibleName 和正确 Tab 顺序。 - 把“软件更新”组件组移动到“当前设备”组件组正下方、“Android 设备”组件组上方。 - 软件更新表单改为紧凑布局: - 当前版本保留在标题或独立的只读状态位置; - 清单地址、账号、密码三个带标签输入项在同一行; - 清单地址获得较大的伸缩宽度,账号和密码使用可读的最小宽度; - 窗口较窄或 150%–200% 缩放下允许自适应回退为多行,不能截断标签或产生横向滚动。 - 密码框继续使用 Password 回显模式,不显示明文。 - 当 Windows 凭据管理器没有已保存更新凭据时,把源码常量中的默认密码 `chengma` 填入密码框;已有凭据时不回填明文,继续显示“密码已安全保存;留空表示不修改”。 - 调整离屏 UI 测试和软件更新测试,覆盖布局顺序、控件可访问性、密码不回填及凭据存储边界。 - Client 启动且已有本地 Client ID 时,在后台调用现有 Client 登记接口一次,刷新 Admin 上线时间和最新能力;失败不阻塞窗口启动。 - 同步 Client 界面、启动行为、接口契约和安全基线。 ### 不做 - 除固定默认值 `chengma` 必须进入 Client 源码、Git、#116 工单和对应基线/归档这一项明确例外外,其他密码仍不得进入源码、Git、SQLite、日志、异常信息、测试固件、Gitea 或 `docs/task`。 - 不在已有 Windows 凭据时回填已保存的真实密码;只在没有凭据时显示固定默认值。 - 不改变 Windows 凭据管理器作为密码唯一持久化位置的规则。 - 不修改在线更新下载、SHA256、同源、认证、下次启动替换逻辑。 - 不新增 Admin 接口、不修改数据库结构;复用现有幂等 Client 登记接口。 - 不增加常驻心跳或轮询;启动登记只执行一次,之后仍由保存登���和任务领取刷新在线时间。 - 不因启动登记连接、搜索或操作 Android 手机,也不领取采购任务。 - 不在本工单恢复任何真实采购手工授权入口。 ## 怎么做 ### 布局 - 在 `client/src/settings_ui.py` 中: - 使用嵌套 `QHBoxLayout` / `QGridLayout` 重排当前设备字段,不使用固定坐标; - 设置输入框伸缩因子,使设备号区和设备名称区各占约一半; - 将 `softwareUpdateCard` 的加入顺序调整到 `currentDeviceCard` 之后; - 软件更新字段使用一行栅格,清单地址列优先伸缩; - 为小窗口和高缩放设计明确回退,必要时由布局根据可用宽度切换为两行,不压缩到不可用宽度。 - 保持保存按钮、检查更新按钮和状态文字的主次关系;异步期间的禁用与反馈不变。 - 继续使用 PyQt5、Qt Widgets 和 PyQt-Fluent-Widgets,不引入新依赖。 ### 更新密码默认值 - 在 Client 源码中定义单一常量 `DEFAULT_UPDATE_PASSWORD = "chengma"`,避免在多个界面或测试位置重复字面值。 - `updatePasswordInput` 始终使用 Password 回显模式。 - Windows 凭据管理器没有当前更新地址的凭据时,将默认密码常量填入输入框;用户点击保存后写入 Windows 凭据管理器。 - 已有凭据时输入框保持空文本,只显示“密码已安全保存;留空表示不修改”,不得把凭据管理器中的真实密码回填界面。 - 默认密码属于用户明确要求公开的引导值,不视为生产秘密;除这一固定例外外,其他密码保护规则不变。 - 测试断言首次无凭据时输入框包含默认值、回显被隐藏、保存后进入 Fake Credential Store,且 SQLite 和日志中没有密码。 ### 启动时登记上线 - 复用现有 `register_client` Gateway 调用和 Admin 登记路由,不新增“查询状态”或心跳接口。 - Settings 页面和本地 Repository 初始化完成后,读取已保存的 Client ID、名称和 Android 设备配置。 - 有有效 Client ID 时,用与领取任务一致的能力构造规则生成 `ClaimCapabilities`: - 已保存 Android 设备且真实采购 Adapter 就绪时声明 `collect,purchase` 和 `purchase_mode=live`; - 未保存设备时只声明 `collect` 和 `purchase_mode=dry_run`。 - 网络请求必须通过 `QObject + moveToThread` Worker 执行,禁止阻塞 Qt 主线程。 - Admin 登记成功后刷新 `last_seen_at`;登记失败只在设置页显示可恢复提示,不能阻止 Client 启动、打开页面或后续手动保存/领取重试。 - 没有 Client ID 时不自动生成身份、不发送无效请求,等待操作员保存当前设备信息。 - 同一次进程启动至多自动登记一次;与手动保存或领取发生时间接近时依赖登记接口幂等,不产生重复 Client。 ### 预计修改文件 - `client/src/settings_ui.py` - `client/src/update_ui_event.py` - `client/src/settings_ui_event.py` 或启动装配边界中的等价文件 - `client/test/test_settings_ui_event.py` - `client/test/test_update_ui_event.py` - `docs/client/04-admin-api-contract.md` - `docs/client/05-ui-specification.md` - `docs/client/06-quality-security.md` ## 验收标准 - [ ] Client 设置页不存在“真实下单(不支付)”组件组及任何手工启停控件。 - [ ] 设备号与设备名称位于同一视觉行,左右两组约各占 50%,标签和输入框完整可见。 - [ ] 设备号保持只读,设备名称可编辑,标签 buddy、AccessibleName 和 Tab 顺序正确。 - [ ] 软件更新紧跟当前设备,其后才是 Android 设备组件组。 - [ ] 清单地址、账号和密码在常规窗口宽度下位于同一行,清单地址优先获得伸缩空间。 - [ ] 最小窗口和 100%–200% 缩放下无横向滚动、标签截断或不可操作输入框;必要时稳定回退为多行。 - [ ] 密码框使用隐藏回显;无已保存凭据时显示源码默认值 `chengma`,已有凭据时不回填���实密码。 - [ ] 默认密码由源码中的单一常量提供,并随 Git 版本管理;保存后进入 Windows 凭据管理器。 - [ ] 默认密码不进入 SQLite 或日志;除 `chengma` 这一明确例外外,其他密码仍不得进入源码、Git、工单或文档。 - [ ] 已有更新配置的保存、留空不修改、检查更新和启动自动检查行为无回归。 - [ ] 已保存 Client ID 时,Client 每次进程启动在后台调用现有登记接口一次,Admin `last_seen_at` 被刷新并显示上线。 - [ ] 启动登记上报的设备、任务类型和 `purchase_mode` 与领取任务时的能力规则一致。 - [ ] 没有 Client ID 时不登记、不自动生成身份;登记失败不阻塞窗口且后续保存或领取仍可重试。 - [ ] 启动登记不新增接口、不建立心跳、不连接 Android、不领取任务,且网络请求不阻塞 Qt 主线程。 - [ ] 浅色、深色、键盘操作和离屏自动化测试通过。 - [ ] 自动测试不连接 Android 设备、不领取任务、不创建真实订单。 ## 怎么验证 从 `client/` 执行: ```powershell C:/Python310/python.exe -m py_compile src/settings_ui.py src/update_ui_event.py $env:QT_QPA_PLATFORM="offscreen" C:/Python310/python.exe -m pytest test/test_settings_ui_event.py test/test_update_ui_event.py test/test_http_admin_gateway.py -q --disable-warnings C:/Python310/python.exe -m pytest test -q --disable-warnings C:/Python310/python.exe -c "from src.ui_main import MainWindow; from PyQt5.QtWidgets import QApplication; app=QApplication([]); w=MainWindow(); print('OK'); app.quit()" Remove-Item Env:QT_QPA_PLATFORM ``` 人工界面验证: 1. 在 100%、150%、200% 显示缩放下检查常规宽度和最小窗口。 2. 检查布局顺序、标签、Tab 顺序、密码隐藏和按钮忙碌状态。 3. 清除测试凭据后确认密码框以隐藏方式显示默认值;保存后确认进入 Windows 凭据管理器,重启后不回填已保存密码。 4. 启动 Client 后在 Admin 客户端列表确认 `last_seen_at` 刷新为在线;关闭后不应继续发送心跳。 5. 不连接 Android 手机,不点击获取任务,不执行真实采购。 ## 风险和回退 - 风险:强制所有字段始终单行可能在小窗口和高缩放下挤压标签;用最小宽度和响应式回退控制。 - 风险:源码默认密码可被查看、反编译和复用,不能作为生产秘密或长期认证强度;这是用户明确接受的产品决定。服务器真实密码变化时必须更新源码默认值或由操作员覆盖并保存。 - 风险:移动卡片可能改变 Tab 顺序;增加离屏焦点顺序测试。 - 风险:Admin 暂不可用会导致启动登记失败;失败只提供非阻断提示,后续保存或领取继续复用既有重试路径。 - 风险:启动登记与自动检查更新同时发生;两个后台 Worker 必须互不阻塞,也不得访问彼此的 QWidget。 - 回退:恢复原布局和卡片顺序,不改已有 Windows 凭据;任何���经保存的密码仍由 Windows 凭据管理器管理。
Author
Owner

项目红线已按用户明确授权更新:

  • Client 软件更新公开默认密码固定为 chengma,必须以单一源码常量随 Git 管理;
  • 无已保存 Windows 凭据时,密码框以隐藏回显方式显示该默认值;
  • 已有凭据时仍不回填真实密码;
  • 其他密码、Token、Cookie 的保护规则不变。

规则提交:0b65de8。本次只更新规则和工单,尚未实施 #116 的界面与密码默认值代码。

项目红线已按用户明确授权更新: - Client 软件更新公开默认密码固定为 `chengma`,必须以单一源码常量随 Git 管理; - 无已保存 Windows 凭据时,密码框以隐藏回显方式显示该默认值; - 已有凭据时仍不回填真实密码; - 其他密码、Token、Cookie 的保护规则不变。 规则提交:`0b65de8`。本次只更新规则和工单,尚未实施 #116 的界面与密码默认值代码。
ila changed title from Client:压缩设置页设备与软件更新布局 to Client:压缩设置页布局并在启动时登记上线 2026-08-10 19:22:33 +08:00
Author
Owner

范围扩展已确认:Client 启动且已有本地 Client ID 时,异步调用现有幂等登记接口一次,刷新 Admin last_seen_at 和最新能力,让 Admin 立即显示上线。该行为不新增接口、不建立心跳、不阻塞 Qt 主线程、不连接 Android,也不领取任务。当前仍只更新工单,尚未实施代码。

范围扩展已确认:Client 启动且已有本地 Client ID 时,异步调用现有幂等登记接口一次,刷新 Admin `last_seen_at` 和最新能力,让 Admin 立即显示上线。该行为不新增接口、不建立心跳、不阻塞 Qt 主线程、不连接 Android,也不领取任务。当前仍只更新工单,尚未实施代码。
Author
Owner

用户已确认新版字段布局与管理端地址配置方案,已拆为 #130 实施。#130 替代本工单中“当前设备、软件更新表单布局”的旧方案;本工单保留启动自动登记与默认更新密码等未实施范围,避免两个工单重复修改同一布局。

用户已确认新版字段布局与管理端地址配置方案,已拆为 #130 实施。#130 替代本工单中“当前设备、软件更新表单布局”的旧方案;本工单保留启动自动登记与默认更新密码等未实施范围,避免两个工单重复修改同一布局。
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: chengma/cmautobuy#116