50 KiB
Merged Venue Miniapp Implementation Plan
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: Build a new merged uni-app WeChat mini program under 场馆小程序/miniprogram that lets users choose and switch between ice venue and comprehensive venue while keeping both Django backends, auth tokens, APIs, and business pages isolated.
Architecture: The app uses one public venue selection shell and two isolated modules: src/modules/ice and src/modules/comprehensive. Route pages live under src/pages/ice/* and src/pages/comprehensive/*, while each module imports only its own API client and state. No backend code is changed.
Tech Stack: uni-app, Vue 3, Vite 5, Pinia, uview-plus, dayjs, WeChat mini program target.
File Structure Map
- Create
miniprogram/package.json: merged project dependency and script manifest copied from the existing miniapp projects. - Create
miniprogram/package-lock.json: copied from the comprehensive project first, then refreshed bynpm installif needed. - Create
miniprogram/vite.config.js: Vite + uni plugin config with two API env variables and two H5 proxy prefixes. - Create
miniprogram/.env: production-like default backend URLs for both venues. - Create
miniprogram/.env.example: local development URL examples for both venues. - Create
miniprogram/src/main.js: Vue app bootstrap with Pinia and uview-plus. - Create
miniprogram/src/App.vue: merged global styles and app lifecycle logging. - Create
miniprogram/src/manifest.json: WeChat app manifest usingwx516b3e0d9940c788. - Create
miniprogram/src/uni.scss: shared SCSS imports. - Create
miniprogram/src/styles/variables.scss: shared design variables and mixins. - Create
miniprogram/src/pages.json: merged page registry, no globaltabBarin first version. - Create
miniprogram/src/stores/venue.js: public Pinia store for current venue selection. - Create
miniprogram/src/utils/venue.js: venue constants, storage helpers, target home paths, and navigation helpers. - Create
miniprogram/src/pages/venue-select/index.vue: first-entry venue selector and switch page. - Create
miniprogram/src/modules/comprehensive/api/*: comprehensive backend API modules. - Create
miniprogram/src/modules/comprehensive/stores/session.js: comprehensive session store with dedicated store id. - Create
miniprogram/src/modules/comprehensive/utils/*: comprehensive business utility modules. - Create
miniprogram/src/pages/comprehensive/*: comprehensive venue route pages. - Create
miniprogram/src/modules/ice/api/*: ice backend API modules. - Create
miniprogram/src/components/ice/StatePanel.vue: ice module state panel component. - Create
miniprogram/src/pages/ice/*: ice venue route pages. - Create
miniprogram/src/static/comprehensive/tab/*: comprehensive tab image assets for future module navigation. - Create
miniprogram/src/static/ice/tab/*: ice tab image assets for future module navigation.
Source Roots
- Existing comprehensive miniapp:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/综合场馆小程序/miniprogram - Existing ice miniapp:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/冰场馆小程序/miniprogram - New merged miniapp:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram
Implementation Tasks
Task 1: Scaffold the Merged Miniapp Shell
Files:
-
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/package.json -
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/package-lock.json -
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/vite.config.js -
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/.env -
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/.env.example -
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/main.js -
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/App.vue -
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/manifest.json -
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/uni.scss -
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/styles/variables.scss -
Step 1: Copy baseline project metadata
Run:
cp '/Volumes/ExternalStorageA/project/2026.5/体育场馆/综合场馆小程序/miniprogram/package.json' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/package.json'
cp '/Volumes/ExternalStorageA/project/2026.5/体育场馆/综合场馆小程序/miniprogram/package-lock.json' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/package-lock.json'
Expected: both files exist in 场馆小程序/miniprogram.
- Step 2: Update merged package metadata
Edit miniprogram/package.json to:
{
"name": "merged-venue-miniapp",
"version": "0.1.0",
"description": "冰场馆与综合场馆合并版微信小程序端",
"type": "module",
"scripts": {
"dev:mp-weixin": "uni -p mp-weixin",
"build:mp-weixin": "uni build -p mp-weixin",
"dev:h5": "uni",
"build:h5": "uni build"
},
"dependencies": {
"@dcloudio/uni-app": "3.0.0-alpha-5000120260211001",
"@dcloudio/uni-components": "3.0.0-alpha-5000120260211001",
"@dcloudio/uni-h5": "3.0.0-alpha-5000120260211001",
"@dcloudio/uni-mp-weixin": "3.0.0-alpha-5000120260211001",
"dayjs": "^1.11.21",
"pinia": "^2.3.1",
"uview-plus": "^3.8.37",
"vue": "^3.4.0"
},
"devDependencies": {
"@dcloudio/uni-automator": "3.0.0-alpha-5000120260211001",
"@dcloudio/uni-cli-shared": "3.0.0-alpha-5000120260211001",
"@dcloudio/uni-stacktracey": "3.0.0-alpha-5000120260211001",
"@dcloudio/vite-plugin-uni": "3.0.0-alpha-5000120260211001",
"sass": "^1.83.0",
"vite": "^5.2.0"
}
}
Expected: package name and description identify the merged app; dependencies remain identical to the two existing miniapps.
- Step 3: Create environment files
Create miniprogram/.env:
VITE_ICE_API_BASE_URL=https://venue.test.chatgqt.top
VITE_COMPREHENSIVE_API_BASE_URL=https://venue2.test.chatgqt.top
Create miniprogram/.env.example:
VITE_ICE_API_BASE_URL=http://127.0.0.1:8000
VITE_COMPREHENSIVE_API_BASE_URL=http://127.0.0.1:8001
Expected: no old single VITE_API_BASE_URL remains in these files.
- Step 4: Create Vite config
Create miniprogram/vite.config.js:
import { fileURLToPath, URL } from 'node:url'
import { defineConfig, loadEnv } from 'vite'
import uniPlugin from '@dcloudio/vite-plugin-uni'
const uni = typeof uniPlugin === 'function' ? uniPlugin : uniPlugin.default
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '')
const iceApiBase = env.VITE_ICE_API_BASE_URL || 'http://127.0.0.1:8000'
const comprehensiveApiBase = env.VITE_COMPREHENSIVE_API_BASE_URL || 'http://127.0.0.1:8001'
return {
plugins: [uni()],
optimizeDeps: {
include: ['uview-plus'],
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
css: {
preprocessorOptions: {
scss: {
api: 'modern-compiler',
},
},
},
server: {
proxy: {
'/ice-api': {
target: iceApiBase,
changeOrigin: true,
secure: false,
rewrite: (path) => path.replace(/^\/ice-api/, ''),
},
'/comprehensive-api': {
target: comprehensiveApiBase,
changeOrigin: true,
secure: false,
rewrite: (path) => path.replace(/^\/comprehensive-api/, ''),
},
},
},
}
})
Expected: H5 development can proxy both backends; WeChat builds still use env URLs directly.
- Step 5: Create app bootstrap
Create miniprogram/src/main.js:
import { createSSRApp } from 'vue'
import App from './App.vue'
import * as Pinia from 'pinia'
import uviewPlus from 'uview-plus'
import 'uview-plus/index.scss'
export function createApp() {
const app = createSSRApp(App)
app.use(Pinia.createPinia())
app.use(uviewPlus)
return {
app,
Pinia
}
}
Expected: Pinia and uview-plus are globally registered.
- Step 6: Create manifest
Create miniprogram/src/manifest.json:
{
"name": "场馆服务",
"appid": "",
"description": "冰场馆与综合场馆合并版会员服务小程序",
"versionName": "0.1.0",
"versionCode": "100",
"transformPx": false,
"mp-weixin": {
"appid": "wx516b3e0d9940c788",
"setting": {
"urlCheck": false,
"es6": true,
"postcss": true,
"minified": true
},
"usingComponents": true
},
"vueVersion": "3"
}
Expected: WeChat appid is wx516b3e0d9940c788.
- Step 7: Create global SCSS files
Create miniprogram/src/uni.scss:
@use "@/styles/variables.scss" as *;
@import "uview-plus/theme.scss";
Create miniprogram/src/styles/variables.scss:
$bg-color-main: #FFFFFF;
$bg-color-soft: #F8FAFC;
$bg-color-hover: #F1F5F9;
$bg-color-panel: #EFF6FF;
$text-color-main: #0F172A;
$text-color-regular: #334155;
$text-color-muted: #64748B;
$text-color-light: #94A3B8;
$color-primary: #0EA5E9;
$color-primary-light: #E0F2FE;
$color-success: #10B981;
$color-success-light: #D1FAE5;
$color-warning: #F59E0B;
$color-warning-light: #FEF3C7;
$color-danger: #EF4444;
$color-danger-light: #FEE2E2;
$border-color: #E2E8F0;
$border-radius-sm: 6rpx;
$border-radius-base: 12rpx;
$border-radius-lg: 20rpx;
$spacing-xs: 8rpx;
$spacing-sm: 16rpx;
$spacing-md: 24rpx;
$spacing-lg: 32rpx;
$spacing-xl: 48rpx;
@mixin minimal-card {
background: $bg-color-main;
border: 1rpx solid $border-color;
border-radius: $border-radius-base;
padding: $spacing-md;
box-shadow: 0 2rpx 8rpx rgba(15, 23, 42, 0.03);
}
@mixin text-ellipsis {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
Expected: SCSS variables used by both old projects are available.
- Step 8: Create global App component
Create miniprogram/src/App.vue:
<script>
export default {
onLaunch() {
console.log('Merged Venue Miniapp Launch')
},
onShow() {
console.log('Merged Venue Miniapp Show')
},
onHide() {
console.log('Merged Venue Miniapp Hide')
}
}
</script>
<style lang="scss">
page {
background-color: $bg-color-soft;
color: $text-color-main;
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, "Segoe UI", Arial, sans-serif;
font-size: 28rpx;
line-height: 1.5;
box-sizing: border-box;
-webkit-text-size-adjust: 100%;
}
view,
scroll-view,
swiper,
button,
input,
textarea,
label,
image {
box-sizing: border-box;
}
button {
margin: 0;
padding: 0;
line-height: 1;
}
button::after {
border: none;
}
.container {
min-height: 100vh;
padding: $spacing-md;
background-color: $bg-color-soft;
}
.page-bottom-safe {
padding-bottom: calc(128rpx + env(safe-area-inset-bottom));
}
.card {
@include minimal-card;
}
.badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 88rpx;
height: 36rpx;
padding: 0 14rpx;
border-radius: $border-radius-sm;
font-size: 20rpx;
font-weight: 700;
&.primary {
color: $color-primary;
background: $color-primary-light;
}
&.success {
color: $color-success;
background: $color-success-light;
}
&.warning {
color: $color-warning;
background: $color-warning-light;
}
&.danger {
color: $color-danger;
background: $color-danger-light;
}
&.muted {
color: $text-color-muted;
background: $bg-color-hover;
}
}
.primary-button {
height: 88rpx;
border-radius: $border-radius-base;
background: $text-color-main;
color: #FFFFFF;
font-size: 28rpx;
font-weight: 800;
display: flex;
align-items: center;
justify-content: center;
&[disabled],
&.disabled {
background: $text-color-light;
color: #FFFFFF;
}
}
.ghost-button {
height: 72rpx;
border-radius: $border-radius-base;
border: 1rpx solid $border-color;
background: $bg-color-main;
color: $text-color-regular;
font-size: 26rpx;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
}
.empty-state,
.load-more-tip {
padding: $spacing-xl $spacing-md;
text-align: center;
color: $text-color-light;
font-size: 24rpx;
}
.fixed-action-bar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 20;
display: flex;
align-items: center;
gap: $spacing-md;
padding: $spacing-sm $spacing-md calc($spacing-sm + env(safe-area-inset-bottom));
background: rgba(255, 255, 255, 0.96);
border-top: 1rpx solid $border-color;
}
.price-text {
color: $text-color-main;
font-size: 34rpx;
font-weight: 900;
}
</style>
Expected: common global classes used by the comprehensive project remain available, and ice pages still get the shared variables.
- Step 9: Commit shell
Run:
cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序'
git add miniprogram/package.json miniprogram/package-lock.json miniprogram/vite.config.js miniprogram/.env miniprogram/.env.example miniprogram/src/main.js miniprogram/src/App.vue miniprogram/src/manifest.json miniprogram/src/uni.scss miniprogram/src/styles/variables.scss
git commit -m "feat: scaffold merged miniapp shell"
Expected: commit succeeds with only shell files staged.
Task 2: Add Public Venue Selection State and Page
Files:
-
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/utils/venue.js -
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/stores/venue.js -
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages/venue-select/index.vue -
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages.json -
Step 1: Create venue utility module
Create miniprogram/src/utils/venue.js:
export const CURRENT_VENUE_KEY = 'CURRENT_VENUE'
export const VENUE_KEYS = Object.freeze({
ICE: 'ice',
COMPREHENSIVE: 'comprehensive'
})
export const VENUE_OPTIONS = Object.freeze([
{
key: VENUE_KEYS.ICE,
name: '冰场馆',
description: '课程预约、会员卡与滑冰服务',
homePath: '/pages/ice/home/index'
},
{
key: VENUE_KEYS.COMPREHENSIVE,
name: '综合场馆',
description: '课程、包场、门票、订单与余额',
homePath: '/pages/comprehensive/home/index'
}
])
export const isVenueKey = (value) => {
return value === VENUE_KEYS.ICE || value === VENUE_KEYS.COMPREHENSIVE
}
export const getVenueOption = (venueKey) => {
return VENUE_OPTIONS.find((item) => item.key === venueKey) || null
}
export const getStoredVenue = () => {
const venue = uni.getStorageSync(CURRENT_VENUE_KEY)
return isVenueKey(venue) ? venue : ''
}
export const setStoredVenue = (venueKey) => {
if (!isVenueKey(venueKey)) {
uni.removeStorageSync(CURRENT_VENUE_KEY)
return ''
}
uni.setStorageSync(CURRENT_VENUE_KEY, venueKey)
return venueKey
}
export const navigateToVenueHome = (venueKey, options = {}) => {
const venue = getVenueOption(venueKey)
if (!venue) {
uni.reLaunch({ url: '/pages/venue-select/index' })
return
}
const navigate = options.redirect === true ? uni.redirectTo : uni.reLaunch
navigate({ url: venue.homePath })
}
Expected: utility has a single source of truth for venue keys and home paths.
- Step 2: Create venue Pinia store
Create miniprogram/src/stores/venue.js:
import { defineStore } from 'pinia'
import { getStoredVenue, getVenueOption, navigateToVenueHome, setStoredVenue } from '@/utils/venue'
export const useVenueStore = defineStore('currentVenue', {
state: () => ({
currentVenue: ''
}),
getters: {
currentVenueOption: (state) => getVenueOption(state.currentVenue),
hasSelectedVenue: (state) => Boolean(getVenueOption(state.currentVenue))
},
actions: {
init() {
this.currentVenue = getStoredVenue()
return this.currentVenue
},
selectVenue(venueKey) {
this.currentVenue = setStoredVenue(venueKey)
return this.currentVenue
},
enterVenue(venueKey) {
const selectedVenue = this.selectVenue(venueKey)
navigateToVenueHome(selectedVenue)
}
}
})
Expected: selection persists and can navigate to module home.
- Step 3: Create venue selection page
Create miniprogram/src/pages/venue-select/index.vue:
<template>
<view class="venue-select-page">
<view class="header">
<text class="title">选择场馆</text>
<text class="subtitle">进入对应场馆后,会员、预约、订单和支付数据彼此独立。</text>
</view>
<view class="venue-list">
<view
v-for="venue in venues"
:key="venue.key"
class="venue-card"
:class="{ active: venue.key === currentVenue }"
@tap="selectVenue(venue.key)"
>
<view class="venue-main">
<text class="venue-name">{{ venue.name }}</text>
<text class="venue-desc">{{ venue.description }}</text>
</view>
<view class="venue-action">
<text>{{ venue.key === currentVenue ? '当前' : '进入' }}</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { computed, onMounted } from 'vue'
import { useVenueStore } from '@/stores/venue'
import { VENUE_OPTIONS } from '@/utils/venue'
const venueStore = useVenueStore()
const venues = VENUE_OPTIONS
const currentVenue = computed(() => venueStore.currentVenue)
onMounted(() => {
venueStore.init()
})
const selectVenue = (venueKey) => {
venueStore.enterVenue(venueKey)
}
</script>
<style lang="scss" scoped>
.venue-select-page {
min-height: 100vh;
padding: 96rpx 32rpx 48rpx;
background: $bg-color-soft;
}
.header {
display: flex;
flex-direction: column;
gap: 18rpx;
margin-bottom: 56rpx;
}
.title {
color: $text-color-main;
font-size: 48rpx;
font-weight: 900;
}
.subtitle {
color: $text-color-muted;
font-size: 26rpx;
line-height: 1.6;
}
.venue-list {
display: flex;
flex-direction: column;
gap: 24rpx;
}
.venue-card {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24rpx;
padding: 32rpx;
border: 1rpx solid $border-color;
border-radius: $border-radius-lg;
background: $bg-color-main;
}
.venue-card.active {
border-color: $color-primary;
background: $color-primary-light;
}
.venue-main {
display: flex;
flex: 1;
min-width: 0;
flex-direction: column;
gap: 12rpx;
}
.venue-name {
color: $text-color-main;
font-size: 34rpx;
font-weight: 900;
}
.venue-desc {
color: $text-color-muted;
font-size: 24rpx;
line-height: 1.5;
}
.venue-action {
display: flex;
align-items: center;
justify-content: center;
min-width: 96rpx;
height: 56rpx;
border-radius: $border-radius-sm;
background: $text-color-main;
color: #FFFFFF;
font-size: 24rpx;
font-weight: 800;
}
.venue-card.active .venue-action {
background: $color-primary;
}
</style>
Expected: first page gives two concrete choices and persists selection before navigation.
- Step 4: Create initial pages registry
Create miniprogram/src/pages.json:
{
"easycom": {
"autoscan": true,
"custom": {
"^u--(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue"
}
},
"pages": [
{
"path": "pages/venue-select/index",
"style": {
"navigationBarTitleText": "选择场馆",
"navigationBarBackgroundColor": "#FFFFFF",
"navigationBarTextStyle": "black"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarBackgroundColor": "#FFFFFF",
"backgroundColor": "#F8FAFC",
"backgroundColorBottom": "#F8FAFC",
"backgroundColorTop": "#FFFFFF"
}
}
Expected: app has a valid first page before business modules are migrated.
- Step 5: Verify selection shell builds
Run:
cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram'
npm install
npm run build:mp-weixin
Expected: build succeeds or fails only because /pages/ice/home/index and /pages/comprehensive/home/index do not exist yet after tapping; compile-time should not require those target files until pages are registered. If compile fails for missing dependencies, fix dependency installation before continuing.
- Step 6: Commit selection shell
Run:
cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序'
git add miniprogram/src/utils/venue.js miniprogram/src/stores/venue.js miniprogram/src/pages/venue-select/index.vue miniprogram/src/pages.json miniprogram/package-lock.json
git commit -m "feat: add venue selection shell"
Expected: commit succeeds with public shell files and any refreshed lockfile.
Task 3: Migrate Comprehensive Venue Module
Files:
-
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/comprehensive/api/*.js -
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/comprehensive/stores/session.js -
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/comprehensive/utils/*.js -
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages/comprehensive/** -
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/static/comprehensive/tab/*.png -
Modify:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages.json -
Step 1: Copy comprehensive source files into namespaced locations
Run:
mkdir -p '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/comprehensive'
mkdir -p '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages/comprehensive'
mkdir -p '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/static/comprehensive'
cp -R '/Volumes/ExternalStorageA/project/2026.5/体育场馆/综合场馆小程序/miniprogram/src/api' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/comprehensive/api'
cp -R '/Volumes/ExternalStorageA/project/2026.5/体育场馆/综合场馆小程序/miniprogram/src/stores' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/comprehensive/stores'
cp -R '/Volumes/ExternalStorageA/project/2026.5/体育场馆/综合场馆小程序/miniprogram/src/utils' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/comprehensive/utils'
cp -R '/Volumes/ExternalStorageA/project/2026.5/体育场馆/综合场馆小程序/miniprogram/src/pages/.' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages/comprehensive/'
cp -R '/Volumes/ExternalStorageA/project/2026.5/体育场馆/综合场馆小程序/miniprogram/src/static/tab' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/static/comprehensive/tab'
Expected: comprehensive pages exist under src/pages/comprehensive, and API/store/utils exist under src/modules/comprehensive.
- Step 2: Update comprehensive request base URL
Edit miniprogram/src/modules/comprehensive/api/request.js so the top constants are:
const KEY_TOKEN = 'COMPREHENSIVE_VENUE_TOKEN'
const API_BASE_URL = import.meta.env.VITE_COMPREHENSIVE_API_BASE_URL || 'http://127.0.0.1:8001'
Expected: VITE_API_BASE_URL is not used in this module.
- Step 3: Rename comprehensive session store id and imports
Edit miniprogram/src/modules/comprehensive/stores/session.js:
import { defineStore } from 'pinia'
import { authApi, authStorage } from '@/modules/comprehensive/api/auth'
import { memberApi } from '@/modules/comprehensive/api/member'
export const useComprehensiveSessionStore = defineStore('comprehensiveVenueSession', {
state: () => ({
isReady: false,
isLoggedIn: false,
member: null
}),
actions: {
async init() {
this.isLoggedIn = authStorage.isLoggedIn()
if (this.isLoggedIn) {
try {
this.member = await memberApi.getMemberMe()
} catch (error) {
if ([40001, 40002, 401, 403].includes(error.code) || [401, 403].includes(error.statusCode)) {
authStorage.clear()
this.member = null
this.isLoggedIn = false
}
}
}
this.isReady = true
},
async login() {
const loginResult = await loginByWechat()
const data = await authApi.wechatLogin({ code: loginResult.code })
this.member = data.member
this.isLoggedIn = true
},
logout() {
authApi.logout()
this.member = null
this.isLoggedIn = false
},
async refreshMember() {
if (!authStorage.isLoggedIn()) return
this.member = await memberApi.getMemberMe()
this.isLoggedIn = true
}
}
})
const loginByWechat = () => new Promise((resolve, reject) => {
uni.login({
provider: 'weixin',
success: (res) => {
if (!res.code) {
reject(new Error('微信登录未返回 code'))
return
}
resolve(res)
},
fail: reject
})
})
Expected: no @/api imports and no defineStore('session') remain in this file.
- Step 4: Rewrite comprehensive page imports
Run these replacements from miniprogram:
perl -pi -e "s#@/api/#@/modules/comprehensive/api/#g" src/pages/comprehensive/**/*.vue
perl -pi -e "s#@/stores/session#@/modules/comprehensive/stores/session#g" src/pages/comprehensive/**/*.vue
perl -pi -e "s#@/utils/#@/modules/comprehensive/utils/#g" src/pages/comprehensive/**/*.vue
perl -pi -e "s#useSessionStore#useComprehensiveSessionStore#g" src/pages/comprehensive/**/*.vue
Expected: comprehensive pages import from @/modules/comprehensive/*.
- Step 5: Rewrite comprehensive route strings
Run these replacements from miniprogram:
perl -pi -e "s#/pages/home#/pages/comprehensive/home#g" src/pages/comprehensive/**/*.vue
perl -pi -e "s#/pages/courses#/pages/comprehensive/courses#g" src/pages/comprehensive/**/*.vue
perl -pi -e "s#/pages/bookings#/pages/comprehensive/bookings#g" src/pages/comprehensive/**/*.vue
perl -pi -e "s#/pages/profile#/pages/comprehensive/profile#g" src/pages/comprehensive/**/*.vue
perl -pi -e "s#/pages/activities#/pages/comprehensive/activities#g" src/pages/comprehensive/**/*.vue
perl -pi -e "s#/pages/private#/pages/comprehensive/private#g" src/pages/comprehensive/**/*.vue
perl -pi -e "s#/pages/tickets#/pages/comprehensive/tickets#g" src/pages/comprehensive/**/*.vue
perl -pi -e "s#/pages/orders#/pages/comprehensive/orders#g" src/pages/comprehensive/**/*.vue
perl -pi -e "s#/pages/pay#/pages/comprehensive/pay#g" src/pages/comprehensive/**/*.vue
Expected: comprehensive pages no longer navigate to unprefixed old routes.
- Step 6: Add comprehensive switch entry to profile menu
Edit miniprogram/src/pages/comprehensive/profile/index.vue.
Add one item to menuItems after the personal info item:
{ title: '切换场馆', url: '/pages/venue-select/index', theme: 'profile-theme', icon: 'profile' }
Expected: logged-in comprehensive users can navigate to the venue selection page. If the page shows no menu when logged out, the venue selector remains available by manually opening the first page; Task 7 will also validate direct switching.
- Step 7: Register comprehensive pages
Modify miniprogram/src/pages.json by appending these page entries after pages/venue-select/index:
{
"path": "pages/comprehensive/home/index",
"style": {
"navigationBarTitleText": "首页",
"enablePullDownRefresh": true
}
},
{
"path": "pages/comprehensive/courses/index",
"style": {
"navigationBarTitleText": "课程",
"enablePullDownRefresh": true
}
},
{
"path": "pages/comprehensive/bookings/index",
"style": {
"navigationBarTitleText": "我的预约",
"enablePullDownRefresh": true
}
},
{
"path": "pages/comprehensive/profile/index",
"style": {
"navigationBarTitleText": "会员中心"
}
},
{
"path": "pages/comprehensive/activities/index",
"style": {
"navigationBarTitleText": "活动公告",
"enablePullDownRefresh": true
}
},
{
"path": "pages/comprehensive/activities/detail",
"style": {
"navigationBarTitleText": "活动详情"
}
},
{
"path": "pages/comprehensive/courses/detail",
"style": {
"navigationBarTitleText": "课程详情"
}
},
{
"path": "pages/comprehensive/courses/confirm",
"style": {
"navigationBarTitleText": "确认课程预约"
}
},
{
"path": "pages/comprehensive/private/index",
"style": {
"navigationBarTitleText": "包场预约"
}
},
{
"path": "pages/comprehensive/private/confirm",
"style": {
"navigationBarTitleText": "确认包场"
}
},
{
"path": "pages/comprehensive/tickets/index",
"style": {
"navigationBarTitleText": "篮球门票"
}
},
{
"path": "pages/comprehensive/pay/index",
"style": {
"navigationBarTitleText": "订单支付"
}
},
{
"path": "pages/comprehensive/orders/detail",
"style": {
"navigationBarTitleText": "订单详情"
}
},
{
"path": "pages/comprehensive/bookings/course-detail",
"style": {
"navigationBarTitleText": "课程预约详情"
}
},
{
"path": "pages/comprehensive/bookings/private-detail",
"style": {
"navigationBarTitleText": "包场预约详情"
}
},
{
"path": "pages/comprehensive/profile/balance",
"style": {
"navigationBarTitleText": "余额流水",
"enablePullDownRefresh": true
}
},
{
"path": "pages/comprehensive/profile/tickets",
"style": {
"navigationBarTitleText": "我的门票",
"enablePullDownRefresh": true
}
},
{
"path": "pages/comprehensive/profile/orders",
"style": {
"navigationBarTitleText": "我的订单",
"enablePullDownRefresh": true
}
},
{
"path": "pages/comprehensive/profile/detail",
"style": {
"navigationBarTitleText": "个人信息"
}
}
Expected: all comprehensive pages from the old pages.json are registered with the pages/comprehensive prefix.
- Step 8: Verify comprehensive module references
Run:
cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram'
rg -n "@/api|@/stores/session|@/utils/" src/pages/comprehensive src/modules/comprehensive
rg -n "'/pages/(home|courses|bookings|profile|activities|private|tickets|orders|pay)|\"/pages/(home|courses|bookings|profile|activities|private|tickets|orders|pay)" src/pages/comprehensive
rg -n "VITE_API_BASE_URL" src/modules/comprehensive
Expected: all three commands return no matches.
- Step 9: Build after comprehensive migration
Run:
cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram'
npm run build:mp-weixin
Expected: build succeeds, or fails only because ice pages have not been registered yet. If the failure references comprehensive imports, fix those references before continuing.
- Step 10: Commit comprehensive module
Run:
cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序'
git add miniprogram/src/modules/comprehensive miniprogram/src/pages/comprehensive miniprogram/src/static/comprehensive miniprogram/src/pages.json
git commit -m "feat: migrate comprehensive venue module"
Expected: commit succeeds with comprehensive module files.
Task 4: Migrate Ice Venue Module
Files:
-
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/ice/api/*.js -
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/components/ice/StatePanel.vue -
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages/ice/** -
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/static/ice/tab/*.png -
Modify:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages.json -
Step 1: Copy ice source files into namespaced locations
Run:
mkdir -p '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/ice'
mkdir -p '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages/ice'
mkdir -p '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/components/ice'
mkdir -p '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/static/ice'
cp -R '/Volumes/ExternalStorageA/project/2026.5/体育场馆/冰场馆小程序/miniprogram/src/api' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/modules/ice/api'
cp '/Volumes/ExternalStorageA/project/2026.5/体育场馆/冰场馆小程序/miniprogram/src/components/StatePanel.vue' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/components/ice/StatePanel.vue'
cp -R '/Volumes/ExternalStorageA/project/2026.5/体育场馆/冰场馆小程序/miniprogram/src/pages/.' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages/ice/'
cp -R '/Volumes/ExternalStorageA/project/2026.5/体育场馆/冰场馆小程序/miniprogram/src/static/tab' '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/static/ice/tab'
Expected: ice pages exist under src/pages/ice, API under src/modules/ice/api, and state panel under src/components/ice.
- Step 2: Update ice request base URL
Edit miniprogram/src/modules/ice/api/request.js so the base URL constant is:
const API_BASE_URL = import.meta.env.VITE_ICE_API_BASE_URL || 'http://127.0.0.1:8000'
Expected: VITE_API_BASE_URL is not used in this module.
- Step 3: Rewrite ice page imports
Run these replacements from miniprogram:
perl -pi -e "s#@/api/#@/modules/ice/api/#g" src/pages/ice/**/*.vue
perl -pi -e "s#@/components/StatePanel.vue#@/components/ice/StatePanel.vue#g" src/pages/ice/**/*.vue
Expected: ice pages import from @/modules/ice/api/* and @/components/ice/StatePanel.vue.
- Step 4: Rewrite ice route strings
Run these replacements from miniprogram:
perl -pi -e "s#/pages/home#/pages/ice/home#g" src/pages/ice/**/*.vue
perl -pi -e "s#/pages/courses#/pages/ice/courses#g" src/pages/ice/**/*.vue
perl -pi -e "s#/pages/bookings#/pages/ice/bookings#g" src/pages/ice/**/*.vue
perl -pi -e "s#/pages/profile#/pages/ice/profile#g" src/pages/ice/**/*.vue
perl -pi -e "s#/pages/activities#/pages/ice/activities#g" src/pages/ice/**/*.vue
Expected: ice pages no longer navigate to unprefixed old routes.
- Step 5: Add ice switch entry to profile page
Edit miniprogram/src/pages/ice/profile/index.vue.
Add a menu item in the logged-in menu group before the logout item:
<view class="menu-item" @tap="navigateTo('/pages/venue-select/index')">
<view class="item-left">
<view class="item-icon-container profile-theme">
<view class="svg-profile-minimal">
<view class="svg-profile-head" />
<view class="svg-profile-body" />
</view>
</view>
<text class="item-title">切换场馆</text>
</view>
<view class="item-right">
<view class="menu-arrow" />
</view>
</view>
If the page does not define .profile-theme or .svg-profile-minimal, use the existing card-theme icon container classes instead:
<view class="item-icon-container card-theme">
<view class="svg-card-minimal">
<view class="svg-card-chip" />
</view>
</view>
Expected: logged-in ice users can navigate to the venue selection page.
- Step 6: Register ice pages
Modify miniprogram/src/pages.json by appending these page entries after the comprehensive entries:
{
"path": "pages/ice/home/index",
"style": {
"navigationBarTitleText": "首页",
"enablePullDownRefresh": true
}
},
{
"path": "pages/ice/courses/index",
"style": {
"navigationBarTitleText": "近期课程",
"enablePullDownRefresh": true
}
},
{
"path": "pages/ice/bookings/index",
"style": {
"navigationBarTitleText": "我的预约",
"enablePullDownRefresh": true
}
},
{
"path": "pages/ice/profile/index",
"style": {
"navigationBarTitleText": "会员中心"
}
},
{
"path": "pages/ice/bookings/detail",
"style": {
"navigationBarTitleText": "预约详情"
}
},
{
"path": "pages/ice/courses/detail",
"style": {
"navigationBarTitleText": "课程详情"
}
},
{
"path": "pages/ice/profile/cards",
"style": {
"navigationBarTitleText": "我的会员卡",
"enablePullDownRefresh": true
}
},
{
"path": "pages/ice/profile/card-history",
"style": {
"navigationBarTitleText": "会员卡使用流水",
"enablePullDownRefresh": true
}
},
{
"path": "pages/ice/profile/detail",
"style": {
"navigationBarTitleText": "个人信息"
}
},
{
"path": "pages/ice/activities/detail",
"style": {
"navigationBarTitleText": "活动详情"
}
}
Expected: all ice pages from the old pages.json are registered with the pages/ice prefix.
- Step 7: Verify ice module references
Run:
cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram'
rg -n "@/api|@/components/StatePanel.vue" src/pages/ice src/modules/ice
rg -n "'/pages/(home|courses|bookings|profile|activities)|\"/pages/(home|courses|bookings|profile|activities)" src/pages/ice
rg -n "VITE_API_BASE_URL" src/modules/ice
Expected: all three commands return no matches.
- Step 8: Build after ice migration
Run:
cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram'
npm run build:mp-weixin
Expected: build succeeds. If build fails, fix the reported import, route registration, or SCSS variable error before continuing.
- Step 9: Commit ice module
Run:
cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序'
git add miniprogram/src/modules/ice miniprogram/src/pages/ice miniprogram/src/components/ice miniprogram/src/static/ice miniprogram/src/pages.json
git commit -m "feat: migrate ice venue module"
Expected: commit succeeds with ice module files.
Task 5: Finalize Cross-Module Navigation and Safety Checks
Files:
-
Modify:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/pages/venue-select/index.vue -
Modify:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/src/utils/venue.js -
Modify: module pages only if checks reveal stale routes or stale imports.
-
Step 1: Ensure venue selector can return to stored current venue
Update miniprogram/src/pages/venue-select/index.vue to keep the existing card UI and add a continue action when a venue is already selected:
<template>
<view class="venue-select-page">
<view class="header">
<text class="title">选择场馆</text>
<text class="subtitle">进入对应场馆后,会员、预约、订单和支付数据彼此独立。</text>
</view>
<view v-if="selectedVenue" class="current-card">
<text class="current-label">当前场馆</text>
<text class="current-name">{{ selectedVenue.name }}</text>
<button class="primary-button continue-btn" @tap="continueCurrentVenue">继续进入</button>
</view>
<view class="venue-list">
<view
v-for="venue in venues"
:key="venue.key"
class="venue-card"
:class="{ active: venue.key === currentVenue }"
@tap="selectVenue(venue.key)"
>
<view class="venue-main">
<text class="venue-name">{{ venue.name }}</text>
<text class="venue-desc">{{ venue.description }}</text>
</view>
<view class="venue-action">
<text>{{ venue.key === currentVenue ? '切换进入' : '进入' }}</text>
</view>
</view>
</view>
</view>
</template>
Add script code:
const selectedVenue = computed(() => venueStore.currentVenueOption)
const continueCurrentVenue = () => {
if (!venueStore.currentVenue) return
venueStore.enterVenue(venueStore.currentVenue)
}
Add styles:
.current-card {
display: flex;
flex-direction: column;
gap: 12rpx;
margin-bottom: 32rpx;
padding: 28rpx;
border-radius: $border-radius-lg;
background: $bg-color-main;
border: 1rpx solid $border-color;
}
.current-label {
color: $text-color-muted;
font-size: 22rpx;
font-weight: 700;
}
.current-name {
color: $text-color-main;
font-size: 32rpx;
font-weight: 900;
}
.continue-btn {
margin-top: 12rpx;
}
Expected: users who return to the selector can continue into the stored venue without changing it.
- Step 2: Run stale import checks
Run:
cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram'
rg -n "@/api|@/stores/session|@/utils/(date|money|status)|@/components/StatePanel.vue" src/pages src/modules
Expected: no matches. If matches appear, rewrite them to module-specific imports:
@/modules/ice/api/<file>
@/modules/comprehensive/api/<file>
@/modules/comprehensive/utils/<file>
@/components/ice/StatePanel.vue
- Step 3: Run stale route checks
Run:
cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram'
rg -n "'/pages/(home|courses|bookings|profile|activities|private|tickets|orders|pay)|\"/pages/(home|courses|bookings|profile|activities|private|tickets|orders|pay)|`/pages/(home|courses|bookings|profile|activities|private|tickets|orders|pay)" src/pages
Expected: no matches. If matches appear, prefix with ice or comprehensive based on the file path.
- Step 4: Run API base URL checks
Run:
cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram'
rg -n "VITE_API_BASE_URL|const API_BASE_URL" src/modules
Expected output includes only:
src/modules/ice/api/request.js:const API_BASE_URL = import.meta.env.VITE_ICE_API_BASE_URL || 'http://127.0.0.1:8000'
src/modules/comprehensive/api/request.js:const API_BASE_URL = import.meta.env.VITE_COMPREHENSIVE_API_BASE_URL || 'http://127.0.0.1:8001'
- Step 5: Run token isolation checks
Run:
cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram'
rg -n "ICE_MEMBER_TOKEN|ICE_MEMBER_TOKEN_EXPIRES_AT|COMPREHENSIVE_VENUE_TOKEN" src/modules
Expected output includes:
src/modules/ice/api/request.js:const KEY_TOKEN = 'ICE_MEMBER_TOKEN'
src/modules/ice/api/request.js:const KEY_TOKEN_EXPIRES_AT = 'ICE_MEMBER_TOKEN_EXPIRES_AT'
src/modules/comprehensive/api/request.js:const KEY_TOKEN = 'COMPREHENSIVE_VENUE_TOKEN'
- Step 6: Build final merged app
Run:
cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram'
npm run build:mp-weixin
Expected: build succeeds and outputs dist/build/mp-weixin.
- Step 7: Commit safety pass
Run:
cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序'
git add miniprogram
git commit -m "fix: finalize merged venue navigation"
Expected: commit succeeds if files changed. If no files changed after checks, skip this commit and record that no safety-pass commit was needed.
Task 6: Manual WeChat Mini Program Smoke Test
Files:
-
No source files should be modified unless the smoke test finds a defect.
-
Step 1: Open generated mini program in WeChat DevTools
Open this directory in WeChat DevTools:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram/dist/build/mp-weixin
Expected: project opens with appid wx516b3e0d9940c788.
- Step 2: Smoke test venue selector
In the simulator:
1. Open first page.
2. Confirm "选择场馆" appears.
3. Tap "冰场馆".
4. Confirm URL is pages/ice/home/index.
5. Navigate back to pages/venue-select/index.
6. Tap "综合场馆".
7. Confirm URL is pages/comprehensive/home/index.
Expected: both venue cards navigate to the correct module home page.
- Step 3: Smoke test ice module navigation
Open these paths in WeChat DevTools page path input:
pages/ice/home/index
pages/ice/courses/index
pages/ice/bookings/index
pages/ice/profile/index
pages/ice/profile/cards
Expected: each page loads without a JavaScript import error. Network errors from unavailable backend are acceptable during local smoke testing if request URLs point to https://venue.test.chatgqt.top.
- Step 4: Smoke test comprehensive module navigation
Open these paths in WeChat DevTools page path input:
pages/comprehensive/home/index
pages/comprehensive/courses/index
pages/comprehensive/bookings/index
pages/comprehensive/profile/index
pages/comprehensive/private/index
pages/comprehensive/tickets/index
pages/comprehensive/pay/index?order_id=1
Expected: each page loads without a JavaScript import error. The pay page may show an order loading error for order_id=1; it must not call the ice backend.
- Step 5: Inspect request domains
Use WeChat DevTools network panel:
1. Open pages/ice/home/index.
2. Confirm requests go to https://venue.test.chatgqt.top.
3. Open pages/comprehensive/home/index.
4. Confirm requests go to https://venue2.test.chatgqt.top.
Expected: requests never cross backend domains.
- Step 6: Commit smoke-test fixes
If source fixes were required, run:
cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序'
git add miniprogram
git commit -m "fix: address merged miniapp smoke test issues"
Expected: commit succeeds only if smoke testing found and fixed defects.
Task 7: Final Verification and Delivery Notes
Files:
-
Create:
/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/docs/merged-miniapp-verification.md -
Step 1: Run final command verification
Run:
cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序/miniprogram'
npm run build:mp-weixin
rg -n "@/api|@/stores/session|@/utils/(date|money|status)|@/components/StatePanel.vue" src/pages src/modules
rg -n "'/pages/(home|courses|bookings|profile|activities|private|tickets|orders|pay)|\"/pages/(home|courses|bookings|profile|activities|private|tickets|orders|pay)|`/pages/(home|courses|bookings|profile|activities|private|tickets|orders|pay)" src/pages
rg -n "VITE_API_BASE_URL" src/modules .env .env.example vite.config.js
Expected:
npm run build:mp-weixin exits 0
stale import rg exits 1 with no matches
stale route rg exits 1 with no matches
VITE_API_BASE_URL rg exits 1 with no matches
- Step 2: Write verification note
Create docs/merged-miniapp-verification.md:
# 合并版场馆小程序验证记录
## 构建
- 命令:`npm run build:mp-weixin`
- 结果:通过
- 产物:`miniprogram/dist/build/mp-weixin`
## 路由隔离
- 检查旧的无前缀页面路径:通过
- 冰场馆页面前缀:`pages/ice`
- 综合场馆页面前缀:`pages/comprehensive`
## API 隔离
- 冰场馆 baseURL:`VITE_ICE_API_BASE_URL`
- 综合场馆 baseURL:`VITE_COMPREHENSIVE_API_BASE_URL`
- 旧 `VITE_API_BASE_URL`:未使用
## 登录态隔离
- 冰场馆 token:`ICE_MEMBER_TOKEN`
- 冰场馆 token 过期时间:`ICE_MEMBER_TOKEN_EXPIRES_AT`
- 综合场馆 token:`COMPREHENSIVE_VENUE_TOKEN`
## 上线前人工检查
- 微信 request 合法域名包含 `venue.test.chatgqt.top`
- 微信 request 合法域名包含 `venue2.test.chatgqt.top`
- 综合场馆支付仍由综合场馆后端生成支付参数
If any command fails, replace 结果:通过 with the exact failing command and actual status, then fix the issue before final delivery.
- Step 3: Commit verification note
Run:
cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序'
git add docs/merged-miniapp-verification.md
git commit -m "docs: record merged miniapp verification"
Expected: commit succeeds with the verification record.
- Step 4: Final git status check
Run:
cd '/Volumes/ExternalStorageA/project/2026.5/体育场馆/场馆小程序'
git status --short
Expected: no uncommitted files except intentionally ignored build output if .gitignore is added later. If build output appears and should not be committed, add a project .gitignore that ignores:
miniprogram/node_modules/
miniprogram/dist/
.DS_Store
Commit .gitignore with:
git add .gitignore
git commit -m "chore: ignore generated miniapp files"
Expected: working tree is clean or only contains intentionally untracked files reported to the user.
Plan Self-Review
Spec coverage:
- New project under
场馆小程序/miniprogram: Task 1. - First-entry venue choice and in-app switching: Tasks 2, 5, and 6.
- Isolated Django backends and API clients: Tasks 1, 3, 4, and 5.
- Token isolation: Tasks 3, 4, 5, and 7.
- Route prefixing: Tasks 3, 4, and 5.
- No backend/admin/CI merge: all tasks operate only under
场馆小程序. - Build and smoke verification: Tasks 5, 6, and 7.
Placeholder scan: the plan intentionally avoids unspecified implementation slots. Each migration step includes exact copy commands, rewrite commands, file paths, expected checks, and commit commands.
Type consistency:
- Venue key values are
iceandcomprehensiveacrossVENUE_KEYS, storage, routes, and page prefixes. - Store exports are
useVenueStoreanduseComprehensiveSessionStore. - Environment variables are
VITE_ICE_API_BASE_URLandVITE_COMPREHENSIVE_API_BASE_URL. - Token keys are
ICE_MEMBER_TOKEN,ICE_MEMBER_TOKEN_EXPIRES_AT, andCOMPREHENSIVE_VENUE_TOKEN.