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