Files
cmautobuy/docs/admin/05-ui-specification.md
T
chengmaandClaude Opus 5 90379347b4 docs: 建立 Admin 子项目的文档基线
仓库从单子项目变成两个:client(Python + PyQt5 桌面端)和
admin(Go + Gin + HTML 模板 Web 管理端)。两者技术栈完全不同,
规则各自独立,只通过三个 HTTP 接口交互。

新增 admin/AGENTS.md 和 docs/admin/ 共 9 份文档,结构与 docs/client/ 对齐。

关键设计决策(均已与用户确认)
- 四模块:蝦皮数据 / 顺运宝数据 / 采购任务 / 客户端列表,
  统一三段式布局(工具条 / 带勾选的表格 / 状态条)
- 蝦皮数据拆成商品表 + SKU 表:报表本身就是两层结构,
  且 PDD 链接是商品级的,放 SKU 级会重复维护
- pdd_data 存商品级,一个 PDD 商品采一次,所有订单共用
- SKU 映射独立成表且可复用,同一蝦皮 SKU 只需人工匹配一次
- PDD 链接人工填写,采集按钮就放在填链接的编辑弹窗里
- 任务分配给指定客户端;不加心跳,注册在领取时完成,
  在线状态由 last_seen_at 派生
- SQLite 驱动固定 modernc.org/sqlite(纯 Go 免 cgo,go build 直接出 exe)
- 不引入前端框架和 npm 构建,只允许原生 JS 或 htmx

基于样本文件 raw_data/蝦皮数据样本.xlsx 实测得出的约束
- 11287 行 = 5195 商品汇总行 + 6092 SKU 行,导入必须分开处理
- 商品規格ID 零重复,是天然主键
- 规格原文两种格式各占 53.4% / 46.6%,只解析【】会漏掉一半
- 平均每商品仅 1.17 个 SKU,报表不是全量目录,
  因此必须支持手动新增,且货运单外键不能加硬约束

同步更新
- 根 AGENTS.md 开头的指针改为两个子项目对照表
- docs/README.md 重构为双子项目索引
- .gitignore 加 admin/data/、admin.exe、Excel 锁文件

说明:Gitea 尚未配置,本次无对应工单号。
raw_data/ 未提交,含台币销售额等商业数据,待用户决定。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 15:49:04 +08:00

