# 产品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` |