|
|
# 产品UI/UX设计方案 v1.0(邮件自动预报系统)
|
|
|
|
|
|
> 状态:可直接画原型 / 写前端(**2026-07-23 现行口径回写**)
|
|
|
> 依据:`docx/需求规格-邮件自动预报-v0.2.md`、`docx/技术设计方案-邮件自动预报-v1.0.md`
|
|
|
> UI 栈锁定:Ant Design 5 + TanStack Virtual(货件表)+ 本规范 Token
|
|
|
> **回写要点**:`/settings`;`/logs`=导入+拉取(无操作审计);空态无种子;详情业务意图摘要替代得分表;详见 `docx/现行口径-修订说明-v1.md`
|
|
|
|
|
|
---
|
|
|
|
|
|
# 1. 信息架构(Information Architecture)
|
|
|
|
|
|
## 1.1 页面列表
|
|
|
|
|
|
| 页面 | 路由 | 职责 |
|
|
|
|---|---|---|
|
|
|
| 登录页 | `/login` | 账号密码登录;写 session Cookie |
|
|
|
| 邮件列表页 | `/mails` | 时间倒序展示邮件;按类型/状态筛选;进入详情 |
|
|
|
| 邮件详情页 | `/mails/[id]` | 摘要、正文、附件、证据、货件预览;重新解析;Admin 改类型;进入确认导入 |
|
|
|
| 确认导入页 | `/mails/[id]/confirm` | 柜头编辑;TransMode/OperationType;货件勾选(虚拟滚动);二次确认后提交 CC |
|
|
|
| 导入日志/补偿页 | `/logs` | Tab:导入日志、拉取记录;失败重试 |
|
|
|
| 设置 | `/settings` | Admin:邮箱绑定、IMAP 间隔/过滤、CC、OAuth |
|
|
|
| Admin 改类型 | **详情页内 Modal**(非独立路由) | `ENABLE_TYPE_OVERRIDE`(及前端可见开关)+ `role=admin`;一期不写 audit_log |
|
|
|
|
|
|
不设 `/dashboard`、不设 `/admin/override` 独立页。登录后默认落点:`/mails`。
|
|
|
|
|
|
## 1.2 页面关系图
|
|
|
|
|
|
```
|
|
|
/login
|
|
|
→ /mails
|
|
|
→ /mails/[id]
|
|
|
→ /mails/[id]/confirm (mail_type=NEW_CONTAINER 且 status=PENDING_CONFIRM;或 Admin 改类型后满足条件)
|
|
|
→ /logs (提交结束:成功跳转;失败可停留 confirm 或跳 logs)
|
|
|
⇢ Modal「改类型」 (Admin)
|
|
|
⇢ 动作「重新解析」 (PARSE_FAILED / REJECTED_VALIDATION / 允许重解析的状态)
|
|
|
→ /logs (顶栏入口)
|
|
|
```
|
|
|
|
|
|
禁用路径:
|
|
|
- `mail_type ≠ NEW_CONTAINER` 且未改类型成功 → 不可进入 confirm(直链访问重定向详情 + Toast)
|
|
|
- `status ∈ {IMPORTING, SUCCESS}` → confirm 只读或重定向详情
|
|
|
|
|
|
## 1.3 路由结构(Next.js App Router)
|
|
|
|
|
|
```
|
|
|
/login
|
|
|
/mails
|
|
|
/mails/[id]
|
|
|
/mails/[id]/confirm
|
|
|
/logs
|
|
|
```
|
|
|
|
|
|
顶栏全局导航(登录后):`邮件列表` | `日志` | `设置`(Admin) | 健康/IMAP/CC 状态 | `用户名` | `Admin` 徽标 | `退出`
|
|
|
|
|
|
---
|
|
|
|
|
|
# 2. 交互原型(Interaction Design)
|
|
|
|
|
|
## 2.0 全站壳层
|
|
|
|
|
|
```
|
|
|
┌──────────────────────────────────────────────────────────┐
|
|
|
│ TopNav: Logo「邮件预报」 | 导航链接 | 用户/Admin | 退出 │
|
|
|
├──────────────────────────────────────────────────────────┤
|
|
|
│ PageHeader: 标题 + 右侧主操作(按页) │
|
|
|
├──────────────────────────────────────────────────────────┤
|
|
|
│ MainContent(白底 Card 或铺满表) │
|
|
|
└──────────────────────────────────────────────────────────┘
|
|
|
```
|
|
|
|
|
|
- 最小视口宽:1280px
|
|
|
- 内容最大宽:1440px,水平居中
|
|
|
- Toast:右上角;Modal:居中
|
|
|
|
|
|
---
|
|
|
|
|
|
## 2.1 登录页 `/login`
|
|
|
|
|
|
**结构**
|
|
|
- 居中 Card(宽 400px)
|
|
|
- 标题:邮件自动预报
|
|
|
- 字段:用户名、密码
|
|
|
- 主按钮:登录
|
|
|
|
|
|
**交互**
|
|
|
- 提交 → 按钮 loading → 成功跳转 `/mails`;失败 Toast error + 字段下方文案
|
|
|
- 已登录访问 `/login` → 重定向 `/mails`
|
|
|
|
|
|
**空/错**
|
|
|
- 用户名或密码空 → 内联校验,不发请求
|
|
|
|
|
|
---
|
|
|
|
|
|
## 2.2 邮件列表页 `/mails`
|
|
|
|
|
|
**结构**
|
|
|
```
|
|
|
PageHeader: 「邮件列表」
|
|
|
FilterBar: [类型 Select] [状态 Select] [关键词 Input] [查询 Button]
|
|
|
MailTable(非卡片列表,密度优先用表格):
|
|
|
列:类型Tag | 状态Pill | 主题 | 发件人 | 接收时间 | 柜号(若有)
|
|
|
Pagination: 底右,默认 pageSize=20
|
|
|
```
|
|
|
|
|
|
**交互**
|
|
|
- 行点击(整行可点)→ `/mails/[id]`
|
|
|
- 筛选变更 → 立即请求列表(防抖 300ms,关键词)
|
|
|
- 手动「刷新」按钮 → 重新拉列表(无 WebSocket)
|
|
|
|
|
|
**空态**
|
|
|
- 文案:暂无邮件,等待 IMAP 拉取
|
|
|
- 次按钮:刷新
|
|
|
|
|
|
**角标规则**
|
|
|
- 类型:左侧色点 8px + Tag 文字
|
|
|
- 状态:StatusPill 文字(不只靠颜色)
|
|
|
|
|
|
---
|
|
|
|
|
|
## 2.3 邮件详情页 `/mails/[id]`
|
|
|
|
|
|
**结构**
|
|
|
```
|
|
|
PageHeader:
|
|
|
[← 返回列表] 主题(H1) StatusPill TypeTag
|
|
|
右侧操作组:
|
|
|
[重新解析](条件可见)
|
|
|
[改类型](Admin 条件可见)
|
|
|
[确认导入](主按钮,条件启用)
|
|
|
|
|
|
SummaryStrip: 发件人 | 时间 | Message 状态
|
|
|
|
|
|
Tabs 或 纵向区块:
|
|
|
1) 正文摘要(等宽可读,max-height 240px 可滚动)
|
|
|
2) 附件列表:文件名 | 大小 | 下载
|
|
|
3) 业务意图摘要(动作/关键字/操作内容/转仓对;不展示得分分值);type_evidence JSON 可选收起
|
|
|
4) 货件预览表(前 50 行 + 「共 N 行,确认导入页查看全部」链接)
|
|
|
```
|
|
|
|
|
|
**确认导入按钮启用条件(全部满足)**
|
|
|
- `mail_type === NEW_CONTAINER`
|
|
|
- `status === PENDING_CONFIRM`
|
|
|
- `valid_shipment_count >= 1`
|
|
|
|
|
|
**禁用时 Tooltip / 旁注文案(写死)**
|
|
|
- 类型非 NEW:`类型为 {mail_type},一期不可导入`
|
|
|
- 状态非 PENDING_CONFIRM:`当前状态为 {status},不可导入`
|
|
|
- 无有效行:`无有效货件行`
|
|
|
|
|
|
**交互**
|
|
|
- 确认导入 → `/mails/[id]/confirm`
|
|
|
- 重新解析 → POST reparse → 按钮 loading → 成功 Toast → 重新拉详情;失败 Toast
|
|
|
- 改类型 → 打开 `TypeOverrideModal`:
|
|
|
- Select 目标类型
|
|
|
- Input 原因(必填)
|
|
|
- 展示当前 type_evidence 摘要
|
|
|
- 确认 → loading → 审计成功 Toast → 刷新详情(可能出现确认导入可点)
|
|
|
|
|
|
**直链保护**
|
|
|
- 非法 id → 404 页:邮件不存在 + 回列表
|
|
|
|
|
|
---
|
|
|
|
|
|
## 2.4 确认导入页 `/mails/[id]/confirm`(核心)
|
|
|
|
|
|
**结构**
|
|
|
```
|
|
|
PageHeader:
|
|
|
[← 返回详情] 主题截断 | 柜号大号展示
|
|
|
右侧无第二主按钮(主 CTA 在底栏)
|
|
|
|
|
|
Section「柜头」Card 两列表单:
|
|
|
左列: TransMode* | OperationType* | ContainerNo* | CabinetType | Classis
|
|
|
右列: ETD | ETA | ShippingLineId(Select 可搜索,可空)| MemoRemark | Instruction
|
|
|
* 必填;ContainerNo 失焦触发 ISO 校验
|
|
|
|
|
|
Section「货件」Card:
|
|
|
Toolbar: [全选有效] [反选] [SearchInput: FBACode/ShipmentID] [有效 m / 无效 k]
|
|
|
VirtualTable 高度固定 480px:
|
|
|
列: ☑ | # | 仓库ID | 渠道 | 件数 | FBA ID | Ref | ShipmentID | 行状态 | 警告
|
|
|
INVALID 行: 背景 #F3F4F6,勾选 disabled,行状态 Tag=INVALID
|
|
|
|
|
|
StickyFooter(视口底):
|
|
|
左: 已选 n / 有效 m
|
|
|
右: [取消] [确认导入 Primary]
|
|
|
```
|
|
|
|
|
|
**默认值**
|
|
|
- TransMode = 解析推荐或 `0`
|
|
|
- OperationType = 解析推荐或 `0`
|
|
|
- 勾选 = 全部 `VALID` 行
|
|
|
- `CHANNEL_UNMAPPED` 行可勾选,提交前若存在未 ack → 额外确认勾选「已知晓未映射渠道」
|
|
|
|
|
|
**二次确认 Modal**
|
|
|
- 标题:确认导入至 CarrierCentral
|
|
|
- 正文:`将向 CC 导入柜 {container_no},货件 {n} 行`
|
|
|
- 展示 TransMode / OperationType 文案
|
|
|
- 按钮:取消 | 确认提交
|
|
|
|
|
|
**提交交互**
|
|
|
1. Primary 点击 → 打开 Modal(本地校验先过)
|
|
|
2. Modal 确认 → Primary+Modal 双 loading;`disabled`
|
|
|
3. 成功 → Toast success → 跳转 `/logs?mail_id={id}`
|
|
|
4. 失败 → Toast error(展示 `error.message`)→ 留在本页;可再改再提(若 status 已 IMPORTING 则整页只读 + 「导入中,请稍后刷新」)
|
|
|
5. `409 VERSION_CONFLICT` → Toast + 强制刷新页面数据
|
|
|
|
|
|
**校验(内联)**
|
|
|
- TransMode / OperationType 空 → 阻止 Modal
|
|
|
- ContainerNo 空或 ISO 失败 → 字段 Error
|
|
|
- n=0 → Primary disabled,文案:请至少勾选一行有效货件
|
|
|
|
|
|
---
|
|
|
|
|
|
## 2.5 导入日志/补偿页 `/logs`
|
|
|
|
|
|
**结构**
|
|
|
```
|
|
|
PageHeader: 「导入日志」 [返回邮件列表]
|
|
|
Filter: 状态 Select | 柜号/主题 Search
|
|
|
Table 列:
|
|
|
时间 | 邮件主题(链到详情) | 柜号 | 导入状态Pill | external_id | last_error | 操作
|
|
|
操作:
|
|
|
FAILED / TIMEOUT_UNKNOWN / CONFLICT(可重试类) → [重试]
|
|
|
SUCCESS → 显示 external_id,无重试
|
|
|
```
|
|
|
|
|
|
**交互**
|
|
|
- 重试 → 按钮 loading → Toast → 刷新行
|
|
|
- `COMPENSATION_EXHAUSTED` → 重试按钮对普通 ops 隐藏;Admin 显示「重置并重试」
|
|
|
|
|
|
**空态**
|
|
|
- 暂无导入记录
|
|
|
|
|
|
---
|
|
|
|
|
|
## 2.6 统一交互规则(写死)
|
|
|
|
|
|
1. 所有触发写操作的按钮:点击即 `loading` + `disabled`,防重复提交。
|
|
|
2. 所有 API 失败:全局 Toast `error`;表单字段错误额外内联。
|
|
|
3. 所有必填/格式:失焦或提交前校验,不通过不发请求。
|
|
|
4. 导入类写操作:必须二次确认 Modal。
|
|
|
5. 货件表 ≥50 行:必须虚拟滚动(本系统确认页固定启用)。
|
|
|
6. 状态更新:一期无 WS;用户点「刷新」或返回列表重进。
|
|
|
7. IMPORTING 期间:详情/确认页禁止再次提交;展示 Alert「导入进行中」。
|
|
|
8. 色标旁必须有文字标签。
|
|
|
|
|
|
---
|
|
|
|
|
|
# 3. 视觉设计(Visual Design System)
|
|
|
|
|
|
## 3.1 色彩 Token
|
|
|
|
|
|
| Token | Hex | 用途 |
|
|
|
|---|---|---|
|
|
|
| `--color-primary` | `#2563EB` | 主按钮、链接、焦点 |
|
|
|
| `--color-primary-hover` | `#1D4ED8` | hover |
|
|
|
| `--color-success` | `#16A34A` | 成功 |
|
|
|
| `--color-warning` | `#F59E0B` | 警告/部分成功 |
|
|
|
| `--color-error` | `#DC2626` | 失败 |
|
|
|
| `--color-bg` | `#F8FAFC` | 页面底 |
|
|
|
| `--color-surface` | `#FFFFFF` | 卡片/表单 |
|
|
|
| `--color-text` | `#111827` | 主文字 |
|
|
|
| `--color-text-secondary` | `#6B7280` | 辅助 |
|
|
|
| `--color-border` | `#E5E7EB` | 边框 |
|
|
|
| `--color-disabled` | `#9CA3AF` | 禁用 |
|
|
|
|
|
|
**类型色(Tag 背景 / 文字白或深按对比度)**
|
|
|
|
|
|
| mail_type | 色 |
|
|
|
|---|---|
|
|
|
| NEW_CONTAINER | `#2563EB` |
|
|
|
| TRANSFER | `#F59E0B` |
|
|
|
| INSTRUCTION_HOLD_SPLIT | `#8B5CF6` |
|
|
|
| INSTRUCTION_LABEL | `#EC4899` |
|
|
|
| WORK_ORDER | `#0D9488` |
|
|
|
| UNKNOWN | `#6B7280` |
|
|
|
|
|
|
**状态色(StatusPill)**
|
|
|
|
|
|
| status | 色 |
|
|
|
|---|---|
|
|
|
| FETCHED / PARSING / PARSED | `#9CA3AF` |
|
|
|
| PENDING_CONFIRM | `#2563EB` |
|
|
|
| IMPORTING | `#F59E0B` |
|
|
|
| SUCCESS | `#16A34A` |
|
|
|
| PARTIAL_SUCCESS | `#F59E0B` |
|
|
|
| FAILED / PARSE_FAILED / REJECTED_VALIDATION | `#DC2626` |
|
|
|
| IGNORED | `#6B7280` |
|
|
|
|
|
|
## 3.2 字体
|
|
|
|
|
|
- 字体栈:`Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif`
|
|
|
- H1:24px / 700 / 1.2
|
|
|
- H2:20px / 600 / 1.3
|
|
|
- Body:14px / 400 / 1.5
|
|
|
- Caption:12px / 400 / 1.4
|
|
|
- Small:11px / 400 / 1.3
|
|
|
- 等宽(证据 JSON):`ui-monospace, SFMono-Regular, Menlo, Consolas, monospace` 12px
|
|
|
|
|
|
## 3.3 间距
|
|
|
|
|
|
`4 / 8 / 12 / 16 / 24 / 32 / 48 / 64`(px)
|
|
|
Card 内边距:16
|
|
|
区块间距:24
|
|
|
Filter 与表:16
|
|
|
|
|
|
## 3.4 圆角
|
|
|
|
|
|
| 元素 | 半径 |
|
|
|
|---|---|
|
|
|
| Button / Input | 6px |
|
|
|
| Card | 8px |
|
|
|
| Modal | 12px |
|
|
|
| Tag / Pill | 12px |
|
|
|
|
|
|
## 3.5 阴影
|
|
|
|
|
|
- Card:`0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04)`
|
|
|
- Modal:`0 20px 60px rgba(0,0,0,0.15)`
|
|
|
|
|
|
## 3.6 组件视觉规格
|
|
|
|
|
|
| 组件 | 规格 |
|
|
|
|---|---|
|
|
|
| PrimaryButton | bg primary,文字白,h=36,px=16,radius 6;hover primary-hover;loading 内嵌 Spinner |
|
|
|
| SecondaryButton | bg 白,border primary,文字 primary |
|
|
|
| DangerButton | bg error,文字白 |
|
|
|
| Input / Select | h=36,border 1px border-color,focus:ring 2px primary 30% |
|
|
|
| Card | surface + shadow + radius 8 + p16 |
|
|
|
| Tag | h=22,px=8,文字 12px |
|
|
|
| StatusPill | 同 Tag |
|
|
|
| Table header | bg `#F9FAFB`,文字 secondary,字重 600 |
|
|
|
| INVALID 行 | bg `#F3F4F6`,文字 secondary |
|
|
|
| StickyFooter | 白底,上边框 border,h=56,z-index 10 |
|
|
|
| Toast success/error/warning | 对应色左边条 4px |
|
|
|
|
|
|
---
|
|
|
|
|
|
# 4. 设计规范(Design System for AI Coding)
|
|
|
|
|
|
## 4.1 React 组件清单(映射 Antd)
|
|
|
|
|
|
| 设计组件 | 实现映射 |
|
|
|
|---|---|
|
|
|
| `PrimaryButton` | `Button type="primary"` |
|
|
|
| `SecondaryButton` | `Button` |
|
|
|
| `DangerButton` | `Button danger` |
|
|
|
| `InputField` | `Form.Item` + `Input` |
|
|
|
| `SelectField` | `Form.Item` + `Select` |
|
|
|
| `DatePickerField` | `Form.Item` + `DatePicker` |
|
|
|
| `Checkbox` | `Checkbox` |
|
|
|
| `AppCard` | `Card` |
|
|
|
| `MailTable` | `Table` |
|
|
|
| `ShipmentVirtualTable` | 自研:TanStack Virtual + 行渲染 |
|
|
|
| `TypeTag` | `Tag` + 类型色 |
|
|
|
| `StatusPill` | `Tag` + 状态色 |
|
|
|
| `LoadingSpinner` | `Spin` |
|
|
|
| `ErrorBanner` | `Alert type="error"` |
|
|
|
| `AppToast` | `message` / `notification` |
|
|
|
| `ConfirmModal` | `Modal.confirm` 或受控 `Modal` |
|
|
|
| `SearchInput` | `Input.Search` |
|
|
|
| `Pagination` | `Pagination` |
|
|
|
| `TypeOverrideModal` | 受控 `Modal` + Form |
|
|
|
| `TopNav` | `Layout.Header` + `Menu` |
|
|
|
| `EmptyState` | `Empty` |
|
|
|
|
|
|
页面组件命名:
|
|
|
- `LoginPage`
|
|
|
- `MailListPage`
|
|
|
- `MailDetailPage`
|
|
|
- `MailConfirmPage`
|
|
|
- `ImportLogsPage`
|
|
|
|
|
|
## 4.2 组件状态
|
|
|
|
|
|
每个可交互组件支持:`idle | loading | success | error | disabled | empty`(按上下文取用)。
|
|
|
|
|
|
页面级状态机(前端):
|
|
|
- `pageStatus: idle | loading | error`
|
|
|
- 详情另:`actionLoading: reparse | override | none`
|
|
|
- 确认页另:`submitPhase: idle | validating | confirming | submitting`
|
|
|
|
|
|
## 4.3 命名规范
|
|
|
|
|
|
- 组件:PascalCase
|
|
|
- 路由文件:`page.tsx` 于 `app/mails/[id]/confirm/page.tsx`
|
|
|
- 状态布尔:`isLoading` `isSubmitting` `isAdmin`
|
|
|
- 文案常量:`src/constants/ui-copy.ts`
|
|
|
- Token CSS 变量:`--color-*` 写入 `globals.css`
|
|
|
|
|
|
## 4.4 UI 硬约束(编码强制)
|
|
|
|
|
|
1. 写操作按钮必须 `loading` + 防重复。
|
|
|
2. API 失败必须可见错误(禁止静默)。
|
|
|
3. 表单校验失败禁止发请求。
|
|
|
4. 导入提交必须二次确认 Modal。
|
|
|
5. 货件表 ≥50 行必须虚拟滚动(confirm 页强制)。
|
|
|
6. 空列表必须 EmptyState。
|
|
|
7. 桌面优先,min-width 1280。
|
|
|
8. Tag/Pill 必须带文字,不只靠颜色。
|
|
|
9. confirm 直链:服务端/客户端双重校验 NEW + PENDING_CONFIRM。
|
|
|
10. Admin 改类型按钮:仅 `isAdmin && enableTypeOverride` 渲染。
|
|
|
|
|
|
## 4.5 文案常量(摘录)
|
|
|
|
|
|
| key | 中文 |
|
|
|
|---|---|
|
|
|
| `empty.mails` | 暂无邮件,等待 IMAP 拉取 |
|
|
|
| `btn.confirmImport` | 确认导入 |
|
|
|
| `btn.reparse` | 重新解析 |
|
|
|
| `btn.overrideType` | 改类型 |
|
|
|
| `btn.retry` | 重试 |
|
|
|
| `modal.import.title` | 确认导入至 CarrierCentral |
|
|
|
| `modal.import.body` | 将向 CC 导入柜 {no},货件 {n} 行 |
|
|
|
| `disable.notNew` | 类型为 {type},一期不可导入 |
|
|
|
| `disable.badStatus` | 当前状态为 {status},不可导入 |
|
|
|
| `alert.importing` | 导入进行中,请稍后刷新查看结果 |
|
|
|
| `ack.unmapped` | 已知晓存在未映射渠道,仍提交原值 |
|
|
|
|
|
|
## 4.6 线框尺寸速查
|
|
|
|
|
|
| 区域 | 尺寸 |
|
|
|
|---|---|
|
|
|
| TopNav 高 | 56px |
|
|
|
| PageHeader 高 | 64px |
|
|
|
| FilterBar 高 | 48px |
|
|
|
| 确认页 VirtualTable | 高 480px |
|
|
|
| StickyFooter | 高 56px |
|
|
|
| 登录 Card | 宽 400px |
|
|
|
| Modal 宽 | 480px(改类型)/ 520px(导入确认) |
|
|
|
|
|
|
---
|
|
|
|
|
|
# 5. 页面 ↔ API 绑定(供前端直接接线)
|
|
|
|
|
|
| 页面 | 主要 API |
|
|
|
|---|---|
|
|
|
| Login | `POST /api/auth/login` |
|
|
|
| MailList | `GET /api/mails` |
|
|
|
| MailDetail | `GET /api/mails/:id`;`POST .../reparse`;`POST .../type` |
|
|
|
| Confirm | `POST /api/mails/:id/import` |
|
|
|
| Logs | `GET /api/imports`;`POST /api/imports/:id/retry` |
|
|
|
|
|
|
---
|
|
|
|
|
|
# 6. 文档索引
|
|
|
|
|
|
| 文档 | 路径 |
|
|
|
|---|---|
|
|
|
| 本设计 | `docx/产品UI-UX设计方案-邮件自动预报-v1.0.md` |
|
|
|
| PRD | `docx/需求规格-邮件自动预报-v0.2.md` |
|
|
|
| 技术设计 | `docx/技术设计方案-邮件自动预报-v1.0.md` |
|