234 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 05 Admin 界面规范
- 文档状态:基线草案,待界面评审
- 技术栈:Go `html/template` 服务端渲染 + 手写 CSS + 原生 JS / htmx
本文档中 `[必须]` / `[建议]` / `[待定]` 的含义见 [文档索引](../README.md#文档标注说明)。
没有标注的默认是 `[必须]`。看不懂的词查 [术语表](00-glossary.md)。
## 1. 设计目标
- 四个模块**长得一样**,写完第一个,其余三个照抄结构改字段;
- 操作员一眼看出"这条数据卡在哪一步、下一步该点什么";
- 不引入前端框架和构建流程;
- 破坏性操作(删除、导入)必须有确认,不能手滑。
## 2. 统一的三段式布局
`[必须]` 四个模块全部用这个结构,**不要给某个模块搞特殊**:
```text
┌────────────────────────────────────────────────────────────┐
│ [导航] 蝦皮数据 │ 顺运宝数据 │ 采购任务 │ 客户端列表 │
├────────────────────────────────────────────────────────────┤
│ 顶部工具条:[操作按钮…] [搜索框] [搜索] [删除] │
├────────────────────────────────────────────────────────────┤
│ ☐ │ 列1 │ 列2 │ 列3 │ … │
│ ☐ │ │ │ │ │
│ 数据表格(占满剩余高度) │
│ │
├────────────────────────────────────────────────────────────┤
│ 底部状态条:最近一次操作的结果和关键统计 │
└────────────────────────────────────────────────────────────┘
```
`[必须]` 三段做成 `templates/partials/` 里的公共片段复用,
**不要每个页面复制一份**——改一次样式要改四处,必然改漏。
`[必须]` 页面在 1366×768 上可用。列多了让表格**横向滚动**,不要压缩列宽把字挤成两行。
## 3. 表格通用规则
- `[必须]` 第一列是勾选框,表头有全选。
- `[必须]` 行的身份用**业务主键**(商品規格ID、货运单ID、任务编号、序列号),
**不得用行号**——排序和筛选一变行号就错位。
- `[必须]` 批量删除要二次确认,弹窗里写清"**将删除 N 条,不可恢复**"。
- `[必须]` 删除、导入用 **POST**,不得用 GET。浏览器和插件会预取 GET 链接。
- `[建议]` 默认按 `更新时间 DESC` 排序。
- `[建议]` 一页 50 条,超过分页。搜索走数据库,不要一次查出来在内存里过滤。
- `[必须]` 长文本(商品标题)截断显示,鼠标悬停给完整内容。
- `[必须]` 空状态要分情况,文案不能都是"暂无数据":
- 从没导入过 → "还没有数据,点左上角『导入』开始"
- 搜索无结果 → "没有匹配的记录" + 清除条件入口
## 4. 蝦皮数据页
### 4.1 工具条
```text
[导入 Excel] [批量采集] 商品ID [________] [搜索] [删除]
```
- **导入 Excel**:选文件 → POST 上传 → 显示结果统计
(导入 5195 商品 / 6092 SKU,失败 N 行)。
`[必须]` 失败行要列出行号和原因,不能静默跳过。
- **批量采集**:勾选若干行 → 服务端**按商品去重**后建采集任务。
`[必须]` 已是"采集中"的商品跳过,并在结果里说明跳过了几个。
### 4.2 表格列
| 列 | 显示规则 |
|---|---|
| ☐ | 勾选 |
| 商品 ID | |
| 商品名称 | 截断 + 悬停完整 |
| 颜色 / 尺码 / 建议 | 解析失败的显示为 `—` 并**整行标黄**,提示需人工补 |
| PDD 链接 | 空的显示"**未填写**"并标红,这是最需要操作员注意的状态 |
| 采集状态 | 未填链接 / 未采集 / 采集中 / 已采集 / 采集失败 |
| 更新时间 | 本地时区 |
`[必须]` 状态不能只靠颜色区分,必须有文字。
### 4.3 编辑弹窗(双击行打开)
这是 **PDD 链接唯一的录入口**,也是采集的唯一发起点。
```text
┌─────────────────────────────────────────┐
│ 编辑商品 40653075144 │
├─────────────────────────────────────────┤
│ 商品名称 【台灣現貨隔日達】西裝外套… │ ← 只读
│ 规格原文 黑色,M【建議40-50公斤】 │ ← 只读,永远显示
│ │
│ 颜色 [黑色___________] │
│ 尺码 [M_______________] │
│ 建议 [40-50公斤_______] │
│ │
│ PDD 链接 [___________________] [采集] │ ← ★
│ 采集状态 已采集(2026-08-06 15:20) │
├─────────────────────────────────────────┤
│ [取消] [保存] │
└─────────────────────────────────────────┘
```
`[必须]` 几条硬规则:
- **采集按钮只在这里出现**,不要放到表格每一行。一个商品有 N 个 SKU 行,
放行上就是 N 个按钮干同一件事,还会建出 N 个重复任务。
- **规格原文只读且永远显示**。操作员靠它判断颜色尺码该怎么填。
- PDD 链接为空时**采集按钮置灰**,旁边提示"请先填写 PDD 链接"。
- 采集状态是"采集中"时按钮置灰,提示"已在采集中"。
- 采集失败时显示错误原因,按钮变成"重新采集"。
### 4.4 手动新增
`[必须]` 必须提供"新增 SKU"入口。
原因:蝦皮报表只包含**有销售成绩的** SKU(样本里平均每个商品仅 1.17 个),
订单来了查无此 SKU 是常态。没有这个入口整条链路就断了。
新增的行 `is_manual = 1`,`[必须]` 后续导入**不得删除**它们。
## 5. 顺运宝数据页
### 5.1 工具条
```text
[同步] [创建采购任务] 订单号 [________] [搜索] [删除]
```
`[待定]` MVP 阶段**同步按钮只做占位**:点击提示"同步功能待接入",不发请求。
### 5.2 表格列
☐ / 货运单ID / 订单号 / 商品标题 / 蝦皮商品ID / 规格SKU / 数量 /
价格(台币)/ 图片 / **匹配状态** / 更新时间
- 图片显示小缩略图,点击看大图。`[必须]` 存 URL,不要把图片塞进数据库。
- 匹配状态是**算出来的**(`sku_mappings` 里有没有记录),不是存的字段。
- 完整货运单 JSON 不作为列显示,在详情里看。
### 5.3 规格匹配弹窗(双击行打开)
```text
┌──────────────────────────────────────────────────┐
│ 规格匹配 · 订单 SO-20260806-001 │
├────────────────────────┬─────────────────────────┤
│ 蝦皮 │ 拼多多 │
│ │ │
│ 颜色 黑色 │ 颜色 [黑色 ▾] │
│ 尺码 M │ 尺码 [M码 ▾] │
│ 建议 40-50公斤 │ │
│ 原文 黑色,M【建議40…】 │ 单价 ¥39.90 │
├────────────────────────┴─────────────────────────┤
│ ⓘ 已有匹配记录,已自动带出,确认无误后保存 │
│ [取消] [保存] │
└──────────────────────────────────────────────────┘
```
`[必须]` 几条硬规则:
- 右侧下拉的选项来自该商品 `pdd_data` 里的 `dimensions`,
**不要写死"颜色/尺码"两个维度**——PDD 商品可能有第三个维度。
- **打开时先查 `sku_mappings`**,有记录就自动带出并提示"已自动带出"。
这是省人工的关键:同一个蝦皮 SKU 只需人工匹配一次。
- 该商品**还没采集**(`pdd_data` 为空)时,不要显示一个空下拉让人困惑,
直接提示:"该商品尚未采集 PDD 数据,请先到『蝦皮数据』填写链接并采集",
并给一个跳转链接。
- 保存写的是 `sku_mappings`(可复用),**不是这一张订单的临时数据**。
### 5.4 创建采购任务
勾选若干行 → 点按钮 → 逐条校验(见 [01 需求](01-requirements.md) §5)。
`[必须]` 校验不过的**不要静默跳过**,要列出来告诉操作员缺什么:
```text
成功创建 3 个任务。以下 2 条未创建:
· SO-...-007 该商品未填写 PDD 链接
· SO-...-009 规格未匹配
```
`[必须]` 创建前弹出确认框,让操作员选**分配给哪个客户端**,并确认价格上限。
价格上限默认从 `pdd_data` 带出,可改,**不允许为空**。
## 6. 采购任务页
工具条:`订单号 [____] [搜索] [删除]`
表格列:☐ / 订单号 / 商品标题 / 颜色 / 尺码 / 数量 / 价格上限 /
蝦皮ID / **分配客户端** / 状态 / 更新时间
- `[必须]` 颜色尺码显示的是 **PDD 侧**的规格(实际要买的),不是蝦皮的。
- `[必须]` 价格上限显示成 `¥42.00`(分转元),底层存的是整数分。
- `[建议]` 提供"改派"操作,把任务分给别的客户端——
没有心跳,客户端挂了要靠人工改派。
- 状态含义见 [01 需求](01-requirements.md) §6.2。
底部状态条显示各状态的条数统计。
## 7. 客户端列表页
工具条:`名称 [____] [搜索] [删除]`
表格列:☐ / 名称 / 序列号 / 状态 / 最近活动 / 更新时间
- `[必须]` **状态是算出来的**:`最近活动` 在 N 分钟内为"在线",否则"离线"。
`[建议]` N 默认 10 分钟。
- `[必须]` 名称可以人工改成好记的("办公室-01")。改过之后
**客户端上报的名称不再覆盖它**。
- `[建议]` 界面上说明一句:"客户端执行长任务期间可能显示为离线,属正常现象。"
因为没有心跳,这是已知且接受的取舍(见 [04](04-client-api.md) §3)。
## 8. 反馈方式
| 场景 | 怎么反馈 |
|---|---|
| 搜索、翻页 | 直接刷新表格,不弹任何东西 |
| 导入完成 | 底部状态条 + 结果统计(成功 N / 失败 M + 失败行号) |
| 保存成功 | 关闭弹窗,刷新行,状态条提示一句 |
| 校验不通过 | 保留用户输入,**焦点移到第一个错误字段**,就近显示错误 |
| 批量操作部分失败 | 列出失败项和原因,不要只说"部分失败" |
| 删除 | 二次确认框,写明"将删除 N 条,不可恢复" |
`[必须]` 报错要说清**哪一步失败、下一步做什么**,不要把 Go 的错误堆栈贴到页面上。
堆栈写日志。
## 9. 可访问性与细节
- `[必须]` 表单控件有可见 `<label>`,占位符不能当标签用。
- `[必须]` 状态不能只靠颜色,必须有文字。
- `[建议]` 搜索框支持回车提交。
- `[建议]` 主要操作支持键盘 Tab 到达。
- `[必须]` 金额显示带币种符号,台币和人民币要能一眼分清(`NT$` / `¥`)。