2026-08-06 11:44:39 +08:00
|
|
|
|
# 05 Client 界面交互规范
|
|
|
|
|
|
|
|
|
|
|
|
- 文档状态:基线草案,待界面评审
|
|
|
|
|
|
- 技术栈:PyQt5、Qt Widgets、PyQt-Fluent-Widgets
|
|
|
|
|
|
- 当前验证组件版本:PyQt-Fluent-Widgets 1.11.3(版本以 `client/requirements.txt` 为准)
|
|
|
|
|
|
|
|
|
|
|
|
本文档中 `[必须]` / `[建议]` / `[待定]` 的含义见 [文档索引](../README.md#文档标注说明)。没有标注的默认是 `[必须]`。看不懂的词查 [术语表](00-glossary.md)。
|
|
|
|
|
|
|
|
|
|
|
|
## 1. 设计目标
|
|
|
|
|
|
|
|
|
|
|
|
- 让操作人员在一个页面完成任务监控、搜索和异常定位。
|
2026-08-11 10:42:51 +08:00
|
|
|
|
- “获取任务”“重新采集”“重新采购”和“重新上报”会产生外部后果;重新上报只重发既有 Outbox,并优先处理未发送事件。“删除”只软隐藏本地记录。
|
2026-08-06 11:44:39 +08:00
|
|
|
|
- 长任务状态始终可找到,不使用连续模态弹窗打断工作。
|
|
|
|
|
|
- 任务表格在数据增长后仍保持响应速度、稳定选择和可访问性。
|
|
|
|
|
|
- 界面只展示任务状态,不在 Qt 主线程执行 Admin 或手机自动化。
|
|
|
|
|
|
|
|
|
|
|
|
## 2. 应用外壳
|
|
|
|
|
|
|
|
|
|
|
|
主窗口使用 `FluentWindow`,顶级导航保持稳定顺序:
|
|
|
|
|
|
|
|
|
|
|
|
1. **PDD 任务**:主要业务页面。
|
|
|
|
|
|
2. **设置**:固定在导航底部。
|
|
|
|
|
|
|
2026-08-11 11:13:16 +08:00
|
|
|
|
启动检查确认存在新版本时,设置导航显示“有更新”文字并使用提醒色,不能只靠颜色表达。
|
|
|
|
|
|
只有确认线上版本不高于本地版本时才清除提醒;进入设置页不会自动清除提醒,检查失败也
|
|
|
|
|
|
不会制造更新提示。软件更新区域同时显示本地版本和线上版本。
|
|
|
|
|
|
|
2026-08-06 11:44:39 +08:00
|
|
|
|
页面必须使用稳定对象名:
|
|
|
|
|
|
|
|
|
|
|
|
- `pddTaskPage`
|
|
|
|
|
|
- `settingsPage`
|
|
|
|
|
|
|
|
|
|
|
|
> **现状提醒:** 当前代码里是 3 个导航项(pdd / 设备 / 设置),和这里写的 2 个不一样。这是已知差异,见 [02 架构 §3.1](02-architecture.md)。设备相关设置最终要并入设置页,但要按工单单独做,不要顺手改。
|
|
|
|
|
|
|
2026-08-11 11:38:29 +08:00
|
|
|
|
Client 首次显示主窗口时使用标准 Windows 最大化状态,不使用全屏模式。用户仍可通过标题栏
|
|
|
|
|
|
最小化、还原、调整大小、再次最大化或关闭窗口。窗口变窄时允许表格水平滚动和顶部命令
|
|
|
|
|
|
换行,不能隐藏主要任务入口。
|
2026-08-06 11:44:39 +08:00
|
|
|
|
|
|
|
|
|
|
## 3. PDD 任务页布局
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
|
|
|
|
|
|
│ PDD 任务 │
|
2026-08-11 10:42:51 +08:00
|
|
|
|
│ [自动获取] [类型▼] [状态▼] [关键词...] [搜索] [刷新] [全选] [反选] [已选0条] [重新上报] [重新采集] [重新采购] [删除] │
|
2026-08-06 11:44:39 +08:00
|
|
|
|
├─────────────────────────────────────────────────────────────┤
|
2026-08-11 11:17:44 +08:00
|
|
|
|
│ 选择 │ 类型 │ 商品标题 │ 店铺名 │ 颜色 │ 尺码 │ 价格 │ 数量 │ 状态 │ 用时 │ 更新时间 │
|
2026-08-06 11:44:39 +08:00
|
|
|
|
│ │
|
|
|
|
|
|
│ 任务数据表格 │
|
|
|
|
|
|
│ │
|
|
|
|
|
|
├─────────────────────────────────────────────────────────────┤
|
|
|
|
|
|
│ 自动获取:已开启 · 当前 PDD-0001 · 正在采集 SKU · 已完成 42 个 │
|
|
|
|
|
|
└─────────────────────────────────────────────────────────────┘
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
垂直区域依次为顶部命令区、可伸展表格区和稳定状态区。表格获得全部剩余高度,状态区不得遮挡最后一行或滚动条。
|
|
|
|
|
|
|
|
|
|
|
|
## 4. 顶部命令区
|
|
|
|
|
|
|
2026-08-10 17:11:59 +08:00
|
|
|
|
### 4.1 自动获取 / 停止自动获取
|
2026-08-06 11:44:39 +08:00
|
|
|
|
|
|
|
|
|
|
- 使用 `PrimaryPushButton`,是页面唯一主要强调操作。
|
2026-08-10 17:11:59 +08:00
|
|
|
|
- 初始文本为“自动获取”,图标表达开始。
|
2026-08-09 10:54:18 +08:00
|
|
|
|
- 启动成功后文本变为“停止自动获取”,按钮位置和宽度尽量稳定。
|
2026-08-06 15:58:09 +08:00
|
|
|
|
- `[必须]` 这两个文案是**按钮标签**。状态栏里的“自动获取:已开启/已停止”说的是**功能状态**,两者不是一回事,不要混改。
|
2026-08-06 11:44:39 +08:00
|
|
|
|
- 启动过程中禁用重复点击,显示“正在启动”。
|
|
|
|
|
|
- 停止表示不再领取新任务;当前任务进入安全停止流程。
|
|
|
|
|
|
- 如果设备、Admin 或必要设置无效,按钮可禁用,但附近必须说明缺少的条件。
|
|
|
|
|
|
|
2026-08-09 10:54:18 +08:00
|
|
|
|
每一轮先补交一条本地 Outbox;若无待提交结果则执行最早的本地采集任务,再没有
|
|
|
|
|
|
才向 Admin 领取一条。领取、手机采集和提交都在单独的工作线程完成,一轮结束且
|
|
|
|
|
|
线程完全退出后,由主线程的单次定时器安排下一轮。暂无任务时默认 5 秒后重试;
|
|
|
|
|
|
可恢复的 Admin 错误按 5、10、20、30 秒退避。需要人工处理、不可恢复错误、设备
|
2026-08-10 16:35:38 +08:00
|
|
|
|
或配置错误会停止自动获取。采购 Adapter 和 Android 设备都准备好时,可以领取
|
|
|
|
|
|
采购任务;任务详情必须按任务的不可变执行模式明确显示“演练”或“真实下单”。
|
2026-08-07 16:42:31 +08:00
|
|
|
|
|
2026-08-10 09:35:46 +08:00
|
|
|
|
- 补交本地 Outbox 不依赖手机,可以在 Android 设备断开时继续执行。
|
|
|
|
|
|
- 执行本地任务或向 Admin 领取新任务前,工作线程必须用 `adb devices -l` 检查已保存的精确设备号。USB 和 Wi-Fi 设备都只接受 `device` 状态。
|
|
|
|
|
|
- 设备未连接、`offline`、`unauthorized` 或 ADB 检查失败时,立即停止本轮和自动获取;不得领取新任务,也不得启动本地任务。
|
2026-08-10 10:05:45 +08:00
|
|
|
|
- 采集、采购、重新采集和设备错误使用较大的 `InfoBar` 提示,显示在软件窗口顶部水平居中,5 秒后自动关闭,并提供明显的“关闭提示”按钮。设备错误另外提供“打开设置”;重复错误替换旧提示,不得堆叠。完整错误原因保留在底部状态区。
|
2026-08-10 09:35:46 +08:00
|
|
|
|
|
2026-08-06 11:44:39 +08:00
|
|
|
|
### 4.2 搜索与筛选
|
|
|
|
|
|
|
|
|
|
|
|
建议控件:
|
|
|
|
|
|
|
|
|
|
|
|
- 任务类型:`ComboBox`,选项为“全部、采集、采购”。
|
|
|
|
|
|
- 任务状态:`ComboBox`,选项为“全部”及标准任务状态。
|
2026-08-10 17:11:59 +08:00
|
|
|
|
- 关键词:`SearchLineEdit`,提示“任务编号、商品编号或商品标题”;建议宽度 150~240,随命令区布局自适应。
|
2026-08-06 11:44:39 +08:00
|
|
|
|
- 搜索:普通 `PushButton`。
|
|
|
|
|
|
|
|
|
|
|
|
筛选条件之间采用 AND。点击搜索或在关键词输入框按 Enter 执行本地数据库查询,不请求领取任务。活动筛选必须可见,筛选无结果时保留条件并提供清除入口。
|
|
|
|
|
|
|
2026-08-11 09:43:29 +08:00
|
|
|
|
### 4.3 刷新、全选、反选与批量操作
|
2026-08-09 21:44:29 +08:00
|
|
|
|
|
2026-08-10 10:56:48 +08:00
|
|
|
|
- “刷新”紧邻“搜索”右侧,只重新读取本地任务列表,不请求 Admin,也不操作手机。
|
2026-08-11 10:42:51 +08:00
|
|
|
|
- 页面右侧显示“全选”“反选”和“已选 N 条”,之后依次是“重新上报”“重新采集”“重新采购”“删除”。
|
2026-08-11 09:43:29 +08:00
|
|
|
|
- “全选”和“反选”只作用于当前筛选条件下已经加载的真实任务行,空白占位行不参与。全选后新领取或继续增量加载的任务默认不勾选,防止批量操作范围在用户不知情时扩大。
|
|
|
|
|
|
- 没有真实任务,或正在重新执行、重新上报、删除时,禁用“全选”和“反选”。
|
2026-08-12 10:23:27 +08:00
|
|
|
|
- “重新采集”和“重新采购”优先作用于复选框勾选集;没有勾选时,作用于鼠标单击或键盘选中的当前行。两者都没有时提示“请勾选任务或单击选择一行”。点击后先批量预检,再显示可执行、类型过滤和安全阻止数量;“取消”默认聚焦,点击、按 `Escape` 或关闭弹窗都不得启动任务。
|
2026-08-10 09:35:46 +08:00
|
|
|
|
- 用户确认后,工作线程必须先检查已保存 Android 设备的实际连接状态,再重置任务和创建执行记录。检查失败时保留原任务状态、采集结果和执行历史。
|
2026-08-11 10:42:51 +08:00
|
|
|
|
- 重新采集只处理采集任务,重新采购只处理采购任务,另一类型计入过滤数量。执行中、结果待提交、仍有未发送 Outbox、已经排队或自动获取忙碌时必须阻止,并用中文说明原因。
|
2026-08-09 21:44:29 +08:00
|
|
|
|
- 确认后只执行选中的稳定任务编号,不领取新任务,不先处理其他任务或 Outbox。
|
2026-08-10 09:08:54 +08:00
|
|
|
|
- 重新采集在工作线程运行。执行期间禁用“获取任务”,“重新执行”变为可点击的“停止重新采集”。
|
|
|
|
|
|
- 点击停止后按钮显示“正在停止…”并禁用重复点击;底部明确说明正在等待手机当前操作结束。uiautomator2/ADB 的单次调用返回后,采集在下一个安全检查点停止;不得强制结束工作线程。
|
2026-08-10 10:16:11 +08:00
|
|
|
|
- 重新采集的前置校验警告和后台错误 `InfoBar` 都显示在软件窗口顶部水平居中,并有可见的“关闭提示”按钮;提示 5 秒后自动关闭,同一时间只保留一条,新提示替换旧提示。
|
2026-08-09 21:44:29 +08:00
|
|
|
|
- 每次重新采集创建新的执行记录和幂等键;当前结果更新,旧结果保存在历史执行记录中。
|
2026-08-11 10:42:51 +08:00
|
|
|
|
- 重新采购逐条串行执行。任一任务未形成“执行、核单、上报”成功闭环时立即停止剩余队列。历史上任何一次执行已有 `irreversible_action_at` 的任务永久禁止重新采购,只能只读核单。
|
2026-08-11 11:20:22 +08:00
|
|
|
|
- 用户确认批量操作后立即刷新列表,并在状态区显示已加入队列的数量。排队中的任务保持原状态;
|
|
|
|
|
|
只有工作线程已经把当前任务写成 `running` 后,才通知主线程刷新该行并显示“采集中”或
|
|
|
|
|
|
“采购中”。每条完成、失败、跳过或取消后再次刷新,再开始下一条。
|
2026-08-11 18:21:25 +08:00
|
|
|
|
- 自动流程对每条任务只执行一次,不显示“等待重试”。普通失败按任务类型显示
|
|
|
|
|
|
“采集失败”或“采购失败”,详情保留具体原因;失败信息成功上报后继续下一条。
|
|
|
|
|
|
只有设备、登录、验证码、风控、Admin 或不可逆采购等全局问题才停止队列。
|
2026-08-10 10:56:48 +08:00
|
|
|
|
- “重新上报”作用于当前已经加载并勾选的任务。执行前显示任务数量,并明确说明不会重新采集、采购或操作手机。
|
2026-08-10 11:18:06 +08:00
|
|
|
|
- 重新上报先查找每条任务最早一条尚未发送的 Outbox,包括 `task_failure`;全部事件都已发送时,才重发最新的 `collect_result` 或 `purchase_result`。
|
|
|
|
|
|
- 重新上报使用事件原来的 `idempotency_key` 和 `payload_json`,不重新组装数据、不创建新 Outbox。历史结果再次得到 Admin 确认时不得覆盖任务最新执行状态;最新失败信息上报成功后应恢复对应失败状态。
|
2026-08-10 10:56:48 +08:00
|
|
|
|
- 重新上报不依赖 Android 设备,在独立工作线程中逐条提交。自动获取、重新执行或另一批上报运行时不得启动。
|
|
|
|
|
|
- 批量完成后显示成功、失败和跳过数量。成功任务取消勾选;失败及没有结果 Outbox 的跳过任务保留勾选,方便继续处理。
|
|
|
|
|
|
- 相同幂等键和相同内容只用于让 Admin 再次确认已接收,不代表创建新结果或覆盖 Admin 数据。
|
2026-08-10 17:23:24 +08:00
|
|
|
|
- “删除”作用于当前勾选集。未勾选时禁用;执行前用主窗口居中的确认框显示准确数量,“暂不删除”默认聚焦,按 `Escape` 或关闭弹窗不修改数据。
|
|
|
|
|
|
- 删除只允许已完成、失败或已取消的任务。存在待发送、发送中、发送失败 Outbox,或任一执行记录已进入不可逆阶段时,整批拒绝并显示中文原因。
|
|
|
|
|
|
- 删除只把任务从普通列表软隐藏,不请求 Admin、不操作 Android,也不修改任务状态、执行记录或 Outbox。成功后清除对应勾选并刷新当前筛选结果。
|
2026-08-09 21:44:29 +08:00
|
|
|
|
|
2026-08-06 11:44:39 +08:00
|
|
|
|
## 5. 任务表格
|
|
|
|
|
|
|
2026-08-10 17:23:24 +08:00
|
|
|
|
表格显示的是**本机已领取且未被软隐藏的任务**,包括正在做的和早已做完的。已完成任务和审计数据永久保留;用户删除后只是不再出现在普通列表,所以数据库数据仍只增不减——增量加载和索引是必须的,不是优化。
|
2026-08-06 11:44:39 +08:00
|
|
|
|
|
|
|
|
|
|
使用 `qfluentwidgets.TableView`、自定义 `QAbstractTableModel` 和 Repository 查询。不得使用行号作为任务身份,也不得把完整 `pdd_data` 放入模型。
|
|
|
|
|
|
|
|
|
|
|
|
### 5.1 列定义
|
|
|
|
|
|
|
|
|
|
|
|
| 列 | 对齐 | 显示规则 |
|
|
|
|
|
|
|---|---|---|
|
2026-08-10 10:56:48 +08:00
|
|
|
|
| 选择 | 居中 | 行首复选框;占位空行不可勾选,身份使用 `remote_task_id` |
|
2026-08-06 11:44:39 +08:00
|
|
|
|
| 任务类型 | 居中 | “采集”或“采购”,颜色只作辅助 |
|
|
|
|
|
|
| 商品标题 | 左对齐、可伸展 | 空值显示“尚未获取标题”,截断时提供完整工具提示 |
|
2026-08-11 11:17:44 +08:00
|
|
|
|
| 店铺名 | 左对齐 | 采集到的店铺名;无值显示 `—` |
|
2026-08-06 11:44:39 +08:00
|
|
|
|
| 颜色 | 左对齐 | 无值显示 `—` |
|
|
|
|
|
|
| 尺码 | 左对齐 | 无值显示 `—` |
|
|
|
|
|
|
| 价格 | 右对齐 | 格式为 `¥39.90`;采集摘要可显示 `¥39.90 起` |
|
|
|
|
|
|
| 数量 | 右对齐 | 采购数量;采集显示 `—` |
|
|
|
|
|
|
| 状态 | 居中 | 中文状态文字和状态图标/标记 |
|
2026-08-11 11:17:44 +08:00
|
|
|
|
| 用时 | 居中 | 已结束显示整数秒,执行中显示“进行中”,尚未执行显示 `—` |
|
2026-08-06 11:44:39 +08:00
|
|
|
|
| 更新时间 | 左对齐 | 本地时区,精确到秒或按产品确认格式 |
|
|
|
|
|
|
|
|
|
|
|
|
### 5.2 数据行为
|
|
|
|
|
|
|
|
|
|
|
|
- 默认排序为 `updated_at DESC, id DESC`。
|
|
|
|
|
|
- 排序、筛选和数据变化后以稳定任务编号恢复当前行。
|
2026-08-10 10:56:48 +08:00
|
|
|
|
- 当前行和勾选集相互独立;点击复选框不打开详情,也不启动任何业务操作。
|
2026-08-12 10:23:27 +08:00
|
|
|
|
- 当前行只作为“重新采集”和“重新采购”在勾选集为空时的单条兜底;已有勾选时不得把当前行混入操作范围。“重新上报”和“删除”仍只作用于勾选集。
|
2026-08-10 10:56:48 +08:00
|
|
|
|
- 勾选集使用 `remote_task_id` 保存。普通刷新保留仍存在的勾选,搜索条件变化清空勾选,避免操作被筛选隐藏的旧任务。
|
2026-08-06 11:44:39 +08:00
|
|
|
|
- 初始加载有限批次,滚动时通过 `canFetchMore/fetchMore` 增量加载后续任务。
|
|
|
|
|
|
- 单击非交互区域只设置当前行和选择。
|
2026-08-11 09:43:29 +08:00
|
|
|
|
- 双击任务行的非复选框区域或按 Enter 执行非破坏性的详情命令;复选框只改变勾选状态。
|
|
|
|
|
|
- 表格提供“双击任务行或按 Enter 查看详情”的工具提示和无障碍说明。
|
2026-08-06 11:44:39 +08:00
|
|
|
|
- 空状态要分情况,文案不能一样:
|
|
|
|
|
|
- 加载中;
|
2026-08-06 15:58:09 +08:00
|
|
|
|
- **从没领取过任务** —— “还没有领取过任务,点击‘获取任务’开始领取”(不要写“暂无数据”,操作人员会以为是出错了);
|
2026-08-06 11:44:39 +08:00
|
|
|
|
- 筛选无结果 —— 提供清除条件入口;
|
|
|
|
|
|
- Admin 离线 —— 本地数据照常显示,只在信息条说明领取失败;
|
|
|
|
|
|
- 加载失败。
|
|
|
|
|
|
|
2026-08-12 09:21:21 +08:00
|
|
|
|
> **界面上说的"任务编号"一律指 `remote_task_id`**(例如采集 `cj1`、采购 `cg1`),不是数据库自增 `id`,更不是表格行号。行号会随排序和筛选变化,拿它当任务身份必出错。
|
2026-08-06 11:44:39 +08:00
|
|
|
|
|
|
|
|
|
|
### 5.3 增量加载模板(照抄即可)
|
|
|
|
|
|
|
|
|
|
|
|
任务可能有几万条,一次全查出来界面会卡住。做法是:先查 50 条,用户滚到底了再查下一批。Qt 的模型/视图自带这个机制,实现 `canFetchMore` 和 `fetchMore` 两个方法就行。
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
from PyQt5.QtCore import QAbstractTableModel, QModelIndex, Qt
|
|
|
|
|
|
|
|
|
|
|
|
PAGE_SIZE = 50
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TaskTableModel(QAbstractTableModel):
|
|
|
|
|
|
"""任务表格的数据来源。只存显示需要的几个字段。"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, repository, parent=None):
|
|
|
|
|
|
super().__init__(parent)
|
|
|
|
|
|
self._repository = repository
|
|
|
|
|
|
self._rows: list[dict] = [] # 已经加载进来的行
|
|
|
|
|
|
self._filters: dict = {} # 当前搜索条件
|
|
|
|
|
|
self._has_more = True # 数据库里还有没有没取完的
|
|
|
|
|
|
|
|
|
|
|
|
def rowCount(self, parent=QModelIndex()) -> int:
|
|
|
|
|
|
return 0 if parent.isValid() else len(self._rows)
|
|
|
|
|
|
|
|
|
|
|
|
def canFetchMore(self, parent=QModelIndex()) -> bool:
|
|
|
|
|
|
"""Qt 会自己调用它,问"还能再加载吗"。"""
|
|
|
|
|
|
return not parent.isValid() and self._has_more
|
|
|
|
|
|
|
|
|
|
|
|
def fetchMore(self, parent=QModelIndex()) -> None:
|
|
|
|
|
|
"""Qt 在用户滚到底时自己调用它。"""
|
|
|
|
|
|
if parent.isValid():
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
page = self._repository.list_tasks(
|
|
|
|
|
|
filters=self._filters, limit=PAGE_SIZE, offset=len(self._rows)
|
|
|
|
|
|
)
|
|
|
|
|
|
if not page:
|
|
|
|
|
|
self._has_more = False
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# 必须用 begin/end 包住,直接改 self._rows 界面不会刷新
|
|
|
|
|
|
start = len(self._rows)
|
|
|
|
|
|
self.beginInsertRows(QModelIndex(), start, start + len(page) - 1)
|
|
|
|
|
|
self._rows.extend(page)
|
|
|
|
|
|
self.endInsertRows()
|
|
|
|
|
|
|
|
|
|
|
|
self._has_more = len(page) == PAGE_SIZE
|
|
|
|
|
|
|
|
|
|
|
|
def apply_filters(self, filters: dict) -> None:
|
|
|
|
|
|
"""换搜索条件时整表重来。"""
|
|
|
|
|
|
self.beginResetModel()
|
|
|
|
|
|
self._filters = filters
|
|
|
|
|
|
self._rows = []
|
|
|
|
|
|
self._has_more = True
|
|
|
|
|
|
self.endResetModel()
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
注意:
|
|
|
|
|
|
|
|
|
|
|
|
| 别这么做 | 为什么 |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| 直接 `self._rows.append(...)` 不加 `beginInsertRows` | 界面不刷新,或者直接崩 |
|
|
|
|
|
|
| 把整个 `pdd_data` 存进 `_rows` | 几万条 JSON 全在内存里,程序会变得很卡 |
|
|
|
|
|
|
| 在 `fetchMore` 里发网络请求 | 这是主线程,界面会卡住。这里只能查本地 SQLite |
|
2026-08-11 09:43:29 +08:00
|
|
|
|
| 给每个单元格 `setIndexWidget` 放按钮 | 几万个控件会明显增加内存;任务详情使用行默认操作,不创建单元格按钮 |
|
2026-08-06 11:44:39 +08:00
|
|
|
|
|
|
|
|
|
|
换筛选之后要恢复用户原来选中的行:**先记下当前行的 `remote_task_id`,重新加载完再按这个编号找回来**,不要记行号。
|
|
|
|
|
|
|
|
|
|
|
|
## 6. 任务详情
|
|
|
|
|
|
|
|
|
|
|
|
详情是信息查看,不要求用户立即决策,优先使用非模态详情窗口或右侧详情面板,不使用简单消息框承载长 JSON。
|
|
|
|
|
|
|
2026-08-11 09:43:29 +08:00
|
|
|
|
当前 Client 使用可调整大小的非模态详情窗口。用户可以双击任务行,或选中任务后按 Enter 打开。相同任务只保留一个详情窗口;再次打开时切回已有窗口。按 Esc 或“关闭”按钮返回任务列表。
|
2026-08-08 09:46:58 +08:00
|
|
|
|
|
2026-08-11 11:32:37 +08:00
|
|
|
|
详情中的字段值支持鼠标选择和键盘选择。双击字段值或在字段获得焦点后按 `Ctrl+C`,
|
|
|
|
|
|
复制该字段的完整文字,并在窗口底部短暂显示“已复制:字段名”。颜色名称、颜色价格和
|
|
|
|
|
|
尺码分别复制;字段名称和章节标题不可复制。商品链接只复制,不自动打开浏览器。
|
|
|
|
|
|
|
2026-08-08 10:29:59 +08:00
|
|
|
|
详情面向采购人员,任务类型、状态、当前步骤和错误说明优先使用中文。数据库中的 `current_step` 等内部值只用于业务判断和日志,不直接显示;遇到尚未适配的新步骤时显示“未知步骤”。
|
2026-08-10 00:27:52 +08:00
|
|
|
|
采购演练和恢复至少要明确显示“演练已在提交前停止”、“结果待提交”、
|
|
|
|
|
|
“只允许核对订单”和“需要人工处理”;不能只用颜色表达安全状态。
|
2026-08-10 16:54:06 +08:00
|
|
|
|
真实提交后的详情还要区分“订单已提交,只允许核对未付款订单”、
|
|
|
|
|
|
“已提交待付款,等待向 Admin 上报”和“订单结果不确定或存在多个候选”。
|
2026-08-08 10:29:59 +08:00
|
|
|
|
|
2026-08-08 09:46:58 +08:00
|
|
|
|
采集规格按下面顺序展示:
|
|
|
|
|
|
|
|
|
|
|
|
1. **颜色:**放在上方,每个颜色旁边直接显示采集到的价格;未采集到价格时显示“价格未采集”。同一颜色意外出现多个价格时显示最低价到最高价,并显示“价格不一致”,方便检查采集结果。
|
|
|
|
|
|
2. **尺码:**放在颜色下方,只显示尺码文字。
|
|
|
|
|
|
|
|
|
|
|
|
颜色和尺码按采集顺序显示并去重。不可用颜色除文字外还要明确显示“不可用”,不能只改变颜色。没有规格数据时保留对应分区并显示空状态。
|
|
|
|
|
|
|
2026-08-06 11:44:39 +08:00
|
|
|
|
详情至少分为:
|
|
|
|
|
|
|
|
|
|
|
|
1. **基本信息:**任务编号、类型、商品、规格、数量、金额和状态。
|
|
|
|
|
|
2. **执行状态:**当前步骤、设备、尝试次数、开始/结束时间和最近错误。
|
|
|
|
|
|
3. **PDD 数据:**标题、店铺、销量、评价、规格维度和 SKU 表格。
|
|
|
|
|
|
4. **采购结果:**演练/真实模式、确认规格、价格、订单编号、下单时间和核对状态。
|
|
|
|
|
|
5. **原始数据与诊断:**格式化 `admin_payload`、`pdd_data`、日志和 Artifact 入口。
|
|
|
|
|
|
|
|
|
|
|
|
原始 JSON 默认折叠,提供复制或导出操作;复制前不得包含凭据和敏感信息。
|
|
|
|
|
|
|
|
|
|
|
|
## 7. 底部状态区
|
|
|
|
|
|
|
|
|
|
|
|
状态区占据稳定位置,示例:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
自动获取:已开启 · 当前任务 PDD-0001(采集)· 正在遍历规格 · 最近领取 16:20:31
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
应表达:
|
|
|
|
|
|
|
|
|
|
|
|
- 引擎状态:已停止、轮询中、领取中、执行中、提交中、退避等待、正在停止。
|
|
|
|
|
|
- 当前任务和当前步骤。
|
|
|
|
|
|
- 最近一次领取到任务的时间。
|
|
|
|
|
|
- 可恢复错误摘要及下一步。
|
|
|
|
|
|
|
|
|
|
|
|
高频轮询无任务时不要连续弹提示。设备断开、认证失败、结果提交持续失败等需要操作的问题使用 `InfoBar`,并提供“重试”“打开设置”或“查看详情”。
|
|
|
|
|
|
|
|
|
|
|
|
## 8. 设置页
|
|
|
|
|
|
|
|
|
|
|
|
设置页使用可滚动布局和 Fluent 设置卡片,分组如下。
|
|
|
|
|
|
|
2026-08-11 09:24:04 +08:00
|
|
|
|
### 当前设备
|
|
|
|
|
|
|
|
|
|
|
|
- 第一行依次显示设备号和设备名;设备号只读,设备名可以修改,两组输入随窗口宽度平均伸缩;
|
2026-08-11 11:50:29 +08:00
|
|
|
|
- 第二行显示“管理端地址”,对应 SQLite 中已有的 `admin.base_url`;默认地址为 `https://buy.833729.com`,没有保存值或保存值仍是旧默认地址 `http://127.0.0.1:8080` 时自动迁移,用户手动保存的其他地址不得覆盖;
|
2026-08-11 09:24:04 +08:00
|
|
|
|
- 点击“保存”前先验证地址;只接受有效的 HTTP/HTTPS 地址,不能包含账号、密码、查询参数或片段。验证失败时不保存设备号、设备名或地址,并把焦点放回地址输入框;
|
|
|
|
|
|
- 保存成功后立即用新地址登记 Client,后续任务领取、结果提交和失败提交也统一切换到新地址;已经发出的请求等待自身结束,不强制中断;
|
|
|
|
|
|
- 保存不新增数据库表,不增加心跳,也不连接或操作 Android 设备。
|
|
|
|
|
|
|
2026-08-06 11:44:39 +08:00
|
|
|
|
### Admin
|
|
|
|
|
|
|
|
|
|
|
|
- 服务地址;
|
|
|
|
|
|
- Client 编号;
|
|
|
|
|
|
- 认证状态,不直接回显完整令牌;
|
|
|
|
|
|
- 请求超时;
|
|
|
|
|
|
- “测试连接”及最近结果。
|
|
|
|
|
|
|
|
|
|
|
|
### Android 设备
|
|
|
|
|
|
|
2026-08-11 12:01:07 +08:00
|
|
|
|
- Client 只使用发布包内置的 ADB,不回退系统 `Path`;启动时缺少 `adb.exe` 或配套 DLL,应在主窗口中央弹出带明显关闭按钮的中文提示,并禁用需要 ADB 的设备命令;
|
2026-08-06 11:44:39 +08:00
|
|
|
|
- ADB 地址;
|
|
|
|
|
|
- PDD 包名;
|
|
|
|
|
|
- “测试连接”;
|
|
|
|
|
|
- 当前设备型号、Android 版本和 PDD 当前状态摘要。
|
2026-08-09 22:18:55 +08:00
|
|
|
|
- 点击设备“搜索”时可以先尝试恢复已保存的 Wi-Fi ADB,但单台设备恢复失败
|
|
|
|
|
|
不能中断普通设备枚举。表格仍展示 `adb devices -l` 实际找到的 USB 和 Wi-Fi
|
|
|
|
|
|
设备,状态文字同时说明恢复失败原因以及“已保存配置仍保留”。
|
|
|
|
|
|
- 只有普通设备枚举本身失败时才显示“搜索失败”并保留旧表格;程序不得因为
|
|
|
|
|
|
设备暂时离线自动删除已保存配置。
|
2026-08-06 11:44:39 +08:00
|
|
|
|
|
|
|
|
|
|
### 自动化
|
|
|
|
|
|
|
|
|
|
|
|
- 轮询周期;
|
|
|
|
|
|
- 任务超时;
|
|
|
|
|
|
- 最大重试次数;
|
|
|
|
|
|
- 演练模式开关。
|
|
|
|
|
|
|
2026-08-10 18:37:35 +08:00
|
|
|
|
### 真实采购运行就绪(不支付)
|
2026-08-10 16:35:38 +08:00
|
|
|
|
|
2026-08-10 18:37:35 +08:00
|
|
|
|
- 设置页不显示真实采购授权、确认文字或启用/关闭按钮;
|
|
|
|
|
|
- Client 身份和 Android 设备保存后,真实采购 Adapter 就绪的 Client 自动登记 live 能力;
|
|
|
|
|
|
- 身份、设备或 Adapter 任一未就绪时不领取真实采购任务,页面继续通过现有设备状态说明缺失项;
|
|
|
|
|
|
- 自动就绪只允许创建未付款订单,不允许自动付款。
|
2026-08-10 16:35:38 +08:00
|
|
|
|
|
2026-08-06 11:44:39 +08:00
|
|
|
|
### 安全与诊断
|
|
|
|
|
|
|
|
|
|
|
|
- 最大购买数量;
|
|
|
|
|
|
- 价格允许偏差;
|
|
|
|
|
|
- Artifact 目录;
|
|
|
|
|
|
- 保留天数;
|
|
|
|
|
|
- 打开日志目录。
|
|
|
|
|
|
|
2026-08-10 12:32:39 +08:00
|
|
|
|
### 软件更新
|
|
|
|
|
|
|
|
|
|
|
|
- 显示只读的当前版本;
|
2026-08-11 09:24:04 +08:00
|
|
|
|
- 清单地址独占一行;账号和密码在下一行并排显示,输入区域按约 45:55 自适应伸缩;
|
|
|
|
|
|
- 三个字段都有可见标签,密码框默认隐藏且不回填;
|
2026-08-10 14:31:44 +08:00
|
|
|
|
- “保存”把 URL/账号写入 SQLite,把密码写入 Windows 凭据管理器;已有密码时留空
|
|
|
|
|
|
表示不修改,修改账号时必须重新输入密码;
|
|
|
|
|
|
- “检查更新”只使用已经保存的配置;存在未保存修改时先提示保存;检查和下载期间
|
|
|
|
|
|
输入框及按钮禁用,防止并行任务和配置竞态;
|
|
|
|
|
|
- 已保存完整配置时启动后自动检查一次;没有新版或网络失败不弹模态框;
|
2026-08-10 12:32:39 +08:00
|
|
|
|
- 状态文字稳定显示未配置、检查中、已是最新、发现新版、下载进度、已准备、失败和
|
|
|
|
|
|
恢复建议;
|
|
|
|
|
|
- 发现新版本时使用有明确“下载更新 / 暂不下载”的确认框;下载完成不强制重启,
|
|
|
|
|
|
提示操作人员完成当前任务后自行关闭并重新启动。
|
|
|
|
|
|
|
2026-08-06 11:44:39 +08:00
|
|
|
|
设置采用显式“保存设置”或项目统一的即时保存模式,不能在同一页面随机混用。MVP 推荐显式保存,验证失败时保留输入并聚焦第一个错误字段。
|
|
|
|
|
|
|
|
|
|
|
|
## 9. 状态与反馈
|
|
|
|
|
|
|
|
|
|
|
|
| 场景 | 反馈方式 |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| 领取到新任务 | 更新表格和状态区,不弹模态框 |
|
|
|
|
|
|
| 暂时没有可领取的任务 | 只更新状态区,**不要连续弹提示** |
|
|
|
|
|
|
| 搜索无结果 | 表格空状态和清除条件入口 |
|
2026-08-10 10:05:45 +08:00
|
|
|
|
| Admin 暂时离线 | 窗口顶部居中的较大信息条,5 秒后自动关闭;底部保留错误,本地数据继续可用 |
|
|
|
|
|
|
| 设备断开 | 窗口顶部居中的较大信息条,提供打开设置和关闭提示,5 秒后自动关闭 |
|
2026-08-06 11:44:39 +08:00
|
|
|
|
| 任务运行 | 底部状态区和必要进度,不阻塞整个窗口 |
|
|
|
|
|
|
| 任务失败 | 行状态、详情和信息条,不显示原始堆栈 |
|
|
|
|
|
|
| 多个订单候选 | 状态改为“需要人工处理”,打开详情决策 |
|
|
|
|
|
|
| 普通任务成功 | 更新行和状态区,不弹“成功”对话框 |
|
2026-08-10 12:32:39 +08:00
|
|
|
|
| 更新检查或下载失败 | 软件更新卡片保留地址并显示原因和重试方式,当前程序不变 |
|
2026-08-10 14:31:44 +08:00
|
|
|
|
| 更新设置保存成功 | 状态区提示已安全保存,密码输入框清空并显示已保存占位文字 |
|
2026-08-10 12:32:39 +08:00
|
|
|
|
| 更新下载完成 | 软件更新卡片持续提示“下次启动生效”,不强制关闭程序 |
|
2026-08-06 11:44:39 +08:00
|
|
|
|
|
|
|
|
|
|
### 9.1 错误提示模板(照抄即可)
|
|
|
|
|
|
|
|
|
|
|
|
规则很简单:**普通成功什么都不弹**(更新界面就够了);**出错用 `InfoBar`**;**只有必须让用户当场做决定时才用对话框**。
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
from PyQt5.QtCore import Qt
|
|
|
|
|
|
from PyQt5.QtWidgets import QPushButton
|
|
|
|
|
|
from qfluentwidgets import InfoBar, InfoBarPosition
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def show_recoverable_error(self, title: str, content: str, on_retry) -> None:
|
|
|
|
|
|
"""显示一条可重试的错误提示。
|
|
|
|
|
|
|
|
|
|
|
|
title:一句话说清出了什么事
|
|
|
|
|
|
content:说清已经保留了什么、下一步该干什么
|
|
|
|
|
|
on_retry:点"重试"时调用的函数
|
|
|
|
|
|
"""
|
|
|
|
|
|
bar = InfoBar.error(
|
|
|
|
|
|
title=title,
|
|
|
|
|
|
content=content,
|
|
|
|
|
|
orient=Qt.Horizontal,
|
|
|
|
|
|
isClosable=True,
|
|
|
|
|
|
position=InfoBarPosition.TOP_RIGHT,
|
|
|
|
|
|
duration=-1, # -1 = 不自动消失。重要错误必须用 -1
|
|
|
|
|
|
parent=self,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
retry_button = QPushButton("重试", bar)
|
|
|
|
|
|
retry_button.clicked.connect(on_retry)
|
|
|
|
|
|
retry_button.clicked.connect(bar.close)
|
|
|
|
|
|
bar.addWidget(retry_button)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
调用示例:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
self.show_recoverable_error(
|
|
|
|
|
|
title="Admin 连接失败",
|
|
|
|
|
|
content="本地任务仍可查看,结果已保存,联网后会自动提交。",
|
|
|
|
|
|
on_retry=self._sync_task_list,
|
|
|
|
|
|
)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
写提示语的三条要求(对应 [06 质量与安全](06-quality-security.md) §5):
|
|
|
|
|
|
|
|
|
|
|
|
1. **说清发生了什么** —— "Admin 连接失败",不是"操作失败"。
|
|
|
|
|
|
2. **说清保住了什么** —— "结果已保存"。用户最怕的是白干。
|
|
|
|
|
|
3. **说清下一步** —— 给"重试"按钮,或者给"打开设置"。
|
|
|
|
|
|
|
|
|
|
|
|
不要做的事:
|
|
|
|
|
|
|
|
|
|
|
|
| 别这么做 | 改成 |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| 把 Python 异常堆栈贴到界面上 | 界面显示人话,堆栈写进日志 |
|
|
|
|
|
|
| 轮询没任务也弹一条提示 | 只更新底部状态区 |
|
|
|
|
|
|
| 用 `QMessageBox` 报告普通错误 | 用 `InfoBar`,别打断用户 |
|
|
|
|
|
|
| 错误提示 3 秒自动消失 | 重要错误 `duration=-1`,让用户自己关 |
|
|
|
|
|
|
|
|
|
|
|
|
## 10. 键盘与无障碍
|
|
|
|
|
|
|
2026-08-11 10:42:51 +08:00
|
|
|
|
- Tab 顺序:自动获取 → 类型 → 状态 → 关键词 → 搜索 → 刷新 → 全选 → 反选 → 重新上报 → 重新采集 → 重新采购 → 删除 → 表格 → 状态区可操作项。
|
2026-08-06 11:44:39 +08:00
|
|
|
|
- `Ctrl+F` 聚焦关键词,Enter 打开当前行详情。不绑定 `F5`——界面上没有需要刷新的远端数据。
|
|
|
|
|
|
- 仅图标按钮必须设置准确的无障碍名称和工具提示。
|
|
|
|
|
|
- 表单具有可见标签,占位符不能替代标签。
|
|
|
|
|
|
- 状态、错误和任务类型不能只依赖颜色。
|
|
|
|
|
|
- 表格焦点、当前行和选择状态必须可区分。
|
|
|
|
|
|
- 支持浅色、深色、高对比度和 100%–200% 显示缩放。
|
|
|
|
|
|
|
|
|
|
|
|
## 11. 原型与验收
|
|
|
|
|
|
|
|
|
|
|
|
该页面属于主要界面改版。正式重写桌面界面前,建议先制作使用模拟数据的可运行 HTML 原型,确认信息架构、列宽、筛选、详情和状态变化。HTML 原型只用于确认交互,不能替代 PyQt 的窗口、性能、DPI 和无障碍验证。
|
|
|
|
|
|
|
|
|
|
|
|
桌面实现至少验证:
|
|
|
|
|
|
|
|
|
|
|
|
- 空数据、少量数据、大量数据和超长标题;
|
|
|
|
|
|
- 紧凑、默认、最大化和 Windows 贴靠;
|
|
|
|
|
|
- 筛选和增量加载后的当前行保持;
|
|
|
|
|
|
- 快速重复点击、后台迟到结果和窗口关闭;
|
|
|
|
|
|
- 键盘、浅色、深色、高对比度和 200% 缩放。
|