# 湖南12301文旅综合平台 — 第一迭代实施计划
> **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:** 搭建用户端分包架构和基础层,实现年卡浏览→购买→激活→入园码的核心业务流程。
**Architecture:** 由底向上分两阶段实施。Phase 1 搭建分包路由、API 层、Store 和共享组件;Phase 2 实现 8 个用户端页面(首页/年卡列表/详情/购买/激活/入园码/绑定/个人中心)。小程序启动默认进入用户端,微信授权登录,模拟支付。
**Tech Stack:** uni-app + Vue 3 + TypeScript + Pinia + uni-ui + Vite
---
## 文件结构
### 新增文件
| 文件路径 | 职责 |
|----------|------|
| `src/config/index.ts` | 环境配置(baseURL 读取) |
| `.env.local` | 本地开发环境变量 |
| `.env.dev` | 开发环境变量 |
| `.env.uat` | 预发布环境变量 |
| `src/api/types.ts` | 共享 TypeScript 类型定义 |
| `src/api/card.ts` | 年卡接口 |
| `src/api/order.ts` | 订单/支付接口 |
| `src/store/card.ts` | 年卡 Pinia Store |
| `src/components/card-item.vue` | 年卡卡片共享组件 |
| `src/components/empty-state.vue` | 空状态共享组件 |
| `src/packages/user/pages/index/index.vue` | 用户端首页(宫格式) |
| `src/packages/user/pages/card/list.vue` | 年卡列表 |
| `src/packages/user/pages/card/detail.vue` | 年卡详情 |
| `src/packages/user/pages/card/buy.vue` | 确认购买(模拟支付) |
| `src/packages/user/pages/card/activate.vue` | 激活年卡 |
| `src/packages/user/pages/card/qrcode.vue` | 入园码(卡片式) |
| `src/packages/user/pages/card/bind.vue` | 绑定年卡 |
| `src/packages/user/pages/profile/index.vue` | 用户端个人中心 |
| `src/static/tabbar/home.png` | TabBar 首页图标(占位) |
| `src/static/tabbar/home-active.png` | TabBar 首页选中图标(占位) |
| `src/static/tabbar/profile.png` | TabBar 我的图标(占位) |
| `src/static/tabbar/profile-active.png` | TabBar 我的选中图标(占位) |
### 修改文件
| 文件路径 | 改动 |
|----------|------|
| `src/api/request.ts` | 重写:baseURL 从 config 读取,清理错误处理 |
| `src/api/user.ts` | 改造:新增 wxLogin,调整类型 |
| `src/api/index.ts` | 更新 barrel 导出 |
| `src/store/user.ts` | 改造:新增 wxLogin action,新增 refreshToken |
| `src/pages.json` | 重写:分包路由 + TabBar 指向用户端 |
| `src/pages/index/index.vue` | 改造:自动跳转到用户端首页 |
| `src/pages/login/login.vue` | 改造:微信授权登录 |
| `src/App.vue` | 改造:启动时检查登录状态 |
| `vite.config.ts` | 新增环境变量相关配置 |
### 删除文件(可选,不影响功能)
| 文件路径 | 原因 |
|----------|------|
| `src/pages/profile/profile.vue` | 被用户端个人中心替代 |
| `src/pages/logs/logs.vue` | 非业务页面,不再需要 |
---
## Task 1:环境配置
**Files:**
- Create: `src/config/index.ts`
- Create: `.env.local`
- Create: `.env.dev`
- Create: `.env.uat`
- Modify: `vite.config.ts`
- [ ] **Step 1:创建环境配置文件**
创建 `.env.local`:
```
VITE_API_BASE_URL=http://localhost:80
VITE_APP_TITLE=湖南12301文旅专区
```
创建 `.env.dev`:
```
VITE_API_BASE_URL=https://dscp-dev.hn12301.net
VITE_APP_TITLE=湖南12301文旅专区
```
创建 `.env.uat`:
```
VITE_API_BASE_URL=https://dscp-uat.hn12301.net
VITE_APP_TITLE=湖南12301文旅专区
```
- [ ] **Step 2:创建 config/index.ts**
创建 `src/config/index.ts`:
```typescript
/**
* 环境配置
* 根据 Vite 环境变量读取 API 基础地址等配置
*/
/** API 基础地址(含 /manage-api 前缀) */
export const BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:80'
/** API 接口前缀 */
export const API_PREFIX = '/manage-api'
/** 完整 API 基础路径 */
export const API_BASE = `${BASE_URL}${API_PREFIX}`
/** 应用标题 */
export const APP_TITLE = import.meta.env.VITE_APP_TITLE || '湖南12301文旅专区'
/** Token 存储键名 */
export const TOKEN_KEY = 'app_token'
/** Refresh Token 存储键名 */
export const REFRESH_TOKEN_KEY = 'app_refresh_token'
```
- [ ] **Step 3:添加 env.d.ts 类型声明**
在 `src/env.d.ts` 中追加 Vite 环境变量类型声明(如果文件已有内容则追加):
```typescript
///
interface ImportMetaEnv {
readonly VITE_API_BASE_URL: string
readonly VITE_APP_TITLE: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
```
- [ ] **Step 4:验证编译**
运行:
```bash
cd zhy-test && npx vue-tsc --noEmit 2>&1 | head -20
```
预期:无新增类型错误(config/index.ts 编译通过)。
- [ ] **Step 5:提交**
```bash
git add src/config/ .env.local .env.dev .env.uat src/env.d.ts
git commit -m "feat: 添加多环境配置(local/dev/uat)"
```
---
## Task 2:重写 API 请求层
**Files:**
- Modify: `src/api/request.ts`(完全重写)
- [ ] **Step 1:重写 request.ts**
将 `src/api/request.ts` 替换为以下内容:
```typescript
import { API_BASE, TOKEN_KEY } from '../config'
/** 请求选项 */
interface RequestOptions {
url: string
method?: 'GET' | 'POST' | 'PUT' | 'DELETE'
data?: any
header?: Record
needToken?: boolean
}
/** 后端统一响应格式 */
interface ResponseData {
code: number
msg: string
data: T
errcode: number
errmsg: string
}
/** 从本地存储获取 token */
function getToken(): string {
return uni.getStorageSync(TOKEN_KEY) || ''
}
/** 保存 token 到本地存储 */
function setToken(token: string): void {
uni.setStorageSync(TOKEN_KEY, token)
}
/** 移除本地存储中的 token */
function removeToken(): void {
uni.removeStorageSync(TOKEN_KEY)
}
/** 通用请求函数 */
function request(options: RequestOptions): Promise {
return new Promise((resolve, reject) => {
const { url, method = 'GET', data, needToken = true } = options
const header: Record = {
'Content-Type': 'application/json',
...options.header
}
// 自动注入 Bearer Token
if (needToken) {
const token = getToken()
if (token) {
header['Authorization'] = `Bearer ${token}`
}
}
// 拼接完整 URL
const fullUrl = url.startsWith('http') ? url : `${API_BASE}${url}`
uni.request({
url: fullUrl,
method,
data,
header,
success: (res: any) => {
const result = res.data as ResponseData
// HTTP 状态码检查
if (res.statusCode === 401 || result?.code === 401) {
removeToken()
uni.showToast({ title: '登录已过期,请重新登录', icon: 'none' })
// 延迟跳转,让 toast 先显示
setTimeout(() => {
uni.navigateTo({ url: '/pages/login/login' })
}, 1500)
reject(new Error('未授权'))
return
}
if (res.statusCode >= 200 && res.statusCode < 300) {
// 业务状态码检查:code === 200 或 errcode === 0 均视为成功
if (result.code === 200 || result.errcode === 0) {
resolve(result.data as T)
} else {
const errMsg = result.msg || result.errmsg || '请求失败'
uni.showToast({ title: errMsg, icon: 'none' })
reject(new Error(errMsg))
}
} else {
uni.showToast({ title: '网络错误', icon: 'none' })
reject(new Error(`HTTP ${res.statusCode}`))
}
},
fail: (err: any) => {
uni.showToast({ title: '网络连接失败', icon: 'none' })
reject(err)
}
})
})
}
function get(url: string, data?: any, needToken = true): Promise {
return request({ url, method: 'GET', data, needToken })
}
function post(url: string, data?: any, needToken = true): Promise {
return request({ url, method: 'POST', data, needToken })
}
function put(url: string, data?: any, needToken = true): Promise {
return request({ url, method: 'PUT', data, needToken })
}
function del(url: string, data?: any, needToken = true): Promise {
return request({ url, method: 'DELETE', data, needToken })
}
export {
request,
get,
post,
put,
del,
getToken,
setToken,
removeToken
}
```
- [ ] **Step 2:验证编译**
运行:
```bash
cd zhy-test && npx vue-tsc --noEmit 2>&1 | head -20
```
预期:无类型错误。
- [ ] **Step 3:提交**
```bash
git add src/api/request.ts
git commit -m "refactor: 重写 request.ts,baseURL 从环境配置读取"
```
---
## Task 3:定义共享类型 + 改造用户 API
**Files:**
- Create: `src/api/types.ts`
- Modify: `src/api/user.ts`
- Modify: `src/api/index.ts`
- [ ] **Step 1:创建共享类型定义**
创建 `src/api/types.ts`:
```typescript
/** 用户信息 */
export interface UserInfo {
id: number
nickname: string
avatarUrl: string
phone?: string
}
/** 微信登录结果 */
export interface WxLoginResult {
accessToken: string
refreshToken: string
userId: number
}
/** 账号密码登录参数(降级方案) */
export interface LoginParams {
username: string
password: string
}
/** 年卡基本信息 */
export interface CardInfo {
id: string
name: string
price: number
originalPrice: number
coverImage: string
description: string
scenicCount: number
}
/** 年卡详情 */
export interface CardDetail extends CardInfo {
benefits: string[]
scenicSpots: ScenicSpot[]
validDays: number
detail: string
}
/** 景区信息 */
export interface ScenicSpot {
id: string
name: string
image: string
address?: string
}
/** 我的年卡 */
export interface MyCard {
id: string
cardId: string
cardName: string
coverImage: string
userName: string
expireDate: string
status: 'active' | 'expired' | 'inactive'
}
/** 激活年卡参数 */
export interface ActivateParams {
realName: string
idCard: string
phone: string
}
/** 入园码信息 */
export interface QrCodeInfo {
qrCode: string
expireTime: string
cardName: string
userName: string
expireDate: string
}
/** 订单信息 */
export interface OrderInfo {
id: string
cardId: string
cardName: string
price: number
status: 'pending' | 'paid' | 'cancelled'
createTime: string
}
/** 创建订单参数 */
export interface CreateOrderParams {
cardId: string
}
/** 模拟支付结果 */
export interface MockPayResult {
success: boolean
orderId: string
}
```
- [ ] **Step 2:改造用户 API**
将 `src/api/user.ts` 替换为:
```typescript
import { get, post } from './request'
import type { UserInfo, WxLoginResult, LoginParams } from './types'
/** 微信授权登录 */
export function wxLogin(code: string): Promise {
return post('/auth/wx-login', { code }, false)
}
/** 账号密码登录(降级方案) */
export function login(params: LoginParams): Promise {
return post('/auth/login', params, false)
}
/** 获取当前用户信息 */
export function getUserInfo(): Promise {
return get('/user/info')
}
export type { UserInfo, WxLoginResult, LoginParams }
```
- [ ] **Step 3:更新 barrel 导出**
将 `src/api/index.ts` 替换为:
```typescript
export * from './types'
export * from './user'
export * from './card'
export * from './order'
export { get, post, put, del, getToken, setToken, removeToken } from './request'
```
注意:`card` 和 `order` 模块在后续 Task 中创建,此处先写好导出,暂时会导致编译警告,Task 4/5 完成后即可消除。
- [ ] **Step 4:验证编译**
运行:
```bash
cd zhy-test && npx vue-tsc --noEmit 2>&1 | head -20
```
预期:可能有关于 card/order 模块未找到的警告(Task 4/5 会消除)。
- [ ] **Step 5:提交**
```bash
git add src/api/types.ts src/api/user.ts src/api/index.ts
git commit -m "feat: 定义共享类型,改造用户 API(新增 wxLogin)"
```
---
## Task 4:创建年卡 API 模块
**Files:**
- Create: `src/api/card.ts`
- [ ] **Step 1:创建 card.ts**
创建 `src/api/card.ts`:
```typescript
import { get, post } from './request'
import type { CardInfo, CardDetail, MyCard, ActivateParams, QrCodeInfo } from './types'
/** 获取年卡列表 */
export function getCardList(params?: { keyword?: string; category?: string }): Promise {
return get('/card/list', params)
}
/** 获取年卡详情 */
export function getCardDetail(id: string): Promise {
return get('/card/detail', { id })
}
/** 激活年卡 */
export function activateCard(cardId: string, data: ActivateParams): Promise {
return post('/card/activate', { cardId, ...data })
}
/** 绑定年卡 */
export function bindCard(cardNo: string): Promise {
return post('/card/bind', { cardNo })
}
/** 获取我的年卡列表 */
export function getMyCards(): Promise {
return get('/card/my-list')
}
/** 获取入园码 */
export function getEntryQrCode(cardId: string): Promise {
return get('/card/qrcode', { cardId })
}
```
- [ ] **Step 2:提交**
```bash
git add src/api/card.ts
git commit -m "feat: 新增年卡 API 模块"
```
---
## Task 5:创建订单 API 模块
**Files:**
- Create: `src/api/order.ts`
- [ ] **Step 1:创建 order.ts**
创建 `src/api/order.ts`:
```typescript
import { get, post } from './request'
import type { OrderInfo, CreateOrderParams, MockPayResult } from './types'
/** 创建订单 */
export function createOrder(data: CreateOrderParams): Promise {
return post('/order/create', data)
}
/** 模拟支付(第一版替代微信支付) */
export function mockPay(orderId: string): Promise {
return post('/order/mock-pay', { orderId })
}
/** 获取订单列表 */
export function getOrderList(params?: { status?: string }): Promise {
return get('/order/list', params)
}
/** 获取订单详情 */
export function getOrderDetail(id: string): Promise {
return get('/order/detail', { id })
}
```
- [ ] **Step 2:验证编译**
运行:
```bash
cd zhy-test && npx vue-tsc --noEmit 2>&1 | head -20
```
预期:API 相关类型错误全部消除。
- [ ] **Step 3:提交**
```bash
git add src/api/order.ts
git commit -m "feat: 新增订单 API 模块(含模拟支付)"
```
---
## Task 6:改造 User Store + 创建 Card Store
**Files:**
- Modify: `src/store/user.ts`(重写)
- Create: `src/store/card.ts`
- [ ] **Step 1:重写 user store**
将 `src/store/user.ts` 替换为:
```typescript
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import { getToken, setToken, removeToken } from '../api/request'
import { wxLogin as apiWxLogin, login as apiLogin, getUserInfo as apiGetUserInfo } from '../api/user'
import type { UserInfo, LoginParams } from '../api/types'
import { TOKEN_KEY, REFRESH_TOKEN_KEY } from '../config'
export const useUserStore = defineStore('user', () => {
const token = ref(getToken())
const refreshToken = ref(uni.getStorageSync(REFRESH_TOKEN_KEY) || '')
const userInfo = ref(null)
const isLogin = computed(() => !!token.value)
/** 微信授权登录 */
async function wxLogin() {
// #ifdef MP-WEIXIN
const loginRes = await new Promise((resolve, reject) => {
uni.login({
provider: 'weixin',
success: resolve,
fail: reject
})
})
const result = await apiWxLogin(loginRes.code)
token.value = result.accessToken
refreshToken.value = result.refreshToken
setToken(result.accessToken)
uni.setStorageSync(REFRESH_TOKEN_KEY, result.refreshToken)
return result
// #endif
// #ifdef H5
throw new Error('H5 登录暂未开放,请在微信小程序中使用')
// #endif
}
/** 账号密码登录(降级方案) */
async function login(params: LoginParams) {
const result = await apiLogin(params)
token.value = result.accessToken
refreshToken.value = result.refreshToken
setToken(result.accessToken)
uni.setStorageSync(REFRESH_TOKEN_KEY, result.refreshToken)
return result
}
/** 获取用户信息 */
async function fetchUserInfo() {
if (!token.value) return
const info = await apiGetUserInfo()
userInfo.value = info
return info
}
/** 退出登录 */
function logout() {
token.value = ''
refreshToken.value = ''
userInfo.value = null
removeToken()
uni.removeStorageSync(REFRESH_TOKEN_KEY)
}
return {
token,
refreshToken,
userInfo,
isLogin,
wxLogin,
login,
fetchUserInfo,
logout
}
})
```
- [ ] **Step 2:创建 card store**
创建 `src/store/card.ts`:
```typescript
import { defineStore } from 'pinia'
import { ref } from 'vue'
import * as cardApi from '../api/card'
import type { CardInfo, CardDetail, MyCard, ActivateParams, QrCodeInfo } from '../api/types'
export const useCardStore = defineStore('card', () => {
const cardList = ref([])
const currentCard = ref(null)
const myCards = ref([])
const loading = ref(false)
/** 获取年卡列表 */
async function fetchCardList(params?: { keyword?: string; category?: string }) {
loading.value = true
try {
cardList.value = await cardApi.getCardList(params)
} finally {
loading.value = false
}
}
/** 获取年卡详情 */
async function fetchCardDetail(id: string) {
loading.value = true
try {
currentCard.value = await cardApi.getCardDetail(id)
} finally {
loading.value = false
}
}
/** 获取我的年卡 */
async function fetchMyCards() {
myCards.value = await cardApi.getMyCards()
}
/** 激活年卡 */
async function activateCard(cardId: string, data: ActivateParams) {
return await cardApi.activateCard(cardId, data)
}
/** 绑定年卡 */
async function bindCard(cardNo: string) {
return await cardApi.bindCard(cardNo)
}
/** 获取入园码 */
async function getEntryQrCode(cardId: string): Promise {
return await cardApi.getEntryQrCode(cardId)
}
return {
cardList,
currentCard,
myCards,
loading,
fetchCardList,
fetchCardDetail,
fetchMyCards,
activateCard,
bindCard,
getEntryQrCode
}
})
```
- [ ] **Step 3:验证编译**
运行:
```bash
cd zhy-test && npx vue-tsc --noEmit 2>&1 | head -20
```
预期:Store 相关文件无类型错误。
- [ ] **Step 4:提交**
```bash
git add src/store/user.ts src/store/card.ts
git commit -m "feat: 改造 user store(微信登录),新增 card store"
```
---
## Task 7:创建共享组件
**Files:**
- Create: `src/components/card-item.vue`
- Create: `src/components/empty-state.vue`
- [ ] **Step 1:创建 card-item 组件**
创建 `src/components/card-item.vue`:
```vue
{{ card.name }}
{{ card.description }}
```
- [ ] **Step 2:创建 empty-state 组件**
创建 `src/components/empty-state.vue`:
```vue
{{ icon }}
{{ text }}
```
- [ ] **Step 3:提交**
```bash
git add src/components/
git commit -m "feat: 新增 card-item 和 empty-state 共享组件"
```
---
## Task 8:创建 TabBar 图标 + 重写路由配置
**Files:**
- Create: `src/static/tabbar/home.png`
- Create: `src/static/tabbar/home-active.png`
- Create: `src/static/tabbar/profile.png`
- Create: `src/static/tabbar/profile-active.png`
- Modify: `src/pages.json`(完全重写)
- [ ] **Step 1:生成 TabBar 占位图标**
TabBar 图标必须是图片文件。使用以下命令生成 4 个简单的 SVG 占位图标并转换为 PNG(或直接用在线占位图服务):
```bash
mkdir -p src/static/tabbar
# 使用 base64 编码的最小 PNG 图标(81x81 像素,灰色/蓝色)
# home.png - 灰色首页图标
echo "iVBORw0KGgoAAAANSUhEUgAAAFEAAABRCAYAAACqj0o2AAAAKElEQVR42u3BAQEAAACCIP+vbkhAAQAAAAAAAAAAAAAAAAAAAADcGlJRAAH0fVWIAAAAAElFTkSuQmCC" | base64 -d > src/static/tabbar/home.png
# home-active.png - 蓝色首页图标
echo "iVBORw0KGgoAAAANSUhEUgAAAFEAAABRCAYAAACqj0o2AAAAKElEQVR42u3BAQEAAACCIP+vbkhAAQAAAAAAAAAAAAAAAAAAAADcGlJRAAH0fVWIAAAAAElFTkSuQmCC" | base64 -d > src/static/tabbar/home-active.png
# profile.png - 灰色我的图标
cp src/static/tabbar/home.png src/static/tabbar/profile.png
# profile-active.png - 蓝色我的图标
cp src/static/tabbar/home-active.png src/static/tabbar/profile-active.png
```
> **注意**:以上是占位 PNG。正式环境中应替换为设计师提供的图标文件(建议 81×81px)。
- [ ] **Step 2:重写 pages.json**
将 `src/pages.json` 替换为:
```json
{
"easycom": {
"autoscan": true,
"custom": {
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
}
},
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "湖南12301"
}
},
{
"path": "pages/login/login",
"style": {
"navigationBarTitleText": "登录"
}
}
],
"subPackages": [
{
"root": "packages/user",
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "文旅专区"
}
},
{
"path": "pages/card/list",
"style": {
"navigationBarTitleText": "年卡列表"
}
},
{
"path": "pages/card/detail",
"style": {
"navigationBarTitleText": "年卡详情"
}
},
{
"path": "pages/card/buy",
"style": {
"navigationBarTitleText": "确认购买"
}
},
{
"path": "pages/card/activate",
"style": {
"navigationBarTitleText": "激活年卡"
}
},
{
"path": "pages/card/qrcode",
"style": {
"navigationBarTitleText": "入园码"
}
},
{
"path": "pages/card/bind",
"style": {
"navigationBarTitleText": "绑定年卡"
}
},
{
"path": "pages/profile/index",
"style": {
"navigationBarTitleText": "个人中心"
}
}
]
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "湖南12301",
"navigationBarBackgroundColor": "#FFFFFF",
"backgroundColor": "#F5F5F5"
},
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#4A90D9",
"borderStyle": "white",
"backgroundColor": "#ffffff",
"list": [
{
"pagePath": "packages/user/pages/index/index",
"text": "首页",
"iconPath": "static/tabbar/home.png",
"selectedIconPath": "static/tabbar/home-active.png"
},
{
"pagePath": "packages/user/pages/profile/index",
"text": "我的",
"iconPath": "static/tabbar/profile.png",
"selectedIconPath": "static/tabbar/profile-active.png"
}
]
}
}
```
- [ ] **Step 3:验证编译**
运行:
```bash
cd zhy-test && npx uni -p mp-weixin 2>&1 | head -30
```
预期:编译启动成功,无路由配置错误。(可用 Ctrl+C 停止)
- [ ] **Step 4:提交**
```bash
git add src/pages.json src/static/tabbar/
git commit -m "feat: 重写路由配置,添加分包和 TabBar"
```
---
## Task 9:改造入口页和登录页
**Files:**
- Modify: `src/pages/index/index.vue`(重写为自动跳转)
- Modify: `src/pages/login/login.vue`(改造为微信登录)
- Modify: `src/App.vue`(添加启动登录检查)
- [ ] **Step 1:改造入口页为自动跳转**
将 `src/pages/index/index.vue` 替换为:
```vue
湖南12301文旅专区
加载中...
```
- [ ] **Step 2:改造登录页为微信授权登录**
将 `src/pages/login/login.vue` 替换为:
```vue
请在微信小程序中使用本应用
账号
密码
登录即表示同意
《用户协议》
和
《隐私政策》
```
- [ ] **Step 3:改造 App.vue 添加启动检查**
将 `src/App.vue` 替换为:
```vue
```
- [ ] **Step 4:验证编译**
运行:
```bash
cd zhy-test && npx vue-tsc --noEmit 2>&1 | head -20
```
预期:入口页和登录页无类型错误。
- [ ] **Step 5:提交**
```bash
git add src/pages/index/index.vue src/pages/login/login.vue src/App.vue
git commit -m "feat: 改造入口页(自动跳转)和登录页(微信授权登录)"
```
---
## Task 10:用户端首页(宫格式)
**Files:**
- Create: `src/packages/user/pages/index/index.vue`
- [ ] **Step 1:创建用户端首页**
创建 `src/packages/user/pages/index/index.vue`:
```vue
🎫
购买年卡
多种卡种可选
📱
激活/绑定
激活已购年卡
🏔️
景区预约
预约入园时间
📋
我的记录
预约/使用记录
加载中...
```
- [ ] **Step 2:验证编译**
运行:
```bash
cd zhy-test && npx vue-tsc --noEmit 2>&1 | head -20
```
预期:无类型错误。
- [ ] **Step 3:提交**
```bash
git add src/packages/user/pages/index/
git commit -m "feat: 用户端首页(宫格式布局 + 推荐年卡)"
```
---
## Task 11:年卡列表页
**Files:**
- Create: `src/packages/user/pages/card/list.vue`
- [ ] **Step 1:创建年卡列表页**
创建 `src/packages/user/pages/card/list.vue`:
```vue
{{ cat.label }}
加载中...
```
- [ ] **Step 2:提交**
```bash
git add src/packages/user/pages/card/list.vue
git commit -m "feat: 年卡列表页(搜索 + 分类筛选)"
```
---
## Task 12:年卡详情页
**Files:**
- Create: `src/packages/user/pages/card/detail.vue`
- [ ] **Step 1:创建年卡详情页**
创建 `src/packages/user/pages/card/detail.vue`:
```vue
{{ card.name }}
¥{{ card.price }}
¥{{ card.originalPrice }}
{{ card.description }}
权益说明
✅
{{ benefit }}
权益景区({{ card.scenicSpots.length }}个)
{{ spot.name }}
年卡详情
价格
¥{{ card.price }}
加载中...
```
- [ ] **Step 2:提交**
```bash
git add src/packages/user/pages/card/detail.vue
git commit -m "feat: 年卡详情页(封面 + 权益 + 景区 + 购买按钮)"
```
---
## Task 13:确认购买页(模拟支付)
**Files:**
- Create: `src/packages/user/pages/card/buy.vue`
- [ ] **Step 1:创建确认购买页**
创建 `src/packages/user/pages/card/buy.vue`:
```vue
订单信息
年卡名称
{{ cardName }}
有效期
365天
价格
¥{{ price }}
支付方式
💳
模拟支付(测试)
✓
应付金额
¥{{ price }}
```
- [ ] **Step 2:提交**
```bash
git add src/packages/user/pages/card/buy.vue
git commit -m "feat: 确认购买页(模拟支付流程)"
```
---
## Task 14:激活年卡页
**Files:**
- Create: `src/packages/user/pages/card/activate.vue`
- [ ] **Step 1:创建激活年卡页**
创建 `src/packages/user/pages/card/activate.vue`:
```vue
{{ cardName }}
请填写实名信息以激活年卡
真实姓名
身份证号
手机号码
```
- [ ] **Step 2:提交**
```bash
git add src/packages/user/pages/card/activate.vue
git commit -m "feat: 激活年卡页(实名表单 + 验证)"
```
---
## Task 15:入园码页(卡片式)
**Files:**
- Create: `src/packages/user/pages/card/qrcode.vue`
- [ ] **Step 1:创建入园码页**
创建 `src/packages/user/pages/card/qrcode.vue`:
```vue
{{ qrInfo?.cardName || '年卡' }}
{{ qrInfo?.userName || '---' }}
有效期至 {{ qrInfo?.expireDate || '---' }}
加载中...
扫码入园 · 每60秒自动刷新
```
- [ ] **Step 2:提交**
```bash
git add src/packages/user/pages/card/qrcode.vue
git commit -m "feat: 入园码页(卡片式 + 60秒自动刷新)"
```
---
## Task 16:绑定年卡页
**Files:**
- Create: `src/packages/user/pages/card/bind.vue`
- [ ] **Step 1:创建绑定年卡页**
创建 `src/packages/user/pages/card/bind.vue`:
```vue
卡号
```
- [ ] **Step 2:提交**
```bash
git add src/packages/user/pages/card/bind.vue
git commit -m "feat: 绑定年卡页"
```
---
## Task 17:用户端个人中心
**Files:**
- Create: `src/packages/user/pages/profile/index.vue`
- [ ] **Step 1:创建用户端个人中心**
创建 `src/packages/user/pages/profile/index.vue`:
```vue
{{ userStore.userInfo?.nickname || '未登录' }}
欢迎使用湖南12301文旅专区
点击登录
```
- [ ] **Step 2:提交**
```bash
git add src/packages/user/pages/profile/
git commit -m "feat: 用户端个人中心(用户信息 + 功能菜单)"
```
---
## Task 18:编译验证 + 最终提交
**Files:** 无新增/修改,仅验证
- [ ] **Step 1:类型检查**
运行:
```bash
cd zhy-test && npx vue-tsc --noEmit 2>&1
```
预期:无类型错误。如有错误,逐一修复。
- [ ] **Step 2:H5 模式编译测试**
运行:
```bash
cd zhy-test && npx uni 2>&1 | head -30
```
预期:Vite 开发服务器启动成功。(Ctrl+C 停止)
- [ ] **Step 3:微信小程序编译测试**
运行:
```bash
cd zhy-test && npx uni build -p mp-weixin 2>&1 | tail -20
```
预期:构建成功,生成 `dist/build/mp-weixin` 目录。
- [ ] **Step 4:检查分包目录结构**
运行:
```bash
find src/packages -type f -name '*.vue' | sort
```
预期输出:
```
src/packages/user/pages/card/activate.vue
src/packages/user/pages/card/bind.vue
src/packages/user/pages/card/buy.vue
src/packages/user/pages/card/detail.vue
src/packages/user/pages/card/list.vue
src/packages/user/pages/card/qrcode.vue
src/packages/user/pages/index/index.vue
src/packages/user/pages/profile/index.vue
```
- [ ] **Step 5:最终提交**
```bash
git add -A
git commit -m "feat: 湖南12301第一迭代完成 — 基础层 + 用户端核心流程"
```
---
## 手动测试清单
编译为微信小程序后,在微信开发者工具中按以下流程手动验证:
| 步骤 | 操作 | 预期结果 |
|------|------|----------|
| 1 | 启动小程序 | 显示启动页,自动跳转到登录页 |
| 2 | 点击"微信一键登录" | 调用微信登录,成功后跳转用户首页 |
| 3 | 用户首页 | 显示宫格入口 + 推荐年卡列表 |
| 4 | 点击"景区预约" | 弹出"即将上线"提示 |
| 5 | 点击"购买年卡" | 进入年卡列表页 |
| 6 | 点击一张年卡 | 进入年卡详情页 |
| 7 | 点击"立即购买" | 进入确认购买页 |
| 8 | 点击"确认支付" | 模拟支付成功,跳转激活页 |
| 9 | 填写实名信息并激活 | 激活成功,跳转入园码页 |
| 10 | 入园码页 | 显示年卡信息 + 二维码,60秒自动刷新 |
| 11 | 切换到"我的"Tab | 显示个人中心 |
| 12 | 点击"退出登录" | 清除状态,回到登录页 |