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

页面结构

  • HeaderLogo + 产品名
  • 主区:登录卡片(用户名、密码)
  • 操作区:登录按钮

状态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(核心)

页面结构

┌─────────────────────────────────────────────┐
│ HeaderAppLayout 顶栏)                      │
├─────────────────────────────────────────────┤
│ 页面标题:卡派查价                             │
│ 副标题:填写货物信息,获取实时报价(约 30 秒内) │
├──────────────────┬──────────────────────────┤
│ 主输入区(左 60% │ 结果区(右 40%,空时占位)   │
│ QuoteForm        │ QuoteResultPanel           │
├──────────────────┴──────────────────────────┤
│ 操作区获取报价PrimaryButton              │
└─────────────────────────────────────────────┘

移动端(<768px:单列;输入区在上,结果区在下。

页面状态机

状态 触发 输入区 结果区 操作区
idle 进入页面 可编辑 空状态插画 +「填写信息后获取报价」 按钮可点
validating 点击提交 只读 不变 按钮 loading
processing POST 成功且 status=processing 只读 Skeleton +「正在查询报价…」+ 进度条030s 按钮 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. 若立即 doneL1/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})?$ 邮编格式无效
重量 150000换算后 lb 重量超出范围
件数 1999 整数 件数无效

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

  • 字段加价比例NumberInput030步长 0.1
  • 按钮保存Primary/ 取消Secondary

状态loading | success | empty | error | saving

状态 UI
saving 保存按钮 loadingDrawer 内字段 disabled
校验失败 字段下「加价比例不能超过 30%」

2.8 P07 预警中心 /admin/alerts

页面结构

  • Header标题 + 筛选(类型、状态 open/resolved
  • 主区表格时间、类型、quote_id、偏差%、状态)
  • 详情:行点击展开 Drawerdetail_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-600neutral=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: 8pxpadding: 0 16pxloading 时左侧 16px 旋转 Spinner

Input

  • 高度 40pxborder: 1px solid #E2E8F0radius: 6px
  • Focusring-2 ring-teal-600/20 border-teal-600
  • Errorborder-error + 下方 12px 红色 caption

CardQuoteCard

  • 背景 #FFFFFFborder: 1px solid #E2E8F0radius: 12pxpadding: 24pxshadow-card
  • 顶部 Tab「标准」「保证送达」
  • 价格区:font-mono text-3xl font-semibold
  • 非实时角标:右上角 Warning 色 pill「非实时报价仅供参考」

Loading

  • 全页/区块Skeleton灰条 bg-slate-200 animate-pulse
  • 按钮内:Spinner 16×16border-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(旋转)