You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
chajia/docs/查价系统-UI设计.md

20 KiB

查价系统 产品 UI/UX 设计方案 v1.0

版本 日期 作者 说明
v1.0 2026-06-16 产品设计/UI/UX 可直接用于前端开发与 Cursor 生成代码

配套文档:查价系统-PRD.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 状态规范(统一枚举)

// 查价页
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)

@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(旋转)