|
|
# 查价系统 产品 UI/UX 设计方案 v1.0
|
|
|
|
|
|
| 版本 | 日期 | 作者 | 说明 |
|
|
|
|------|------|------|------|
|
|
|
| v1.0 | 2026-06-16 | 产品设计/UI/UX | 可直接用于前端开发与 Cursor 生成代码 |
|
|
|
|
|
|
> 配套文档:[查价系统-PRD.md](./查价系统-PRD.md) · [查价系统-技术设计.md](./查价系统-技术设计.md)
|
|
|
> 技术栈:Next.js 15 App Router · Tailwind CSS · shadcn/ui(定制 token)· Geist 字体
|
|
|
|
|
|
---
|
|
|
|
|
|
## 1. 信息架构(Information Architecture)
|
|
|
|
|
|
### 1.1 页面列表
|
|
|
|
|
|
| 编号 | 页面名称 | 路由 | 角色 | 优先级 |
|
|
|
|------|----------|------|------|--------|
|
|
|
| P01 | 登录页 | `/login` | 全部 | P0 |
|
|
|
| P02 | 工作台 | `/dashboard` | customer / operator / admin | P0 |
|
|
|
| P03 | 卡派查价页 | `/quote` | customer | P0 |
|
|
|
| P04 | 询价结果页 | `/quote/result/[quoteId]` | customer | P0 |
|
|
|
| P05 | 询价历史 | `/history` | customer | P0 |
|
|
|
| P06 | 加价配置 | `/admin/markup` | operator / admin | P0 |
|
|
|
| P07 | 预警中心 | `/admin/alerts` | admin | P0 |
|
|
|
| P08 | 403 无权限 | `/403` | 全部 | P1 |
|
|
|
| P09 | 404 未找到 | `/not-found` | 全部 | P1 |
|
|
|
|
|
|
**MVP 不包含页面**:注册页、个人设置、CC 对接页、下单页、多币种设置页。
|
|
|
|
|
|
### 1.2 页面关系图
|
|
|
|
|
|
```
|
|
|
登录页 (/login)
|
|
|
│
|
|
|
├─ customer ──► 工作台 (/dashboard)
|
|
|
│ │
|
|
|
│ ├─► 卡派查价 (/quote)
|
|
|
│ │ │
|
|
|
│ │ └─► 询价结果 (/quote/result/:quoteId)
|
|
|
│ │
|
|
|
│ └─► 询价历史 (/history)
|
|
|
│ │
|
|
|
│ └─► 询价结果 (/quote/result/:quoteId)
|
|
|
│
|
|
|
├─ operator ──► 工作台 (/dashboard)
|
|
|
│ └─► 加价配置 (/admin/markup)
|
|
|
│
|
|
|
└─ admin ──► 工作台 (/dashboard)
|
|
|
├─► 加价配置 (/admin/markup)
|
|
|
└─► 预警中心 (/admin/alerts)
|
|
|
```
|
|
|
|
|
|
**全局规则**:
|
|
|
- 未登录访问受保护路由 → 重定向 `/login?redirect={path}`
|
|
|
- `customer` 访问 `/admin/*` → `/403`
|
|
|
- `operator` 访问 `/admin/alerts` → `/403`
|
|
|
- 登录成功默认跳转 `/dashboard`
|
|
|
|
|
|
### 1.3 路由结构(Next.js App Router)
|
|
|
|
|
|
```
|
|
|
app/
|
|
|
├── login/page.tsx # P01
|
|
|
├── dashboard/page.tsx # P02
|
|
|
├── quote/
|
|
|
│ ├── page.tsx # P03
|
|
|
│ └── result/[quoteId]/page.tsx # P04
|
|
|
├── history/page.tsx # P05
|
|
|
├── admin/
|
|
|
│ ├── markup/page.tsx # P06
|
|
|
│ └── alerts/page.tsx # P07
|
|
|
├── 403/page.tsx # P08
|
|
|
└── not-found.tsx # P09
|
|
|
```
|
|
|
|
|
|
| 路由 | 鉴权 | 布局 |
|
|
|
|------|------|------|
|
|
|
| `/login` | 公开 | `AuthLayout`(无侧边栏) |
|
|
|
| `/dashboard` | JWT | `AppLayout` |
|
|
|
| `/quote` | JWT + customer | `AppLayout` |
|
|
|
| `/quote/result/[quoteId]` | JWT + customer(本人) | `AppLayout` |
|
|
|
| `/history` | JWT + customer | `AppLayout` |
|
|
|
| `/admin/markup` | JWT + operator/admin | `AdminLayout` |
|
|
|
| `/admin/alerts` | JWT + admin | `AdminLayout` |
|
|
|
|
|
|
### 1.4 导航结构
|
|
|
|
|
|
**客户侧 `AppLayout` 顶栏**:
|
|
|
- Logo「美美与共 · 卡派查价」
|
|
|
- 导航:工作台 | 卡派查价 | 询价历史
|
|
|
- 右侧:客户 ID + 退出
|
|
|
|
|
|
**管理侧 `AdminLayout` 顶栏 + 侧栏**:
|
|
|
- 侧栏:工作台 | 加价配置 | 预警中心(admin 可见)
|
|
|
- 顶栏:角色标签 + 退出
|
|
|
|
|
|
---
|
|
|
|
|
|
## 2. 交互原型(Interaction Design)
|
|
|
|
|
|
### 2.1 全局交互规则
|
|
|
|
|
|
| 规则 ID | 规则 |
|
|
|
|---------|------|
|
|
|
| IX-01 | 所有提交按钮点击后 `disabled` + `loading`,最短 3s |
|
|
|
| IX-02 | 表单字段 `onBlur` 触发校验;错误文案显示在字段下方 |
|
|
|
| IX-03 | API 错误统一用 `ErrorBanner` 展示在页面主内容区顶部 |
|
|
|
| IX-04 | 查价接口 `Cache-Control: no-store`,禁止浏览器缓存 |
|
|
|
| IX-05 | 金额展示固定 `USD`,格式 `$1,234.56` |
|
|
|
| IX-06 | 所有异步操作最长等待 30s,超时展示可重试入口 |
|
|
|
|
|
|
---
|
|
|
|
|
|
### 2.2 P01 登录页 `/login`
|
|
|
|
|
|
**页面结构**:
|
|
|
- Header:Logo + 产品名
|
|
|
- 主区:登录卡片(用户名、密码)
|
|
|
- 操作区:登录按钮
|
|
|
|
|
|
**状态**:`idle` | `loading` | `error` | `success`
|
|
|
|
|
|
| 状态 | UI |
|
|
|
|------|-----|
|
|
|
| idle | 表单可编辑,按钮「登录」 |
|
|
|
| loading | 按钮 loading,输入框 disabled |
|
|
|
| error | ErrorBanner「用户名或密码错误」 |
|
|
|
| success | 跳转 `/dashboard` |
|
|
|
|
|
|
**按钮行为**:点击 → `POST /api/auth/login` → 存 token → 按角色跳转
|
|
|
|
|
|
---
|
|
|
|
|
|
### 2.3 P02 工作台 `/dashboard`
|
|
|
|
|
|
**页面结构**:
|
|
|
- Header:欢迎语 + 角色
|
|
|
- 主区:快捷入口卡片(按角色渲染)
|
|
|
- 操作区:无
|
|
|
|
|
|
**客户卡片**:「卡派查价」「询价历史」
|
|
|
**运营卡片**:「加价配置」
|
|
|
**管理员卡片**:「加价配置」「预警中心」+ 未处理预警数量角标
|
|
|
|
|
|
---
|
|
|
|
|
|
### 2.4 P03 卡派查价页 `/quote`(核心)
|
|
|
|
|
|
**页面结构**:
|
|
|
```
|
|
|
┌─────────────────────────────────────────────┐
|
|
|
│ Header(AppLayout 顶栏) │
|
|
|
├─────────────────────────────────────────────┤
|
|
|
│ 页面标题:卡派查价 │
|
|
|
│ 副标题:填写货物信息,获取实时报价(约 30 秒内) │
|
|
|
├──────────────────┬──────────────────────────┤
|
|
|
│ 主输入区(左 60%) │ 结果区(右 40%,空时占位) │
|
|
|
│ QuoteForm │ QuoteResultPanel │
|
|
|
├──────────────────┴──────────────────────────┤
|
|
|
│ 操作区:获取报价(PrimaryButton) │
|
|
|
└─────────────────────────────────────────────┘
|
|
|
```
|
|
|
|
|
|
**移动端(<768px)**:单列;输入区在上,结果区在下。
|
|
|
|
|
|
**页面状态机**:
|
|
|
|
|
|
| 状态 | 触发 | 输入区 | 结果区 | 操作区 |
|
|
|
|------|------|--------|--------|--------|
|
|
|
| `idle` | 进入页面 | 可编辑 | 空状态插画 +「填写信息后获取报价」 | 按钮可点 |
|
|
|
| `validating` | 点击提交 | 只读 | 不变 | 按钮 loading |
|
|
|
| `processing` | POST 成功且 status=processing | 只读 | Skeleton +「正在查询报价…」+ 进度条(0–30s) | 按钮 disabled |
|
|
|
| `success` | GET status=done 且 is_realtime=true | 可编辑(新询价生成新 request_id) | QuoteCard 双等级 | 按钮可点 |
|
|
|
| `fallback` | GET status=done 且 is_realtime=false | 同 success | QuoteCard + WarningBanner「非实时报价,仅供参考」 | 按钮可点 |
|
|
|
| `error` | GET status=failed 或超时 | 可编辑 | ErrorBanner + 重试按钮 | 按钮可点 |
|
|
|
| `expired` | 倒计时归零 | 可编辑 | 蒙层 +「报价已过期」ConfirmDialog | 确认后回到 idle |
|
|
|
|
|
|
**关键交互**:
|
|
|
|
|
|
1. 点击「获取报价」→ 前端生成 `request_id`(UUID v4) → `POST /api/quotes`
|
|
|
2. 若立即 `done`(L1/L2 命中)→ 直接 `success`/`fallback`
|
|
|
3. 若 `processing` → 每 2s `GET /api/quotes/{quoteId}`,最多 15 次
|
|
|
4. `success` 后展示 3 分钟倒计时 `CountdownTimer`;<30s 时文字变 Warning 色
|
|
|
5. 防重复:提交后按钮 disabled 3000ms;相同 request_id 由后端幂等
|
|
|
|
|
|
**表单字段布局(QuoteForm)**:
|
|
|
|
|
|
| 分组 | 字段 |
|
|
|
|------|------|
|
|
|
| 提货地址 | 街道、城市、州(2 字母)、邮编 |
|
|
|
| 派送地址 | 同上 |
|
|
|
| 货物信息 | 重量+单位、长×宽×高+单位、件数、货物类型(Select) |
|
|
|
| 偏好 | 单位制(公制/英制)、默认展示等级(标准/保证送达) |
|
|
|
|
|
|
**即时校验**:
|
|
|
|
|
|
| 字段 | 规则 | 错误文案 |
|
|
|
|------|------|----------|
|
|
|
| 邮编 | `^\d{5}(-\d{4})?$` | 邮编格式无效 |
|
|
|
| 重量 | 1–50000(换算后 lb) | 重量超出范围 |
|
|
|
| 件数 | 1–999 整数 | 件数无效 |
|
|
|
|
|
|
---
|
|
|
|
|
|
### 2.5 P04 询价结果页 `/quote/result/[quoteId]`
|
|
|
|
|
|
**页面结构**:
|
|
|
- Header:面包屑「卡派查价 / 报价详情」
|
|
|
- 主区:QuoteCard(双等级 Tab)+ 明细 breakdown
|
|
|
- 操作区:「重新询价」「查看历史」
|
|
|
|
|
|
**状态**:`loading` | `success` | `fallback` | `error` | `expired`
|
|
|
|
|
|
| 状态 | UI |
|
|
|
|------|-----|
|
|
|
| loading | 全页 Skeleton |
|
|
|
| success | QuoteCard + CountdownTimer |
|
|
|
| fallback | QuoteCard + WarningBanner |
|
|
|
| error | ErrorBanner「无法获取报价」 |
|
|
|
| expired | 过期提示 + 跳转 `/quote` 按钮 |
|
|
|
|
|
|
**数据来源**:`GET /api/quotes/{quoteId}`
|
|
|
|
|
|
---
|
|
|
|
|
|
### 2.6 P05 询价历史 `/history`
|
|
|
|
|
|
**页面结构**:
|
|
|
- Header:标题「询价历史」
|
|
|
- 主区:表格(时间、路线摘要、价格、来源、状态)
|
|
|
- 操作区:分页器(page/size 必填)
|
|
|
|
|
|
**状态**:`loading` | `success` | `empty` | `error`
|
|
|
|
|
|
| 状态 | UI |
|
|
|
|------|-----|
|
|
|
| loading | 表格 Skeleton 5 行 |
|
|
|
| success | 数据表格 + 分页 |
|
|
|
| empty | EmptyState「暂无询价记录」+ 按钮「去查价」 |
|
|
|
| error | ErrorBanner |
|
|
|
|
|
|
**行点击**:跳转 `/quote/result/{quoteId}`
|
|
|
|
|
|
---
|
|
|
|
|
|
### 2.7 P06 加价配置 `/admin/markup`
|
|
|
|
|
|
**页面结构**:
|
|
|
- Header:标题 + 搜索框(customer_id)
|
|
|
- 主区:表格(客户 ID、加价比例、操作人、更新时间、操作)
|
|
|
- 操作区:分页
|
|
|
|
|
|
**编辑交互**:行内「编辑」→ 右侧 Drawer
|
|
|
- 字段:加价比例(NumberInput,0–30,步长 0.1)
|
|
|
- 按钮:保存(Primary)/ 取消(Secondary)
|
|
|
|
|
|
**状态**:`loading` | `success` | `empty` | `error` | `saving`
|
|
|
|
|
|
| 状态 | UI |
|
|
|
|------|-----|
|
|
|
| saving | 保存按钮 loading,Drawer 内字段 disabled |
|
|
|
| 校验失败 | 字段下「加价比例不能超过 30%」 |
|
|
|
|
|
|
---
|
|
|
|
|
|
### 2.8 P07 预警中心 `/admin/alerts`
|
|
|
|
|
|
**页面结构**:
|
|
|
- Header:标题 + 筛选(类型、状态 open/resolved)
|
|
|
- 主区:表格(时间、类型、quote_id、偏差%、状态)
|
|
|
- 详情:行点击展开 Drawer(detail_json)
|
|
|
- 操作区:「标记已处理」
|
|
|
|
|
|
**预警类型标签色**:
|
|
|
|
|
|
| 类型 | 标签色 |
|
|
|
|------|--------|
|
|
|
| PRICE_DEVIATION | Warning |
|
|
|
| STALE_FALLBACK | Warning |
|
|
|
| RPA_FAILED | Error |
|
|
|
| RPA_CAPTCHA | Error |
|
|
|
| STRUCT_CHANGE | Error |
|
|
|
|
|
|
---
|
|
|
|
|
|
## 3. 视觉设计(Visual Design System)
|
|
|
|
|
|
### 3.1 色彩系统
|
|
|
|
|
|
| Token | HEX | 用途 |
|
|
|
|-------|-----|------|
|
|
|
| `--color-primary` | `#0D9488` | 主按钮、链接、焦点环 |
|
|
|
| `--color-primary-hover` | `#0F766E` | 主按钮 hover |
|
|
|
| `--color-primary-active` | `#115E59` | 主按钮 active |
|
|
|
| `--color-success` | `#16A34A` | 成功状态、实时报价标识 |
|
|
|
| `--color-warning` | `#D97706` | 非实时角标、倒计时 <30s、预警 |
|
|
|
| `--color-error` | `#DC2626` | 错误、失败状态 |
|
|
|
| `--color-bg` | `#F8FAFC` | 页面背景 |
|
|
|
| `--color-surface` | `#FFFFFF` | 卡片、表单背景 |
|
|
|
| `--color-border` | `#E2E8F0` | 分割线、输入框边框 |
|
|
|
| `--color-text-primary` | `#0F172A` | 标题、正文 |
|
|
|
| `--color-text-secondary` | `#64748B` | 辅助说明、标签 |
|
|
|
| `--color-text-disabled` | `#94A3B8` | 禁用文字 |
|
|
|
|
|
|
**Tailwind 映射**:primary=`teal-600`,neutral=`slate`
|
|
|
|
|
|
### 3.2 字体系统
|
|
|
|
|
|
| Token | 字体 | 字号 | 字重 | 行高 | 用途 |
|
|
|
|-------|------|------|------|------|------|
|
|
|
| `--font-display` | Geist Sans | 30px / 1.875rem | 600 | 1.2 | 页面 H1 |
|
|
|
| `--font-h2` | Geist Sans | 24px / 1.5rem | 600 | 1.3 | 区块标题 |
|
|
|
| `--font-h3` | Geist Sans | 18px / 1.125rem | 600 | 1.4 | 卡片标题 |
|
|
|
| `--font-body` | Geist Sans | 16px / 1rem | 400 | 1.6 | 正文 |
|
|
|
| `--font-caption` | Geist Sans | 14px / 0.875rem | 400 | 1.5 | 辅助说明 |
|
|
|
| `--font-label` | Geist Sans | 14px / 0.875rem | 500 | 1.4 | 表单标签 |
|
|
|
| `--font-mono` | Geist Mono | 16px / 1rem | 500 | 1.4 | 金额、quote_id |
|
|
|
|
|
|
**规则**:
|
|
|
- 金额、单号使用 `font-mono`
|
|
|
- 正文最大宽度 `max-w-[65ch]`
|
|
|
|
|
|
### 3.3 间距系统(Spacing)
|
|
|
|
|
|
| Token | 值 | 用途 |
|
|
|
|-------|-----|------|
|
|
|
| `--space-1` | 4px | 图标与文字间距 |
|
|
|
| `--space-2` | 8px | 字段内 padding、标签间距 |
|
|
|
| `--space-3` | 12px | 紧凑列表项 |
|
|
|
| `--space-4` | 16px | 表单项间距、卡片内 padding |
|
|
|
| `--space-6` | 24px | 区块间距 |
|
|
|
| `--space-8` | 32px | 页面边距、卡片间距 |
|
|
|
|
|
|
**页面边距**:`px-4 md:px-6 lg:px-8`;内容区 `max-w-7xl mx-auto`
|
|
|
|
|
|
### 3.4 圆角系统
|
|
|
|
|
|
| Token | 值 | 用途 |
|
|
|
|-------|-----|------|
|
|
|
| `--radius-sm` | 6px | 输入框、标签 |
|
|
|
| `--radius-md` | 8px | 按钮 |
|
|
|
| `--radius-lg` | 12px | 卡片 |
|
|
|
| `--radius-xl` | 16px | 弹窗 |
|
|
|
|
|
|
### 3.5 阴影系统
|
|
|
|
|
|
| Token | 值 | 用途 |
|
|
|
|-------|-----|------|
|
|
|
| `--shadow-card` | `0 1px 3px rgba(15,23,42,0.06), 0 1px 2px rgba(15,23,42,0.04)` | 卡片 |
|
|
|
| `--shadow-modal` | `0 10px 25px rgba(15,23,42,0.12)` | 弹窗、Drawer |
|
|
|
| `--shadow-none` | none | 表格行、内嵌面板 |
|
|
|
|
|
|
### 3.6 组件风格
|
|
|
|
|
|
#### Button
|
|
|
|
|
|
| 变体 | 背景 | 文字 | 边框 | 高度 |
|
|
|
|------|------|------|------|------|
|
|
|
| Primary | `#0D9488` | `#FFFFFF` | none | 40px |
|
|
|
| Secondary | `#FFFFFF` | `#0F172A` | `#E2E8F0` | 40px |
|
|
|
| Ghost | transparent | `#0D9488` | none | 40px |
|
|
|
| Danger | `#DC2626` | `#FFFFFF` | none | 40px |
|
|
|
|
|
|
`border-radius: 8px`;`padding: 0 16px`;loading 时左侧 16px 旋转 Spinner
|
|
|
|
|
|
#### Input
|
|
|
|
|
|
- 高度 40px;`border: 1px solid #E2E8F0`;`radius: 6px`
|
|
|
- Focus:`ring-2 ring-teal-600/20 border-teal-600`
|
|
|
- Error:`border-error` + 下方 12px 红色 caption
|
|
|
|
|
|
#### Card(QuoteCard)
|
|
|
|
|
|
- 背景 `#FFFFFF`;`border: 1px solid #E2E8F0`;`radius: 12px`;`padding: 24px`;`shadow-card`
|
|
|
- 顶部 Tab:「标准」「保证送达」
|
|
|
- 价格区:`font-mono text-3xl font-semibold`
|
|
|
- 非实时角标:右上角 Warning 色 pill「非实时报价,仅供参考」
|
|
|
|
|
|
#### Loading
|
|
|
|
|
|
- 全页/区块:Skeleton(灰条 `bg-slate-200 animate-pulse`)
|
|
|
- 按钮内:`Spinner` 16×16,`border-2 border-white/30 border-t-white`
|
|
|
- 查价进度:线性进度条 + 文案「正在查询报价…({elapsed}s / 30s)」
|
|
|
|
|
|
#### Error 提示
|
|
|
|
|
|
- `ErrorBanner`:背景 `#FEF2F2`,左边框 4px `#DC2626`,内边距 16px
|
|
|
- `WarningBanner`:背景 `#FFFBEB`,左边框 4px `#D97706`
|
|
|
|
|
|
---
|
|
|
|
|
|
## 4. 设计规范(Design System for AI Coding)
|
|
|
|
|
|
### 4.1 UI 组件清单
|
|
|
|
|
|
| 组件名 | 路径 | 用途 |
|
|
|
|--------|------|------|
|
|
|
| `PrimaryButton` | `components/ui/primary-button.tsx` | 主操作 |
|
|
|
| `SecondaryButton` | `components/ui/secondary-button.tsx` | 取消、次要操作 |
|
|
|
| `InputField` | `components/ui/input-field.tsx` | 文本输入 + label + error |
|
|
|
| `SelectField` | `components/ui/select-field.tsx` | 货物类型、州 |
|
|
|
| `QuoteForm` | `components/quote/quote-form.tsx` | 查价表单 |
|
|
|
| `QuoteCard` | `components/quote/quote-card.tsx` | 双等级报价展示 |
|
|
|
| `QuoteResultPanel` | `components/quote/quote-result-panel.tsx` | 结果区容器(含状态切换) |
|
|
|
| `CountdownTimer` | `components/quote/countdown-timer.tsx` | 3 分钟倒计时 |
|
|
|
| `LoadingSpinner` | `components/ui/loading-spinner.tsx` | 按钮/行内 loading |
|
|
|
| `Skeleton` | `components/ui/skeleton.tsx` | 骨架屏 |
|
|
|
| `ErrorBanner` | `components/ui/error-banner.tsx` | 错误提示 |
|
|
|
| `WarningBanner` | `components/ui/warning-banner.tsx` | 非实时/预警提示 |
|
|
|
| `EmptyState` | `components/ui/empty-state.tsx` | 空列表 |
|
|
|
| `ConfirmDialog` | `components/ui/confirm-dialog.tsx` | 过期重询确认 |
|
|
|
| `DataTable` | `components/ui/data-table.tsx` | 历史/加价/预警表格 |
|
|
|
| `PageHeader` | `components/layout/page-header.tsx` | 标题区 |
|
|
|
| `AppLayout` | `components/layout/app-layout.tsx` | 客户布局 |
|
|
|
| `AdminLayout` | `components/layout/admin-layout.tsx` | 管理端布局 |
|
|
|
|
|
|
### 4.2 状态规范(统一枚举)
|
|
|
|
|
|
```typescript
|
|
|
// 查价页
|
|
|
type QuotePageStatus =
|
|
|
| 'idle'
|
|
|
| 'validating'
|
|
|
| 'processing'
|
|
|
| 'success'
|
|
|
| 'fallback'
|
|
|
| 'error'
|
|
|
| 'expired';
|
|
|
|
|
|
// 通用数据页
|
|
|
type DataPageStatus = 'loading' | 'success' | 'empty' | 'error';
|
|
|
|
|
|
// 按钮
|
|
|
type ButtonState = 'default' | 'loading' | 'disabled';
|
|
|
```
|
|
|
|
|
|
| 状态 | 视觉 | 交互 |
|
|
|
|------|------|------|
|
|
|
| loading | Skeleton 或 Spinner | 禁止重复提交 |
|
|
|
| success | 正常内容 + Success/Primary 强调 | 可操作 |
|
|
|
| error | ErrorBanner | 展示重试 |
|
|
|
| empty | EmptyState 插画 + CTA | 引导主路径 |
|
|
|
| disabled | opacity 0.5 + cursor-not-allowed | 不响应点击 |
|
|
|
|
|
|
### 4.3 命名规范
|
|
|
|
|
|
| 类型 | 规则 | 示例 |
|
|
|
|------|------|------|
|
|
|
| React 组件 | PascalCase | `QuoteCard` |
|
|
|
| 文件 | kebab-case | `quote-card.tsx` |
|
|
|
| Props 接口 | 组件名 + Props | `QuoteCardProps` |
|
|
|
| 页面状态 hook | use + 页面 + Status | `useQuotePageStatus` |
|
|
|
| Tailwind class | 语义组合,禁止随意 magic number | `text-text-secondary` 用 CSS 变量 |
|
|
|
| API 状态字段 | 与 PRD 一致 | `status`, `is_realtime`, `source_type` |
|
|
|
|
|
|
### 4.4 UI 约束规则(写死)
|
|
|
|
|
|
| ID | 规则 |
|
|
|
|----|------|
|
|
|
| UI-01 | 所有触发 API 的按钮必须有 `loading` 状态 |
|
|
|
| UI-02 | 所有 API 请求必须有 `error` 状态展示(ErrorBanner) |
|
|
|
| UI-03 | 所有表单字段必须有 `validation` 状态(字段下错误文案) |
|
|
|
| UI-04 | 查价提交按钮提交后 disabled 3000ms |
|
|
|
| UI-05 | 轮询最长 30s,超时进入 `error` 状态 |
|
|
|
| UI-06 | `is_realtime=false` 必须显示 WarningBanner,不可隐藏 |
|
|
|
| UI-07 | 金额必须用 `font-mono`,保留 2 位小数 |
|
|
|
| UI-08 | 界面文案全部中文 |
|
|
|
| UI-09 | 禁止使用 emoji 作为 UI 图标;使用 `@phosphor-icons/react` |
|
|
|
| UI-10 | 移动端所有页面单列布局,禁止横向滚动 |
|
|
|
| UI-11 | 全高区域使用 `min-h-[100dvh]`,禁止 `h-screen` |
|
|
|
|
|
|
### 4.5 Tailwind 配置片段(globals.css)
|
|
|
|
|
|
```css
|
|
|
@tailwind base;
|
|
|
@tailwind components;
|
|
|
@tailwind utilities;
|
|
|
|
|
|
:root {
|
|
|
--color-primary: #0d9488;
|
|
|
--color-primary-hover: #0f766e;
|
|
|
--color-success: #16a34a;
|
|
|
--color-warning: #d97706;
|
|
|
--color-error: #dc2626;
|
|
|
--color-bg: #f8fafc;
|
|
|
--color-surface: #ffffff;
|
|
|
--color-border: #e2e8f0;
|
|
|
--color-text-primary: #0f172a;
|
|
|
--color-text-secondary: #64748b;
|
|
|
--radius-md: 8px;
|
|
|
--radius-lg: 12px;
|
|
|
--space-4: 16px;
|
|
|
--space-6: 24px;
|
|
|
--space-8: 32px;
|
|
|
}
|
|
|
|
|
|
body {
|
|
|
@apply bg-[var(--color-bg)] text-[var(--color-text-primary)] antialiased;
|
|
|
font-family: var(--font-geist-sans), system-ui, sans-serif;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 4.6 响应式断点
|
|
|
|
|
|
| 断点 | 宽度 | 布局规则 |
|
|
|
|------|------|----------|
|
|
|
| default | <768px | 单列;查价表单全宽;隐藏侧栏改底部 Tab |
|
|
|
| `md` | ≥768px | 查价页左右 60/40 分栏 |
|
|
|
| `lg` | ≥1024px | 管理端显示固定侧栏 240px |
|
|
|
| `xl` | ≥1280px | 内容区 `max-w-7xl` 居中 |
|
|
|
|
|
|
### 4.7 页面 ↔ 组件映射(Cursor 直接生成)
|
|
|
|
|
|
| 页面 | 主要组件 |
|
|
|
|------|----------|
|
|
|
| `/login` | `InputField` ×2, `PrimaryButton`, `ErrorBanner` |
|
|
|
| `/dashboard` | `PageHeader`, 快捷入口 Card ×N |
|
|
|
| `/quote` | `QuoteForm`, `QuoteResultPanel`, `QuoteCard`, `CountdownTimer`, `ConfirmDialog` |
|
|
|
| `/quote/result/[id]` | `QuoteCard`, `CountdownTimer`, `WarningBanner` |
|
|
|
| `/history` | `DataTable`, `EmptyState`, 分页 |
|
|
|
| `/admin/markup` | `DataTable`, Drawer + `InputField`, `PrimaryButton` |
|
|
|
| `/admin/alerts` | `DataTable`, 类型 Tag, Drawer 详情 |
|
|
|
|
|
|
---
|
|
|
|
|
|
## 附录 A:与 PRD / 技术设计映射
|
|
|
|
|
|
| PRD/技术项 | UI 实现 |
|
|
|
|------------|---------|
|
|
|
| GD-5 异步轮询 | `/quote` processing 状态 + 2s 轮询 |
|
|
|
| 双等级报价 | `QuoteCard` Tab:标准 / 保证送达 |
|
|
|
| 3 分钟有效期 | `CountdownTimer` |
|
|
|
| is_realtime=false | `WarningBanner` |
|
|
|
| 过期重询 | `ConfirmDialog` |
|
|
|
| 运营加价 | `/admin/markup` Drawer |
|
|
|
| 预警中心 | `/admin/alerts` 表格 + 处理按钮 |
|
|
|
|
|
|
## 附录 B:文案清单(中文)
|
|
|
|
|
|
| 场景 | 文案 |
|
|
|
|------|------|
|
|
|
| 提交查价 | 获取报价 |
|
|
|
| 处理中 | 正在查询报价,请稍候… |
|
|
|
| 非实时 | 非实时报价,仅供参考 |
|
|
|
| 失败 | 暂时无法获取报价,请稍后重试 |
|
|
|
| 超时 | 查询超时,请重试 |
|
|
|
| 过期确认 | 报价已过期,是否重新询价? |
|
|
|
| 空历史 | 暂无询价记录 |
|
|
|
|
|
|
## 附录 C:图标(Phosphor)
|
|
|
|
|
|
| 场景 | 图标名 |
|
|
|
|------|--------|
|
|
|
| 查价 | `Truck` |
|
|
|
| 历史 | `ClockCounterClockwise` |
|
|
|
| 预警 | `Warning` |
|
|
|
| 成功 | `CheckCircle` |
|
|
|
| 错误 | `XCircle` |
|
|
|
| 加载 | `CircleNotch`(旋转) |
|