Files
xuanzhi-wx/.ai-specs/coding-specs/2026-04-23-frontend-static-page-migration-implementation-plan.md

1745 lines
53 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
<!-- pages/home/index.wxml -->
<view class="section-card">
<text class="section-card__title">{{portalTitle}}</text>
<view class="portal-grid">
<view
class="portal-card"
wx:for="{{portalCards}}"
wx:key="key"
data-route="{{item.route}}"
bindtap="handlePortalTap"
>
<text class="portal-card__badge">{{item.badge}}</text>
<text class="portal-card__title">{{item.title}}</text>
<text class="portal-card__subtitle">{{item.subtitle}}</text>
</view>
</view>
</view>
```
```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
<!-- pages/library/index.wxml -->
<view class="library-page__bridge" bindtap="handleDomainBridgeTap">
<text class="library-page__bridge-title">{{domainBridge.title}}</text>
<text class="library-page__bridge-subtitle">{{domainBridge.subtitle}}</text>
</view>
```
```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
<!-- pages/ai/index.wxml -->
<view class="ai-page__secondary-list">
<view
class="ai-page__secondary-item"
wx:for="{{secondaryEntries}}"
wx:key="key"
data-route="{{item.route}}"
bindtap="handleSecondaryEntryTap"
>
<text class="ai-page__secondary-title">{{item.title}}</text>
</view>
</view>
```
- [ ] **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"
```