From f3cd0c3a98670274d45cb6f78f09a1c50b6f9172 Mon Sep 17 00:00:00 2001 From: wdh-home <243823965@qq.com> Date: Thu, 23 Apr 2026 11:49:41 +0800 Subject: [PATCH] docs: add migration implementation plan --- ...atic-page-migration-implementation-plan.md | 1744 +++++++++++++++++ .gitignore | 1 + AGENTS.md | 3 +- 3 files changed, 1747 insertions(+), 1 deletion(-) create mode 100644 .ai-specs/coding-specs/2026-04-23-frontend-static-page-migration-implementation-plan.md 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 字典参考