- Fix type import syntax in mock example (Scenic[] → Scenic) - Add pagination example to mock mechanism - Clarify $uv-placeholder-color placement in uni.scss - Separate $color-price vs $uv-error in migration table - Split task 1.1 into 1.1a/1.1b/1.1c sub-tasks - Add App.vue global style example - Add SCSS import rules Co-Authored-By: Claude <noreply@anthropic.com>
596 lines
19 KiB
Markdown
596 lines
19 KiB
Markdown
# UI/UX 标准化设计规范
|
||
|
||
> 湖南12301文旅专区微信小程序(C端)
|
||
> 2026-06-26
|
||
|
||
## 背景
|
||
|
||
项目现有 35 个 .vue 文件,存在以下问题:
|
||
- 双 token 系统冲突(`uni.scss` vs `_tokens.scss`)
|
||
- 14 个页面自定义局部 SCSS 变量,绕过全局体系
|
||
- 3 套字体族混用(SourceHanSerifCN / Noto Serif SC / 系统字体)
|
||
- 55+ 种硬编码颜色值(4 种品牌蓝、4 种错误红、5 种背景灰)
|
||
- 19 种字号仅 8 种有对应变量
|
||
- 9 种按钮高度
|
||
- mock 数据分散在各页面,格式不统一
|
||
|
||
目标:统一所有页面的字体、字号、颜色、间距、组件规范,以及 mock 数据管理方式,为后续后端 API 对接做好准备。
|
||
|
||
## 已确认的决策
|
||
|
||
| 决策项 | 选择 | 理由 |
|
||
|--------|------|------|
|
||
| 颜色体系 | uni.scss 体系 | 偏暖偏亮,`#3c9cff` 主色 |
|
||
| 字体族 | 思源宋体系 | 文化/旅游感强 |
|
||
| 模拟数据 | 集中管理 + 类型先行 | 对接 API 时页面零改动 |
|
||
| 执行节奏 | 基础设施先行 → 核心链路 → 剩余页面 | 风险最小 |
|
||
|
||
---
|
||
|
||
## 1. Token 体系统一
|
||
|
||
### 1.1 Source of Truth
|
||
|
||
- 保留 `src/uni.scss` 作为唯一 token 文件(uni-app 自动注入到所有 `<style lang="scss">`)
|
||
- `_tokens.scss` 中与 `uni.scss` 冲突的变量值改为直接引用 `uni.scss` 变量,或删除
|
||
- 禁止任何页面在 `<style>` 中重新定义 `$font`、`$primary`、`$text-main` 等局部变量
|
||
|
||
### 1.2 字号体系
|
||
|
||
10 级字号,消灭所有游离值:
|
||
|
||
| 变量 | 值 | 场景 |
|
||
|------|-----|------|
|
||
| `$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-2xl` | 44rpx | 页面大标题 |
|
||
| `$font-size-3xl` | 56rpx | 大号展示数字 |
|
||
| `$font-size-4xl` | 64rpx | 促销大数字(介于 3xl 和 display 之间) |
|
||
| `$font-size-display` | 72rpx | 超大展示(极少使用) |
|
||
|
||
**游离值迁移对照**:
|
||
|
||
| 原值 | 迁移到 | 理由 |
|
||
|------|--------|------|
|
||
| 18rpx | 新增 `$font-size-xxs` | 使用频率高,值得单独层级 |
|
||
| 22rpx | `$font-size-xs` (20rpx) | 视觉差异极小 |
|
||
| 26rpx | `$font-size-base` (28rpx) | 正文统一 28rpx |
|
||
| 30rpx | `$font-size-lg` (32rpx) | 卡片标题统一 32rpx |
|
||
| 34rpx | `$font-size-xl` (36rpx) | 差异极小 |
|
||
|
||
### 1.3 字重
|
||
|
||
| 变量 | 值 | 场景 |
|
||
|------|-----|------|
|
||
| `$font-weight-normal` | 400 | 正文、说明 |
|
||
| `$font-weight-medium` | 500 | 次要标题、按钮 |
|
||
| `$font-weight-bold` | 700 | 主标题、价格数字、强调 |
|
||
|
||
禁止使用 CSS 关键字 `bold`,统一用数字变量。
|
||
|
||
### 1.4 行高
|
||
|
||
| 变量 | 值 | 场景 |
|
||
|------|-----|------|
|
||
| `$line-height-tight` | 1.2 | 标题 |
|
||
| `$line-height-normal` | 1.5 | 正文 |
|
||
| `$line-height-relaxed` | 1.75 | 长段落、说明文 |
|
||
|
||
---
|
||
|
||
## 2. 颜色体系
|
||
|
||
### 2.1 品牌 & 功能色
|
||
|
||
以 `uni.scss` 现有值为准,不做修改:
|
||
|
||
| 变量 | 值 | 用途 |
|
||
|------|-----|------|
|
||
| `$uv-primary` | `#3c9cff` | 主色/品牌蓝 |
|
||
| `$uv-error` | `#e53935` | 错误/危险红 |
|
||
| `$uv-warning` | `#f9ae3d` | 警告/橙 |
|
||
| `$uv-success` | `#5ac725` | 成功/绿 |
|
||
|
||
### 2.2 中性色(文字 & 背景)
|
||
|
||
沿用 `uni.scss` 现有 `$uv-*` 命名,补充缺失值,替代现有 55+ 种硬编码灰值:
|
||
|
||
**主变量(uni.scss 已有,保持不变)**:
|
||
|
||
| 变量 | 值 | 用途 |
|
||
|------|-----|------|
|
||
| `$uv-main-color` | `#111111` | 标题、重要正文 |
|
||
| `$uv-content-color` | `#333333` | 正文 |
|
||
| `$uv-tips-color` | `#666666` | 次要说明 |
|
||
| `$uv-placeholder-color` | `#999999` | 占位符(**新增**,放在 `$uv-tips-color` 下方) |
|
||
| `$uv-disabled-color` | `#c8c9cc` | 不可操作文字 |
|
||
| `$uv-bg-color` | `#f3f4f6` | 页面背景 |
|
||
| `$uv-bg-color-hover` | `#f1f1f1` | 按压态背景 |
|
||
| `$uv-border-color` | `#eeeeee` | 分割线、边框 |
|
||
| `$color-card` | `#ffffff` | 卡片/模块背景(uni.scss 已有) |
|
||
|
||
**语义别名(向后兼容,指向上述主变量)**:
|
||
|
||
| 别名 | 指向 | 说明 |
|
||
|------|------|------|
|
||
| `$color-foreground` | `$uv-main-color` | uni.scss 已有 |
|
||
| `$color-muted-foreground` | `$uv-content-color` | uni.scss 已有 |
|
||
| `$color-background` | `$uv-bg-color` | uni.scss 已有 |
|
||
| `$color-border` | `$uv-border-color` | uni.scss 已有 |
|
||
|
||
> 迁移时优先使用主变量(`$uv-*`),语义别名仅在已有代码中保留,新代码禁止使用别名。
|
||
|
||
### 2.3 语义色
|
||
|
||
订单状态、标签等场景专用:
|
||
|
||
| 变量 | 值 | 用途 |
|
||
|------|-----|------|
|
||
| `$color-price` | `#e53935` | 价格红 |
|
||
| `$color-price-highlight` | `#FFD600` | 促销亮黄(首页大卡专用) |
|
||
| `$color-status-pending` | `#ff9500` | 待处理/待支付 |
|
||
| `$color-status-success` | `#00b42a` | 已完成/已支付 |
|
||
| `$color-status-cancel` | `#86909c` | 已取消/已关闭 |
|
||
|
||
### 2.4 透明度规范
|
||
|
||
暗底白字(5 档):
|
||
```
|
||
rgba(255, 255, 255, 0.35) — 最弱(信任标签等)
|
||
rgba(255, 255, 255, 0.55) — 弱(次要说明)
|
||
rgba(255, 255, 255, 0.7) — 中(描述文字)
|
||
rgba(255, 255, 255, 0.85) — 强(正文)
|
||
rgba(255, 255, 255, 0.95) — 最强(标题)
|
||
```
|
||
|
||
白底投影(3 档):
|
||
```
|
||
rgba(0, 0, 0, 0.04) — 轻投影(卡片)
|
||
rgba(0, 0, 0, 0.08) — 中投影(浮层)
|
||
rgba(0, 0, 0, 0.12) — 重投影(弹窗)
|
||
```
|
||
|
||
### 2.5 硬编码颜色迁移对照
|
||
|
||
| 现有硬编码值 | 迁移到 |
|
||
|-------------|--------|
|
||
| `#111`, `#1A1A1A`, `#222` | `$uv-main-color` |
|
||
| `#333` | `$uv-content-color` |
|
||
| `#666` | `$uv-tips-color` |
|
||
| `#999`, `#888888` | `$uv-placeholder-color` |
|
||
| `#cccccc`, `#BBBBBB`, `#dadbde` | `$uv-disabled-color` / `$uv-light-color` |
|
||
| `#e5e7eb`, `#E2E8F0` | `$uv-border-color` |
|
||
| `#f7f8fa`, `#f5f5f5`, `#f7f7f7`, `#FAFAFA`, `#F8FAFC` | `$uv-bg-color` |
|
||
| `#fff`, `#ffffff`, `#FFFFFF` | `$color-card` |
|
||
| `#e53935`(价格场景) | `$color-price`(价格红,值 = `$uv-error`) |
|
||
| `#EF4444`, `#f53f3f`, `#E74C3C`(错误/状态场景) | `$uv-error` |
|
||
| `#3c9cff`, `#0052FF`, `#4A90D9`, `#4D7CFF` | `$uv-primary` |
|
||
| `#5ac725`, `#10B981`, `#00b42a` | `$uv-success` 或 `$color-status-success` |
|
||
| `#f9ae3d`, `#F59E0B`, `#ff9500`, `#ff7d00` | `$uv-warning` 或 `$color-status-pending` |
|
||
|
||
---
|
||
|
||
## 3. 字体族 & 排版
|
||
|
||
### 3.1 字体声明
|
||
|
||
```scss
|
||
// uni.scss 现状(display 和 body 都用了思源宋体):
|
||
$font-family-display: 'SourceHanSerifCN', '思源宋体', serif; // ✅ 保持不变
|
||
$font-family-body: 'SourceHanSerifCN', '思源宋体', -apple-system, 'PingFang SC', sans-serif; // ⚠️ 需修改
|
||
|
||
// 修改后:
|
||
$font-family-display: 'SourceHanSerifCN', '思源宋体', serif; // 标题装饰字体
|
||
$font-family-body: -apple-system, 'PingFang SC', 'Helvetica Neue', sans-serif; // 正文系统字体
|
||
```
|
||
|
||
> **为什么要改 `$font-family-body`**:uni.scss 现状把思源宋体(衬线体)用于正文,导致所有文字都是衬线风格,阅读体验偏重且加载慢。改为系统字体后,正文干净利落,仅标题/品牌名保留思源宋体的文化感。
|
||
|
||
- `$font-family-display`:标题、品牌名、卡名等装饰性文字
|
||
- `$font-family-body`:正文、按钮、表单、说明文字(默认字体,加载快)
|
||
|
||
全局在 `App.vue` 中声明 `font-family: $font-family-body`,仅需要装饰性的元素单独指定 `$font-family-display`。
|
||
|
||
### 3.2 字号场景对照
|
||
|
||
| 场景 | 变量 | 值 |
|
||
|------|------|-----|
|
||
| 页面大标题 | `$font-size-2xl` | 44rpx |
|
||
| 区域标题 | `$font-size-xl` | 36rpx |
|
||
| 卡片标题 | `$font-size-lg` | 32rpx |
|
||
| 正文/按钮 | `$font-size-base` | 28rpx |
|
||
| 次要说明 | `$font-size-sm` | 24rpx |
|
||
| 辅助标注/标签 | `$font-size-xs` | 20rpx |
|
||
| 极小文字/角标 | `$font-size-xxs` | 18rpx |
|
||
|
||
---
|
||
|
||
## 4. 组件规范
|
||
|
||
### 4.1 按钮
|
||
|
||
从 9 种高度收敛到 3 种:
|
||
|
||
| 尺寸 | 高度 | 字号 | 场景 |
|
||
|------|------|------|------|
|
||
| sm | 64rpx | `$font-size-sm` (24rpx) | 列表内操作、小按钮 |
|
||
| md | 80rpx | `$font-size-base` (28rpx) | 页面内主要操作 |
|
||
| lg | 96rpx | `$font-size-lg` (32rpx) | 底部固定栏、支付按钮 |
|
||
|
||
- 统一圆角 `$radius-full`(胶囊形)
|
||
- 主操作:`$uv-primary` 填充色 + 白色文字
|
||
- 次要操作:描边样式(border `$uv-primary` + `$uv-primary` 文字)
|
||
- 按压态:`transform: scale(0.95)` + `transition: transform $transition-fast`
|
||
|
||
### 4.2 卡片
|
||
|
||
```scss
|
||
圆角:$radius-lg (24rpx)
|
||
投影:$shadow-base → 0 4rpx 16rpx rgba(15, 23, 42, 0.08)
|
||
内边距:$spacing-4 (32rpx)
|
||
卡片间距:$spacing-3 (24rpx)
|
||
背景:$color-card (#ffffff)
|
||
```
|
||
|
||
### 4.3 箭头/Chevron
|
||
|
||
统一一种实现:
|
||
- 尺寸:12rpx × 12rpx
|
||
- border-width:2rpx
|
||
- 颜色:`$uv-placeholder-color`
|
||
- 右箭头:`transform: rotate(45deg)`
|
||
- 下箭头:`transform: rotate(135deg)`
|
||
|
||
### 4.4 间距规则
|
||
|
||
**推荐使用**(日常开发只用这 7 个):
|
||
|
||
| 变量 | 值 | 场景 |
|
||
|------|-----|------|
|
||
| `$spacing-1` | 8rpx | 图标与文字间隙 |
|
||
| `$spacing-2` | 16rpx | 紧凑元素间距 |
|
||
| `$spacing-3` | 24rpx | 列表项间距、卡片间距 |
|
||
| `$spacing-4` | 32rpx | 区域内容边距 |
|
||
| `$spacing-5` | 40rpx | 区块间距 |
|
||
| `$spacing-6` | 48rpx | 页面顶部/底部安全区 |
|
||
| `$spacing-8` | 64rpx | 大区块分隔 |
|
||
|
||
**保留但少用**(uni.scss 已有,仅特殊场景允许):
|
||
|
||
| 变量 | 值 | 场景 |
|
||
|------|-----|------|
|
||
| `$spacing-0` | 0 | 重置间距 |
|
||
| `$spacing-10` | 80rpx | 超大区块分隔 |
|
||
| `$spacing-12` | 96rpx | 页面级留白 |
|
||
| `$spacing-16` | 128rpx | 极少使用 |
|
||
| `$spacing-20` | 160rpx | 极少使用 |
|
||
|
||
禁止硬编码间距值(如 `padding: 20rpx`)。
|
||
|
||
### 4.5 圆角规则
|
||
|
||
| 变量 | 值 | 场景 |
|
||
|------|-----|------|
|
||
| `$radius-sm` | 8rpx | 标签、小徽标 |
|
||
| `$radius-base` | 16rpx | 输入框、小卡片 |
|
||
| `$radius-lg` | 24rpx | 卡片、弹窗 |
|
||
| `$radius-xl` | 32rpx | 大面板(少用) |
|
||
| `$radius-full` | 9999rpx | 胶囊按钮、圆形头像 |
|
||
|
||
### 4.6 投影规则
|
||
|
||
以 `uni.scss` 现有值为准(使用 `rgba(15, 23, 42, ...)` 冷灰色基底):
|
||
|
||
| 变量 | 值 | 场景 |
|
||
|------|-----|------|
|
||
| `$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)` | 大弹窗/浮层 |
|
||
|
||
### 4.7 过渡动画
|
||
|
||
| 变量 | 值 | 场景 |
|
||
|------|-----|------|
|
||
| `$transition-fast` | `150ms ease` | 按压、hover |
|
||
| `$transition-base` | `250ms ease` | 展开/收起、淡入淡出 |
|
||
|
||
禁止使用 `0.15s ease`、`0.2s ease` 等硬编码值。
|
||
|
||
---
|
||
|
||
## 5. 类型定义 & Mock 数据
|
||
|
||
### 5.1 文件结构
|
||
|
||
```
|
||
src/
|
||
├── api/
|
||
│ ├── types.ts # 所有业务类型(唯一来源)
|
||
│ ├── request.ts # HTTP 封装(已有,不动)
|
||
│ ├── scenic.ts # 景区接口
|
||
│ ├── card.ts # 年卡接口
|
||
│ ├── order.ts # 订单接口
|
||
│ ├── user.ts # 用户接口(已有,补充类型)
|
||
│ └── reserve.ts # 预约接口
|
||
├── mock/
|
||
│ ├── index.ts # 统一导出 + USE_MOCK 开关
|
||
│ ├── scenic.ts # 景区 mock
|
||
│ ├── card.ts # 年卡 mock
|
||
│ ├── order.ts # 订单 mock
|
||
│ ├── reserve.ts # 预约记录 mock
|
||
│ ├── notice.ts # 公告 mock
|
||
│ └── images.ts # 占位图片 URL 常量
|
||
```
|
||
|
||
### 5.2 types.ts 业务类型
|
||
|
||
```typescript
|
||
interface Scenic {
|
||
id: number
|
||
name: string
|
||
cover: string
|
||
address: string
|
||
lat: number
|
||
lng: number
|
||
openTime: string
|
||
description: string
|
||
ticketPrice: number
|
||
freeTimes: number
|
||
}
|
||
|
||
interface ScenicSession {
|
||
id: number
|
||
scenicId: number
|
||
date: string
|
||
startTime: string
|
||
endTime: string
|
||
stock: number
|
||
remainStock: number
|
||
}
|
||
|
||
interface Card {
|
||
id: number
|
||
name: string
|
||
price: number
|
||
originalPrice: number
|
||
cover: string
|
||
benefits: string
|
||
scenicIds: number[]
|
||
}
|
||
|
||
interface Order {
|
||
id: number
|
||
cardId: number
|
||
cardName: string
|
||
amount: number
|
||
status: 'pending' | 'paid' | 'cancelled' | 'closed'
|
||
payTime: string | null
|
||
createTime: string
|
||
}
|
||
|
||
interface OrderDetail extends Order {
|
||
orderNo: string
|
||
userId: number
|
||
userName: string
|
||
phone: string
|
||
payMethod: string
|
||
}
|
||
|
||
interface ReserveRecord {
|
||
id: number
|
||
scenicId: number
|
||
scenicName: string
|
||
date: string
|
||
session: string
|
||
status: 'pending' | 'confirmed' | 'cancelled' | 'used'
|
||
}
|
||
|
||
interface Notice {
|
||
id: number
|
||
title: string
|
||
content: string
|
||
publishTime: string
|
||
isTop: boolean
|
||
}
|
||
|
||
interface QuickAction {
|
||
id: number
|
||
icon: string
|
||
label: string
|
||
path: string
|
||
}
|
||
|
||
interface Announcement {
|
||
id: number
|
||
title: string
|
||
content: string
|
||
}
|
||
|
||
// 分页通用
|
||
interface PageResult<T> {
|
||
list: T[]
|
||
total: number
|
||
page: number
|
||
pageSize: number
|
||
}
|
||
|
||
// 分页请求参数
|
||
interface PageQuery {
|
||
page: number
|
||
pageSize: number
|
||
}
|
||
```
|
||
|
||
### 5.3 Mock 切换机制
|
||
|
||
```typescript
|
||
// mock/index.ts
|
||
export const USE_MOCK = import.meta.env.VITE_USE_MOCK === 'true'
|
||
|
||
// api/scenic.ts
|
||
import { USE_MOCK } from '@/mock'
|
||
import { mockScenicList } from '@/mock/scenic'
|
||
import type { Scenic } from './types'
|
||
|
||
export function getScenicList(): Promise<Scenic[]> {
|
||
if (USE_MOCK) return Promise.resolve(mockScenicList)
|
||
return request.get('/mp-api/scenic/list')
|
||
}
|
||
|
||
// 分页示例
|
||
export function getScenicListPaged(query: PageQuery): Promise<PageResult<Scenic>> {
|
||
if (USE_MOCK) {
|
||
const start = (query.page - 1) * query.pageSize
|
||
return Promise.resolve({
|
||
list: mockScenicList.slice(start, start + query.pageSize),
|
||
total: mockScenicList.length,
|
||
page: query.page,
|
||
pageSize: query.pageSize,
|
||
})
|
||
}
|
||
return request.get('/mp-api/scenic/list', query)
|
||
}
|
||
```
|
||
|
||
- `.env` 文件中 `VITE_USE_MOCK=true` 控制开关
|
||
- 对接 API 时改为 `false`,页面代码零改动
|
||
|
||
### 5.4 Mock 状态管理
|
||
|
||
对于需要交互的 mock 数据(如订单状态变更、预约操作),提供简单的内存状态管理:
|
||
|
||
```typescript
|
||
// mock/store.ts
|
||
import { ref } from 'vue'
|
||
import type { Order, ReserveRecord } from '@/api/types'
|
||
import { mockOrders } from './order'
|
||
import { mockReserves } from './reserve'
|
||
|
||
export const orderStore = ref<Order[]>([...mockOrders])
|
||
export const reserveStore = ref<ReserveRecord[]>([...mockReserves])
|
||
|
||
export function updateOrderStatus(orderId: number, status: Order['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)) + 1
|
||
reserveStore.value.push({ ...record, id })
|
||
}
|
||
```
|
||
|
||
> 仅 mock 模式使用。对接 API 后这些操作改为 API 调用,页面逻辑不变。
|
||
|
||
### 5.5 占位图片规范
|
||
|
||
统一收到 `mock/images.ts`:
|
||
|
||
```typescript
|
||
export const IMG_SCENIC_COVER = 'https://images.unsplash.com/...'
|
||
export const IMG_AVATAR = 'https://...'
|
||
export const IMG_CARD_BANNER = 'https://...'
|
||
// ...
|
||
```
|
||
|
||
对接 API 后这些常量直接废弃,不影响页面代码。
|
||
|
||
---
|
||
|
||
## 6. 图片资源规范
|
||
|
||
| 类型 | 尺寸 | 格式 | 说明 |
|
||
|------|------|------|------|
|
||
| TabBar 图标 | 81px × 81px | PNG | 无透明通道,选中/未选中各一套 |
|
||
| 页面分享图 | 500px × 400px | JPEG | < 128KB |
|
||
| 年卡封面 | 750px × 420px | JPEG | < 200KB |
|
||
| 景区封面 | 750px × 420px | JPEG | < 200KB,统一裁切比例 16:9 |
|
||
| 背景图 | 优先 CSS 渐变 | — | 避免大图,减小包体积 |
|
||
|
||
- 网络图片必须在微信公众平台配置 `downloadFile` 合法域名
|
||
- 本地图片放 `src/static/` 目录,构建时自动打包
|
||
- 占位图统一使用 `mock/images.ts` 中的常量
|
||
|
||
---
|
||
|
||
## 7. 无障碍规范
|
||
|
||
- 所有可交互元素(`<view @click>`、`<button>`)必须设置 `role` 或 `aria-role` 属性
|
||
- `<image>` 标签必须提供有意义的描述(小程序用 `aria-label` 或父容器 `aria-role`)
|
||
- 颜色对比度至少 4.5:1(WCAG AA 标准),正文文字不得仅靠颜色传达信息
|
||
- 错误状态必须配合图标 + 文字,不能只用红色标识
|
||
- 表单输入框必须关联 `<label>` 或使用 `aria-label`
|
||
|
||
---
|
||
|
||
## 8. 执行计划
|
||
|
||
### 第1步:基础设施(不改任何页面)
|
||
|
||
| 序号 | 任务 | 验证方式 |
|
||
|------|------|---------|
|
||
| 1.1a | `uni.scss` 新增缺失变量:`$font-size-xxs` (18rpx)、`$font-size-4xl` (64rpx)、`$uv-placeholder-color` (#999999) | 编译通过 |
|
||
| 1.1b | `uni.scss` 新增语义色变量:`$color-price`、`$color-price-highlight`、`$color-status-pending`、`$color-status-success`、`$color-status-cancel` | 编译通过 |
|
||
| 1.1c | `uni.scss` 修改 `$font-family-body` 为系统字体(去掉思源宋体前缀) | 编译通过 |
|
||
| 1.2 | 处理 `_tokens.scss`:冲突值改为引用 `uni.scss` 或删除 | 编译通过,无变量冲突 |
|
||
| 1.3 | `api/types.ts` 补全所有业务 interface(含 `PageResult<T>`、`PageQuery`) | `pnpm run type-check` 通过 |
|
||
| 1.4 | 创建 `src/mock/` 目录,迁移所有硬编码数据,实现 `USE_MOCK` 开关和 MockStore | mock 数据可正常 import |
|
||
| 1.5 | `App.vue` 全局样式重置(见下方示例) | 构建预览正常 |
|
||
| 1.6 | 删除 14 个页面中的局部 `$font`、`$primary`、`$text-main` 等变量声明 | 编译通过 |
|
||
|
||
**1.5 App.vue 全局样式示例**:
|
||
|
||
```scss
|
||
// App.vue <style lang="scss">
|
||
page {
|
||
font-family: $font-family-body;
|
||
font-size: $font-size-base;
|
||
color: $uv-content-color;
|
||
background-color: $uv-bg-color;
|
||
line-height: $line-height-normal;
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
```
|
||
|
||
**SCSS 导入规则**:
|
||
|
||
- `uni.scss`:uni-app 自动注入,所有 `.vue` 文件的 `<style lang="scss">` 可直接使用变量,无需 `@import`
|
||
- `App.vue`:通过 `<style>` 设置全局 `page` 样式和默认字体
|
||
- 页面/组件中**不需要**再次 `@import uni.scss`
|
||
|
||
### 第2步:核心链路迁移(4 个页面)
|
||
|
||
迁移页面:`首页 → 年卡列表 → 年卡详情 → 购买/订单`
|
||
|
||
每页迁移清单:
|
||
1. 删除局部 SCSS 变量,改用全局 token
|
||
2. 硬编码颜色值 → 全局变量
|
||
3. 硬编码字号 → 对应 `$font-size-*` 变量
|
||
4. 硬编码间距 → 对应 `$spacing-*` 变量
|
||
5. 模板中硬编码数据 → import mock 数据
|
||
6. 组件样式对齐规范(按钮高度、卡片圆角、箭头样式)
|
||
|
||
验证:`pnpm run dev:mp-weixin` 构建 + 微信开发者工具预览,确认视觉无回退。
|
||
|
||
### 第3步:剩余页面迁移(~21 个页面)
|
||
|
||
- 同样 6 项清单逐页执行
|
||
- 分包页面(`packages/user/pages/card/`)6 个文件同步处理
|
||
- 纯 CSS 组件(`card-item.vue`、`empty-state.vue`、`McGlass.vue`)改为 SCSS + 全局变量
|
||
|
||
---
|
||
|
||
## 9. 迁移规则(禁止项)
|
||
|
||
- ❌ 禁止在 `<style>` 中定义 `$font`、`$primary`、`$text-main` 等局部变量
|
||
- ❌ 禁止使用 `font-weight: bold` 关键字,必须用 `$font-weight-bold`
|
||
- ❌ 禁止硬编码颜色值(如 `#333`、`#666`),必须用全局变量
|
||
- ❌ 禁止硬编码间距值(如 `padding: 20rpx`),必须用 `$spacing-*`
|
||
- ❌ 禁止在页面中硬编码业务数据,必须从 `mock/` 或 `api/` import
|
||
- ❌ 禁止使用 `0.15s ease` 等硬编码过渡值,必须用 `$transition-*`
|