# Frontend Static Page Migration 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:** 在保持当前 `4 tab` 不变的前提下,把 `D:\Code3\wdp\xuanzhi\frontend` 的主要页面迁移为当前小程序仓库中的静态页面体系,做到页面可见、可点、可切换,但不接 API、store、旧数据结构和真实业务逻辑。
**Architecture:** 先在 `app.json` 中注册 `tcm / mingli / learning` 分包,再建立 `utils/static-ux/*` 纯静态数据层,随后按“主包页 -> tcm 分包 -> mingli + learning 分包”的顺序落地页面。所有页面 `index.js` 统一暴露 `create*PageData` 工厂函数,Jest 直接验证页面数据和事件处理,页面间只通过当前仓库自定义的静态参数做场景切换。
**Tech Stack:** 微信原生小程序、JavaScript、WXML、WXSS、Jest、TDesign(仅限当前仓库已存在组件)
---
## File Structure
- Modify: `app.json`
- Modify: `app.wxss`
- Create: `utils/static-ux/route-map.js`
- Create: `utils/static-ux/shared.js`
- Create: `utils/static-ux/tcm.js`
- Create: `utils/static-ux/mingli.js`
- Create: `utils/static-ux/learning.js`
- Modify: `pages/home/index.js`
- Modify: `pages/home/index.wxml`
- Modify: `pages/home/index.wxss`
- Modify: `pages/library/index.js`
- Modify: `pages/library/index.wxml`
- Modify: `pages/library/index.wxss`
- Modify: `pages/ai/index.js`
- Modify: `pages/ai/index.wxml`
- Modify: `pages/ai/index.wxss`
- Modify: `pages/profile/index.js`
- Modify: `pages/profile/index.wxml`
- Modify: `pages/profile/index.wxss`
- Modify: `pages/login/index.js`
- Modify: `pages/login/index.wxml`
- Modify: `pages/login/index.wxss`
- Create: `packages/tcm/pages/ai-history/index.js`
- Create: `packages/tcm/pages/ai-history/index.json`
- Create: `packages/tcm/pages/ai-history/index.wxml`
- Create: `packages/tcm/pages/ai-history/index.wxss`
- Create: `packages/tcm/pages/assets/index.js`
- Create: `packages/tcm/pages/assets/index.json`
- Create: `packages/tcm/pages/assets/index.wxml`
- Create: `packages/tcm/pages/assets/index.wxss`
- Create: `packages/tcm/pages/bianzheng/index.js`
- Create: `packages/tcm/pages/bianzheng/index.json`
- Create: `packages/tcm/pages/bianzheng/index.wxml`
- Create: `packages/tcm/pages/bianzheng/index.wxss`
- Create: `packages/tcm/pages/book-detail/index.js`
- Create: `packages/tcm/pages/book-detail/index.json`
- Create: `packages/tcm/pages/book-detail/index.wxml`
- Create: `packages/tcm/pages/book-detail/index.wxss`
- Create: `packages/tcm/pages/search-books/index.js`
- Create: `packages/tcm/pages/search-books/index.json`
- Create: `packages/tcm/pages/search-books/index.wxml`
- Create: `packages/tcm/pages/search-books/index.wxss`
- Create: `packages/tcm/pages/section/index.js`
- Create: `packages/tcm/pages/section/index.json`
- Create: `packages/tcm/pages/section/index.wxml`
- Create: `packages/tcm/pages/section/index.wxss`
- Create: `packages/tcm/pages/placeholder/index.js`
- Create: `packages/tcm/pages/placeholder/index.json`
- Create: `packages/tcm/pages/placeholder/index.wxml`
- Create: `packages/tcm/pages/placeholder/index.wxss`
- Create: `packages/mingli/pages/hall/index.js`
- Create: `packages/mingli/pages/hall/index.json`
- Create: `packages/mingli/pages/hall/index.wxml`
- Create: `packages/mingli/pages/hall/index.wxss`
- Create: `packages/mingli/pages/bazi/index.js`
- Create: `packages/mingli/pages/bazi/index.json`
- Create: `packages/mingli/pages/bazi/index.wxml`
- Create: `packages/mingli/pages/bazi/index.wxss`
- Create: `packages/mingli/pages/book-detail/index.js`
- Create: `packages/mingli/pages/book-detail/index.json`
- Create: `packages/mingli/pages/book-detail/index.wxml`
- Create: `packages/mingli/pages/book-detail/index.wxss`
- Create: `packages/mingli/pages/search-books/index.js`
- Create: `packages/mingli/pages/search-books/index.json`
- Create: `packages/mingli/pages/search-books/index.wxml`
- Create: `packages/mingli/pages/search-books/index.wxss`
- Create: `packages/mingli/pages/section/index.js`
- Create: `packages/mingli/pages/section/index.json`
- Create: `packages/mingli/pages/section/index.wxml`
- Create: `packages/mingli/pages/section/index.wxss`
- Create: `packages/mingli/pages/interpret/index.js`
- Create: `packages/mingli/pages/interpret/index.json`
- Create: `packages/mingli/pages/interpret/index.wxml`
- Create: `packages/mingli/pages/interpret/index.wxss`
- Create: `packages/learning/pages/center/index.js`
- Create: `packages/learning/pages/center/index.json`
- Create: `packages/learning/pages/center/index.wxml`
- Create: `packages/learning/pages/center/index.wxss`
- Create: `tests/static-ux-route-map.test.js`
- Create: `tests/static-ux-domain-data.test.js`
- Create: `tests/profile-page.test.js`
- Create: `tests/profile-page-render.test.js`
- Create: `tests/login-page.test.js`
- Create: `tests/login-page-render.test.js`
- Create: `tests/tcm-support-pages.test.js`
- Create: `tests/tcm-reading-pages.test.js`
- Create: `tests/mingli-learning-pages.test.js`
- Modify: `tests/project-config.test.js`
- Modify: `tests/home-page.test.js`
- Modify: `tests/home-page-render.test.js`
- Modify: `tests/library-page.test.js`
- Modify: `tests/library-page-render.test.js`
- Modify: `tests/ai-page.test.js`
- Modify: `tests/ai-page-render.test.js`
## Execution Notes
- 工作区已经存在与本计划无关的改动;每一步只 `git add` 本任务涉及的文件。
- 页面参数只允许使用 `scene / mode / kind / domain / keyword` 这类静态场景参数,不允许恢复 `bookId / recordId / passageId` 的真实业务语义。
- 现有页面测试已经约束 `pages/home/index.wxss` 不能使用 `display: grid`、`gap:`、`width: fit-content`,实现时必须继续遵守。
### Task 1: Register Subpackages And Static UX Foundation
**Files:**
- Modify: `app.json`
- Modify: `tests/project-config.test.js`
- Create: `utils/static-ux/route-map.js`
- Create: `utils/static-ux/shared.js`
- Create: `utils/static-ux/tcm.js`
- Create: `utils/static-ux/mingli.js`
- Create: `utils/static-ux/learning.js`
- Create: `tests/static-ux-route-map.test.js`
- Create: `tests/static-ux-domain-data.test.js`
- [ ] **Step 1: Write the failing route and data-factory tests**
```js
// tests/static-ux-route-map.test.js
const { ROUTES } = require('../utils/static-ux/route-map')
describe('static route map', () => {
test('exposes full route paths for all static migration pages', () => {
expect(ROUTES.tabs.home).toBe('/pages/home/index')
expect(ROUTES.tcm.aiHistory).toBe('/packages/tcm/pages/ai-history/index')
expect(ROUTES.tcm.section).toBe('/packages/tcm/pages/section/index')
expect(ROUTES.mingli.hall).toBe('/packages/mingli/pages/hall/index')
expect(ROUTES.mingli.interpret).toBe('/packages/mingli/pages/interpret/index')
expect(ROUTES.learning.center).toBe('/packages/learning/pages/center/index')
})
})
```
```js
// tests/static-ux-domain-data.test.js
const {
createTcmHomeHubCards,
createTcmAssetPageData
} = require('../utils/static-ux/tcm')
const {
createMingliHallPageData,
createBaziPageData
} = require('../utils/static-ux/mingli')
const { createLearningCenterPageData } = require('../utils/static-ux/learning')
describe('static domain factories', () => {
test('returns scene-safe static data for tcm, mingli and learning domains', () => {
expect(createTcmHomeHubCards()).toEqual(
expect.arrayContaining([
expect.objectContaining({ key: 'tcm-library', title: '中医馆' }),
expect.objectContaining({ key: 'mingli-hall', title: '易学阁' })
])
)
expect(createTcmAssetPageData('notes')).toEqual(
expect.objectContaining({
title: '学习资产',
activeKind: 'notes'
})
)
expect(createMingliHallPageData()).toEqual(
expect.objectContaining({
title: '易学阁'
})
)
expect(createBaziPageData('result')).toEqual(
expect.objectContaining({
title: '八字排盘',
scene: 'result'
})
)
expect(createLearningCenterPageData()).toEqual(
expect.objectContaining({
title: '学习中心'
})
)
})
})
```
```js
// tests/project-config.test.js
test('registers static migration subpackages in app config', () => {
const appConfig = readJson('app.json')
expect(appConfig.subPackages).toEqual(
expect.arrayContaining([
expect.objectContaining({
root: 'packages/tcm',
pages: expect.arrayContaining([
'pages/ai-history/index',
'pages/assets/index',
'pages/bianzheng/index',
'pages/book-detail/index',
'pages/search-books/index',
'pages/section/index',
'pages/placeholder/index'
])
}),
expect.objectContaining({
root: 'packages/mingli',
pages: expect.arrayContaining([
'pages/hall/index',
'pages/bazi/index',
'pages/book-detail/index',
'pages/search-books/index',
'pages/section/index',
'pages/interpret/index'
])
}),
expect.objectContaining({
root: 'packages/learning',
pages: ['pages/center/index']
})
])
)
})
```
- [ ] **Step 2: Run the targeted tests to verify they fail**
Run: `npm test -- tests/project-config.test.js tests/static-ux-route-map.test.js tests/static-ux-domain-data.test.js`
Expected: FAIL with `Cannot find module '../utils/static-ux/route-map'` and missing `packages/tcm` / `packages/mingli` / `packages/learning` registration assertions.
- [ ] **Step 3: Write the minimal subpackage and static-ux implementation**
```js
// utils/static-ux/route-map.js
const ROUTES = Object.freeze({
tabs: {
home: '/pages/home/index',
library: '/pages/library/index',
ai: '/pages/ai/index',
profile: '/pages/profile/index',
login: '/pages/login/index'
},
tcm: {
aiHistory: '/packages/tcm/pages/ai-history/index',
assets: '/packages/tcm/pages/assets/index',
bianzheng: '/packages/tcm/pages/bianzheng/index',
bookDetail: '/packages/tcm/pages/book-detail/index',
searchBooks: '/packages/tcm/pages/search-books/index',
section: '/packages/tcm/pages/section/index',
placeholder: '/packages/tcm/pages/placeholder/index'
},
mingli: {
hall: '/packages/mingli/pages/hall/index',
bazi: '/packages/mingli/pages/bazi/index',
bookDetail: '/packages/mingli/pages/book-detail/index',
searchBooks: '/packages/mingli/pages/search-books/index',
section: '/packages/mingli/pages/section/index',
interpret: '/packages/mingli/pages/interpret/index'
},
learning: {
center: '/packages/learning/pages/center/index'
}
})
module.exports = {
ROUTES
}
```
```js
// utils/static-ux/shared.js
function cloneItem(item) {
return { ...item }
}
function cloneList(items) {
return items.map(cloneItem)
}
function resolveScene(rawScene, allowedScenes, fallbackScene) {
return allowedScenes.includes(rawScene) ? rawScene : fallbackScene
}
function resolveKind(rawKind, allowedKinds, fallbackKind) {
return allowedKinds.includes(rawKind) ? rawKind : fallbackKind
}
module.exports = {
cloneItem,
cloneList,
resolveScene,
resolveKind
}
```
```js
// utils/static-ux/tcm.js
const { cloneList, resolveKind } = require('./shared')
const { ROUTES } = require('./route-map')
const TCM_HOME_HUB_CARDS = Object.freeze([
{ key: 'tcm-library', title: '中医馆', subtitle: '典籍与目录', route: ROUTES.tabs.library, badge: 'TCM' },
{ key: 'mingli-hall', title: '易学阁', subtitle: '命理与经典', route: ROUTES.mingli.hall, badge: 'NEW' },
{ key: 'bazi', title: '八字排盘', subtitle: '静态排盘结果', route: ROUTES.mingli.bazi, badge: 'BETA' },
{ key: 'learning-center', title: '学习中心', subtitle: '继续学习与回顾', route: ROUTES.learning.center, badge: 'GO' }
])
const TCM_ASSET_SURFACES = Object.freeze({
notes: { title: '学习资产', activeKind: 'notes', kinds: ['notes', 'bookshelf', 'favorites', 'history'] },
bookshelf: { title: '学习资产', activeKind: 'bookshelf', kinds: ['notes', 'bookshelf', 'favorites', 'history'] },
favorites: { title: '学习资产', activeKind: 'favorites', kinds: ['notes', 'bookshelf', 'favorites', 'history'] },
history: { title: '学习资产', activeKind: 'history', kinds: ['notes', 'bookshelf', 'favorites', 'history'] }
})
function createTcmHomeHubCards() {
return cloneList(TCM_HOME_HUB_CARDS)
}
function createTcmAssetPageData(rawKind) {
const activeKind = resolveKind(rawKind, ['notes', 'bookshelf', 'favorites', 'history'], 'notes')
return {
...TCM_ASSET_SURFACES[activeKind],
cards: createTcmHomeHubCards()
}
}
module.exports = {
createTcmHomeHubCards,
createTcmAssetPageData
}
```
```js
// utils/static-ux/mingli.js
const { cloneList, resolveScene } = require('./shared')
const { ROUTES } = require('./route-map')
const MINGLI_QUICK_CARDS = Object.freeze([
{ key: 'hall', title: '易学阁', subtitle: '总览入口', route: ROUTES.mingli.hall, icon: '阁' },
{ key: 'bazi', title: '八字排盘', subtitle: '静态排盘结果', route: ROUTES.mingli.bazi, icon: '盘' },
{ key: 'interpret', title: '命理解读', subtitle: '问题与解读', route: ROUTES.mingli.interpret, icon: '解' }
])
function createMingliHallPageData() {
return {
title: '易学阁',
quickCards: cloneList(MINGLI_QUICK_CARDS)
}
}
function createBaziPageData(rawScene) {
const scene = resolveScene(rawScene, ['default', 'result'], 'default')
return {
title: '八字排盘',
scene,
quickCards: cloneList(MINGLI_QUICK_CARDS)
}
}
module.exports = {
createMingliHallPageData,
createBaziPageData
}
```
```js
// utils/static-ux/learning.js
function createLearningCenterPageData() {
return {
title: '学习中心',
summaryCards: [
{ key: 'qa', value: 8, label: '问答' },
{ key: 'analysis', value: 5, label: '辨证' },
{ key: 'interpret', value: 6, label: '解读' },
{ key: 'bazi', value: 4, label: '排盘' }
]
}
}
module.exports = {
createLearningCenterPageData
}
```
```json
// app.json
{
"subPackages": [
{
"root": "packages/demo",
"pages": ["pages/workbench/index"]
},
{
"root": "packages/tcm",
"pages": [
"pages/ai-history/index",
"pages/assets/index",
"pages/bianzheng/index",
"pages/book-detail/index",
"pages/search-books/index",
"pages/section/index",
"pages/placeholder/index"
]
},
{
"root": "packages/mingli",
"pages": [
"pages/hall/index",
"pages/bazi/index",
"pages/book-detail/index",
"pages/search-books/index",
"pages/section/index",
"pages/interpret/index"
]
},
{
"root": "packages/learning",
"pages": ["pages/center/index"]
}
]
}
```
- [ ] **Step 4: Run the targeted tests to verify they pass**
Run: `npm test -- tests/project-config.test.js tests/static-ux-route-map.test.js tests/static-ux-domain-data.test.js`
Expected: PASS, with the new route map and subpackage assertions green.
- [ ] **Step 5: Commit**
```bash
git add app.json utils/static-ux/route-map.js utils/static-ux/shared.js utils/static-ux/tcm.js utils/static-ux/mingli.js utils/static-ux/learning.js tests/project-config.test.js tests/static-ux-route-map.test.js tests/static-ux-domain-data.test.js
git commit -m "feat: add static page route foundation"
```
### Task 2: Enhance Home Page As The Global Portal
**Files:**
- Modify: `app.wxss`
- Modify: `pages/home/index.js`
- Modify: `pages/home/index.wxml`
- Modify: `pages/home/index.wxss`
- Modify: `tests/home-page.test.js`
- Modify: `tests/home-page-render.test.js`
- [ ] **Step 1: Write the failing home-page portal tests**
```js
// tests/home-page.test.js
test('adds direct portal cards for tcm, mingli, bazi and learning routes', () => {
let capturedPageConfig
global.Page = config => {
capturedPageConfig = config
}
const homePageModule = require('../pages/home/index')
const pageData = homePageModule.createHomePageData()
expect(pageData.portalTitle).toBe('学习入口')
expect(pageData.portalCards).toEqual([
expect.objectContaining({ key: 'tcm-library', title: '中医馆' }),
expect.objectContaining({ key: 'mingli-hall', title: '易学阁' }),
expect.objectContaining({ key: 'bazi', title: '八字排盘' }),
expect.objectContaining({ key: 'learning-center', title: '学习中心' })
])
global.wx = {
navigateTo: jest.fn()
}
capturedPageConfig.handlePortalTap({
currentTarget: {
dataset: {
route: '/packages/mingli/pages/hall/index'
}
}
})
expect(global.wx.navigateTo).toHaveBeenCalledWith({
url: '/packages/mingli/pages/hall/index'
})
})
```
```js
// tests/home-page-render.test.js
test('renders the portal section without forbidden grid or gap styles', () => {
const wxml = fs.readFileSync(path.join(process.cwd(), 'pages/home/index.wxml'), 'utf8')
const wxss = fs.readFileSync(path.join(process.cwd(), 'pages/home/index.wxss'), 'utf8')
expect(wxml).toContain('{{portalTitle}}')
expect(wxml).toContain('portal-grid')
expect(wxml).toContain('bindtap="handlePortalTap"')
expect(wxss).toContain('.portal-grid')
expect(wxss).toContain('.portal-card')
expect(wxss).not.toContain('display: grid')
expect(wxss).not.toContain('gap:')
})
```
- [ ] **Step 2: Run the targeted tests to verify they fail**
Run: `npm test -- tests/home-page.test.js tests/home-page-render.test.js`
Expected: FAIL because `portalTitle`, `portalCards`, `handlePortalTap`, `.portal-grid`, and `.portal-card` do not exist yet.
- [ ] **Step 3: Write the home portal implementation**
```js
// pages/home/index.js
const { createTcmHomeHubCards } = require('../../utils/static-ux/tcm')
function showNavigate(route) {
if (typeof wx?.navigateTo === 'function') {
wx.navigateTo({ url: route })
}
}
function createHomePageData() {
return {
brandName: '玄知中医',
greeting: '晚上好',
subtitle: '今天想学点什么?从典籍、工具或养生主题开始。',
searchPlaceholder: '搜索典籍、术语、AI问答...',
searchBadge: 'AI',
portalTitle: '学习入口',
portalCards: createTcmHomeHubCards(),
encyclopediaTitle: '中医百科',
encyclopediaCards: [
{ key: 'classic', icon: '书', title: '经典书城' },
{ key: 'meridian', icon: '穴', title: '经络穴位', status: '待开放' },
{ key: 'disease', icon: '病', title: '疾病百科', status: '待开放' }
],
toolsTitle: '学习工具',
toolCards: [
{ key: 'qa', icon: '问', title: 'AI问答' },
{ key: 'formula', icon: '方', title: '方剂笔记' },
{ key: 'constitution', icon: '诊', title: '体质诊断' },
{ key: 'wellness', icon: '养', title: '智能饮片' }
],
wellnessTitle: '养生调理',
wellnessCards: [
{ key: 'constitution-check', icon: '🧬', title: 'AI体质检测', status: '待开放' },
{ key: 'medicated-diet', icon: '🍲', title: '药膳', status: '待开放' },
{ key: 'ingredient', icon: '🥬', title: '食材', status: '待开放' }
],
classicsTitle: '热门典籍',
classicsActionText: '进入书城',
classicsBooks: [
{ key: 'huangdi-neijing-suwen', coverText: '黄', title: '黄帝内经素问' },
{ key: 'shang-han-lun', coverText: '伤', title: '伤寒论' },
{ key: 'wen-bing-tiao-bian', coverText: '温', title: '温病条辨' },
{ key: 'bencao-gangmu-bieming-lu', coverText: '本', title: '本草纲目别名录' }
]
}
}
Page({
data: createHomePageData(),
handlePortalTap(event) {
showNavigate(event.currentTarget.dataset.route)
}
})
module.exports = {
createHomePageData
}
```
```xml
{{portalTitle}}
{{item.badge}}
{{item.title}}
{{item.subtitle}}
```
```css
/* pages/home/index.wxss */
.portal-grid {
margin-top: 16rpx;
overflow: hidden;
}
.portal-card {
float: left;
width: 48%;
min-height: 156rpx;
margin-right: 4%;
margin-bottom: 16rpx;
padding: 18rpx;
border-radius: 24rpx;
background: linear-gradient(180deg, rgba(255, 252, 246, 0.98), rgba(245, 236, 221, 0.92));
border: 1rpx solid rgba(118, 83, 42, 0.08);
}
.portal-card:nth-child(2n) {
margin-right: 0;
}
.portal-card__badge {
display: inline-block;
padding: 6rpx 14rpx;
border-radius: 999rpx;
background: rgba(111, 66, 22, 0.08);
color: #8f5c1f;
font-size: 18rpx;
}
.portal-card__title {
display: block;
margin-top: 16rpx;
font-size: 30rpx;
font-weight: 700;
color: #2c2419;
}
.portal-card__subtitle {
display: block;
margin-top: 8rpx;
font-size: 22rpx;
line-height: 1.6;
color: #7b6b57;
}
```
```css
/* app.wxss */
.xz-page--warm {
min-height: 100vh;
background: linear-gradient(180deg, #f8edd6 0%, #f9f0de 24%, #f6ead4 100%);
}
.xz-page--mingli {
min-height: 100vh;
background: linear-gradient(180deg, #fbf6f3 0%, #f4ebe6 100%);
}
.xz-card {
border-radius: 28rpx;
border: 1rpx solid rgba(84, 58, 29, 0.08);
box-shadow: 0 16rpx 36rpx rgba(84, 58, 29, 0.06);
}
```
- [ ] **Step 4: Run the targeted tests to verify they pass**
Run: `npm test -- tests/home-page.test.js tests/home-page-render.test.js`
Expected: PASS, with `portalTitle`, `portalCards`, and `handlePortalTap` verified.
- [ ] **Step 5: Commit**
```bash
git add app.wxss pages/home/index.js pages/home/index.wxml pages/home/index.wxss tests/home-page.test.js tests/home-page-render.test.js
git commit -m "feat: add global portal section to home page"
```
### Task 3: Bridge Library And AI Tabs To New Static Domains
**Files:**
- Modify: `pages/library/index.js`
- Modify: `pages/library/index.wxml`
- Modify: `pages/library/index.wxss`
- Modify: `pages/ai/index.js`
- Modify: `pages/ai/index.wxml`
- Modify: `pages/ai/index.wxss`
- Modify: `tests/library-page.test.js`
- Modify: `tests/library-page-render.test.js`
- Modify: `tests/ai-page.test.js`
- Modify: `tests/ai-page-render.test.js`
- [ ] **Step 1: Write the failing bridge-entry tests**
```js
// tests/library-page.test.js
test('adds a visible mingli-hall bridge entry to the library tab', () => {
let capturedPageConfig
global.Page = config => {
capturedPageConfig = config
}
global.wx = {
navigateTo: jest.fn(),
showToast: jest.fn()
}
const libraryPageModule = require('../pages/library/index')
const pageData = libraryPageModule.createLibraryPageData()
expect(pageData.domainBridge).toEqual(
expect.objectContaining({
title: '进入易学阁',
route: '/packages/mingli/pages/hall/index'
})
)
capturedPageConfig.handleDomainBridgeTap()
expect(global.wx.navigateTo).toHaveBeenCalledWith({
url: '/packages/mingli/pages/hall/index'
})
})
```
```js
// tests/ai-page.test.js
test('adds a visible interpret entry and routes history to the tcm history page', () => {
let capturedPageConfig
global.Page = config => {
capturedPageConfig = config
}
global.wx = {
navigateTo: jest.fn(),
showToast: jest.fn()
}
const aiPageModule = require('../pages/ai/index')
const pageData = aiPageModule.createAiPageData()
expect(pageData.secondaryEntries).toEqual(
expect.arrayContaining([
expect.objectContaining({ key: 'ai-history', title: 'AI历史' }),
expect.objectContaining({ key: 'mingli-interpret', title: '命理解读' })
])
)
capturedPageConfig.handleHistoryTap()
capturedPageConfig.handleSecondaryEntryTap({
currentTarget: {
dataset: {
route: '/packages/mingli/pages/interpret/index'
}
}
})
expect(global.wx.navigateTo).toHaveBeenNthCalledWith(1, {
url: '/packages/tcm/pages/ai-history/index'
})
expect(global.wx.navigateTo).toHaveBeenNthCalledWith(2, {
url: '/packages/mingli/pages/interpret/index'
})
})
```
- [ ] **Step 2: Run the targeted tests to verify they fail**
Run: `npm test -- tests/library-page.test.js tests/library-page-render.test.js tests/ai-page.test.js tests/ai-page-render.test.js`
Expected: FAIL because `domainBridge`, `handleDomainBridgeTap`, `secondaryEntries`, and new navigation handlers are not implemented.
- [ ] **Step 3: Write the bridge-entry implementation**
```js
// pages/library/index.js
const { ROUTES } = require('../../utils/static-ux/route-map')
// 继续复用当前文件里已经存在的 LIBRARY_CATEGORIES / getBooksByCategory / getCategoryById
function createLibraryPageData(categoryId) {
const activeCategory = getCategoryById(categoryId)
return {
title: '典籍',
searchButtonText: '搜索',
domainBridge: {
title: '进入易学阁',
subtitle: '查看命理经典与八字相关静态页面',
route: ROUTES.mingli.hall
},
categories: LIBRARY_CATEGORIES.map(item => ({ ...item })),
activeCategoryId: activeCategory.id,
currentCategoryName: activeCategory.name,
visibleBooks: getBooksByCategory(activeCategory.id)
}
}
Page({
data: createLibraryPageData(),
handleDomainBridgeTap() {
wx.navigateTo({
url: ROUTES.mingli.hall
})
}
})
```
```xml
{{domainBridge.title}}
{{domainBridge.subtitle}}
```
```css
/* pages/library/index.wxss */
.library-page__bridge {
margin-bottom: 18rpx;
padding: 20rpx 24rpx;
border-radius: 24rpx;
background: rgba(255, 250, 242, 0.94);
border: 1rpx solid rgba(118, 83, 42, 0.08);
}
```
```js
// pages/ai/index.js
const { ROUTES } = require('../../utils/static-ux/route-map')
// 继续复用当前文件里已经存在的 AI_PAGE_BLUEPRINT / createPanelByKey
function createAiPageData() {
return {
title: 'AI助手',
historyText: '历史',
disclaimerText: AI_PAGE_BLUEPRINT.disclaimerText,
secondaryEntries: [
{ key: 'ai-history', title: 'AI历史', route: ROUTES.tcm.aiHistory },
{ key: 'mingli-interpret', title: '命理解读', route: ROUTES.mingli.interpret }
],
activeTabKey: 'qa',
tabs: AI_PAGE_BLUEPRINT.tabs.map(item => ({ ...item })),
currentPanel: createPanelByKey('qa')
}
}
Page({
data: createAiPageData(),
handleHistoryTap() {
wx.navigateTo({
url: ROUTES.tcm.aiHistory
})
},
handleSecondaryEntryTap(event) {
wx.navigateTo({
url: event.currentTarget.dataset.route
})
}
})
```
```xml
{{item.title}}
```
- [ ] **Step 4: Run the targeted tests to verify they pass**
Run: `npm test -- tests/library-page.test.js tests/library-page-render.test.js tests/ai-page.test.js tests/ai-page-render.test.js`
Expected: PASS, with the new bridge entries and `wx.navigateTo` calls green.
- [ ] **Step 5: Commit**
```bash
git add pages/library/index.js pages/library/index.wxml pages/library/index.wxss pages/ai/index.js pages/ai/index.wxml pages/ai/index.wxss tests/library-page.test.js tests/library-page-render.test.js tests/ai-page.test.js tests/ai-page-render.test.js
git commit -m "feat: connect library and ai tabs to static domain pages"
```
### Task 4: Replace Profile And Login Placeholders With Static UX Pages
**Files:**
- Modify: `pages/profile/index.js`
- Modify: `pages/profile/index.wxml`
- Modify: `pages/profile/index.wxss`
- Modify: `pages/login/index.js`
- Modify: `pages/login/index.wxml`
- Modify: `pages/login/index.wxss`
- Create: `tests/profile-page.test.js`
- Create: `tests/profile-page-render.test.js`
- Create: `tests/login-page.test.js`
- Create: `tests/login-page-render.test.js`
- [ ] **Step 1: Write the failing profile and login tests**
```js
// tests/profile-page.test.js
describe('profile page', () => {
afterEach(() => {
delete global.Page
delete global.wx
jest.resetModules()
})
test('exposes static profile sections and navigable shortcuts', () => {
let capturedPageConfig
global.Page = config => {
capturedPageConfig = config
}
global.wx = {
navigateTo: jest.fn()
}
const profilePageModule = require('../pages/profile/index')
const pageData = profilePageModule.createProfilePageData()
expect(pageData.userCard.title).toBe('点击登录')
expect(pageData.assetItems).toHaveLength(4)
expect(pageData.moreItems).toEqual(
expect.arrayContaining([
expect.objectContaining({ key: 'learning-center', title: '学习中心' }),
expect.objectContaining({ key: 'ai-history', title: 'AI历史' })
])
)
capturedPageConfig.handleMoreTap({
currentTarget: {
dataset: {
route: '/packages/learning/pages/center/index'
}
}
})
expect(global.wx.navigateTo).toHaveBeenCalledWith({
url: '/packages/learning/pages/center/index'
})
})
})
```
```js
// tests/login-page.test.js
const fs = require('fs')
const path = require('path')
describe('login page', () => {
afterEach(() => {
delete global.Page
jest.resetModules()
})
test('renders a static login surface without session-store wiring', () => {
let capturedPageConfig
global.Page = config => {
capturedPageConfig = config
}
const loginPageModule = require('../pages/login/index')
const pageData = loginPageModule.createLoginPageData()
const source = fs.readFileSync(path.join(process.cwd(), 'pages/login/index.js'), 'utf8')
expect(pageData.title).toBe('欢迎来到玄志')
expect(pageData.actions).toEqual(
expect.arrayContaining([
expect.objectContaining({ key: 'wechat', title: '微信授权登录' }),
expect.objectContaining({ key: 'browse', title: '先看看静态页面' })
])
)
expect(source).not.toContain('sessionStore')
expect(source).not.toContain('handleMockLogin')
})
})
```
- [ ] **Step 2: Run the targeted tests to verify they fail**
Run: `npm test -- tests/profile-page.test.js tests/profile-page-render.test.js tests/login-page.test.js tests/login-page-render.test.js`
Expected: FAIL because `pages/profile/index.js` does not export `createProfilePageData`, `pages/login/index.js` still depends on session logic, and the new render assertions are missing.
- [ ] **Step 3: Write the static profile and login implementation**
```js
// pages/profile/index.js
const { ROUTES } = require('../../utils/static-ux/route-map')
function createProfilePageData() {
return {
userCard: {
avatarText: '?',
title: '点击登录',
subtitle: '登录后查看你的学习资产'
},
assetItems: [
{ key: 'notes', title: '我的笔记', count: 0, route: `${ROUTES.tcm.assets}?kind=notes` },
{ key: 'bookshelf', title: '我的书架', count: 0, route: `${ROUTES.tcm.assets}?kind=bookshelf` },
{ key: 'favorites', title: '我的收藏', count: 0, route: `${ROUTES.tcm.assets}?kind=favorites` },
{ key: 'history', title: '浏览历史', count: 0, route: `${ROUTES.tcm.assets}?kind=history` }
],
moreItems: [
{ key: 'learning-center', title: '学习中心', route: ROUTES.learning.center },
{ key: 'ai-history', title: 'AI历史', route: ROUTES.tcm.aiHistory },
{ key: 'placeholder-about', title: '关于我们', route: `${ROUTES.tcm.placeholder}?kind=about` },
{ key: 'placeholder-settings', title: '设置', route: `${ROUTES.tcm.placeholder}?kind=settings` }
]
}
}
Page({
data: createProfilePageData(),
handleAssetTap(event) {
wx.navigateTo({ url: event.currentTarget.dataset.route })
},
handleMoreTap(event) {
wx.navigateTo({ url: event.currentTarget.dataset.route })
}
})
module.exports = {
createProfilePageData
}
```
```js
// pages/login/index.js
const { ROUTES } = require('../../utils/static-ux/route-map')
function createLoginPageData() {
return {
title: '欢迎来到玄志',
subtitle: '当前阶段先提供静态登录页,后续再接入新的认证方案。',
actions: [
{ key: 'wechat', title: '微信授权登录', type: 'toast' },
{ key: 'browse', title: '先看看静态页面', type: 'route', route: ROUTES.tabs.home }
]
}
}
function showComingSoon() {
if (typeof wx?.showToast === 'function') {
wx.showToast({
title: '登录功能建设中',
icon: 'none'
})
}
}
Page({
data: createLoginPageData(),
handleActionTap(event) {
const { actionType, route } = event.currentTarget.dataset
if (actionType === 'route') {
wx.switchTab({ url: route })
return
}
showComingSoon()
}
})
module.exports = {
createLoginPageData
}
```
- [ ] **Step 4: Run the targeted tests to verify they pass**
Run: `npm test -- tests/profile-page.test.js tests/profile-page-render.test.js tests/login-page.test.js tests/login-page-render.test.js`
Expected: PASS, with `profile` shortcut routes and `login` static action data confirmed.
- [ ] **Step 5: Commit**
```bash
git add pages/profile/index.js pages/profile/index.wxml pages/profile/index.wxss pages/login/index.js pages/login/index.wxml pages/login/index.wxss tests/profile-page.test.js tests/profile-page-render.test.js tests/login-page.test.js tests/login-page-render.test.js
git commit -m "feat: add static profile and login pages"
```
### Task 5: Build TCM Support Pages
**Files:**
- Create: `packages/tcm/pages/ai-history/index.js`
- Create: `packages/tcm/pages/ai-history/index.json`
- Create: `packages/tcm/pages/ai-history/index.wxml`
- Create: `packages/tcm/pages/ai-history/index.wxss`
- Create: `packages/tcm/pages/assets/index.js`
- Create: `packages/tcm/pages/assets/index.json`
- Create: `packages/tcm/pages/assets/index.wxml`
- Create: `packages/tcm/pages/assets/index.wxss`
- Create: `packages/tcm/pages/bianzheng/index.js`
- Create: `packages/tcm/pages/bianzheng/index.json`
- Create: `packages/tcm/pages/bianzheng/index.wxml`
- Create: `packages/tcm/pages/bianzheng/index.wxss`
- Create: `packages/tcm/pages/placeholder/index.js`
- Create: `packages/tcm/pages/placeholder/index.json`
- Create: `packages/tcm/pages/placeholder/index.wxml`
- Create: `packages/tcm/pages/placeholder/index.wxss`
- Create: `tests/tcm-support-pages.test.js`
- [ ] **Step 1: Write the failing TCM support-page tests**
```js
// tests/tcm-support-pages.test.js
describe('tcm support pages', () => {
afterEach(() => {
delete global.Page
delete global.wx
jest.resetModules()
})
test('assets page switches by static kind and keeps route-only navigation', () => {
let capturedPageConfig
global.Page = config => {
capturedPageConfig = config
}
const assetsPageModule = require('../packages/tcm/pages/assets/index')
const pageData = assetsPageModule.createTcmAssetsPageData('favorites')
expect(pageData.activeKind).toBe('favorites')
expect(pageData.title).toBe('学习资产')
expect(pageData.filterItems).toHaveLength(4)
})
test('ai history, bianzheng and placeholder pages expose scene-driven static data', () => {
global.Page = () => {}
const aiHistoryModule = require('../packages/tcm/pages/ai-history/index')
const bianzhengModule = require('../packages/tcm/pages/bianzheng/index')
const placeholderModule = require('../packages/tcm/pages/placeholder/index')
expect(aiHistoryModule.createTcmAiHistoryPageData('empty')).toEqual(
expect.objectContaining({ scene: 'empty', title: 'AI对话历史' })
)
expect(bianzhengModule.createTcmBianzhengPageData('result')).toEqual(
expect.objectContaining({ scene: 'result', title: '辨证分析' })
)
expect(placeholderModule.createTcmPlaceholderPageData('membership')).toEqual(
expect.objectContaining({ kind: 'membership' })
)
})
})
```
- [ ] **Step 2: Run the targeted tests to verify they fail**
Run: `npm test -- tests/tcm-support-pages.test.js`
Expected: FAIL because the `packages/tcm/pages/*` modules and `create*PageData` exports do not exist yet.
- [ ] **Step 3: Write the TCM support pages**
```js
// packages/tcm/pages/assets/index.js
const { createTcmAssetPageData } = require('../../../../utils/static-ux/tcm')
const { resolveKind } = require('../../../../utils/static-ux/shared')
function createTcmAssetsPageData(rawKind) {
const data = createTcmAssetPageData(rawKind)
return {
...data,
filterItems: data.kinds.map(kind => ({
key: kind,
label: kind === 'notes' ? '笔记' : kind === 'bookshelf' ? '书架' : kind === 'favorites' ? '收藏' : '历史',
route: `/packages/tcm/pages/assets/index?kind=${kind}`
}))
}
}
Page({
data: createTcmAssetsPageData('notes'),
onLoad(options) {
this.setData(createTcmAssetsPageData(resolveKind(options.kind, ['notes', 'bookshelf', 'favorites', 'history'], 'notes')))
}
})
module.exports = {
createTcmAssetsPageData
}
```
```js
// packages/tcm/pages/ai-history/index.js
const { resolveScene } = require('../../../../utils/static-ux/shared')
function createTcmAiHistoryPageData(rawScene) {
const scene = resolveScene(rawScene, ['default', 'empty'], 'default')
return {
title: 'AI对话历史',
scene,
groups:
scene === 'empty'
? []
: [
{ key: 'today', label: '今天', items: [{ key: 'qa-1', title: '什么是阴阳?', summary: '从阴阳对待看基础结构。' }] }
]
}
}
Page({
data: createTcmAiHistoryPageData('default'),
onLoad(options) {
this.setData(createTcmAiHistoryPageData(options.scene))
}
})
module.exports = {
createTcmAiHistoryPageData
}
```
```js
// packages/tcm/pages/bianzheng/index.js
const { resolveScene } = require('../../../../utils/static-ux/shared')
function createTcmBianzhengPageData(rawScene) {
const scene = resolveScene(rawScene, ['default', 'result'], 'default')
return {
title: '辨证分析',
scene,
form: {
mainSymptoms: '',
tongue: '',
pulse: '',
constitution: '',
duration: ''
},
result:
scene === 'result'
? {
title: '学习型辨证结果',
summary: '以脾虚湿困作为静态展示结果,后续接真实逻辑时再替换。'
}
: null
}
}
Page({
data: createTcmBianzhengPageData('default'),
onLoad(options) {
this.setData(createTcmBianzhengPageData(options.scene))
},
handleSubmitTap() {
this.setData(createTcmBianzhengPageData('result'))
}
})
module.exports = {
createTcmBianzhengPageData
}
```
```js
// packages/tcm/pages/placeholder/index.js
function createTcmPlaceholderPageData(kind) {
const activeKind = ['membership', 'feedback', 'share', 'about', 'settings'].includes(kind)
? kind
: 'about'
return {
title: activeKind === 'membership' ? '开通会员' : activeKind === 'feedback' ? '意见反馈' : activeKind === 'share' ? '分享 APP' : activeKind === 'settings' ? '设置' : '关于我们',
kind: activeKind,
tips: [
'本页当前只承接静态展示。',
'后续接入真实逻辑时保持当前页面信息层级。',
'不要把旧项目流程直接迁移过来。'
]
}
}
Page({
data: createTcmPlaceholderPageData('about'),
onLoad(options) {
this.setData(createTcmPlaceholderPageData(options.kind))
}
})
module.exports = {
createTcmPlaceholderPageData
}
```
- [ ] **Step 4: Run the targeted tests to verify they pass**
Run: `npm test -- tests/tcm-support-pages.test.js`
Expected: PASS, with `scene` / `kind`-driven static data verified and no service layer required.
- [ ] **Step 5: Commit**
```bash
git add packages/tcm/pages/ai-history packages/tcm/pages/assets packages/tcm/pages/bianzheng packages/tcm/pages/placeholder tests/tcm-support-pages.test.js
git commit -m "feat: add tcm support pages"
```
### Task 6: Build TCM Reading, Search And Detail Pages
**Files:**
- Create: `packages/tcm/pages/book-detail/index.js`
- Create: `packages/tcm/pages/book-detail/index.json`
- Create: `packages/tcm/pages/book-detail/index.wxml`
- Create: `packages/tcm/pages/book-detail/index.wxss`
- Create: `packages/tcm/pages/search-books/index.js`
- Create: `packages/tcm/pages/search-books/index.json`
- Create: `packages/tcm/pages/search-books/index.wxml`
- Create: `packages/tcm/pages/search-books/index.wxss`
- Create: `packages/tcm/pages/section/index.js`
- Create: `packages/tcm/pages/section/index.json`
- Create: `packages/tcm/pages/section/index.wxml`
- Create: `packages/tcm/pages/section/index.wxss`
- Create: `tests/tcm-reading-pages.test.js`
- [ ] **Step 1: Write the failing TCM reading-page tests**
```js
// tests/tcm-reading-pages.test.js
describe('tcm reading pages', () => {
afterEach(() => {
delete global.Page
delete global.wx
jest.resetModules()
})
test('book detail, search and section pages use static scenes instead of business ids', () => {
global.Page = () => {}
const bookDetailModule = require('../packages/tcm/pages/book-detail/index')
const searchModule = require('../packages/tcm/pages/search-books/index')
const sectionModule = require('../packages/tcm/pages/section/index')
expect(bookDetailModule.createTcmBookDetailPageData('classic-a')).toEqual(
expect.objectContaining({ scene: 'classic-a', title: '典籍详情' })
)
expect(searchModule.createTcmSearchBooksPageData('keyword-demo')).toEqual(
expect.objectContaining({ keyword: 'keyword-demo', title: '搜索典籍' })
)
expect(sectionModule.createTcmSectionPageData('reader-a')).toEqual(
expect.objectContaining({ scene: 'reader-a', title: '典籍阅读' })
)
})
})
```
- [ ] **Step 2: Run the targeted tests to verify they fail**
Run: `npm test -- tests/tcm-reading-pages.test.js`
Expected: FAIL because the three reading-page modules do not exist yet.
- [ ] **Step 3: Write the TCM reading-page implementation**
```js
// packages/tcm/pages/book-detail/index.js
const { resolveScene } = require('../../../../utils/static-ux/shared')
function createTcmBookDetailPageData(rawScene) {
const scene = resolveScene(rawScene, ['classic-a', 'classic-b'], 'classic-a')
return {
title: '典籍详情',
scene,
book: {
title: scene === 'classic-a' ? '黄帝内经素问' : '伤寒论',
subtitle: '传世中医典籍 · 学习阅读入口'
},
catalog: [
{ key: 'chapter-1', title: '上古天真论', children: ['节录一', '节录二'] },
{ key: 'chapter-2', title: '四气调神大论', children: ['节录一', '节录二'] }
]
}
}
Page({
data: createTcmBookDetailPageData('classic-a'),
onLoad(options) {
this.setData(createTcmBookDetailPageData(options.scene))
}
})
module.exports = {
createTcmBookDetailPageData
}
```
```js
// packages/tcm/pages/search-books/index.js
function createTcmSearchBooksPageData(keyword) {
return {
title: '搜索典籍',
keyword: keyword || '',
history: ['黄帝内经', '伤寒论', '温病条辨'],
results:
keyword && keyword.trim()
? [
{ key: 'result-1', title: '黄帝内经素问', subtitle: '书名命中' },
{ key: 'result-2', title: '黄帝内经灵枢', subtitle: '相关结果' }
]
: []
}
}
Page({
data: createTcmSearchBooksPageData(''),
onLoad(options) {
this.setData(createTcmSearchBooksPageData(options.keyword || options.q))
}
})
module.exports = {
createTcmSearchBooksPageData
}
```
```js
// packages/tcm/pages/section/index.js
const { resolveScene } = require('../../../../utils/static-ux/shared')
function createTcmSectionPageData(rawScene) {
const scene = resolveScene(rawScene, ['reader-a', 'reader-b'], 'reader-a')
return {
title: '典籍阅读',
scene,
toolbarVisible: false,
settings: {
fontScale: 1,
lineMode: 'normal',
bgMode: 'default'
},
passages:
scene === 'reader-a'
? ['上古之人,其知道者,法于阴阳,和于术数。', '食饮有节,起居有常,不妄作劳。']
: ['伤寒者,太阳病也。', '太阳之为病,脉浮,头项强痛而恶寒。']
}
}
Page({
data: createTcmSectionPageData('reader-a'),
onLoad(options) {
this.setData(createTcmSectionPageData(options.scene))
},
handleToolbarToggle() {
this.setData({
toolbarVisible: !this.data.toolbarVisible
})
}
})
module.exports = {
createTcmSectionPageData
}
```
- [ ] **Step 4: Run the targeted tests to verify they pass**
Run: `npm test -- tests/tcm-reading-pages.test.js`
Expected: PASS, with `scene`-based page data and reader toggles confirmed.
- [ ] **Step 5: Commit**
```bash
git add packages/tcm/pages/book-detail packages/tcm/pages/search-books packages/tcm/pages/section tests/tcm-reading-pages.test.js
git commit -m "feat: add tcm reading and search pages"
```
### Task 7: Build Mingli Domain And Learning Center Pages
**Files:**
- Create: `packages/mingli/pages/hall/index.js`
- Create: `packages/mingli/pages/hall/index.json`
- Create: `packages/mingli/pages/hall/index.wxml`
- Create: `packages/mingli/pages/hall/index.wxss`
- Create: `packages/mingli/pages/bazi/index.js`
- Create: `packages/mingli/pages/bazi/index.json`
- Create: `packages/mingli/pages/bazi/index.wxml`
- Create: `packages/mingli/pages/bazi/index.wxss`
- Create: `packages/mingli/pages/book-detail/index.js`
- Create: `packages/mingli/pages/book-detail/index.json`
- Create: `packages/mingli/pages/book-detail/index.wxml`
- Create: `packages/mingli/pages/book-detail/index.wxss`
- Create: `packages/mingli/pages/search-books/index.js`
- Create: `packages/mingli/pages/search-books/index.json`
- Create: `packages/mingli/pages/search-books/index.wxml`
- Create: `packages/mingli/pages/search-books/index.wxss`
- Create: `packages/mingli/pages/section/index.js`
- Create: `packages/mingli/pages/section/index.json`
- Create: `packages/mingli/pages/section/index.wxml`
- Create: `packages/mingli/pages/section/index.wxss`
- Create: `packages/mingli/pages/interpret/index.js`
- Create: `packages/mingli/pages/interpret/index.json`
- Create: `packages/mingli/pages/interpret/index.wxml`
- Create: `packages/mingli/pages/interpret/index.wxss`
- Create: `packages/learning/pages/center/index.js`
- Create: `packages/learning/pages/center/index.json`
- Create: `packages/learning/pages/center/index.wxml`
- Create: `packages/learning/pages/center/index.wxss`
- Create: `tests/mingli-learning-pages.test.js`
- [ ] **Step 1: Write the failing mingli and learning tests**
```js
// tests/mingli-learning-pages.test.js
describe('mingli and learning pages', () => {
afterEach(() => {
delete global.Page
delete global.wx
jest.resetModules()
})
test('hall, bazi, interpret and learning center pages expose static route-first surfaces', () => {
global.Page = () => {}
const hallModule = require('../packages/mingli/pages/hall/index')
const baziModule = require('../packages/mingli/pages/bazi/index')
const interpretModule = require('../packages/mingli/pages/interpret/index')
const learningModule = require('../packages/learning/pages/center/index')
expect(hallModule.createMingliHallPageData()).toEqual(
expect.objectContaining({ title: '易学阁' })
)
expect(baziModule.createMingliBaziPageData('result')).toEqual(
expect.objectContaining({ scene: 'result', title: '八字排盘' })
)
expect(interpretModule.createMingliInterpretPageData('result')).toEqual(
expect.objectContaining({ scene: 'result', title: '命理解读' })
)
expect(learningModule.createLearningCenterPageData()).toEqual(
expect.objectContaining({ title: '学习中心' })
)
})
})
```
- [ ] **Step 2: Run the targeted tests to verify they fail**
Run: `npm test -- tests/mingli-learning-pages.test.js`
Expected: FAIL because the mingli and learning page modules do not exist yet.
- [ ] **Step 3: Write the mingli and learning implementation**
```js
// packages/mingli/pages/hall/index.js
const { createMingliHallPageData } = require('../../../../utils/static-ux/mingli')
Page({
data: createMingliHallPageData()
})
module.exports = {
createMingliHallPageData
}
```
```js
// packages/mingli/pages/bazi/index.js
const { resolveScene } = require('../../../../utils/static-ux/shared')
function createMingliBaziPageData(rawScene) {
const scene = resolveScene(rawScene, ['default', 'result'], 'default')
return {
title: '八字排盘',
scene,
form: {
name: '张三',
gender: '男',
birthDate: '1990-01-01',
birthTime: '08:30'
},
result:
scene === 'result'
? {
pillars: [
{ key: 'year', label: '年柱', value: '庚午' },
{ key: 'month', label: '月柱', value: '戊寅' },
{ key: 'day', label: '日柱', value: '甲辰' },
{ key: 'time', label: '时柱', value: '丁卯' }
]
}
: null
}
}
Page({
data: createMingliBaziPageData('default'),
onLoad(options) {
this.setData(createMingliBaziPageData(options.scene))
},
handleSubmitTap() {
this.setData(createMingliBaziPageData('result'))
}
})
module.exports = {
createMingliBaziPageData
}
```
```js
// packages/mingli/pages/interpret/index.js
const { resolveScene } = require('../../../../utils/static-ux/shared')
function createMingliInterpretPageData(rawScene) {
const scene = resolveScene(rawScene, ['default', 'result'], 'default')
return {
title: '命理解读',
scene,
question: '',
result:
scene === 'result'
? {
title: '学习型解读',
summary: '围绕日主与经典段落给出静态说明。'
}
: null
}
}
Page({
data: createMingliInterpretPageData('default'),
onLoad(options) {
this.setData(createMingliInterpretPageData(options.scene))
},
handleSubmitTap() {
this.setData(createMingliInterpretPageData('result'))
}
})
module.exports = {
createMingliInterpretPageData
}
```
```js
// packages/learning/pages/center/index.js
const { createLearningCenterPageData } = require('../../../../utils/static-ux/learning')
Page({
data: createLearningCenterPageData()
})
module.exports = {
createLearningCenterPageData
}
```
- [ ] **Step 4: Run the targeted tests to verify they pass**
Run: `npm test -- tests/mingli-learning-pages.test.js`
Expected: PASS, with the mingli and learning pages exposed as static route-first surfaces.
- [ ] **Step 5: Commit**
```bash
git add packages/mingli packages/learning tests/mingli-learning-pages.test.js
git commit -m "feat: add mingli and learning static pages"
```
### Task 8: Full Verification And Integration Sweep
**Files:**
- Modify: any files required to fix failing tests or lint issues from prior tasks
- Test: all touched files under `pages/`, `packages/`, `utils/static-ux/`, `tests/`, `app.json`, `app.wxss`
- [ ] **Step 1: Run the focused render and data suites**
Run: `npm test -- tests/home-page.test.js tests/home-page-render.test.js tests/library-page.test.js tests/library-page-render.test.js tests/ai-page.test.js tests/ai-page-render.test.js tests/profile-page.test.js tests/profile-page-render.test.js tests/login-page.test.js tests/login-page-render.test.js tests/tcm-support-pages.test.js tests/tcm-reading-pages.test.js tests/mingli-learning-pages.test.js tests/static-ux-route-map.test.js tests/static-ux-domain-data.test.js tests/project-config.test.js`
Expected: PASS with all migration-related tests green.
- [ ] **Step 2: Run the full repository test suite**
Run: `npm test`
Expected: PASS with zero failing Jest suites.
- [ ] **Step 3: Run the repository linter**
Run: `npm run lint`
Expected: exit code `0` and no lint errors.
- [ ] **Step 4: Perform the route and manual UX checklist**
```text
1. 打开首页,确认能看到并点击进入:中医馆、易学阁、八字排盘、学习中心。
2. 打开典籍 tab,确认能进入中医搜索页和易学阁入口。
3. 打开 AI tab,确认历史按钮进入中医 AI 历史页,命理解读入口可达。
4. 打开我的 tab,确认资产、AI 历史、学习中心、占位说明页都可达。
5. 打开 tcm 和 mingli 页,确认没有真实登录、接口、store、mock session 逻辑。
6. 检查所有页面参数都只使用 scene / mode / kind / keyword 这类静态参数。
```
- [ ] **Step 5: Create the final feature commit**
```bash
git add app.json app.wxss pages/home pages/library pages/ai pages/profile pages/login packages/tcm packages/mingli packages/learning utils/static-ux tests/static-ux-route-map.test.js tests/static-ux-domain-data.test.js tests/home-page.test.js tests/home-page-render.test.js tests/library-page.test.js tests/library-page-render.test.js tests/ai-page.test.js tests/ai-page-render.test.js tests/profile-page.test.js tests/profile-page-render.test.js tests/login-page.test.js tests/login-page-render.test.js tests/tcm-support-pages.test.js tests/tcm-reading-pages.test.js tests/mingli-learning-pages.test.js tests/project-config.test.js
git commit -m "feat: migrate frontend static pages into miniprogram"
```