17 tasks across 3 phases: - Phase 1 (Tasks 1-8): Infrastructure (tokens, types, mock, cleanup) - Phase 2 (Tasks 9-12): Core pages (index, card list/detail, buy) - Phase 3 (Tasks 13-17): Remaining pages and components Co-Authored-By: Claude <noreply@anthropic.com>
36 KiB
UI/UX 标准化实施计划
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 统一所有页面的设计 token、类型定义和 mock 数据管理,为后端 API 对接做好准备。
Architecture: 分三步执行。第1步建立基础设施(token 统一 + 类型 + mock 层),不改任何页面视觉。第2步迁移核心链路(首页→年卡→购买→订单)。第3步迁移剩余页面和组件。
Tech Stack: uni-app 3.0 + Vue 3 + TypeScript + Pinia + SCSS
Global Constraints
- 颜色体系统一使用
uni.scss的$uv-*变量($uv-primary: #3c9cff) - 字体族:标题
$font-family-display(思源宋体),正文$font-family-body(系统字体) - 禁止在页面
<style>中定义局部$font、$primary、$text-main等变量 - 禁止
font-weight: bold关键字,必须用$font-weight-bold - 禁止硬编码颜色值(如
#333),必须用全局变量 - 禁止硬编码间距值(如
padding: 20rpx),必须用$spacing-* - 禁止在页面中硬编码业务数据,必须从
mock/或api/import - 验证方式:
pnpm run dev:mp-weixin构建通过 +pnpm run type-check通过
File Structure
新建文件
src/mock/index.ts — USE_MOCK 开关
src/mock/images.ts — 占位图片 URL 常量
src/mock/scenic.ts — 景区 mock(42 条)
src/mock/scenic-detail.ts — 景区详情 mock(8 条)
src/mock/order.ts — 订单 mock(6 条)
src/mock/reserve.ts — 预约记录 mock(6 条)
src/mock/notice.ts — 公告 mock(5 条)
src/mock/store.ts — Mock 内存状态管理
src/api/scenic.ts — 景区 API 函数
src/api/order.ts — 订单 API 函数
src/api/reserve.ts — 预约 API 函数
修改文件
src/uni.scss — 新增缺失变量
src/styles/_tokens.scss — 对齐 uni.scss 值
src/api/types.ts — 补全业务类型
src/App.vue — 全局样式
.env.local / .env.dev / .env.uat — 新增 VITE_USE_MOCK
9 个页面 — 删除局部 SCSS 变量
~25 个页面/组件 — 迁移硬编码值
Phase 1: 基础设施(不改页面视觉)
Task 1: 更新 uni.scss — 补齐缺失变量
Files:
- Modify:
src/uni.scss:71-83(字号区域) - Modify:
src/uni.scss:51($uv-tips-color后插入$uv-placeholder-color) - Modify:
src/uni.scss:65-67(渐变后插入语义色)
Interfaces:
-
Produces:
$font-size-xxs,$font-size-4xl,$uv-placeholder-color,$color-price,$color-price-highlight,$color-status-pending,$color-status-success,$color-status-cancel -
Step 1: 新增
$font-size-xxs和$font-size-4xl
在 src/uni.scss 的字号区域(第 75 行 $font-size-xs 之前),插入:
$font-size-xxs: 18rpx;
在 $font-size-3xl: 56rpx;(第 82 行)之后、$font-size-display: 72rpx; 之前,插入:
$font-size-4xl: 64rpx;
- Step 2: 新增
$uv-placeholder-color
在 $uv-tips-color: #666666;(第 51 行)之后插入:
$uv-placeholder-color: #999999;
- Step 3: 新增语义色变量
在 $gradient-accent 定义(第 67 行)之后插入:
// Semantic colors (价格/状态)
$color-price: $uv-error;
$color-price-highlight: #FFD600;
$color-status-pending: #ff9500;
$color-status-success: #00b42a;
$color-status-cancel: #86909c;
- Step 4: 修改
$font-family-body
将第 72 行:
$font-family-body: 'SourceHanSerifCN', '思源宋体', -apple-system, 'PingFang SC', sans-serif;
改为:
$font-family-body: -apple-system, 'PingFang SC', 'Helvetica Neue', sans-serif;
- Step 5: 验证编译
Run: pnpm run dev:mp-weixin
Expected: 构建成功,无 SCSS 编译错误
- Step 6: Commit
git add src/uni.scss
git commit -m "feat(tokens): add missing variables to uni.scss
- \$font-size-xxs (18rpx), \$font-size-4xl (64rpx)
- \$uv-placeholder-color (#999999)
- Semantic colors: \$color-price, \$color-status-*
- Change \$font-family-body to system fonts"
Task 2: 对齐 _tokens.scss 与 uni.scss
Files:
- Modify:
src/styles/_tokens.scss(全文替换变量值)
Interfaces:
-
Consumes: Task 1 产生的所有 uni.scss 变量
-
Produces: 与 uni.scss 值一致的
_tokens.scss -
Step 1: 替换 _tokens.scss 全文
将 src/styles/_tokens.scss 全文替换为以下内容(所有颜色值对齐 uni.scss,字体对齐思源宋体系):
// ============================================
// Design Tokens — Aligned with uni.scss
// ============================================
// ⚠️ 所有值必须与 src/uni.scss 保持一致
// uni.scss 是 source of truth,此文件仅为 main.scss 的兼容层
// === COLORS ===
// Brand accent
$color-accent: #3c9cff;
$color-accent-secondary: #398ade;
$color-accent-foreground: #FFFFFF;
// Semantic
$color-success: #5ac725;
$color-warning: #f9ae3d;
$color-error: #e53935;
// Neutrals
$color-background: #f3f4f6;
$color-foreground: #111111;
$color-muted: #f4f4f5;
$color-muted-foreground: #333333;
$color-border: #eeeeee;
$color-card: #FFFFFF;
$color-ring: #3c9cff;
// Semantic (价格/状态)
$color-price: #e53935;
$color-price-highlight: #FFD600;
$color-status-pending: #ff9500;
$color-status-success: #00b42a;
$color-status-cancel: #86909c;
// Gradients
$gradient-accent: linear-gradient(135deg, #{$color-accent}, #{$color-accent-secondary});
// === TYPOGRAPHY ===
$font-family-display: 'SourceHanSerifCN', '思源宋体', serif;
$font-family-body: -apple-system, 'PingFang SC', 'Helvetica Neue', sans-serif;
$font-family-mono: 'Menlo', 'Monaco', 'Courier New', monospace;
$font-size-xxs: 18rpx;
$font-size-xs: 20rpx;
$font-size-sm: 24rpx;
$font-size-base: 28rpx;
$font-size-lg: 32rpx;
$font-size-xl: 36rpx;
$font-size-title: 38rpx;
$font-size-2xl: 44rpx;
$font-size-3xl: 56rpx;
$font-size-4xl: 64rpx;
$font-size-display: 72rpx;
$font-weight-normal: 400;
$font-weight-medium: 500;
$font-weight-bold: 700;
$line-height-tight: 1.2;
$line-height-normal: 1.5;
$line-height-relaxed: 1.75;
// === SPACING (rpx) ===
$spacing-0: 0;
$spacing-1: 8rpx;
$spacing-2: 16rpx;
$spacing-3: 24rpx;
$spacing-4: 32rpx;
$spacing-5: 40rpx;
$spacing-6: 48rpx;
$spacing-8: 64rpx;
$spacing-10: 80rpx;
$spacing-12: 96rpx;
$spacing-16: 128rpx;
$spacing-20: 160rpx;
// === BORDER RADIUS ===
$radius-sm: 8rpx;
$radius-base: 16rpx;
$radius-lg: 24rpx;
$radius-xl: 32rpx;
$radius-2xl: 48rpx;
$radius-full: 9999rpx;
// === SHADOWS ===
$shadow-sm: 0 2rpx 8rpx rgba(15, 23, 42, 0.04);
$shadow-base: 0 4rpx 16rpx rgba(15, 23, 42, 0.08);
$shadow-lg: 0 8rpx 32rpx rgba(15, 23, 42, 0.12);
$shadow-xl: 0 16rpx 48rpx rgba(15, 23, 42, 0.16);
$shadow-accent: 0 8rpx 24rpx rgba(60, 156, 255, 0.25);
// === TRANSITIONS ===
$transition-fast: 150ms ease;
$transition-base: 250ms ease;
$transition-slow: 400ms ease;
// === Z-INDEX ===
$z-base: 0;
$z-dropdown: 100;
$z-sticky: 200;
$z-modal: 300;
$z-toast: 400;
// === BORDER ===
$border-width: 1rpx;
// ============================================
// Legacy $uni-* compatibility (for uni-ui)
// ============================================
$uni-color-primary: $color-accent;
$uni-color-success: $color-success;
$uni-color-warning: $color-warning;
$uni-color-error: $color-error;
$uni-text-color: $color-foreground;
$uni-text-color-inverse: #FFFFFF;
$uni-text-color-grey: $color-muted-foreground;
$uni-text-color-placeholder: #999999;
$uni-text-color-disable: #c8c9cc;
$uni-bg-color: $color-background;
$uni-bg-color-grey: $color-muted;
$uni-bg-color-hover: #f1f1f1;
$uni-bg-color-mask: rgba(0, 0, 0, 0.4);
$uni-border-color: $color-border;
$uni-font-size-sm: $font-size-sm;
$uni-font-size-base: $font-size-base;
$uni-font-size-lg: $font-size-lg;
$uni-font-size-title: 40rpx;
$uni-font-size-subtitle: $font-size-xl;
$uni-font-size-paragraph: $font-size-base;
$uni-img-size-sm: 40rpx;
$uni-img-size-base: 52rpx;
$uni-img-size-lg: 80rpx;
$uni-border-radius-sm: $radius-sm;
$uni-border-radius-base: $radius-base;
$uni-border-radius-lg: $radius-lg;
$uni-border-radius-circle: 50%;
$uni-spacing-row-sm: 10rpx;
$uni-spacing-row-base: 20rpx;
$uni-spacing-row-lg: 30rpx;
$uni-spacing-col-sm: 8rpx;
$uni-spacing-col-base: 16rpx;
$uni-spacing-col-lg: 24rpx;
$uni-opacity-disabled: 0.3;
$uni-color-title: $color-foreground;
$uni-color-subtitle: $color-muted-foreground;
$uni-color-paragraph: $color-muted-foreground;
- Step 2: 验证编译
Run: pnpm run dev:mp-weixin
Expected: 构建成功,main.scss 中的 $color-background、$color-foreground、$font-family-body 使用新值
- Step 3: Commit
git add src/styles/_tokens.scss
git commit -m "refactor(tokens): align _tokens.scss with uni.scss values
All color, typography, and spacing values now match uni.scss.
Added semantic colors and \$font-size-xxs/\$font-size-4xl."
Task 3: 扩展 types.ts 业务类型
Files:
- Modify:
src/api/types.ts
Interfaces:
-
Produces:
ScenicSession,ReserveRecord,Notice,QuickAction,Announcement,PageResult<T>,PageQuery,OrderDetail,FaqItem -
Step 1: 在 types.ts 末尾追加新类型
在 src/api/types.ts 文件末尾(MockPayResult 接口之后)追加:
/** 景区场次 */
export interface ScenicSession {
id: number
scenicId: number
date: string
startTime: string
endTime: string
stock: number
remainStock: number
}
/** 预约记录 */
export interface ReserveRecord {
id: number
scenicId: number
scenicName: string
date: string
session: string
status: 'pending' | 'confirmed' | 'cancelled' | 'used'
userName?: string
maskedId?: string
cardType?: string
cardNo?: string
applyTime?: string
cancelTime?: string
verifyTime?: string
verifyPhoto?: string
}
/** 公告 */
export interface Notice {
id: number
title: string
content: string
body?: string
publishTime: string
isTop: boolean
}
/** 首页快捷入口 */
export interface QuickAction {
id: number
icon: string
label: string
path: string
}
/** 首页弹窗公告 */
export interface Announcement {
id: number
type?: string
typeLabel?: string
title: string
date?: string
content: string
body?: string
}
/** 订单详情 */
export interface OrderDetail extends OrderInfo {
orderNo?: string
payTime?: string
payChannel?: string
redeemCode?: string
activatedTime?: string
refundedTime?: string
originalPrice?: number
cardType?: string
}
/** FAQ 条目 */
export interface FaqItem {
q: string
a: Array<{ text: string; highlight?: boolean; copy?: string }>
}
/** 分页结果 */
export interface PageResult<T> {
list: T[]
total: number
page: number
pageSize: number
}
/** 分页请求参数 */
export interface PageQuery {
page: number
pageSize: number
}
- Step 2: 验证类型检查
Run: pnpm run type-check
Expected: 无类型错误
- Step 3: Commit
git add src/api/types.ts
git commit -m "feat(types): add ScenicSession, ReserveRecord, Notice, FaqItem, PageResult types"
Task 4: 创建 Mock 基础设施
Files:
- Create:
src/mock/index.ts - Create:
src/mock/images.ts - Create:
src/mock/store.ts - Modify:
.env.local,.env.dev,.env.uat
Interfaces:
-
Produces:
USE_MOCK常量、占位图片常量、orderStore/reserveStore响应式数据 -
Step 1: 添加环境变量
在 .env.local、.env.dev、.env.uat 末尾各追加一行:
VITE_USE_MOCK=true
- Step 2: 创建
src/mock/index.ts
/** Mock 数据开关 — 由 .env 文件的 VITE_USE_MOCK 控制 */
export const USE_MOCK = import.meta.env.VITE_USE_MOCK === 'true'
- Step 3: 创建
src/mock/images.ts
从现有页面中收集所有 Unsplash URL,集中管理。实现时从 src/pages/index/index.vue、src/pages/scenic-list/ 等文件提取所有图片 URL,按用途命名:
// 景区封面占位图
export const IMG_SCENIC_1 = 'https://images.unsplash.com/...' // 从 index.vue hotSpots 提取
export const IMG_SCENIC_2 = 'https://images.unsplash.com/...'
// ... 每个现有 Unsplash URL 对应一个常量
// 头像占位图
export const IMG_AVATAR_DEFAULT = 'https://...'
// 年卡 Banner
export const IMG_CARD_BANNER = 'https://...'
// 通用占位
export const IMG_PLACEHOLDER = 'https://...'
实现时从各页面
<script>中提取实际的 Unsplash URL,不要发明新 URL。
- Step 4: 创建
src/mock/store.ts
import { ref } from 'vue'
import type { OrderInfo, ReserveRecord } from '@/api/types'
import { mockOrders } from './order'
import { mockReserves } from './reserve'
export const orderStore = ref<OrderInfo[]>([...mockOrders])
export const reserveStore = ref<ReserveRecord[]>([...mockReserves])
export function updateOrderStatus(orderId: string, status: OrderInfo['status']) {
const order = orderStore.value.find(o => o.id === orderId)
if (order) order.status = status
}
export function addReserve(record: Omit<ReserveRecord, 'id'>) {
const id = Math.max(...reserveStore.value.map(r => r.id), 0) + 1
reserveStore.value.push({ ...record, id })
}
- Step 5: 验证
Run: pnpm run type-check
Expected: mock 文件无类型错误
- Step 6: Commit
git add src/mock/ .env.local .env.dev .env.uat
git commit -m "feat(mock): create mock infrastructure with USE_MOCK switch"
Task 5: 迁移 Mock 数据到 mock/ 目录
Files:
- Create:
src/mock/scenic.ts— 从scenic-list.vue提取 42 条景区数据 - Create:
src/mock/scenic-detail.ts— 从scenic-detail.vue提取 8 条详情 - Create:
src/mock/order.ts— 从order-list.vue提取 6 条订单 - Create:
src/mock/reserve.ts— 从reserve-list.vue提取 6 条预约 - Create:
src/mock/notice.ts— 从notice-list.vue提取 5 条公告
Interfaces:
-
Consumes: Task 3 的
Scenic,OrderInfo,ReserveRecord,Notice类型 -
Produces:
mockScenicList,mockScenicDetails,mockOrders,mockReserves,mockNotices -
Step 1: 创建
src/mock/scenic.ts
从 src/pages/scenic-list/scenic-list.vue 第 247-337 行提取 scenicList 数组和筛选选项(regionOptions、levelOptions、cardTypeOptions),转换为符合 ScenicSpot 类型。
类型对齐注意:现有
ScenicSpot的id是string类型。如果 scenic-list.vue 中使用数字 id,在 mock 中转为String(id)。保持ScenicSpot接口不变,后续对接 API 时再统一调整。
import type { ScenicSpot } from '@/api/types'
import { IMG_SCENIC_1 /* ... */ } from './images'
export const mockScenicList: ScenicSpot[] = [
// 从 scenic-list.vue 提取的 42 条数据
// 保持原始字段值,image 字段使用 images.ts 常量
]
export const regionOptions = [
// 从 scenic-list.vue 提取
]
export const levelOptions = [
// 从 scenic-list.vue 提取
]
实现时逐条复制 scenic-list.vue 中的数据,确保 42 条完整。
- Step 2: 创建
src/mock/scenic-detail.ts
从 src/pages/scenic-detail/scenic-detail.vue 第 172-181 行提取 scenicData 数组(8 条详情)。
- Step 3: 创建
src/mock/order.ts
从 src/pages/order/order-list.vue 第 151-158 行提取 orders 数组(6 条订单)。
import type { OrderDetail } from '@/api/types'
export const mockOrders: OrderDetail[] = [
// 从 order-list.vue 提取的 6 条数据
]
- Step 4: 创建
src/mock/reserve.ts
从 src/pages/reserve/reserve-list.vue 第 228-235 行提取 records 数组(6 条预约记录)。
- Step 5: 创建
src/mock/notice.ts
从 src/pages/notice-list/notice-list.vue 第 47-53 行提取 notices 数组(5 条公告)。
- Step 6: 验证类型检查
Run: pnpm run type-check
Expected: 所有 mock 文件类型正确
- Step 7: Commit
git add src/mock/
git commit -m "feat(mock): extract all hardcoded data into mock/ modules
- scenic: 42 items from scenic-list.vue
- scenic-detail: 8 items from scenic-detail.vue
- order: 6 items from order-list.vue
- reserve: 6 items from reserve-list.vue
- notice: 5 items from notice-list.vue"
Task 6: 创建 API 函数层
Files:
- Create:
src/api/scenic.ts - Create:
src/api/order.ts - Create:
src/api/reserve.ts
Interfaces:
-
Consumes: Task 4 的
USE_MOCK、Task 5 的 mock 数据、Task 3 的类型 -
Produces:
getScenicList(),getScenicDetail(),getOrderList(),getOrderDetail(),getReserveList()等函数 -
Step 1: 创建
src/api/scenic.ts
import { USE_MOCK } from '@/mock'
import { mockScenicList } from '@/mock/scenic'
import { mockScenicDetails } from '@/mock/scenic-detail'
import type { ScenicSpot } from './types'
import { get } from './request'
/** 获取景区列表 */
export function getScenicList(): Promise<ScenicSpot[]> {
if (USE_MOCK) return Promise.resolve(mockScenicList)
return get<ScenicSpot[]>('/mp-api/scenic/list')
}
/** 获取景区详情 */
export function getScenicDetail(id: string): Promise<ScenicSpot | undefined> {
if (USE_MOCK) {
return Promise.resolve(mockScenicDetails.find(s => s.id === id))
}
return get<ScenicSpot>(`/mp-api/scenic/detail?id=${id}`)
}
- Step 2: 创建
src/api/order.ts
import { USE_MOCK } from '@/mock'
import { mockOrders } from '@/mock/order'
import { orderStore } from '@/mock/store'
import type { OrderDetail, OrderInfo, CreateOrderParams, MockPayResult } from './types'
import { get, post } from './request'
/** 获取订单列表 */
export function getOrderList(): Promise<OrderInfo[]> {
if (USE_MOCK) return Promise.resolve(orderStore.value)
return get<OrderInfo[]>('/mp-api/order/list')
}
/** 获取订单详情 */
export function getOrderDetail(id: string): Promise<OrderDetail | undefined> {
if (USE_MOCK) {
return Promise.resolve(mockOrders.find(o => o.id === id))
}
return get<OrderDetail>(`/mp-api/order/detail?id=${id}`)
}
/** 创建订单 */
export function createOrder(params: CreateOrderParams): Promise<{ orderId: string }> {
if (USE_MOCK) {
return Promise.resolve({ orderId: `mock_${Date.now()}` })
}
return post<{ orderId: string }>('/mp-api/order/create', params)
}
- Step 3: 创建
src/api/reserve.ts
import { USE_MOCK } from '@/mock'
import { reserveStore } from '@/mock/store'
import type { ReserveRecord } from './types'
import { get, post } from './request'
/** 获取预约记录列表 */
export function getReserveList(): Promise<ReserveRecord[]> {
if (USE_MOCK) return Promise.resolve(reserveStore.value)
return get<ReserveRecord[]>('/mp-api/reserve/list')
}
/** 创建预约 */
export function createReserve(params: Omit<ReserveRecord, 'id' | 'status'>): Promise<{ id: number }> {
if (USE_MOCK) {
const id = Math.max(...reserveStore.value.map(r => r.id), 0) + 1
reserveStore.value.push({ ...params, id, status: 'pending' })
return Promise.resolve({ id })
}
return post<{ id: number }>('/mp-api/reserve/create', params)
}
- Step 4: 验证类型检查
Run: pnpm run type-check
Expected: 无类型错误
- Step 5: Commit
git add src/api/scenic.ts src/api/order.ts src/api/reserve.ts
git commit -m "feat(api): add scenic, order, reserve API functions with mock fallback"
Task 7: 更新 App.vue 全局样式
Files:
-
Modify:
src/App.vue(<style>部分) -
Step 1: 更新 App.vue 的 page 全局样式
main.scss 中已有 page { ... } 定义,且 _tokens.scss 已在 Task 2 中对齐。验证 main.scss 的 page 样式是否生效:
// main.scss 已有(无需修改):
page {
background-color: $color-background; // → #f3f4f6
color: $color-foreground; // → #111111
font-family: $font-family-body; // → -apple-system, 'PingFang SC'...
font-size: $font-size-base; // → 28rpx
line-height: $line-height-normal; // → 1.5
}
确认 App.vue 的 <style> 中 import 了 main.scss(当前已有 @import './styles/main.scss')。
如果 App.vue 中有重复的 page { min-height: 100vh } 样式,保留它(与 main.scss 不冲突)。
- Step 2: 验证
Run: pnpm run dev:mp-weixin
Expected: 构建成功,微信开发者工具中页面背景和字体符合规范
- Step 3: Commit
git add src/App.vue
git commit -m "chore: verify App.vue global styles aligned with token system"
Task 8: 清理 9 个页面的局部 SCSS 变量
Files:
- Modify:
src/pages/faq/faq.vue(删除 lines 243-249 的局部变量) - Modify:
src/pages/order/order-list.vue(删除 lines 232-240) - Modify:
src/pages/order/order-detail.vue(删除 lines 225-234) - Modify:
src/pages/qrcode/index.vue(删除 lines 249-250) - Modify:
src/pages/card-buy/card-buy.vue(删除 lines 85-90) - Modify:
src/pages/reserve/reserve-success.vue(删除 lines 122-130) - Modify:
src/pages/card-bind/card-bind.vue(删除 lines 270-279) - Modify:
src/pages/reserve/reserve-index.vue(删除 lines 229-238) - Modify:
src/pages/reserve/reserve-list.vue(删除 lines 305-313)
Interfaces:
- Consumes: Task 1 的 uni.scss 全局变量
每个文件的变量映射关系:
| 局部变量 | 全局替换为 | 值 |
|---|---|---|
$font |
$font-family-body |
系统字体 |
$primary |
$uv-primary |
#3c9cff |
$text-main |
$uv-main-color |
#111111 |
$text-label |
$uv-main-color |
#222222→#111111(合并) |
$text-body |
$uv-content-color |
#333333 |
$text-hint |
$uv-tips-color |
#666666 |
$text-weak |
$uv-placeholder-color |
#999999 |
$divider |
$uv-border-color |
#eeeeee |
$card-radius |
$radius-lg |
24rpx |
$accent |
$uv-primary |
#3c9cff |
注意:
$bar-height: 120rpx是组件特定值,保留为局部变量。
- Step 1: 逐文件删除局部变量定义块
对每个文件:
- 删除
<style>开头的局部变量定义行($font: ...;到$divider: ...;等) - 在
<style>内全文搜索替换:$font→$font-family-body(注意不要替换$font-size等前缀匹配)$primary→$uv-primary(注意不要替换$primary-dark先处理)$text-main→$uv-main-color$text-label→$uv-main-color(合并到主色)$text-body→$uv-content-color$text-hint→$uv-tips-color$text-weak→$uv-placeholder-color$divider→$uv-border-color$card-radius→$radius-lg$accent→$uv-primary
- 特殊处理 card-bind.vue:
$primary-dark: #e89a2a是 bug(值是金色#e89a2a,不是蓝色)。检查该变量在样式中的使用场景:- 如果用于金色/警告相关样式 → 替换为
$uv-warning-dark(#f1a532) - 如果用于主色深蓝相关样式 → 替换为
$uv-primary-dark(#398ade)
- 如果用于金色/警告相关样式 → 替换为
- Step 2: 验证编译
Run: pnpm run dev:mp-weixin
Expected: 构建成功,所有 9 个页面视觉无回退
- Step 3: Commit
git add src/pages/
git commit -m "refactor: remove local SCSS variables from 9 pages
Replace with global uni.scss tokens:
\$font → \$font-family-body
\$primary → \$uv-primary
\$text-* → \$uv-main-color/\$uv-content-color/\$uv-tips-color/\$uv-placeholder-color
\$divider → \$uv-border-color
\$card-radius → \$radius-lg"
Phase 2: 核心链路迁移(4 个页面 + 相关组件)
迁移检查清单(每个页面必须执行全部 6 项):
- 删除局部 SCSS 变量(已在 Task 8 处理)
- 硬编码颜色值 → 全局变量(见 spec 2.5 迁移对照表)
- 硬编码字号 → 对应
$font-size-*变量(见 spec 1.2 游离值迁移表)- 硬编码间距 → 对应
$spacing-*变量- 模板中硬编码数据 → import mock 数据
font-weight: bold→$font-weight-bold,硬编码过渡 →$transition-*
Task 9: 迁移首页 (index.vue)
Files:
- Modify:
src/pages/index/index.vue
当前状态:已使用 SCSS 变量,但有部分硬编码值需修正。
- Step 1: 颜色迁移
搜索替换(在 <style> 中):
-
#e53935→$color-price(价格场景)或$uv-error(错误场景) -
#FFD600→$color-price-highlight(促销价,已有) -
rgba(255, 255, 255, ...)→ 按 spec 2.4 规范保留(暗底白字透明度已是标准值) -
rgba(60, 156, 255, ...)→ 保留(品牌蓝透明度变体) -
Step 2: 字号迁移
-
font-size: 22rpx→$font-size-xs(20rpx) -
font-size: 144rpx→ 保留(首页大卡促销数字,特殊值) -
Step 3: 模板数据迁移
将 <script> 中的硬编码数据替换为 import:
-
hotSpots数组 →import { mockScenicList } from '@/mock/scenic'(取前 6 条) -
announcements数组 →import { mockNotices } from '@/mock/notice' -
quickActions数组 → 保留(UI 配置,非业务数据) -
Step 4: 验证
Run: pnpm run dev:mp-weixin
Expected: 首页视觉无回退,数据正常显示
- Step 5: Commit
git add src/pages/index/
git commit -m "refactor(index): migrate hardcoded values to design tokens and mock data"
Task 10: 迁移年卡列表 (card/list.vue)
Files:
- Modify:
src/packages/user/pages/card/list.vue
当前状态:已使用 SCSS 变量,少量硬编码。
-
Step 1: 颜色迁移
-
#fff→$color-card -
rgba(48, 49, 51, ...)→ 保留(暗底渐变,特殊视觉效果) -
rgba(255, 255, 255, 0.8)→ 保留(暗底白字标准透明度) -
Step 2: 间距迁移
无关键间距值需要迁移(已有 token 化),保留 icon/decoration 的硬编码像素值。
- Step 3: 验证并提交
git add src/packages/user/pages/card/list.vue
git commit -m "refactor(card/list): migrate remaining hardcoded colors to tokens"
Task 11: 迁移年卡详情 (card/detail.vue)
Files:
- Modify:
src/packages/user/pages/card/detail.vue
当前状态:大量硬编码值(13 种颜色、22 种字号/间距、4 个 font-weight: bold)。
- Step 1: 颜色迁移
| 硬编码值 | 替换为 |
|---|---|
#ddd |
$uv-border-color |
#fff |
$color-card |
#333 |
$uv-content-color |
#e53935 |
$color-price |
#ccc |
$uv-disabled-color |
#666 |
$uv-tips-color |
#eee |
$uv-border-color |
#999 |
$uv-placeholder-color |
- Step 2: 字号迁移
| 硬编码值 | 替换为 |
|---|---|
36rpx |
$font-size-xl |
44rpx |
$font-size-2xl |
26rpx |
$font-size-base(28rpx,向上取整) |
30rpx |
$font-size-lg(32rpx) |
28rpx |
$font-size-base |
24rpx |
$font-size-sm |
22rpx |
$font-size-xs(20rpx) |
- Step 3: 间距迁移
| 硬编码值 | 替换为 |
|---|---|
24rpx |
$spacing-3 |
30rpx |
$spacing-4(32rpx) |
16rpx |
$spacing-2 |
12rpx |
$spacing-2(16rpx,向上取整) |
8rpx |
$spacing-1 |
20rpx |
$spacing-3(24rpx) |
- Step 4: font-weight 迁移
全部 font-weight: bold → font-weight: $font-weight-bold
- Step 5: 验证并提交
git add src/packages/user/pages/card/detail.vue
git commit -m "refactor(card/detail): migrate all hardcoded values to design tokens"
Task 12: 迁移购买流程 (card/buy.vue + card-buy.vue)
Files:
-
Modify:
src/packages/user/pages/card/buy.vue -
Modify:
src/pages/card-buy/card-buy.vue -
Step 1: 迁移
packages/card/buy.vue
颜色迁移(同 Task 11 对照表):
#f5f5f5→$uv-bg-color#fff→$color-card#333→$uv-content-color#e53935→$color-price#eeeeee→$uv-border-color#666→$uv-tips-color#3c9cff→$uv-primary#999→$uv-placeholder-color#eee→$uv-border-color#e5e7eb→$uv-border-color
字号迁移:
30rpx→$font-size-lg28rpx→$font-size-base36rpx→$font-size-xl32rpx→$font-size-lg22rpx→$font-size-xs40rpx→$font-size-xl(36rpx,向下取整)
间距迁移:
20rpx→$spacing-3(24rpx)24rpx→$spacing-330rpx→$spacing-4(32rpx)16rpx→$spacing-2
font-weight: bold → $font-weight-bold(4 处)
- Step 2: 迁移
pages/card-buy/card-buy.vue
删除局部变量(已在 Task 8 处理)。
颜色迁移:
#ffffff→$color-card#e53935→$color-pricergba(0, 0, 0, 0.06)→ 保留(投影变体)rgba(60, 156, 255, 0.3)→ 保留(品牌蓝投影)
字号迁移:
26rpx→$font-size-base(28rpx)28rpx→$font-size-base38rpx→$font-size-xl(36rpx)22rpx→$font-size-xs(20rpx)
font-weight: 700 → $font-weight-bold(3 处)
过渡迁移:
-
all 0.2s ease→all $transition-fast -
Step 3: 验证并提交
git add src/packages/user/pages/card/buy.vue src/pages/card-buy/
git commit -m "refactor(card/buy): migrate purchase flow to design tokens"
Phase 3: 剩余页面迁移
Task 13: 迁移订单页面
Files:
-
Modify:
src/pages/order/order-list.vue -
Modify:
src/pages/order/order-detail.vue -
Step 1: 迁移 order-list.vue
局部变量已删除(Task 8)。
颜色迁移:
- 搜索所有
#111/#222→$uv-main-color #333→$uv-content-color#666→$uv-tips-color#999→$uv-placeholder-color#eeeeee→$uv-border-color#ff9500→$color-status-pending#00b42a→$color-status-success#86909c→$color-status-cancel#f53f3f→$uv-error
字号迁移(26rpx → $font-size-base,其他按对照表)。
模板数据迁移:
-
ordersref 数组 →import { getOrderList } from '@/api/order',onLoad中调用 -
Step 2: 迁移 order-detail.vue
同 order-list.vue 的颜色/字号迁移。
模板数据迁移:
-
mockOrders数组 →import { getOrderDetail } from '@/api/order' -
Step 3: 验证并提交
git add src/pages/order/
git commit -m "refactor(order): migrate order pages to design tokens and API layer"
Task 14: 迁移预约页面
Files:
-
Modify:
src/pages/reserve/reserve-index.vue -
Modify:
src/pages/reserve/reserve-list.vue -
Modify:
src/pages/reserve/reserve-success.vue -
Step 1: 迁移 reserve-list.vue
局部变量已删除(Task 8)。
颜色/字号迁移同订单页面模式。
状态颜色:pending → $color-status-pending,used/auto_used → $color-status-success,cancelled → $color-status-cancel。
模板数据迁移:records → import { getReserveList } from '@/api/reserve'。
- Step 2: 迁移 reserve-index.vue
局部变量已删除(Task 8)。
颜色/字号迁移同上。
模板数据:scenic、userInfo、mockStocks、sessionMap → 后续对接 API 时替换,当前保留。
- Step 3: 迁移 reserve-success.vue
局部变量已删除(Task 8)。 颜色/字号迁移同上。
- Step 4: 验证并提交
git add src/pages/reserve/
git commit -m "refactor(reserve): migrate reserve pages to design tokens"
Task 15: 迁移剩余页面
Files:
-
src/pages/login/login.vue— 已有 SCSS 变量,少量硬编码 -
src/pages/login/agreement.vue— 已有 SCSS 变量 -
src/pages/login/privacy.vue— 已有 SCSS 变量 -
src/pages/profile/index.vue— 已有 SCSS 变量 -
src/pages/qrcode/index.vue— 局部变量已删除(Task 8) -
src/pages/faq/faq.vue— 局部变量已删除(Task 8) -
src/pages/card-bind/card-bind.vue— 局部变量已删除(Task 8) -
src/pages/notice-list/notice-list.vue— 混合状态 -
src/pages/scenic-detail/scenic-detail.vue— 已有 SCSS 变量 -
src/pages/scenic-list/scenic-list.vue— 已有 SCSS 变量 -
src/pages/union-buy/union-buy.vue— 已有 SCSS 变量 -
src/pages/usage-guide/usage-guide.vue— 纯图片页面 -
src/packages/user/pages/card/activate.vue— 硬编码字体 -
src/packages/user/pages/card/bind.vue— 硬编码字体 -
src/packages/user/pages/card/qrcode.vue— 硬编码字体 -
Step 1: 逐文件迁移
对每个文件执行 6 项检查清单:
删除局部变量(已在 Task 8 处理)- 硬编码颜色 → 全局变量
- 硬编码字号 →
$font-size-* - 硬编码间距 →
$spacing-* - 模板数据 → mock import(notice-list、qrcode、faq)
font-weight: bold→$font-weight-bold
分包页面特殊处理(activate/bind/qrcode):
- 这些文件使用
font-family: 'SourceHanSerifCN'...硬编码字符串 - 替换为
font-family: $font-family-display(标题)或$font-family-body(正文)
notice-list 数据迁移:
notices数组 →import { mockNotices } from '@/mock/notice'
qrcode 数据迁移:
-
userCards数组 → 保留(需用户登录后从 API 获取) -
Step 2: 验证并提交
git add src/pages/ src/packages/
git commit -m "refactor: migrate remaining pages to design tokens"
Task 16: 迁移公共组件
Files:
-
Modify:
src/components/card-item.vue— 从纯 CSS 改为 SCSS -
Modify:
src/components/empty-state.vue— 从纯 CSS 改为 SCSS -
Modify:
src/components/McGlass.vue— 从纯 CSS 改为 SCSS -
Step 1: 迁移 card-item.vue
将 <style scoped> 改为 <style lang="scss" scoped>。
颜色迁移:
| 硬编码值 | 替换为 |
|---|---|
#fff |
$color-card |
#eee |
$uv-border-color |
#333 |
$uv-content-color |
#999 |
$uv-placeholder-color |
#E74C3C |
$color-price |
#ccc |
$uv-disabled-color |
#4A90D9 |
$uv-primary |
#E8F0FE |
$uv-primary-light |
字号迁移:
| 硬编码值 | 替换为 |
|---|---|
30rpx |
$font-size-lg |
24rpx |
$font-size-sm |
32rpx |
$font-size-lg |
22rpx |
$font-size-xs |
间距迁移:
| 硬编码值 | 替换为 |
|---|---|
16rpx |
$spacing-2 |
20rpx |
$spacing-3(24rpx) |
8rpx |
$spacing-1 |
4rpx |
保留(极小装饰值) |
12rpx |
$spacing-2(16rpx) |
圆角迁移:
16rpx→$radius-base20rpx→$radius-base(16rpx,向下取整)
font-weight: bold → $font-weight-bold(2 处)
- Step 2: 迁移 empty-state.vue
改为 <style lang="scss" scoped>。
颜色迁移:
#999→$uv-placeholder-color#4A90D9→$uv-primary#E8F0FE→$uv-primary-light
字号/间距迁移:
80rpx(icon) → 保留(装饰尺寸)28rpx→$font-size-base40rpx→$spacing-520rpx→$spacing-3(24rpx)30rpx→$spacing-4(32rpx)12rpx→$spacing-2(16rpx)
圆角:30rpx → $radius-full
- Step 3: 迁移 McGlass.vue
改为 <style lang="scss" scoped>。
-
rgba 值保留(毛玻璃效果的特殊视觉值)
-
24rpx圆角 →$radius-lg -
backdrop-filter 值保留
-
Step 4: 验证并提交
git add src/components/
git commit -m "refactor(components): migrate card-item, empty-state, McGlass to SCSS tokens"
Task 17: 最终验证
- Step 1: TypeScript 类型检查
Run: pnpm run type-check
Expected: 无错误
- Step 2: 全量构建
Run: pnpm run dev:mp-weixin
Expected: 构建成功
- Step 3: 视觉回归检查
在微信开发者工具中逐页检查:
-
首页:轮播、景区卡片、超级玩家卡价格(¥98 亮黄色)
-
年卡列表/详情:布局、颜色、字号
-
购买流程:价格显示、按钮样式
-
订单列表/详情:状态标签颜色
-
预约页面:表单、列表
-
个人中心、登录页
-
Step 4: 清理遗留
-
删除
src/data/scenic-spots.ts(已被mock/scenic.ts替代) -
确认无残留的局部
$font/$primary定义:grep -r '\$font:' src/pages/ src/packages/ -
确认无残留的
font-weight: bold:grep -r 'font-weight: bold' src/ -
Step 5: 最终提交
git add -A
git commit -m "chore: final verification and cleanup after UI/UX standardization"