1745 lines
53 KiB
Markdown
1745 lines
53 KiB
Markdown
# 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"
|
||
```
|