diff --git a/app.js b/app.js index 555e979..e3523cd 100644 --- a/app.js +++ b/app.js @@ -1,6 +1,6 @@ const { getRuntimeConfig } = require('./config/env') -const { setUnauthorizedHandler } = require('./services/request') -const { sessionStore } = require('./stores') +const { setUnauthorizedHandler } = require('./services/request/index') +const { sessionStore } = require('./stores/index') App({ globalData: { diff --git a/app.json b/app.json index 83cdbc6..2f5ef5d 100644 --- a/app.json +++ b/app.json @@ -1,6 +1,9 @@ { "pages": [ "pages/home/index", + "pages/library/index", + "pages/ai/index", + "pages/profile/index", "pages/login/index" ], "subPackages": [ @@ -9,6 +12,35 @@ "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" + ] } ], "preloadRule": { @@ -25,6 +57,38 @@ "navigationBarBackgroundColor": "#f5f7fa", "backgroundColor": "#f5f7fa" }, + "tabBar": { + "color": "#9f7c56", + "selectedColor": "#8f5c1f", + "backgroundColor": "#fff8ee", + "borderStyle": "white", + "list": [ + { + "pagePath": "pages/home/index", + "text": "首页", + "iconPath": "pages/home/tab-home.png", + "selectedIconPath": "pages/home/tab-home-active.png" + }, + { + "pagePath": "pages/library/index", + "text": "典籍", + "iconPath": "pages/library/tab-library.png", + "selectedIconPath": "pages/library/tab-library-active.png" + }, + { + "pagePath": "pages/ai/index", + "text": "AI", + "iconPath": "pages/ai/tab-ai.png", + "selectedIconPath": "pages/ai/tab-ai-active.png" + }, + { + "pagePath": "pages/profile/index", + "text": "我的", + "iconPath": "pages/profile/tab-profile.png", + "selectedIconPath": "pages/profile/tab-profile-active.png" + } + ] + }, "networkTimeout": { "request": 10000 }, diff --git a/app.wxss b/app.wxss index 03299cd..defb07c 100644 --- a/app.wxss +++ b/app.wxss @@ -1,4 +1,4 @@ -@import 'tdesign-miniprogram/common/style/index.wxss'; +@import './miniprogram_npm/tdesign-miniprogram/common/style/index.wxss'; page { min-height: 100%; @@ -45,3 +45,19 @@ text { justify-content: space-between; gap: 24rpx; } + +.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); +} diff --git a/packages/demo/pages/workbench/index.js b/packages/demo/pages/workbench/index.js index 6f418cf..44aed68 100644 --- a/packages/demo/pages/workbench/index.js +++ b/packages/demo/pages/workbench/index.js @@ -1,4 +1,5 @@ -const { sessionStore } = require('../../../../stores') +const { sessionStore } = require('../../../../stores/index') +const { openStaticRoute } = require('../../../../utils/static-ux/route-map') const MODULES = [ { @@ -41,8 +42,6 @@ Page({ return } - wx.navigateTo({ - url: path - }) + openStaticRoute(path, wx) } }) diff --git a/packages/learning/pages/center/index.js b/packages/learning/pages/center/index.js new file mode 100644 index 0000000..6f66f37 --- /dev/null +++ b/packages/learning/pages/center/index.js @@ -0,0 +1,74 @@ +const { + createLearningCenterPageData: createBaseLearningCenterPageData +} = require('../../../../utils/static-ux/learning') +const { ROUTES, openStaticRoute } = require('../../../../utils/static-ux/route-map') + +function createLearningCenterPageData() { + const baseData = createBaseLearningCenterPageData() + + return { + ...baseData, + heroTitle: '先继续上次学习,再回看静态记录', + heroDescription: '学习中心不再承接旧后台列表,而是把最值得继续的学习动作放到最上面。', + quickDeck: [ + { + key: 'qa-history', + title: 'AI历史', + description: '继续查看中医问答与辨证分析的静态记录页', + route: ROUTES.tcm.aiHistory, + domain: 'tcm' + }, + { + key: 'assets', + title: '学习资产', + description: '回到笔记、书架、收藏和历史四类资产入口', + route: `${ROUTES.tcm.assets}?kind=notes`, + domain: 'tcm' + }, + { + key: 'interpret', + title: '命理解读', + description: '进入易学侧的静态问题解读页', + route: `${ROUTES.mingli.interpret}?scene=result`, + domain: 'mingli' + }, + { + key: 'bazi', + title: '八字排盘', + description: '继续查看静态排盘结果与四柱结构', + route: `${ROUTES.mingli.bazi}?scene=result`, + domain: 'mingli' + } + ], + recentItems: [ + { + key: 'recent-reading', + title: '典籍阅读', + description: '回到《黄帝内经素问》的静态阅读页。', + route: `${ROUTES.tcm.section}?scene=reader-a` + }, + { + key: 'recent-yixue', + title: '易学阅读', + description: '继续浏览《滴天髓》的静态阅读页。', + route: `${ROUTES.mingli.section}?scene=reader-a` + } + ] + } +} + +function showNavigate(route) { + openStaticRoute(route, wx) +} + +Page({ + data: createLearningCenterPageData(), + + handleRouteTap(event) { + showNavigate(event.currentTarget.dataset.route) + } +}) + +module.exports = { + createLearningCenterPageData +} diff --git a/packages/learning/pages/center/index.json b/packages/learning/pages/center/index.json new file mode 100644 index 0000000..ce57338 --- /dev/null +++ b/packages/learning/pages/center/index.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "学习中心", + "navigationBarBackgroundColor": "#f7f0e6", + "navigationBarTextStyle": "black" +} diff --git a/packages/learning/pages/center/index.wxml b/packages/learning/pages/center/index.wxml new file mode 100644 index 0000000..f2457a1 --- /dev/null +++ b/packages/learning/pages/center/index.wxml @@ -0,0 +1,44 @@ + + + {{heroTitle}} + {{heroDescription}} + + + {{item.value}} + {{item.label}} + + + + + + + {{item.domain === 'tcm' ? '中医继续学习' : '易学继续学习'}} + {{item.title}} + {{item.description}} + + + + + 最近学习动作 + + + + {{item.title}} + {{item.description}} + + > + + + diff --git a/packages/learning/pages/center/index.wxss b/packages/learning/pages/center/index.wxss new file mode 100644 index 0000000..bd0ff83 --- /dev/null +++ b/packages/learning/pages/center/index.wxss @@ -0,0 +1,150 @@ +page { + min-height: 100%; + background: linear-gradient(180deg, #f7f2ea 0%, #efe7dc 100%); +} + +.learning-page { + box-sizing: border-box; + min-height: 100vh; + padding: 28rpx 20rpx 72rpx; +} + +.hero-card, +.section-card, +.quick-card { + border: 1rpx solid rgba(84, 58, 29, 0.08); + border-radius: 28rpx; + background: rgba(255, 251, 244, 0.92); + box-shadow: 0 10rpx 26rpx rgba(78, 50, 22, 0.06); +} + +.hero-card { + padding: 24rpx; +} + +.hero-card__title, +.section-card__title, +.quick-card__title, +.timeline-item__title { + display: block; + color: #2f261d; + font-family: 'STSong', 'Songti SC', serif; + font-size: 34rpx; + font-weight: 700; + line-height: 1.3; +} + +.hero-card__desc, +.quick-card__desc, +.timeline-item__desc { + display: block; + margin-top: 10rpx; + color: #7b6d60; + font-size: 24rpx; + line-height: 1.6; +} + +.stats-row { + display: flex; + flex-wrap: wrap; + margin: 14rpx -5rpx 0; +} + +.stat-pill { + box-sizing: border-box; + width: 25%; + padding: 5rpx; +} + +.stat-pill__value, +.stat-pill__label { + display: block; + text-align: center; +} + +.stat-pill__value { + padding: 12rpx 0 6rpx; + border-radius: 18rpx 18rpx 0 0; + background: rgba(255, 250, 242, 0.8); + color: #2f261d; + font-size: 28rpx; + font-weight: 700; +} + +.stat-pill__label { + padding: 0 0 12rpx; + border-radius: 0 0 18rpx 18rpx; + background: rgba(255, 250, 242, 0.8); + color: #7b6d60; + font-size: 22rpx; +} + +.quick-grid { + display: flex; + flex-wrap: wrap; + margin: 12rpx -6rpx 0; +} + +.quick-card { + box-sizing: border-box; + width: 50%; + margin-top: 12rpx; + padding: 24rpx 18rpx; + margin-left: 6rpx; + margin-right: 6rpx; +} + +.quick-card__tag { + display: inline-block; + padding: 8rpx 12rpx; + border-radius: 999rpx; + background: rgba(84, 58, 29, 0.08); + color: #6d4d2f; + font-size: 18rpx; + line-height: 1; +} + +.quick-card__title { + margin-top: 14rpx; +} + +.section-card { + margin-top: 18rpx; + padding: 24rpx; +} + +.timeline-item { + display: flex; + align-items: center; + margin-top: 16rpx; + padding-top: 16rpx; + border-top: 1rpx solid rgba(84, 58, 29, 0.08); +} + +.timeline-item:first-of-type { + margin-top: 10rpx; + padding-top: 0; + border-top: 0; +} + +.timeline-item__icon { + width: 72rpx; + height: 72rpx; + border-radius: 18rpx; + background: rgba(84, 58, 29, 0.08); + color: #6d4d2f; + font-size: 26rpx; + line-height: 72rpx; + text-align: center; +} + +.timeline-item__body { + flex: 1; + margin-left: 14rpx; +} + +.timeline-item__arrow { + color: #b4946a; + font-size: 28rpx; + line-height: 1; +} diff --git a/packages/mingli/pages/bazi/index.js b/packages/mingli/pages/bazi/index.js new file mode 100644 index 0000000..ab813ad --- /dev/null +++ b/packages/mingli/pages/bazi/index.js @@ -0,0 +1,52 @@ +const { resolveScene } = require('../../../../utils/static-ux/shared') +const { ROUTES, openStaticRoute } = require('../../../../utils/static-ux/route-map') + +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' + ? { + headline: '学习型排盘结果', + subline: '以静态四柱结果承接旧页面的结果层级,不迁移旧排盘引擎。', + pillars: [ + { key: 'year', label: '年柱', value: '庚午' }, + { key: 'month', label: '月柱', value: '戊寅' }, + { key: 'day', label: '日柱', value: '甲辰' }, + { key: 'time', label: '时柱', value: '丁卯' } + ] + } + : null, + primaryActionText: scene === 'result' ? '重新排盘' : '开始排盘', + secondaryActionText: '命理解读' + } +} + +Page({ + data: createMingliBaziPageData('default'), + + onLoad(options) { + this.setData(createMingliBaziPageData(options.scene)) + }, + + handlePrimaryTap() { + this.setData(createMingliBaziPageData(this.data.scene === 'result' ? 'default' : 'result')) + }, + + handleSecondaryTap() { + openStaticRoute(`${ROUTES.mingli.interpret}?scene=result`, wx) + } +}) + +module.exports = { + createMingliBaziPageData +} diff --git a/packages/mingli/pages/bazi/index.json b/packages/mingli/pages/bazi/index.json new file mode 100644 index 0000000..4d7c784 --- /dev/null +++ b/packages/mingli/pages/bazi/index.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "八字排盘", + "navigationBarBackgroundColor": "#f8f0ee", + "navigationBarTextStyle": "black" +} diff --git a/packages/mingli/pages/bazi/index.wxml b/packages/mingli/pages/bazi/index.wxml new file mode 100644 index 0000000..ac54c2f --- /dev/null +++ b/packages/mingli/pages/bazi/index.wxml @@ -0,0 +1,63 @@ + + + 命理练习台 + {{title}} + + 排盘页当前只保留输入层级、四柱结果和跳转关系,并把阅读节奏整体放大。 + + + 静态录入 + 四柱结果 + 命理解读 + + + + + + 基础信息 + 保留排盘必需字段,用更大的信息卡呈现当前静态示例。 + + + + 姓名 + {{form.name}} + + + 性别 + {{form.gender}} + + + + + 出生日期 + {{form.birthDate}} + + + 出生时间 + {{form.birthTime}} + + + + {{primaryActionText}} + + {{secondaryActionText}} + + + + + + + {{result.headline}} + {{result.subline}} + + + + + {{item.label}} + {{item.value}} + + + + 结果区保留静态展示,可继续进入命理解读页查看后续层级。 + + diff --git a/packages/mingli/pages/bazi/index.wxss b/packages/mingli/pages/bazi/index.wxss new file mode 100644 index 0000000..c4bb0fe --- /dev/null +++ b/packages/mingli/pages/bazi/index.wxss @@ -0,0 +1,194 @@ +page { + min-height: 100%; + background: linear-gradient(180deg, #f9f7f5 0%, #f3ebe8 100%); +} + +.bazi-page { + box-sizing: border-box; + min-height: 100vh; + padding: 36rpx 24rpx 96rpx; +} + +.hero-card, +.form-card, +.result-card { + margin-top: 22rpx; + padding: 34rpx 28rpx; + border: 1rpx solid rgba(139, 59, 49, 0.08); + border-radius: 32rpx; + background: rgba(255, 252, 248, 0.94); + box-shadow: 0 14rpx 34rpx rgba(139, 59, 49, 0.08); +} + +.hero-card { + margin-top: 0; +} + +.hero-card__eyebrow { + display: block; + color: #9f594e; + font-size: 22rpx; + font-weight: 600; + letter-spacing: 6rpx; + line-height: 1.4; +} + +.hero-card__title { + display: block; + margin-top: 14rpx; + color: #2c2621; + font-family: 'STSong', 'Songti SC', serif; + font-size: 46rpx; + font-weight: 700; + line-height: 1.28; +} + +.hero-card__desc { + display: block; + margin-top: 18rpx; + color: #7a6f64; + font-size: 28rpx; + line-height: 1.8; +} + +.hero-card__meta { + margin: 20rpx -6rpx 0; + font-size: 0; +} + +.hero-card__meta-item { + display: inline-block; + margin: 10rpx 6rpx 0; + padding: 10rpx 18rpx; + border-radius: 999rpx; + background: rgba(139, 59, 49, 0.08); + color: #8b3b31; + font-size: 22rpx; + line-height: 1.4; +} + +.panel-header { + margin-bottom: 4rpx; +} + +.panel-header__title { + display: block; + color: #2c2621; + font-family: 'STSong', 'Songti SC', serif; + font-size: 34rpx; + font-weight: 700; + line-height: 1.35; +} + +.panel-header__desc, +.result-card__hint { + display: block; + margin-top: 12rpx; + color: #7a6f64; + font-size: 24rpx; + line-height: 1.7; +} + +.panel-header--result { + margin-bottom: 8rpx; +} + +.field-row { + display: flex; + margin: 0 -8rpx; +} + +.field-block { + box-sizing: border-box; + width: 50%; + padding: 0 8rpx; + margin-top: 20rpx; +} + +.field-block__label { + display: block; + color: #8b5a3c; + font-size: 24rpx; + line-height: 1.5; +} + +.field-block__value { + box-sizing: border-box; + width: 100%; + min-height: 94rpx; + margin-top: 12rpx; + padding: 24rpx 22rpx; + border: 1rpx solid rgba(139, 90, 60, 0.1); + border-radius: 24rpx; + background: linear-gradient(180deg, #fffdfa 0%, #f7f1ec 100%); + color: #5a4335; + font-size: 30rpx; + font-weight: 600; + line-height: 1.4; +} + +.action-group { + margin-top: 26rpx; +} + +.action-button { + margin-top: 18rpx; + padding: 28rpx 24rpx; + border-radius: 24rpx; + background: linear-gradient(135deg, #a64e44 0%, #7a342b 100%); + color: #fff; + font-size: 30rpx; + font-weight: 600; + line-height: 1.2; + text-align: center; +} + +.action-button--ghost { + background: rgba(139, 59, 49, 0.08); + color: #8b3b31; +} + +.result-card__grid { + display: flex; + flex-wrap: wrap; + margin: 18rpx -8rpx 0; +} + +.pillar-card { + box-sizing: border-box; + width: 50%; + padding: 0 8rpx; + margin-top: 16rpx; +} + +.pillar-card__inner { + min-height: 156rpx; + padding: 24rpx 18rpx; + border-radius: 24rpx; + background: linear-gradient(180deg, #fff7f3 0%, #fff 100%); + border: 1rpx solid rgba(139, 59, 49, 0.08); + box-shadow: inset 0 1rpx 0 rgba(255, 255, 255, 0.7); +} + +.pillar-card__label { + display: block; + color: #8b3b31; + font-size: 24rpx; + line-height: 1.4; + text-align: center; +} + +.pillar-card__value { + display: block; + margin-top: 16rpx; + color: #5d312a; + font-family: 'STSong', 'Songti SC', serif; + font-size: 40rpx; + font-weight: 700; + line-height: 1.3; + text-align: center; +} + +.result-card__hint { + margin-top: 24rpx; +} diff --git a/packages/mingli/pages/book-detail/index.js b/packages/mingli/pages/book-detail/index.js new file mode 100644 index 0000000..daa58c6 --- /dev/null +++ b/packages/mingli/pages/book-detail/index.js @@ -0,0 +1,63 @@ +const { resolveScene } = require('../../../../utils/static-ux/shared') +const { ROUTES, openStaticRoute } = require('../../../../utils/static-ux/route-map') + +const BOOK_SURFACES = Object.freeze({ + 'classic-a': { + coverText: '滴', + title: '滴天髓', + subtitle: '命理经典研习入口', + route: `${ROUTES.mingli.section}?scene=reader-a`, + catalog: ['总论', '天干地支', '格局取法'], + recommends: [{ key: 'classic-b', title: '穷通宝鉴', route: `${ROUTES.mingli.bookDetail}?scene=classic-b` }] + }, + 'classic-b': { + coverText: '穷', + title: '穷通宝鉴', + subtitle: '格局与用神的静态阅读入口', + route: `${ROUTES.mingli.section}?scene=reader-b`, + catalog: ['四时旺衰', '五行喜忌', '命局评析'], + recommends: [{ key: 'classic-a', title: '滴天髓', route: `${ROUTES.mingli.bookDetail}?scene=classic-a` }] + } +}) + +function createMingliBookDetailPageData(rawScene) { + const scene = resolveScene(rawScene, ['classic-a', 'classic-b'], 'classic-a') + const surface = BOOK_SURFACES[scene] + + return { + title: '易学典籍详情', + scene, + coverText: surface.coverText, + book: { + title: surface.title, + subtitle: surface.subtitle + }, + catalog: [...surface.catalog], + primaryRoute: surface.route, + recommends: surface.recommends.map(item => ({ ...item })) + } +} + +function showNavigate(route) { + openStaticRoute(route, wx) +} + +Page({ + data: createMingliBookDetailPageData('classic-a'), + + onLoad(options) { + this.setData(createMingliBookDetailPageData(options.scene)) + }, + + handlePrimaryTap() { + showNavigate(this.data.primaryRoute) + }, + + handleRecommendTap(event) { + showNavigate(event.currentTarget.dataset.route) + } +}) + +module.exports = { + createMingliBookDetailPageData +} diff --git a/packages/mingli/pages/book-detail/index.json b/packages/mingli/pages/book-detail/index.json new file mode 100644 index 0000000..be9b2a2 --- /dev/null +++ b/packages/mingli/pages/book-detail/index.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "易学典籍详情", + "navigationBarBackgroundColor": "#f8f0ee", + "navigationBarTextStyle": "black" +} diff --git a/packages/mingli/pages/book-detail/index.wxml b/packages/mingli/pages/book-detail/index.wxml new file mode 100644 index 0000000..3243577 --- /dev/null +++ b/packages/mingli/pages/book-detail/index.wxml @@ -0,0 +1,28 @@ + + + {{coverText}} + + {{book.title}} + {{book.subtitle}} + 进入阅读 + + + + + 目录预览 + {{item}} + + + + 相关推荐 + + {{item.title}} + + + diff --git a/packages/mingli/pages/book-detail/index.wxss b/packages/mingli/pages/book-detail/index.wxss new file mode 100644 index 0000000..d5b4ea0 --- /dev/null +++ b/packages/mingli/pages/book-detail/index.wxss @@ -0,0 +1,75 @@ +page { + min-height: 100%; + background: linear-gradient(180deg, #f9f7f5 0%, #f3ebe8 100%); +} + +.mingli-book-page { + box-sizing: border-box; + min-height: 100vh; + padding: 28rpx 20rpx 72rpx; +} + +.hero-card, +.section-card { + margin-top: 18rpx; + padding: 24rpx; + border: 1rpx solid rgba(139, 59, 49, 0.08); + border-radius: 28rpx; + background: rgba(255, 252, 248, 0.94); + box-shadow: 0 10rpx 26rpx rgba(139, 59, 49, 0.06); +} + +.hero-card { + display: flex; + align-items: flex-start; + margin-top: 0; +} + +.hero-card__cover { + width: 110rpx; + height: 150rpx; + border-radius: 22rpx; + background: rgba(139, 59, 49, 0.12); + color: #8b3b31; + font-family: 'STSong', 'Songti SC', serif; + font-size: 38rpx; + font-weight: 700; + line-height: 150rpx; + text-align: center; +} + +.hero-card__body { + flex: 1; + margin-left: 18rpx; +} + +.hero-card__title, +.section-card__title { + display: block; + color: #2c2621; + font-family: 'STSong', 'Songti SC', serif; + font-size: 34rpx; + font-weight: 700; + line-height: 1.3; +} + +.hero-card__subtitle, +.section-card__item { + display: block; + margin-top: 10rpx; + color: #7a6f64; + font-size: 24rpx; + line-height: 1.6; +} + +.hero-card__button, +.section-card__recommend { + margin-top: 16rpx; + padding: 18rpx 22rpx; + border-radius: 20rpx; + background: rgba(139, 59, 49, 0.08); + color: #8b3b31; + font-size: 24rpx; + line-height: 1; + text-align: center; +} diff --git a/packages/mingli/pages/hall/index.js b/packages/mingli/pages/hall/index.js new file mode 100644 index 0000000..c5bfab0 --- /dev/null +++ b/packages/mingli/pages/hall/index.js @@ -0,0 +1,81 @@ +const { createMingliHallPageData: createBaseMingliHallPageData } = require('../../../../utils/static-ux/mingli') +const { ROUTES, openStaticRoute } = require('../../../../utils/static-ux/route-map') + +function createMingliHallPageData() { + const baseData = createBaseMingliHallPageData() + + return { + ...baseData, + almanac: { + date: '乙巳年 · 宜研习 · 忌浮躁', + ganzhi: '壬寅日 · 辰时', + yi: '宜:开卷、推演 · 忌:断章', + icon: '盘' + }, + wisdomCard: { + text: '知进退存亡,而不失其正者,其唯圣人乎。', + from: '《周易》' + }, + hotTopics: [ + { + key: 'topic-drops', + label: '滴天髓', + route: `${ROUTES.mingli.searchBooks}?keyword=滴天髓` + }, + { + key: 'topic-bazi', + label: '八字', + route: ROUTES.mingli.bazi + }, + { + key: 'topic-interpret', + label: '命理解读', + route: ROUTES.mingli.interpret + } + ], + guideCards: [ + { + key: 'guide-1', + title: '从排盘到解读', + description: '先完成静态排盘,再进入命理解读页承接结果结构。', + route: ROUTES.mingli.bazi + }, + { + key: 'guide-2', + title: '从经典到术语', + description: '从易学典籍页进入静态阅读,再回到问题解释。', + route: `${ROUTES.mingli.bookDetail}?scene=classic-a` + } + ], + recommendedBooks: [ + { + key: 'book-a', + title: '滴天髓', + subtitle: '命理经典研习入口', + route: `${ROUTES.mingli.bookDetail}?scene=classic-a` + }, + { + key: 'book-b', + title: '穷通宝鉴', + subtitle: '格局与用神的静态学习入口', + route: `${ROUTES.mingli.bookDetail}?scene=classic-b` + } + ] + } +} + +function showNavigate(route) { + openStaticRoute(route, wx) +} + +Page({ + data: createMingliHallPageData(), + + handleRouteTap(event) { + showNavigate(event.currentTarget.dataset.route) + } +}) + +module.exports = { + createMingliHallPageData +} diff --git a/packages/mingli/pages/hall/index.json b/packages/mingli/pages/hall/index.json new file mode 100644 index 0000000..cdb8629 --- /dev/null +++ b/packages/mingli/pages/hall/index.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "易学阁", + "navigationBarBackgroundColor": "#f8f0ee", + "navigationBarTextStyle": "black" +} diff --git a/packages/mingli/pages/hall/index.wxml b/packages/mingli/pages/hall/index.wxml new file mode 100644 index 0000000..20fef1b --- /dev/null +++ b/packages/mingli/pages/hall/index.wxml @@ -0,0 +1,76 @@ + + + + {{almanac.date}} + {{almanac.ganzhi}} + {{almanac.yi}} + + {{almanac.icon}} + + + + + {{item.icon}} + {{item.title}} + {{item.subtitle}} + + + + + 每日一言 + {{wisdomCard.text}} + —— {{wisdomCard.from}} + + + + 搜索热词 + + + {{item.label}} + + + + + + 导读推荐 + + {{item.title}} + {{item.description}} + + + + + 经典研习 + + + + {{item.title}} + {{item.subtitle}} + + + + diff --git a/packages/mingli/pages/hall/index.wxss b/packages/mingli/pages/hall/index.wxss new file mode 100644 index 0000000..18bec5f --- /dev/null +++ b/packages/mingli/pages/hall/index.wxss @@ -0,0 +1,166 @@ +page { + min-height: 100%; + background: linear-gradient(180deg, #f9f7f5 0%, #f3ebe8 100%); +} + +.hall-page { + box-sizing: border-box; + min-height: 100vh; + padding: 28rpx 20rpx 72rpx; +} + +.almanac-card, +.section-card, +.hall-card { + border: 1rpx solid rgba(139, 59, 49, 0.08); + border-radius: 28rpx; + background: rgba(255, 252, 248, 0.94); + box-shadow: 0 10rpx 26rpx rgba(139, 59, 49, 0.06); +} + +.almanac-card { + display: flex; + align-items: center; + justify-content: space-between; + padding: 24rpx; +} + +.almanac-card__date, +.almanac-card__yi, +.section-card__from { + display: block; + color: #7a6f64; + font-size: 22rpx; + line-height: 1.6; +} + +.almanac-card__ganzhi { + display: block; + margin: 8rpx 0; + color: #8b3b31; + font-family: 'STSong', 'Songti SC', serif; + font-size: 34rpx; + font-weight: 700; + line-height: 1.3; +} + +.almanac-card__icon { + color: #8b3b31; + font-size: 46rpx; +} + +.hall-grid { + display: flex; + flex-wrap: wrap; + margin: 12rpx -6rpx 0; +} + +.hall-card { + box-sizing: border-box; + width: 50%; + margin-top: 12rpx; + padding: 24rpx 18rpx; + background: #fff; + border-radius: 24rpx; + margin-left: 6rpx; + margin-right: 6rpx; +} + +.hall-card__icon { + width: 82rpx; + height: 82rpx; + border-radius: 20rpx; + background: rgba(139, 59, 49, 0.08); + color: #8b3b31; + font-size: 34rpx; + line-height: 82rpx; + text-align: center; +} + +.hall-card__title, +.section-card__title, +.guide-card__title, +.book-row__title { + display: block; + color: #2c2621; + font-family: 'STSong', 'Songti SC', serif; + font-size: 32rpx; + font-weight: 700; + line-height: 1.3; +} + +.hall-card__title { + margin-top: 16rpx; +} + +.hall-card__subtitle, +.guide-card__desc, +.book-row__subtitle, +.section-card__text { + display: block; + margin-top: 8rpx; + color: #7a6f64; + font-size: 24rpx; + line-height: 1.6; +} + +.section-card { + margin-top: 18rpx; + padding: 24rpx; +} + +.section-card__text { + margin-top: 14rpx; + font-size: 30rpx; + line-height: 1.8; + color: #352f29; +} + +.chips-row { + margin: 14rpx -6rpx 0; +} + +.chips-row__item { + display: inline-block; + margin: 6rpx; + padding: 12rpx 18rpx; + border-radius: 999rpx; + background: rgba(139, 59, 49, 0.08); + color: #8b3b31; + font-size: 22rpx; + line-height: 1; +} + +.guide-card, +.book-row { + margin-top: 16rpx; + padding: 20rpx 0 0; + border-top: 1rpx solid rgba(139, 59, 49, 0.08); +} + +.guide-card:first-of-type, +.book-row:first-of-type { + padding-top: 16rpx; + border-top: 0; +} + +.book-row { + display: flex; + align-items: center; +} + +.book-row__cover { + width: 72rpx; + height: 96rpx; + border-radius: 18rpx; + background: rgba(139, 59, 49, 0.1); + color: #8b3b31; + font-size: 28rpx; + line-height: 96rpx; + text-align: center; +} + +.book-row__body { + flex: 1; + margin-left: 16rpx; +} diff --git a/packages/mingli/pages/interpret/index.js b/packages/mingli/pages/interpret/index.js new file mode 100644 index 0000000..2b685eb --- /dev/null +++ b/packages/mingli/pages/interpret/index.js @@ -0,0 +1,42 @@ +const { resolveScene } = require('../../../../utils/static-ux/shared') +const { ROUTES, openStaticRoute } = require('../../../../utils/static-ux/route-map') + +function createMingliInterpretPageData(rawScene) { + const scene = resolveScene(rawScene, ['default', 'result'], 'default') + + return { + title: '命理解读', + scene, + contextText: '围绕命理问题或盘面结果,保留一个学习型解释区块。', + result: + scene === 'result' + ? { + title: '学习型命理解读', + summary: '当前静态结果围绕“甲木日主”的基本气象来展示结果版式和引用层级。', + references: ['《滴天髓》重视日主气势', '先看时令,再看格局与用神'] + } + : null, + primaryActionText: scene === 'result' ? '重新解读' : '提交解读', + secondaryActionText: '查看排盘' + } +} + +Page({ + data: createMingliInterpretPageData('default'), + + onLoad(options) { + this.setData(createMingliInterpretPageData(options.scene)) + }, + + handlePrimaryTap() { + this.setData(createMingliInterpretPageData(this.data.scene === 'result' ? 'default' : 'result')) + }, + + handleSecondaryTap() { + openStaticRoute(`${ROUTES.mingli.bazi}?scene=result`, wx) + } +}) + +module.exports = { + createMingliInterpretPageData +} diff --git a/packages/mingli/pages/interpret/index.json b/packages/mingli/pages/interpret/index.json new file mode 100644 index 0000000..daf5567 --- /dev/null +++ b/packages/mingli/pages/interpret/index.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "命理解读", + "navigationBarBackgroundColor": "#f8f0ee", + "navigationBarTextStyle": "black" +} diff --git a/packages/mingli/pages/interpret/index.wxml b/packages/mingli/pages/interpret/index.wxml new file mode 100644 index 0000000..5e7c75d --- /dev/null +++ b/packages/mingli/pages/interpret/index.wxml @@ -0,0 +1,24 @@ + + + {{title}} + {{contextText}} + + + + + + {{primaryActionText}} + + {{secondaryActionText}} + + + + + {{result.title}} + {{result.summary}} + + · + {{item}} + + + diff --git a/packages/mingli/pages/interpret/index.wxss b/packages/mingli/pages/interpret/index.wxss new file mode 100644 index 0000000..62d7795 --- /dev/null +++ b/packages/mingli/pages/interpret/index.wxss @@ -0,0 +1,91 @@ +page { + min-height: 100%; + background: linear-gradient(180deg, #f9f7f5 0%, #f3ebe8 100%); +} + +.interpret-page { + box-sizing: border-box; + min-height: 100vh; + padding: 28rpx 20rpx 72rpx; +} + +.hero-card, +.form-card, +.result-card { + margin-top: 18rpx; + padding: 24rpx; + border: 1rpx solid rgba(139, 59, 49, 0.08); + border-radius: 28rpx; + background: rgba(255, 252, 248, 0.94); + box-shadow: 0 10rpx 26rpx rgba(139, 59, 49, 0.06); +} + +.hero-card { + margin-top: 0; +} + +.hero-card__title, +.result-card__title { + display: block; + color: #2c2621; + font-family: 'STSong', 'Songti SC', serif; + font-size: 36rpx; + font-weight: 700; + line-height: 1.3; +} + +.hero-card__desc, +.result-card__desc, +.reference-item__text { + display: block; + margin-top: 10rpx; + color: #7a6f64; + font-size: 24rpx; + line-height: 1.7; +} + +.form-card__textarea, +.form-card__input { + box-sizing: border-box; + width: 100%; + padding: 18rpx 20rpx; + border: 1rpx solid rgba(139, 90, 60, 0.12); + border-radius: 18rpx; + background: #fff; + color: #5a4335; + font-size: 24rpx; + line-height: 1.6; +} + +.form-card__input { + margin-top: 12rpx; +} + +.action-button { + margin-top: 16rpx; + padding: 22rpx 24rpx; + border-radius: 22rpx; + background: linear-gradient(135deg, #a64e44 0%, #7a342b 100%); + color: #fff; + font-size: 26rpx; + line-height: 1; + text-align: center; +} + +.action-button--ghost { + background: rgba(139, 59, 49, 0.08); + color: #8b3b31; +} + +.reference-item { + display: flex; + align-items: flex-start; + margin-top: 10rpx; +} + +.reference-item__dot { + margin-right: 10rpx; + color: #8b3b31; + font-size: 26rpx; + line-height: 1.6; +} diff --git a/packages/mingli/pages/search-books/index.js b/packages/mingli/pages/search-books/index.js new file mode 100644 index 0000000..e9ed77e --- /dev/null +++ b/packages/mingli/pages/search-books/index.js @@ -0,0 +1,61 @@ +const { ROUTES, openStaticRoute } = require('../../../../utils/static-ux/route-map') + +const SEARCH_CATALOG = Object.freeze([ + { + key: 'classic-a', + title: '滴天髓', + subtitle: '命理经典结果', + aliases: ['滴天', '天髓'], + route: `${ROUTES.mingli.bookDetail}?scene=classic-a` + }, + { + key: 'classic-b', + title: '穷通宝鉴', + subtitle: '格局与用神', + aliases: ['穷通', '宝鉴'], + route: `${ROUTES.mingli.bookDetail}?scene=classic-b` + } +]) + +function createMingliSearchBooksPageData(keyword) { + const normalizedKeyword = (keyword || '').trim() + const results = normalizedKeyword + ? SEARCH_CATALOG.filter(item => { + return ( + item.title.includes(normalizedKeyword) || + item.aliases.some(alias => alias.includes(normalizedKeyword)) + ) + }).map(item => ({ ...item })) + : [] + + return { + title: '搜索易学典籍', + keyword: normalizedKeyword, + history: ['滴天髓', '穷通宝鉴', '子平真诠'], + results + } +} + +function showNavigate(route) { + openStaticRoute(route, wx) +} + +Page({ + data: createMingliSearchBooksPageData(''), + + onLoad(options) { + this.setData(createMingliSearchBooksPageData(options.keyword || options.q)) + }, + + handleHistoryTap(event) { + this.setData(createMingliSearchBooksPageData(event.currentTarget.dataset.keyword)) + }, + + handleResultTap(event) { + showNavigate(event.currentTarget.dataset.route) + } +}) + +module.exports = { + createMingliSearchBooksPageData +} diff --git a/packages/mingli/pages/search-books/index.json b/packages/mingli/pages/search-books/index.json new file mode 100644 index 0000000..452401d --- /dev/null +++ b/packages/mingli/pages/search-books/index.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "搜索易学典籍", + "navigationBarBackgroundColor": "#f8f0ee", + "navigationBarTextStyle": "black" +} diff --git a/packages/mingli/pages/search-books/index.wxml b/packages/mingli/pages/search-books/index.wxml new file mode 100644 index 0000000..7a1cdff --- /dev/null +++ b/packages/mingli/pages/search-books/index.wxml @@ -0,0 +1,21 @@ + + + {{title}} + + {{item}} + + + + + 搜索结果 + + {{item.title}} · {{item.subtitle}} + + + diff --git a/packages/mingli/pages/search-books/index.wxss b/packages/mingli/pages/search-books/index.wxss new file mode 100644 index 0000000..73cfa8d --- /dev/null +++ b/packages/mingli/pages/search-books/index.wxss @@ -0,0 +1,43 @@ +page { + min-height: 100%; + background: linear-gradient(180deg, #f9f7f5 0%, #f3ebe8 100%); +} + +.mingli-search-page { + box-sizing: border-box; + min-height: 100vh; + padding: 28rpx 20rpx 72rpx; +} + +.section-card { + margin-top: 18rpx; + padding: 24rpx; + border: 1rpx solid rgba(139, 59, 49, 0.08); + border-radius: 28rpx; + background: rgba(255, 252, 248, 0.94); + box-shadow: 0 10rpx 26rpx rgba(139, 59, 49, 0.06); +} + +.section-card:first-child { + margin-top: 0; +} + +.section-card__title { + display: block; + color: #2c2621; + font-family: 'STSong', 'Songti SC', serif; + font-size: 34rpx; + font-weight: 700; + line-height: 1.3; +} + +.section-card__item { + display: block; + margin-top: 14rpx; + padding: 18rpx 20rpx; + border-radius: 20rpx; + background: rgba(139, 59, 49, 0.08); + color: #8b3b31; + font-size: 24rpx; + line-height: 1.6; +} diff --git a/packages/mingli/pages/section/index.js b/packages/mingli/pages/section/index.js new file mode 100644 index 0000000..c52a2db --- /dev/null +++ b/packages/mingli/pages/section/index.js @@ -0,0 +1,57 @@ +const { resolveScene } = require('../../../../utils/static-ux/shared') +const { ROUTES, openStaticRoute } = require('../../../../utils/static-ux/route-map') + +const READER_SURFACES = Object.freeze({ + 'reader-a': { + title: '滴天髓 · 总论', + passages: ['欲识三元万法宗,先观帝载与神功。', '气机流转之间,先看格局成败。'] + }, + 'reader-b': { + title: '穷通宝鉴 · 四时旺衰', + passages: ['论命之法,首重月令。', '得时得地者旺,失时失地者衰。'] + } +}) + +function createMingliSectionPageData(rawScene) { + const scene = resolveScene(rawScene, ['reader-a', 'reader-b'], 'reader-a') + const surface = READER_SURFACES[scene] + + return { + title: '易学阅读', + scene, + chapterTitle: surface.title, + passages: [...surface.passages], + actions: [ + { + key: 'interpret', + title: '打开命理解读', + route: ROUTES.mingli.interpret + }, + { + key: 'hall', + title: '返回易学阁', + route: ROUTES.mingli.hall + } + ] + } +} + +function showNavigate(route) { + openStaticRoute(route, wx) +} + +Page({ + data: createMingliSectionPageData('reader-a'), + + onLoad(options) { + this.setData(createMingliSectionPageData(options.scene)) + }, + + handleActionTap(event) { + showNavigate(event.currentTarget.dataset.route) + } +}) + +module.exports = { + createMingliSectionPageData +} diff --git a/packages/mingli/pages/section/index.json b/packages/mingli/pages/section/index.json new file mode 100644 index 0000000..03b5552 --- /dev/null +++ b/packages/mingli/pages/section/index.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "易学阅读", + "navigationBarBackgroundColor": "#f8f0ee", + "navigationBarTextStyle": "black" +} diff --git a/packages/mingli/pages/section/index.wxml b/packages/mingli/pages/section/index.wxml new file mode 100644 index 0000000..46cb46b --- /dev/null +++ b/packages/mingli/pages/section/index.wxml @@ -0,0 +1,19 @@ + + + {{chapterTitle}} + + + + {{item}} + + + + {{item.title}} + + diff --git a/packages/mingli/pages/section/index.wxss b/packages/mingli/pages/section/index.wxss new file mode 100644 index 0000000..017cad7 --- /dev/null +++ b/packages/mingli/pages/section/index.wxss @@ -0,0 +1,56 @@ +page { + min-height: 100%; + background: linear-gradient(180deg, #f9f7f5 0%, #f3ebe8 100%); +} + +.mingli-section-page { + box-sizing: border-box; + min-height: 100vh; + padding: 28rpx 20rpx 72rpx; +} + +.hero-card, +.passage-card { + margin-top: 18rpx; + padding: 24rpx; + border: 1rpx solid rgba(139, 59, 49, 0.08); + border-radius: 28rpx; + background: rgba(255, 252, 248, 0.94); + box-shadow: 0 10rpx 26rpx rgba(139, 59, 49, 0.06); +} + +.hero-card { + margin-top: 0; +} + +.hero-card__title { + display: block; + color: #2c2621; + font-family: 'STSong', 'Songti SC', serif; + font-size: 34rpx; + font-weight: 700; + line-height: 1.3; +} + +.passage-card__item { + display: block; + margin-top: 16rpx; + color: #453730; + font-size: 28rpx; + line-height: 1.8; +} + +.passage-card__item:first-child { + margin-top: 0; +} + +.action-button { + margin-top: 16rpx; + padding: 22rpx 24rpx; + border-radius: 22rpx; + background: rgba(139, 59, 49, 0.08); + color: #8b3b31; + font-size: 26rpx; + line-height: 1; + text-align: center; +} diff --git a/packages/tcm/pages/ai-history/index.js b/packages/tcm/pages/ai-history/index.js new file mode 100644 index 0000000..b335f67 --- /dev/null +++ b/packages/tcm/pages/ai-history/index.js @@ -0,0 +1,80 @@ +const { resolveScene } = require('../../../../utils/static-ux/shared') +const { ROUTES, openStaticRoute } = require('../../../../utils/static-ux/route-map') + +const HISTORY_GROUPS = Object.freeze([ + { + key: 'today', + label: '今天', + items: [ + { + key: 'qa-1', + tag: 'AI答疑', + title: '什么是阴阳?', + summary: '从基础概念切回到静态 AI 答疑面板。', + route: ROUTES.tabs.ai + }, + { + key: 'analysis-1', + tag: '辨证分析', + title: '脾虚湿困的学习型分析', + summary: '查看静态辨证结果展示页。', + route: `${ROUTES.tcm.bianzheng}?scene=result` + } + ] + }, + { + key: 'week', + label: '近七天', + items: [ + { + key: 'qa-2', + tag: 'AI答疑', + title: '肝主疏泄如何理解?', + summary: '保留历史列表样式,不保留旧问答记录模型。', + route: ROUTES.tabs.ai + } + ] + } +]) + +function cloneGroups(groups) { + return groups.map(group => ({ + ...group, + items: group.items.map(item => ({ ...item })) + })) +} + +function createTcmAiHistoryPageData(rawScene) { + const scene = resolveScene(rawScene, ['default', 'empty'], 'default') + + return { + title: 'AI对话历史', + scene, + emptyTitle: '还没有 AI 记录', + emptyDescription: '去 AI 页先完成一次答疑或辨证分析。', + actionText: '返回 AI', + groups: scene === 'empty' ? [] : cloneGroups(HISTORY_GROUPS) + } +} + +Page({ + data: createTcmAiHistoryPageData('default'), + + onLoad(options) { + this.setData(createTcmAiHistoryPageData(options.scene)) + }, + + handlePrimaryTap() { + openStaticRoute(ROUTES.tabs.ai, wx) + }, + + handleHistoryTap(event) { + const { route } = event.currentTarget.dataset + + openStaticRoute(route, wx) + } +}) + +module.exports = { + createTcmAiHistoryPageData +} diff --git a/packages/tcm/pages/ai-history/index.json b/packages/tcm/pages/ai-history/index.json new file mode 100644 index 0000000..121c905 --- /dev/null +++ b/packages/tcm/pages/ai-history/index.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "AI对话历史", + "navigationBarBackgroundColor": "#f8ecd8", + "navigationBarTextStyle": "black" +} diff --git a/packages/tcm/pages/ai-history/index.wxml b/packages/tcm/pages/ai-history/index.wxml new file mode 100644 index 0000000..0447eaf --- /dev/null +++ b/packages/tcm/pages/ai-history/index.wxml @@ -0,0 +1,27 @@ + + {{title}} + + + {{emptyTitle}} + {{emptyDescription}} + {{actionText}} + + + + + {{item.label}} + + {{historyItem.tag}} + {{historyItem.title}} + {{historyItem.summary}} + + + + diff --git a/packages/tcm/pages/ai-history/index.wxss b/packages/tcm/pages/ai-history/index.wxss new file mode 100644 index 0000000..0bbf312 --- /dev/null +++ b/packages/tcm/pages/ai-history/index.wxss @@ -0,0 +1,82 @@ +page { + min-height: 100%; + background: linear-gradient(180deg, #faf3e8 0%, #f1e9dd 100%); +} + +.ai-history-page { + box-sizing: border-box; + min-height: 100vh; + padding: 28rpx 20rpx 72rpx; +} + +.ai-history-page__title { + display: block; + color: #2f1f12; + font-family: 'STSong', 'Songti SC', serif; + font-size: 56rpx; + font-weight: 700; + line-height: 1.2; +} + +.empty-card, +.history-group { + margin-top: 18rpx; + padding: 26rpx; + border: 1rpx solid rgba(118, 83, 42, 0.08); + border-radius: 32rpx; + background: rgba(255, 250, 242, 0.92); + box-shadow: 0 18rpx 42rpx rgba(86, 58, 25, 0.08); +} + +.empty-card__title, +.history-group__label, +.history-item__title { + display: block; + color: #2c2419; + font-family: 'STSong', 'Songti SC', serif; + font-size: 34rpx; + font-weight: 700; + line-height: 1.35; +} + +.empty-card__description, +.history-item__description { + display: block; + margin-top: 10rpx; + color: #7c705e; + font-size: 26rpx; + line-height: 1.7; +} + +.empty-card__action { + display: inline-block; + margin-top: 20rpx; + padding: 18rpx 24rpx; + border-radius: 999rpx; + background: linear-gradient(135deg, #9e652f 0%, #75441a 100%); + color: #fff7eb; + font-size: 26rpx; + line-height: 1; +} + +.history-item { + margin-top: 14rpx; + padding: 22rpx; + border-radius: 24rpx; + background: rgba(255, 254, 250, 0.94); + border: 1rpx solid rgba(118, 83, 42, 0.06); +} + +.history-item__tag { + display: inline-block; + padding: 8rpx 14rpx; + border-radius: 999rpx; + background: rgba(111, 66, 22, 0.08); + color: #6f4216; + font-size: 22rpx; + line-height: 1; +} + +.history-item__title { + margin-top: 12rpx; +} diff --git a/packages/tcm/pages/assets/index.js b/packages/tcm/pages/assets/index.js new file mode 100644 index 0000000..b8f4af7 --- /dev/null +++ b/packages/tcm/pages/assets/index.js @@ -0,0 +1,153 @@ +const { createTcmAssetPageData } = require('../../../../utils/static-ux/tcm') +const { resolveKind } = require('../../../../utils/static-ux/shared') +const { ROUTES, openStaticRoute } = require('../../../../utils/static-ux/route-map') + +const ASSET_KIND_LABELS = Object.freeze({ + notes: '笔记', + bookshelf: '书架', + favorites: '收藏', + history: '历史' +}) + +const ASSET_ITEMS = Object.freeze({ + notes: [ + { + key: 'note-1', + title: '阴阳学习摘记', + subtitle: '来自《黄帝内经素问》静态演示卡片', + description: '把阴阳作为纲领来理解脏腑、经络与病机的对应关系。', + route: `${ROUTES.tcm.bookDetail}?scene=classic-a` + }, + { + key: 'note-2', + title: '脾虚湿困辨证提要', + subtitle: '辨证分析里的高频主题', + description: '先看脾运失健,再看湿困中焦的症状组合。', + route: `${ROUTES.tcm.bianzheng}?scene=result` + } + ], + bookshelf: [ + { + key: 'book-1', + title: '黄帝内经素问', + subtitle: '已加入静态书架', + description: '继续阅读:上古天真论', + route: `${ROUTES.tcm.bookDetail}?scene=classic-a` + }, + { + key: 'book-2', + title: '伤寒论', + subtitle: '已加入静态书架', + description: '继续阅读:太阳病篇', + route: `${ROUTES.tcm.bookDetail}?scene=classic-b` + } + ], + favorites: [ + { + key: 'favorite-1', + title: '经络总览卡片', + subtitle: '已收藏条目', + description: '保留为静态收藏示例,后续再接新的收藏体系。', + route: `${ROUTES.tcm.placeholder}?kind=about` + }, + { + key: 'favorite-2', + title: '命理解读入口', + subtitle: '跨域收藏示例', + description: '当前用于展示从中医域跳到命理域的 UI 连接。', + route: ROUTES.mingli.interpret + } + ], + history: [ + { + key: 'history-1', + title: 'AI答疑: 什么是气虚?', + subtitle: '最近浏览', + description: '返回 AI 页查看静态问答入口。', + route: ROUTES.tabs.ai + }, + { + key: 'history-2', + title: '典籍阅读: 上古天真论', + subtitle: '最近阅读', + description: '回到典籍阅读页继续浏览。', + route: `${ROUTES.tcm.section}?scene=reader-a` + } + ] +}) + +const ASSET_EMPTY_COPY = Object.freeze({ + notes: { + title: '还没有学习笔记', + description: '后续接入新后端后,这里会汇总你的静态摘录与标注。' + }, + bookshelf: { + title: '书架还是空的', + description: '可以先从典籍页进入静态详情,再决定如何组织新的书架逻辑。' + }, + favorites: { + title: '还没有收藏内容', + description: '本轮只保留收藏区块的 UI 结构,不迁移旧收藏模型。' + }, + history: { + title: '还没有浏览记录', + description: '继续浏览典籍、AI 或易学页后,后续可在这里接入新的记录源。' + } +}) + +function cloneItems(items) { + return items.map(item => ({ ...item })) +} + +function createFilterItems(kinds) { + return kinds.map(kind => ({ + key: kind, + label: ASSET_KIND_LABELS[kind], + route: `${ROUTES.tcm.assets}?kind=${kind}` + })) +} + +function createTcmAssetsPageData(rawKind) { + const baseData = createTcmAssetPageData(rawKind) + const items = cloneItems(ASSET_ITEMS[baseData.activeKind] || []) + const emptyCopy = ASSET_EMPTY_COPY[baseData.activeKind] + + return { + title: baseData.title, + activeKind: baseData.activeKind, + filterItems: createFilterItems(baseData.kinds), + items, + emptyTitle: emptyCopy.title, + emptyDescription: emptyCopy.description + } +} + +function showNavigate(route) { + openStaticRoute(route, wx) +} + +Page({ + data: createTcmAssetsPageData('notes'), + + onLoad(options) { + this.setData( + createTcmAssetsPageData( + resolveKind(options.kind, ['notes', 'bookshelf', 'favorites', 'history'], 'notes') + ) + ) + }, + + handleFilterTap(event) { + const { kind } = event.currentTarget.dataset + + this.setData(createTcmAssetsPageData(kind)) + }, + + handleEntryTap(event) { + showNavigate(event.currentTarget.dataset.route) + } +}) + +module.exports = { + createTcmAssetsPageData +} diff --git a/packages/tcm/pages/assets/index.json b/packages/tcm/pages/assets/index.json new file mode 100644 index 0000000..aaf8a18 --- /dev/null +++ b/packages/tcm/pages/assets/index.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "学习资产", + "navigationBarBackgroundColor": "#f8ecd8", + "navigationBarTextStyle": "black" +} diff --git a/packages/tcm/pages/assets/index.wxml b/packages/tcm/pages/assets/index.wxml new file mode 100644 index 0000000..fbbca41 --- /dev/null +++ b/packages/tcm/pages/assets/index.wxml @@ -0,0 +1,36 @@ + + + {{title}} + + + + + {{item.label}} + + + + + + {{item.title}} + {{item.subtitle}} + {{item.description}} + + + + + {{emptyTitle}} + {{emptyDescription}} + + diff --git a/packages/tcm/pages/assets/index.wxss b/packages/tcm/pages/assets/index.wxss new file mode 100644 index 0000000..ef30a57 --- /dev/null +++ b/packages/tcm/pages/assets/index.wxss @@ -0,0 +1,80 @@ +page { + min-height: 100%; + background: linear-gradient(180deg, #faf3e8 0%, #f1e9dd 100%); +} + +.asset-page { + box-sizing: border-box; + min-height: 100vh; + padding: 28rpx 20rpx 72rpx; + background: + radial-gradient(circle at top right, rgba(177, 130, 74, 0.14) 0%, rgba(177, 130, 74, 0) 24%), + linear-gradient(135deg, rgba(255, 255, 255, 0.54) 0%, rgba(255, 249, 240, 0) 38%); +} + +.asset-page__header { + padding: 8rpx 0 22rpx; +} + +.asset-page__title { + display: block; + color: #2f1f12; + font-family: 'STSong', 'Songti SC', serif; + font-size: 56rpx; + font-weight: 700; + line-height: 1.2; +} + +.asset-page__filters { + margin: 0 -6rpx 14rpx; +} + +.asset-page__filter { + display: inline-block; + padding: 6rpx; +} + +.asset-page__filter-label { + display: block; + padding: 14rpx 20rpx; + border-radius: 999rpx; + background: rgba(111, 66, 22, 0.08); + color: #6f4216; + font-size: 24rpx; + line-height: 1; +} + +.asset-page__filter--active .asset-page__filter-label { + background: linear-gradient(135deg, #a56a31 0%, #7c4b1d 100%); + color: #fff7eb; +} + +.asset-card, +.asset-page__empty { + margin-top: 16rpx; + padding: 26rpx; + border: 1rpx solid rgba(118, 83, 42, 0.08); + border-radius: 32rpx; + background: rgba(255, 250, 242, 0.92); + box-shadow: 0 18rpx 42rpx rgba(86, 58, 25, 0.08); +} + +.asset-card__title, +.asset-page__empty-title { + display: block; + color: #2c2419; + font-family: 'STSong', 'Songti SC', serif; + font-size: 34rpx; + font-weight: 700; + line-height: 1.35; +} + +.asset-card__subtitle, +.asset-card__description, +.asset-page__empty-description { + display: block; + margin-top: 10rpx; + color: #7c705e; + font-size: 26rpx; + line-height: 1.7; +} diff --git a/packages/tcm/pages/bianzheng/index.js b/packages/tcm/pages/bianzheng/index.js new file mode 100644 index 0000000..3652fc4 --- /dev/null +++ b/packages/tcm/pages/bianzheng/index.js @@ -0,0 +1,50 @@ +const { resolveScene } = require('../../../../utils/static-ux/shared') +const { ROUTES, openStaticRoute } = require('../../../../utils/static-ux/route-map') + +function createTcmBianzhengPageData(rawScene) { + const scene = resolveScene(rawScene, ['default', 'result'], 'default') + + return { + title: '辨证分析', + scene, + eyebrow: 'AI 辨证分析', + intro: '把症状整理成结构化输入,再输出学习型辨证结果。', + fields: [ + { key: 'mainSymptoms', label: '核心症状', placeholder: '主要症状,用逗号分隔' }, + { key: 'tongue', label: '舌象', placeholder: '舌象' }, + { key: 'pulse', label: '脉象', placeholder: '脉象' }, + { key: 'constitution', label: '体质', placeholder: '体质' }, + { key: 'duration', label: '病程', placeholder: '病程' } + ], + result: + scene === 'result' + ? { + title: '学习型辨证结果', + summary: '当前静态结果以“脾虚湿困”为例,用于承接老页面的信息层级和结果区块。', + references: ['《素问》重视脾胃运化', '症见困倦乏力、纳呆便溏可作为学习型线索'] + } + : null, + primaryActionText: scene === 'result' ? '重新整理症状' : '提交辨证分析', + secondaryActionText: '返回 AI' + } +} + +Page({ + data: createTcmBianzhengPageData('default'), + + onLoad(options) { + this.setData(createTcmBianzhengPageData(options.scene)) + }, + + handlePrimaryTap() { + this.setData(createTcmBianzhengPageData(this.data.scene === 'result' ? 'default' : 'result')) + }, + + handleSecondaryTap() { + openStaticRoute(ROUTES.tabs.ai, wx) + } +}) + +module.exports = { + createTcmBianzhengPageData +} diff --git a/packages/tcm/pages/bianzheng/index.json b/packages/tcm/pages/bianzheng/index.json new file mode 100644 index 0000000..b0f4ca1 --- /dev/null +++ b/packages/tcm/pages/bianzheng/index.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "辨证分析", + "navigationBarBackgroundColor": "#f8ecd8", + "navigationBarTextStyle": "black" +} diff --git a/packages/tcm/pages/bianzheng/index.wxml b/packages/tcm/pages/bianzheng/index.wxml new file mode 100644 index 0000000..ac1f56b --- /dev/null +++ b/packages/tcm/pages/bianzheng/index.wxml @@ -0,0 +1,30 @@ + + + {{eyebrow}} + {{title}} + {{intro}} + + + + + {{item.label}} + + + + + {{primaryActionText}} + + {{secondaryActionText}} + + + + + + {{result.title}} + {{result.summary}} + + · + {{item}} + + + diff --git a/packages/tcm/pages/bianzheng/index.wxss b/packages/tcm/pages/bianzheng/index.wxss new file mode 100644 index 0000000..74aebb5 --- /dev/null +++ b/packages/tcm/pages/bianzheng/index.wxss @@ -0,0 +1,120 @@ +page { + min-height: 100%; + background: linear-gradient(180deg, #faf3e8 0%, #f1e9dd 100%); +} + +.bianzheng-page { + box-sizing: border-box; + min-height: 100vh; + padding: 28rpx 20rpx 72rpx; +} + +.hero-card, +.form-card, +.result-card { + margin-top: 18rpx; + padding: 26rpx; + border: 1rpx solid rgba(118, 83, 42, 0.08); + border-radius: 32rpx; + background: rgba(255, 250, 242, 0.92); + box-shadow: 0 18rpx 42rpx rgba(86, 58, 25, 0.08); +} + +.hero-card { + margin-top: 0; +} + +.hero-card__eyebrow { + display: inline-block; + padding: 8rpx 14rpx; + border-radius: 999rpx; + background: rgba(111, 66, 22, 0.08); + color: #6f4216; + font-size: 22rpx; + line-height: 1; +} + +.hero-card__title, +.result-card__title { + display: block; + margin-top: 18rpx; + color: #2c2419; + font-family: 'STSong', 'Songti SC', serif; + font-size: 38rpx; + font-weight: 700; + line-height: 1.3; +} + +.hero-card__description, +.result-card__summary, +.result-card__reference-text { + display: block; + margin-top: 12rpx; + color: #7c705e; + font-size: 26rpx; + line-height: 1.75; +} + +.field-block { + margin-top: 16rpx; +} + +.field-block:first-child { + margin-top: 0; +} + +.field-block__label { + display: block; + color: #6f4b2d; + font-size: 24rpx; + line-height: 1.5; +} + +.field-block__input { + box-sizing: border-box; + width: 100%; + margin-top: 10rpx; + padding: 18rpx 20rpx; + border: 1rpx solid rgba(199, 171, 133, 0.5); + border-radius: 22rpx; + background: rgba(255, 254, 250, 0.92); + color: #8f6842; + font-size: 26rpx; + line-height: 1.5; +} + +.form-card__actions { + margin-top: 22rpx; +} + +.form-card__button { + padding: 20rpx 24rpx; + border-radius: 24rpx; + background: linear-gradient(135deg, #9e652f 0%, #75441a 100%); + color: #fff7eb; + font-size: 28rpx; + line-height: 1; + text-align: center; +} + +.form-card__button + .form-card__button { + margin-top: 12rpx; +} + +.form-card__button--ghost { + background: rgba(111, 66, 22, 0.08); + color: #6f4216; +} + +.result-card__reference { + display: flex; + align-items: flex-start; + margin-top: 10rpx; +} + +.result-card__reference-dot { + margin-right: 10rpx; + color: #9a622d; + font-size: 28rpx; + line-height: 1.6; +} diff --git a/packages/tcm/pages/book-detail/index.js b/packages/tcm/pages/book-detail/index.js new file mode 100644 index 0000000..8707c9e --- /dev/null +++ b/packages/tcm/pages/book-detail/index.js @@ -0,0 +1,125 @@ +const { resolveScene } = require('../../../../utils/static-ux/shared') +const { ROUTES, openStaticRoute } = require('../../../../utils/static-ux/route-map') + +const BOOK_DETAIL_SURFACES = Object.freeze({ + 'classic-a': { + coverText: '黄', + title: '黄帝内经素问', + subtitle: '传世中医典籍 · 学习阅读入口', + tags: ['经典', '入门'], + description: '当前典籍详情页只保留封面、简介、目录和推荐区块,不迁移旧项目的详情接口。', + progressText: '已读到:上古天真论', + primaryRoute: `${ROUTES.tcm.section}?scene=reader-a`, + catalog: [ + { + key: 'chapter-1', + title: '上古天真论', + route: `${ROUTES.tcm.section}?scene=reader-a`, + children: ['节录一', '节录二'] + }, + { + key: 'chapter-2', + title: '四气调神大论', + route: `${ROUTES.tcm.section}?scene=reader-a`, + children: ['节录一', '节录二'] + } + ], + recommends: [ + { + key: 'recommend-b', + title: '伤寒论', + route: `${ROUTES.tcm.bookDetail}?scene=classic-b` + } + ] + }, + 'classic-b': { + coverText: '伤', + title: '伤寒论', + subtitle: '经方经典 · 学习阅读入口', + tags: ['经方', '进阶'], + description: '静态详情页保留目录结构和推荐卡片,为后续新数据层留出清晰接口边界。', + progressText: '已读到:太阳病篇', + primaryRoute: `${ROUTES.tcm.section}?scene=reader-b`, + catalog: [ + { + key: 'chapter-1', + title: '太阳病篇', + route: `${ROUTES.tcm.section}?scene=reader-b`, + children: ['条文一', '条文二'] + }, + { + key: 'chapter-2', + title: '阳明病篇', + route: `${ROUTES.tcm.section}?scene=reader-b`, + children: ['条文一', '条文二'] + } + ], + recommends: [ + { + key: 'recommend-a', + title: '黄帝内经素问', + route: `${ROUTES.tcm.bookDetail}?scene=classic-a` + } + ] + } +}) + +function cloneCatalog(items) { + return items.map(item => ({ + ...item, + children: [...item.children] + })) +} + +function cloneItems(items) { + return items.map(item => ({ ...item })) +} + +function createTcmBookDetailPageData(rawScene) { + const scene = resolveScene(rawScene, ['classic-a', 'classic-b'], 'classic-a') + const surface = BOOK_DETAIL_SURFACES[scene] + + return { + title: '典籍详情', + scene, + coverText: surface.coverText, + book: { + title: surface.title, + subtitle: surface.subtitle + }, + tags: [...surface.tags], + description: surface.description, + progressText: surface.progressText, + primaryRoute: surface.primaryRoute, + catalog: cloneCatalog(surface.catalog), + recommends: cloneItems(surface.recommends) + } +} + +function showNavigate(route) { + openStaticRoute(route, wx) +} + +Page({ + data: createTcmBookDetailPageData('classic-a'), + + onLoad(options) { + this.setData(createTcmBookDetailPageData(options.scene)) + }, + + handlePrimaryTap() { + showNavigate(this.data.primaryRoute) + }, + + handleCatalogTap(event) { + showNavigate(event.currentTarget.dataset.route) + }, + + handleRecommendTap(event) { + showNavigate(event.currentTarget.dataset.route) + } +}) + +module.exports = { + createTcmBookDetailPageData +} diff --git a/packages/tcm/pages/book-detail/index.json b/packages/tcm/pages/book-detail/index.json new file mode 100644 index 0000000..10efc9d --- /dev/null +++ b/packages/tcm/pages/book-detail/index.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "典籍详情", + "navigationBarBackgroundColor": "#f8ecd8", + "navigationBarTextStyle": "black" +} diff --git a/packages/tcm/pages/book-detail/index.wxml b/packages/tcm/pages/book-detail/index.wxml new file mode 100644 index 0000000..b523516 --- /dev/null +++ b/packages/tcm/pages/book-detail/index.wxml @@ -0,0 +1,54 @@ + + + {{coverText}} + + {{book.title}} + {{book.subtitle}} + + {{item}} + + 开始阅读 + + + + + 简介 + {{description}} + + + + 我的阅读进度 + {{progressText}} + + + + 章节目录 + + + {{item.title}} + {{item}} + + > + + + + + 相关推荐 + + + {{item.title}} + + + diff --git a/packages/tcm/pages/book-detail/index.wxss b/packages/tcm/pages/book-detail/index.wxss new file mode 100644 index 0000000..2339bfc --- /dev/null +++ b/packages/tcm/pages/book-detail/index.wxss @@ -0,0 +1,147 @@ +page { + min-height: 100%; + background: linear-gradient(180deg, #fbf4e9 0%, #f3eadf 100%); +} + +.book-detail-page { + box-sizing: border-box; + min-height: 100vh; + padding: 28rpx 20rpx 72rpx; +} + +.hero-card, +.section-card { + margin-top: 18rpx; + padding: 28rpx; + border: 1rpx solid rgba(118, 83, 42, 0.08); + border-radius: 32rpx; + background: rgba(255, 250, 242, 0.92); + box-shadow: 0 18rpx 42rpx rgba(86, 58, 25, 0.08); +} + +.hero-card { + display: flex; + align-items: flex-start; + margin-top: 0; +} + +.hero-card__cover { + width: 126rpx; + height: 168rpx; + border-radius: 24rpx; + background: linear-gradient(180deg, #d0b288 0%, #b98b5a 100%); + color: #fff9ef; + font-family: 'STSong', 'Songti SC', serif; + font-size: 42rpx; + font-weight: 700; + line-height: 168rpx; + text-align: center; +} + +.hero-card__body { + flex: 1; + margin-left: 22rpx; +} + +.hero-card__title, +.section-card__title, +.catalog-row__title { + display: block; + color: #2c2419; + font-family: 'STSong', 'Songti SC', serif; + font-size: 36rpx; + font-weight: 700; + line-height: 1.3; +} + +.hero-card__subtitle, +.section-card__description, +.catalog-row__child { + display: block; + margin-top: 10rpx; + color: #7d705d; + font-size: 26rpx; + line-height: 1.7; +} + +.hero-card__tags { + margin-top: 14rpx; +} + +.hero-card__tag { + display: inline-block; + margin-right: 8rpx; + padding: 8rpx 16rpx; + border-radius: 999rpx; + background: rgba(111, 66, 22, 0.08); + color: #6f4216; + font-size: 22rpx; + line-height: 1; +} + +.hero-card__button { + display: inline-block; + margin-top: 18rpx; + padding: 18rpx 24rpx; + border-radius: 24rpx; + background: linear-gradient(135deg, #9a622d 0%, #6f4216 100%); + color: #fff8ef; + font-size: 26rpx; + line-height: 1; +} + +.catalog-row { + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 16rpx; + padding: 18rpx 0; + border-bottom: 1rpx solid rgba(118, 83, 42, 0.08); +} + +.catalog-row:last-child { + border-bottom: 0; +} + +.catalog-row__body { + min-width: 0; + flex: 1; +} + +.catalog-row__arrow { + color: #bfa882; + font-size: 28rpx; + line-height: 1; +} + +.recommend-card { + display: flex; + align-items: center; + margin-top: 16rpx; + padding: 18rpx 0; + border-bottom: 1rpx solid rgba(118, 83, 42, 0.08); +} + +.recommend-card:last-child { + border-bottom: 0; +} + +.recommend-card__cover { + width: 70rpx; + height: 90rpx; + border-radius: 18rpx; + background: linear-gradient(180deg, #d0b288 0%, #b98b5a 100%); + color: #fff9ef; + font-family: 'STSong', 'Songti SC', serif; + font-size: 28rpx; + font-weight: 700; + line-height: 90rpx; + text-align: center; +} + +.recommend-card__title { + margin-left: 16rpx; + color: #2c2419; + font-size: 28rpx; + line-height: 1.5; +} diff --git a/packages/tcm/pages/placeholder/index.js b/packages/tcm/pages/placeholder/index.js new file mode 100644 index 0000000..0ab2a5a --- /dev/null +++ b/packages/tcm/pages/placeholder/index.js @@ -0,0 +1,99 @@ +const { ROUTES, openStaticRoute } = require('../../../../utils/static-ux/route-map') + +const PLACEHOLDER_SURFACES = Object.freeze({ + membership: { + title: '开通会员', + eyebrow: 'Membership', + description: '会员页当前只承接静态展示,后续接入新的权益体系时保持当前信息层级。', + tips: [ + '保留老页面的会员引导区块和 CTA 位置', + '不迁移旧项目的付费逻辑、订单状态和接口', + '后续只在这里接新的会员说明与权益列表' + ] + }, + feedback: { + title: '意见反馈', + eyebrow: 'Feedback', + description: '反馈入口先保留为静态说明页,用来承接“我的”页的跳转。', + tips: [ + '后续可接表单或工单系统', + '当前不提交任何内容', + '只保留页面层级和提示文案' + ] + }, + share: { + title: '分享 APP', + eyebrow: 'Share', + description: '分享页先保留 UI 占位,不接旧项目的传播逻辑。', + tips: [ + '后续可接小程序分享卡片', + '当前只展示说明与返回入口', + '不接邀请码、海报或裂变结构' + ] + }, + about: { + title: '关于我们', + eyebrow: 'About', + description: '关于页保持静态信息卡片结构,为后续新内容预留容器。', + tips: [ + '保留页面标题和说明卡片', + '后续可以接版本信息与项目介绍', + '当前不复用旧工程中的关于数据结构' + ] + }, + settings: { + title: '设置', + eyebrow: 'Settings', + description: '设置页先只保留静态说明,不接旧登录态和缓存策略。', + tips: [ + '后续可接新账号体系下的设置项', + '当前不读写 storage', + '不迁移旧项目的 session 或偏好逻辑' + ] + } +}) + +function createTcmPlaceholderPageData(kind) { + const activeKind = Object.prototype.hasOwnProperty.call(PLACEHOLDER_SURFACES, kind) ? kind : 'about' + const surface = PLACEHOLDER_SURFACES[activeKind] + + return { + kind: activeKind, + title: surface.title, + eyebrow: surface.eyebrow, + description: surface.description, + tips: [...surface.tips], + actions: [ + { + key: 'back-home', + title: '返回首页', + actionType: 'tab', + route: ROUTES.tabs.home + }, + { + key: 'go-profile', + title: '去我的页', + actionType: 'tab', + route: ROUTES.tabs.profile + } + ] + } +} + +Page({ + data: createTcmPlaceholderPageData('about'), + + onLoad(options) { + this.setData(createTcmPlaceholderPageData(options.kind)) + }, + + handleActionTap(event) { + const { route } = event.currentTarget.dataset + + openStaticRoute(route, wx) + } +}) + +module.exports = { + createTcmPlaceholderPageData +} diff --git a/packages/tcm/pages/placeholder/index.json b/packages/tcm/pages/placeholder/index.json new file mode 100644 index 0000000..86b98f8 --- /dev/null +++ b/packages/tcm/pages/placeholder/index.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "说明页", + "navigationBarBackgroundColor": "#f8ecd8", + "navigationBarTextStyle": "black" +} diff --git a/packages/tcm/pages/placeholder/index.wxml b/packages/tcm/pages/placeholder/index.wxml new file mode 100644 index 0000000..9e1b0a5 --- /dev/null +++ b/packages/tcm/pages/placeholder/index.wxml @@ -0,0 +1,26 @@ + + + {{eyebrow}} + {{title}} + {{description}} + + + + 当前状态 + + + {{item}} + + + + + {{item.title}} + + diff --git a/packages/tcm/pages/placeholder/index.wxss b/packages/tcm/pages/placeholder/index.wxss new file mode 100644 index 0000000..81bea9f --- /dev/null +++ b/packages/tcm/pages/placeholder/index.wxss @@ -0,0 +1,83 @@ +page { + min-height: 100%; + background: linear-gradient(180deg, #faf3e8 0%, #f1e9dd 100%); +} + +.placeholder-page { + box-sizing: border-box; + min-height: 100vh; + padding: 28rpx 20rpx 72rpx; +} + +.hero-card, +.tips-card { + margin-top: 18rpx; + padding: 28rpx; + border: 1rpx solid rgba(118, 83, 42, 0.08); + border-radius: 32rpx; + background: rgba(255, 250, 242, 0.92); + box-shadow: 0 18rpx 42rpx rgba(86, 58, 25, 0.08); +} + +.hero-card { + margin-top: 0; +} + +.hero-card__eyebrow { + display: inline-block; + padding: 8rpx 16rpx; + border-radius: 999rpx; + background: rgba(111, 66, 22, 0.08); + color: #6f4216; + font-size: 22rpx; + line-height: 1; +} + +.hero-card__title, +.tips-card__title { + display: block; + margin-top: 18rpx; + color: #2c2419; + font-family: 'STSong', 'Songti SC', serif; + font-size: 36rpx; + font-weight: 700; + line-height: 1.35; +} + +.hero-card__description, +.tips-card__text { + display: block; + margin-top: 12rpx; + color: #7c705e; + font-size: 26rpx; + line-height: 1.75; +} + +.tips-card__item { + display: flex; + align-items: flex-start; + margin-top: 12rpx; +} + +.tips-card__dot { + margin-right: 10rpx; + color: #9a622d; + font-size: 28rpx; + line-height: 1.6; +} + +.action-row__button { + margin-top: 16rpx; + padding: 22rpx 24rpx; + border-radius: 24rpx; + background: linear-gradient(135deg, #9e652f 0%, #75441a 100%); + color: #fff7eb; + font-size: 28rpx; + line-height: 1; + text-align: center; +} + +.action-row__button--ghost { + background: rgba(111, 66, 22, 0.08); + color: #6f4216; +} diff --git a/packages/tcm/pages/search-books/index.js b/packages/tcm/pages/search-books/index.js new file mode 100644 index 0000000..0ce498d --- /dev/null +++ b/packages/tcm/pages/search-books/index.js @@ -0,0 +1,90 @@ +const { ROUTES, openStaticRoute } = require('../../../../utils/static-ux/route-map') + +const SEARCH_CATALOG = Object.freeze([ + { + key: 'classic-a', + title: '黄帝内经素问', + subtitle: '书名命中', + aliases: ['黄帝内经', '素问'], + route: `${ROUTES.tcm.bookDetail}?scene=classic-a` + }, + { + key: 'classic-b', + title: '伤寒论', + subtitle: '经典结果', + aliases: ['伤寒', '仲景'], + route: `${ROUTES.tcm.bookDetail}?scene=classic-b` + } +]) + +const SEARCH_HISTORY = Object.freeze(['黄帝内经', '伤寒论', '温病条辨']) + +function createResults(keyword) { + const normalizedKeyword = (keyword || '').trim() + + if (!normalizedKeyword) { + return [] + } + + return SEARCH_CATALOG.filter(item => { + return ( + item.title.includes(normalizedKeyword) || + item.aliases.some(alias => alias.includes(normalizedKeyword)) + ) + }).map(item => ({ ...item })) +} + +function createTcmSearchBooksPageData(keyword) { + const normalizedKeyword = (keyword || '').trim() + + return { + title: '搜索典籍', + keyword: normalizedKeyword, + draftKeyword: normalizedKeyword, + summary: '只保留静态搜索 UI 和结果列表,不接旧项目的检索接口与搜索历史存储。', + placeholder: '搜索书名或常见别名', + historyTitle: '最近搜索', + history: SEARCH_HISTORY.map(item => ({ + key: item, + label: item + })), + results: createResults(normalizedKeyword), + searched: Boolean(normalizedKeyword) + } +} + +function showNavigate(route) { + openStaticRoute(route, wx) +} + +Page({ + data: createTcmSearchBooksPageData(''), + + onLoad(options) { + this.setData(createTcmSearchBooksPageData(options.keyword || options.q)) + }, + + handleKeywordInput(event) { + this.setData({ + draftKeyword: event.detail.value + }) + }, + + handleSearchTap() { + this.setData(createTcmSearchBooksPageData(this.data.draftKeyword)) + }, + + handleHistoryTap(event) { + const keyword = event.currentTarget.dataset.keyword + + this.setData(createTcmSearchBooksPageData(keyword)) + }, + + handleResultTap(event) { + showNavigate(event.currentTarget.dataset.route) + } +}) + +module.exports = { + createTcmSearchBooksPageData +} diff --git a/packages/tcm/pages/search-books/index.json b/packages/tcm/pages/search-books/index.json new file mode 100644 index 0000000..7f6e415 --- /dev/null +++ b/packages/tcm/pages/search-books/index.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "搜索典籍", + "navigationBarBackgroundColor": "#f8ecd8", + "navigationBarTextStyle": "black" +} diff --git a/packages/tcm/pages/search-books/index.wxml b/packages/tcm/pages/search-books/index.wxml new file mode 100644 index 0000000..a2b8c44 --- /dev/null +++ b/packages/tcm/pages/search-books/index.wxml @@ -0,0 +1,50 @@ + + + {{title}} + {{summary}} + + + + 搜索 + + + + + {{historyTitle}} + + + {{item.label}} + + + + + + 搜索结果 + + {{item.title}} + {{item.subtitle}} + + + + + 没有找到相关典籍 + 换一个书名或常见别名再试试。 + + diff --git a/packages/tcm/pages/search-books/index.wxss b/packages/tcm/pages/search-books/index.wxss new file mode 100644 index 0000000..d091ab5 --- /dev/null +++ b/packages/tcm/pages/search-books/index.wxss @@ -0,0 +1,96 @@ +page { + min-height: 100%; + background: linear-gradient(180deg, #fbf4e9 0%, #f3eadf 100%); +} + +.search-books-page { + box-sizing: border-box; + min-height: 100vh; + padding: 28rpx 20rpx 72rpx; +} + +.hero-card, +.section-card { + margin-top: 18rpx; + padding: 28rpx; + border: 1rpx solid rgba(118, 83, 42, 0.08); + border-radius: 32rpx; + background: rgba(255, 250, 242, 0.92); + box-shadow: 0 18rpx 42rpx rgba(86, 58, 25, 0.08); +} + +.hero-card { + margin-top: 0; +} + +.hero-card__title, +.section-card__title, +.result-card__title { + display: block; + color: #2c2419; + font-family: 'STSong', 'Songti SC', serif; + font-size: 36rpx; + font-weight: 700; + line-height: 1.3; +} + +.hero-card__description, +.section-card__description, +.result-card__subtitle { + display: block; + margin-top: 10rpx; + color: #7d705d; + font-size: 26rpx; + line-height: 1.7; +} + +.search-row { + display: flex; + align-items: center; + margin-top: 18rpx; +} + +.search-row__input { + flex: 1; + height: 84rpx; + padding: 0 20rpx; + border-radius: 22rpx; + background: rgba(255, 254, 250, 0.92); + color: #3f2e1f; + font-size: 26rpx; +} + +.search-row__button { + margin-left: 12rpx; + padding: 20rpx 24rpx; + border-radius: 22rpx; + background: linear-gradient(135deg, #9a622d 0%, #6f4216 100%); + color: #fff8ef; + font-size: 26rpx; + line-height: 1; +} + +.history-list { + margin: 14rpx -6rpx 0; +} + +.history-chip { + display: inline-block; + margin: 6rpx; + padding: 12rpx 18rpx; + border-radius: 999rpx; + background: rgba(111, 66, 22, 0.08); + color: #6f4216; + font-size: 22rpx; + line-height: 1; +} + +.result-card { + margin-top: 16rpx; + padding: 22rpx 0; + border-bottom: 1rpx solid rgba(118, 83, 42, 0.08); +} + +.result-card:last-child { + border-bottom: 0; +} diff --git a/packages/tcm/pages/section/index.js b/packages/tcm/pages/section/index.js new file mode 100644 index 0000000..ed135e6 --- /dev/null +++ b/packages/tcm/pages/section/index.js @@ -0,0 +1,97 @@ +const { resolveScene } = require('../../../../utils/static-ux/shared') +const { ROUTES, openStaticRoute } = require('../../../../utils/static-ux/route-map') + +const READER_SURFACES = Object.freeze({ + 'reader-a': { + chapterTitle: '上古天真论', + passages: [ + '上古之人,其知道者,法于阴阳,和于术数。', + '食饮有节,起居有常,不妄作劳,故能形与神俱。' + ] + }, + 'reader-b': { + chapterTitle: '太阳病篇', + passages: [ + '伤寒者,太阳病也。', + '太阳之为病,脉浮,头项强痛而恶寒。' + ] + } +}) + +function createTcmSectionPageData(rawScene) { + const scene = resolveScene(rawScene, ['reader-a', 'reader-b'], 'reader-a') + const surface = READER_SURFACES[scene] + + return { + title: '典籍阅读', + scene, + chapterTitle: surface.chapterTitle, + passages: [...surface.passages], + toolbarVisible: false, + settings: { + fontScale: 1, + lineMode: 'normal', + bgMode: 'default' + }, + lineModes: [ + { key: 'compact', label: '紧凑' }, + { key: 'normal', label: '适中' }, + { key: 'relaxed', label: '宽松' } + ], + backgroundModes: [ + { key: 'default', label: '默认' }, + { key: 'paper', label: '纸张' }, + { key: 'care', label: '护眼' } + ], + catalogItems: [ + { + key: 'reader-a', + title: '上古天真论', + route: `${ROUTES.tcm.section}?scene=reader-a` + }, + { + key: 'reader-b', + title: '太阳病篇', + route: `${ROUTES.tcm.section}?scene=reader-b` + } + ] + } +} + +function showNavigate(route) { + openStaticRoute(route, wx) +} + +Page({ + data: createTcmSectionPageData('reader-a'), + + onLoad(options) { + this.setData(createTcmSectionPageData(options.scene)) + }, + + handleToolbarToggle() { + this.setData({ + toolbarVisible: !this.data.toolbarVisible + }) + }, + + handleLineModeTap(event) { + this.setData({ + 'settings.lineMode': event.currentTarget.dataset.mode + }) + }, + + handleBackgroundTap(event) { + this.setData({ + 'settings.bgMode': event.currentTarget.dataset.mode + }) + }, + + handleCatalogTap(event) { + showNavigate(event.currentTarget.dataset.route) + } +}) + +module.exports = { + createTcmSectionPageData +} diff --git a/packages/tcm/pages/section/index.json b/packages/tcm/pages/section/index.json new file mode 100644 index 0000000..2eddc0a --- /dev/null +++ b/packages/tcm/pages/section/index.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "典籍阅读", + "navigationBarBackgroundColor": "#f8ecd8", + "navigationBarTextStyle": "black" +} diff --git a/packages/tcm/pages/section/index.wxml b/packages/tcm/pages/section/index.wxml new file mode 100644 index 0000000..51c0fd9 --- /dev/null +++ b/packages/tcm/pages/section/index.wxml @@ -0,0 +1,49 @@ + + + {{chapterTitle}} + {{item}} + + + + {{title}} + + + 行间距 + + {{item.label}} + + + + + 背景 + + {{item.label}} + + + + + 目录 + + {{item.title}} + + + + diff --git a/packages/tcm/pages/section/index.wxss b/packages/tcm/pages/section/index.wxss new file mode 100644 index 0000000..b489957 --- /dev/null +++ b/packages/tcm/pages/section/index.wxss @@ -0,0 +1,102 @@ +page { + min-height: 100%; + background: #f8f1e6; +} + +.reading-page { + box-sizing: border-box; + min-height: 100vh; + padding: 0 0 40rpx; +} + +.reading-page--default { + background: #f8f1e6; +} + +.reading-page--paper { + background: #f4ead1; +} + +.reading-page--care { + background: #eef4e8; +} + +.reading-page__article { + padding: 72rpx 40rpx 48rpx; +} + +.reading-page__chapter { + display: block; + color: #2c2419; + font-family: 'STSong', 'Songti SC', serif; + font-size: 40rpx; + font-weight: 700; + line-height: 1.35; + text-align: center; +} + +.reading-page__passage { + display: block; + margin-top: 24rpx; + color: #3b3027; + font-size: 30rpx; + line-height: 1.9; + text-align: justify; +} + +.reading-toolbar { + margin: 0 20rpx; + padding: 24rpx; + border: 1rpx solid rgba(118, 83, 42, 0.08); + border-radius: 28rpx; + background: rgba(255, 251, 244, 0.96); + box-shadow: 0 18rpx 42rpx rgba(86, 58, 25, 0.08); +} + +.reading-toolbar__title, +.reading-toolbar__label { + display: block; + color: #2c2419; +} + +.reading-toolbar__title { + font-family: 'STSong', 'Songti SC', serif; + font-size: 32rpx; + font-weight: 700; + line-height: 1.35; +} + +.reading-toolbar__group { + margin-top: 20rpx; +} + +.reading-toolbar__label { + font-size: 24rpx; + line-height: 1.5; +} + +.reading-toolbar__pill { + display: inline-block; + margin: 10rpx 8rpx 0 0; + padding: 12rpx 18rpx; + border-radius: 999rpx; + background: rgba(111, 66, 22, 0.08); + color: #6f4216; + font-size: 22rpx; + line-height: 1; +} + +.reading-toolbar__pill--active { + background: linear-gradient(135deg, #9a622d 0%, #6f4216 100%); + color: #fff8ef; +} + +.catalog-item { + margin-top: 12rpx; + padding: 18rpx 20rpx; + border-radius: 20rpx; + background: rgba(255, 254, 250, 0.94); + color: #4f3e2e; + font-size: 24rpx; + line-height: 1.5; +} diff --git a/pages/ai/index.js b/pages/ai/index.js new file mode 100644 index 0000000..601428d --- /dev/null +++ b/pages/ai/index.js @@ -0,0 +1,185 @@ +const { ROUTES, openStaticRoute } = require('../../utils/static-ux/route-map') + +const AI_PAGE_BLUEPRINT = Object.freeze({ + title: 'AI助手', + historyText: '历史', + disclaimerText: '本功能仅供学习参考,不能替代专业医师诊断。', + tabs: [ + { + key: 'qa', + label: 'AI答疑' + }, + { + key: 'analysis', + label: '辨证分析' + }, + { + key: 'constitution', + label: '体质检测' + } + ], + panels: { + qa: { + key: 'qa', + badge: 'AI答疑', + heading: '您好,我是中医学习助手', + subheading: '围绕中医理论、术语和典籍原文快速提问', + highlights: ['解答中医理论问题', '解释典籍原文含义', '给出学习型出处引用'], + examples: ['什么是阴阳?', '气虚和阳虚有什么区别?', '如何理解肝主疏泄?'], + formType: 'qa', + actionTitle: '输入你的问题', + inputPlaceholder: '例如:什么是气虚?', + actionButtonText: '发送' + }, + analysis: { + key: 'analysis', + badge: '辨证分析', + heading: '先整理症状,再查看学习型辨证分析', + subheading: '把症状整理为结构化输入,再看学习型辨证思路', + highlights: ['输入主要症状', '补充舌象、脉象、体质', '查看学习型辨证结论'], + examples: ['乏力、睡眠差应如何整理输入?', '舌淡苔白常见于哪些证型?', '如何理解脾虚湿困?'], + formType: 'analysis', + actionTitle: '整理辨证输入', + primaryField: { + label: '主要症状', + placeholder: '主要症状,多个用逗号分隔' + }, + secondaryFields: [ + { + key: 'tongue', + placeholder: '舌象' + }, + { + key: 'pulse', + placeholder: '脉象' + }, + { + key: 'constitution', + placeholder: '体质' + }, + { + key: 'course', + placeholder: '病程' + } + ], + noteField: { + label: '补充说明', + placeholder: '例如:近两周明显乏力,睡眠浅。' + }, + actionButtonText: '开始分析' + }, + constitution: { + key: 'constitution', + badge: '体质检测', + heading: '体质检测即将开放', + subheading: '九种体质入口先按设计稿保留为 UI-first 模式', + highlights: ['后续会补齐问卷式体质评估', '保持学习参考语义', '不会输出医疗诊断结论'], + examples: ['我总怕冷属于哪种体质?', '气虚质有哪些特征?', '体质结果将如何回到典籍学习?'], + formType: 'constitution', + actionTitle: '体质检测即将开放', + actionDescription: + '当前回合先严格对齐 UI 结构,问卷式体质检测会在后续接入真实题目与结果逻辑。', + actionButtonText: '查看其它待开放能力' + } + } +}) + +function cloneTabs(tabs) { + return tabs.map(item => ({ ...item })) +} + +function cloneSecondaryFields(fields) { + return fields.map(item => ({ ...item })) +} + +function createPanelByKey(panelKey) { + const sourcePanel = AI_PAGE_BLUEPRINT.panels[panelKey] || AI_PAGE_BLUEPRINT.panels.qa + + return { + ...sourcePanel, + highlights: [...sourcePanel.highlights], + examples: [...sourcePanel.examples], + primaryField: sourcePanel.primaryField ? { ...sourcePanel.primaryField } : undefined, + secondaryFields: sourcePanel.secondaryFields + ? cloneSecondaryFields(sourcePanel.secondaryFields) + : undefined, + noteField: sourcePanel.noteField ? { ...sourcePanel.noteField } : undefined + } +} + +function createAiPageData() { + return { + title: AI_PAGE_BLUEPRINT.title, + historyText: AI_PAGE_BLUEPRINT.historyText, + disclaimerText: AI_PAGE_BLUEPRINT.disclaimerText, + secondaryEntries: [ + { + key: 'ai-history', + title: 'AI历史', + subtitle: '查看中医问答与辨证分析的静态历史页', + route: ROUTES.tcm.aiHistory + }, + { + key: 'mingli-interpret', + title: '命理解读', + subtitle: '进入命理方向的静态解读页', + route: ROUTES.mingli.interpret + } + ], + tabs: cloneTabs(AI_PAGE_BLUEPRINT.tabs), + activeTabKey: 'qa', + currentPanel: createPanelByKey('qa') + } +} + +function showPlaceholderToast(title) { + if (typeof wx?.showToast === 'function') { + wx.showToast({ + title: title || '功能建设中', + icon: 'none' + }) + } +} + +function showNavigate(route) { + if (openStaticRoute(route, wx)) { + return + } + + showPlaceholderToast() +} + +Page({ + data: createAiPageData(), + + handleTabTap(event) { + const { tabKey } = event.currentTarget.dataset + + if (!AI_PAGE_BLUEPRINT.panels[tabKey] || tabKey === this.data.activeTabKey) { + return + } + + this.setData({ + activeTabKey: tabKey, + currentPanel: createPanelByKey(tabKey) + }) + }, + + handleHistoryTap() { + showNavigate(ROUTES.tcm.aiHistory) + }, + + handleSecondaryEntryTap(event) { + showNavigate(event.currentTarget.dataset.route) + }, + + handleActionTap() { + showPlaceholderToast() + } +}) + +module.exports = { + AI_PAGE_BLUEPRINT, + createPanelByKey, + createAiPageData +} diff --git a/pages/ai/index.json b/pages/ai/index.json new file mode 100644 index 0000000..40cbce1 --- /dev/null +++ b/pages/ai/index.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "AI助手", + "navigationBarBackgroundColor": "#f8ecd8", + "navigationBarTextStyle": "black" +} diff --git a/pages/ai/index.wxml b/pages/ai/index.wxml new file mode 100644 index 0000000..4e19874 --- /dev/null +++ b/pages/ai/index.wxml @@ -0,0 +1,107 @@ + + + {{title}} + {{historyText}} + + + + + {{item.label}} + + + + + + {{item.title}} + {{item.subtitle}} + + + + + {{currentPanel.badge}} + {{currentPanel.heading}} + {{currentPanel.subheading}} + + + + · + {{item}} + + + + + + {{item}} + + + + + + {{disclaimerText}} + + + + + {{currentPanel.actionTitle}} + + + {{currentPanel.actionButtonText}} + + + + + {{currentPanel.actionTitle}} + {{currentPanel.primaryField.label}} + + + + + + + + + {{currentPanel.noteField.label}} + + + + {{currentPanel.actionButtonText}} + + + + + {{currentPanel.actionTitle}} + {{currentPanel.actionDescription}} + + {{currentPanel.actionButtonText}} + + + diff --git a/pages/ai/index.wxss b/pages/ai/index.wxss new file mode 100644 index 0000000..49db62f --- /dev/null +++ b/pages/ai/index.wxss @@ -0,0 +1,306 @@ +page { + min-height: 100%; + background: + linear-gradient(180deg, #f7ecd9 0%, #fbf3e6 24%, #f7eedf 100%); +} + +.ai-page { + box-sizing: border-box; + min-height: 100vh; + padding: 16rpx 20rpx 48rpx; + background: + radial-gradient(circle at top right, rgba(197, 150, 86, 0.12) 0, rgba(197, 150, 86, 0) 26%), + linear-gradient(135deg, rgba(255, 255, 255, 0.7) 0%, rgba(255, 248, 238, 0) 38%); +} + +.ai-page__nav { + display: flex; + align-items: center; + justify-content: space-between; + padding: 22rpx 0 26rpx; +} + +.ai-page__title { + font-family: 'STSong', 'Songti SC', serif; + font-size: 62rpx; + font-weight: 700; + line-height: 1.1; + color: #2b1a0e; +} + +.ai-page__history { + min-width: 94rpx; + padding: 16rpx 22rpx; + border-radius: 999rpx; + background: rgba(247, 236, 220, 0.92); + color: #9f7142; + font-size: 24rpx; + line-height: 1; + text-align: center; + box-shadow: 0 10rpx 20rpx rgba(186, 141, 83, 0.08); +} + +.ai-page__tabs { + display: flex; + margin: 0 -8rpx; +} + +.ai-page__tab { + box-sizing: border-box; + width: 33.3333%; + padding: 0 8rpx; +} + +.ai-page__tab-label { + display: block; + padding: 24rpx 0; + border: 1rpx solid rgba(213, 194, 165, 0.6); + border-radius: 999rpx; + background: rgba(255, 252, 246, 0.96); + color: #a37545; + font-family: 'STSong', 'Songti SC', serif; + font-size: 30rpx; + line-height: 1; + text-align: center; + box-shadow: 0 12rpx 28rpx rgba(188, 152, 103, 0.08); +} + +.ai-page__tab--active .ai-page__tab-label { + border-color: rgba(150, 94, 34, 0.92); + background: linear-gradient(135deg, #b07434 0%, #8f5920 100%); + color: #fff6ea; + box-shadow: 0 14rpx 24rpx rgba(144, 89, 31, 0.26); +} + +.ai-page__secondary-list { + margin-top: 18rpx; +} + +.ai-page__secondary-item { + padding: 22rpx 24rpx; + border: 1rpx solid rgba(212, 189, 152, 0.24); + border-radius: 28rpx; + background: rgba(255, 250, 243, 0.94); + box-shadow: 0 14rpx 28rpx rgba(186, 148, 96, 0.1); +} + +.ai-page__secondary-item + .ai-page__secondary-item { + margin-top: 14rpx; +} + +.ai-page__secondary-title { + display: block; + color: #6d431d; + font-family: 'STSong', 'Songti SC', serif; + font-size: 34rpx; + font-weight: 700; + line-height: 1.35; +} + +.ai-page__secondary-subtitle { + display: block; + margin-top: 8rpx; + color: #96704a; + font-size: 26rpx; + line-height: 1.7; +} + +.panel-card, +.action-card { + margin-top: 20rpx; + padding: 28rpx 20rpx 20rpx; + border: 1rpx solid rgba(209, 185, 148, 0.22); + border-radius: 34rpx; + background: rgba(255, 251, 245, 0.95); + box-shadow: 0 18rpx 36rpx rgba(178, 145, 96, 0.14); +} + +.panel-card__badge { + display: inline-block; + padding: 10rpx 18rpx; + border-radius: 999rpx; + background: rgba(247, 238, 227, 0.96); + color: #a36f3d; + font-size: 24rpx; + line-height: 1; +} + +.panel-card__heading { + display: block; + margin-top: 24rpx; + font-family: 'STSong', 'Songti SC', serif; + font-size: 56rpx; + font-weight: 700; + line-height: 1.22; + color: #2c1b10; +} + +.panel-card__subheading { + display: block; + margin-top: 18rpx; + color: #9a7248; + font-size: 28rpx; + line-height: 1.75; +} + +.panel-card__highlights { + margin-top: 16rpx; +} + +.panel-card__highlight { + display: flex; + align-items: flex-start; + margin-top: 8rpx; +} + +.panel-card__highlight-dot { + margin-right: 10rpx; + color: #a66c34; + font-size: 32rpx; + line-height: 1.5; +} + +.panel-card__highlight-text { + flex: 1; + color: #6f4b2d; + font-size: 30rpx; + line-height: 1.8; +} + +.panel-card__examples { + display: flex; + flex-wrap: wrap; + margin: 18rpx -6rpx 0; +} + +.example-chip { + box-sizing: border-box; + padding: 0 6rpx 12rpx; +} + +.example-chip__text { + display: block; + padding: 14rpx 22rpx; + border-radius: 999rpx; + background: rgba(246, 236, 225, 0.94); + color: #9d6d3e; + font-size: 26rpx; + line-height: 1.2; +} + +.panel-card__notice { + display: flex; + align-items: center; + margin-top: 10rpx; + padding: 22rpx 20rpx; + border-radius: 24rpx; + background: rgba(247, 239, 229, 0.92); +} + +.panel-card__notice-icon { + margin-right: 12rpx; + color: #d08c2f; + font-size: 24rpx; + line-height: 1; +} + +.panel-card__notice-text { + flex: 1; + color: #9b7247; + font-size: 26rpx; + line-height: 1.6; +} + +.action-card__title { + display: block; + font-family: 'STSong', 'Songti SC', serif; + font-size: 46rpx; + font-weight: 700; + line-height: 1.25; + color: #2d1c11; +} + +.action-card__label { + display: block; + margin-top: 20rpx; + color: #6d492c; + font-size: 28rpx; + line-height: 1.5; +} + +.action-card__input, +.action-card__textarea { + box-sizing: border-box; + width: 100%; + margin-top: 16rpx; + padding: 18rpx 22rpx; + border: 1rpx solid rgba(214, 193, 162, 0.72); + border-radius: 24rpx; + background: rgba(255, 252, 247, 0.96); + color: #8f6842; + font-size: 30rpx; + line-height: 1.5; +} + +.action-card__input--single { + min-height: 86rpx; +} + +.action-card__grid { + display: flex; + flex-wrap: wrap; + margin: 8rpx -8rpx 0; +} + +.action-card__grid-item { + box-sizing: border-box; + width: 50%; + padding: 8rpx; +} + +.action-card__input--half { + margin-top: 0; +} + +.action-card__textarea { + min-height: 104rpx; +} + +.action-card__description { + display: block; + margin-top: 20rpx; + color: #8e6640; + font-size: 30rpx; + line-height: 1.8; +} + +.action-card__button { + display: inline-flex; + align-items: center; + justify-content: center; + align-self: center; + min-width: 180rpx; + margin: 28rpx auto 4rpx; + padding: 20rpx 36rpx; + border-radius: 24rpx; + background: linear-gradient(135deg, #b07434 0%, #8f5920 100%); + color: #fff7eb; + font-family: 'STSong', 'Songti SC', serif; + font-size: 36rpx; + font-weight: 700; + line-height: 1; + text-align: center; + box-shadow: 0 14rpx 24rpx rgba(144, 89, 31, 0.22); +} + +.action-card__button--full { + display: flex; + width: 100%; + min-width: 0; + min-height: 84rpx; + margin-top: 24rpx; + border-radius: 22rpx; + background: rgba(244, 232, 213, 0.95); + color: #7d5327; + box-shadow: none; +} diff --git a/pages/ai/tab-ai-active.png b/pages/ai/tab-ai-active.png new file mode 100644 index 0000000..0eef72e Binary files /dev/null and b/pages/ai/tab-ai-active.png differ diff --git a/pages/ai/tab-ai.png b/pages/ai/tab-ai.png new file mode 100644 index 0000000..4e99e92 Binary files /dev/null and b/pages/ai/tab-ai.png differ diff --git a/pages/home/index.js b/pages/home/index.js index 0e50e90..67307b3 100644 --- a/pages/home/index.js +++ b/pages/home/index.js @@ -1,93 +1,205 @@ -const { getRuntimeConfig } = require('../../config/env') -const { sessionStore } = require('../../stores') -const { maskToken } = require('../../utils/util') +const { ROUTES, openStaticRoute } = require('../../utils/static-ux/route-map') +const { createTcmHomeHubCards } = require('../../utils/static-ux/tcm') -const QUICK_ENTRIES = [ - { - title: '登录主包', - description: '演示主包内的认证入口、全局会话同步和基础 UI 组件封装。', - badge: '主包', - actionText: '打开登录页', - actionPath: '/pages/login/index' - }, - { - title: '业务工作台', - description: '演示分包页面,只在需要时加载,保持主包轻量和首页启动稳定。', - badge: '分包', - actionText: '进入工作台', - actionPath: '/packages/demo/pages/workbench/index' - } -] +const HOME_PAGE_BLUEPRINT = Object.freeze({ + brandName: '玄知中医', + greeting: '晚上好', + subtitle: '今天想学点什么?从典籍、工具或养生主题开始。', + searchPlaceholder: '搜索典籍、术语、AI问答...', + searchBadge: 'AI', + searchRoute: ROUTES.tcm.searchBooks, + portalTitle: '学习入口', + encyclopediaTitle: '中医百科', + encyclopediaCards: [ + { + key: 'classic', + icon: '书', + title: '经典书城', + route: ROUTES.tabs.library + }, + { + key: 'meridian', + icon: '穴', + title: '经络穴位', + status: '待开放' + }, + { + key: 'disease', + icon: '病', + title: '疾病百科', + status: '待开放' + } + ], + toolsTitle: '学习工具', + toolCards: [ + { + key: 'qa', + icon: '问', + title: 'AI问答', + route: ROUTES.tabs.ai + }, + { + key: 'formula', + icon: '方', + title: '方剂笔记', + route: `${ROUTES.tcm.assets}?kind=notes` + }, + { + key: 'constitution', + icon: '诊', + title: '体质诊断', + route: ROUTES.tabs.ai + }, + { + key: 'wellness', + icon: '养', + title: '智能饮片' + } + ], + wellnessTitle: '养生调理', + wellnessCards: [ + { + key: 'constitution-check', + icon: '🧬', + title: 'AI体质检测', + status: '待开放', + route: ROUTES.tabs.ai + }, + { + key: 'medicated-diet', + icon: '🍲', + title: '药膳', + status: '待开放' + }, + { + key: 'ingredient', + icon: '🥬', + title: '食材', + status: '待开放' + } + ], + classicsTitle: '热门典籍', + classicsActionText: '进入书城', + classicsActionRoute: ROUTES.tabs.library, + classicsBooks: [ + { + key: 'huangdi-neijing-suwen', + coverText: '黄', + title: '黄帝内经素问', + route: `${ROUTES.tcm.bookDetail}?scene=classic-a` + }, + { + key: 'shang-han-lun', + coverText: '伤', + title: '伤寒论', + route: `${ROUTES.tcm.bookDetail}?scene=classic-b` + }, + { + key: 'wen-bing-tiao-bian', + coverText: '温', + title: '温病条辨', + route: `${ROUTES.tcm.searchBooks}?keyword=温病条辨` + }, + { + key: 'bencao-gangmu-bieming-lu', + coverText: '本', + title: '本草纲目别名录', + route: `${ROUTES.tcm.searchBooks}?keyword=本草纲目` + } + ] +}) -function buildSessionView(state) { - const userName = state.userInfo?.nickname || state.userInfo?.name || '访客' +function cloneItems(items) { + return items.map(item => ({ ...item })) +} +function createHomePageData() { return { - statusLabel: state.isLoggedIn ? '已登录' : '未登录', - userName, - tokenLabel: maskToken(state.token), - permissionsLabel: state.permissions.length ? state.permissions.join(' / ') : '暂无权限' + brandName: HOME_PAGE_BLUEPRINT.brandName, + greeting: HOME_PAGE_BLUEPRINT.greeting, + subtitle: HOME_PAGE_BLUEPRINT.subtitle, + searchPlaceholder: HOME_PAGE_BLUEPRINT.searchPlaceholder, + searchBadge: HOME_PAGE_BLUEPRINT.searchBadge, + searchRoute: HOME_PAGE_BLUEPRINT.searchRoute, + portalTitle: HOME_PAGE_BLUEPRINT.portalTitle, + portalCards: createTcmHomeHubCards(), + encyclopediaTitle: HOME_PAGE_BLUEPRINT.encyclopediaTitle, + encyclopediaCards: cloneItems(HOME_PAGE_BLUEPRINT.encyclopediaCards), + toolsTitle: HOME_PAGE_BLUEPRINT.toolsTitle, + toolCards: cloneItems(HOME_PAGE_BLUEPRINT.toolCards), + wellnessTitle: HOME_PAGE_BLUEPRINT.wellnessTitle, + wellnessCards: cloneItems(HOME_PAGE_BLUEPRINT.wellnessCards), + classicsTitle: HOME_PAGE_BLUEPRINT.classicsTitle, + classicsActionText: HOME_PAGE_BLUEPRINT.classicsActionText, + classicsActionRoute: HOME_PAGE_BLUEPRINT.classicsActionRoute, + classicsBooks: cloneItems(HOME_PAGE_BLUEPRINT.classicsBooks) } } +function navigateToRoute(route) { + openStaticRoute(route, wx) +} + +function showPendingToast(title) { + if (typeof wx?.showToast !== 'function') { + return + } + + wx.showToast({ + title: title ? `${title}待开放` : '功能建设中', + icon: 'none' + }) +} + +function handleHomeEntry(route, title) { + if (openStaticRoute(route, wx)) { + return + } + + showPendingToast(title) +} + Page({ - data: { - quickEntries: QUICK_ENTRIES, - envName: '', - apiBaseUrl: '', - isLoggedIn: false, - sessionView: buildSessionView(sessionStore.getState()) - }, - onLoad() { - const runtimeConfig = getRuntimeConfig() + data: createHomePageData(), - this.unsubscribe = sessionStore.subscribe(nextState => { - this.syncSession(nextState) - }) + handleSearchTap() { + handleHomeEntry(this.data.searchRoute, '搜索') + }, - this.setData({ - envName: runtimeConfig.name.toUpperCase(), - apiBaseUrl: runtimeConfig.baseURL - }) - this.syncSession(sessionStore.getState()) - }, - onUnload() { - this.unsubscribe?.() - }, - syncSession(state) { - this.setData({ - isLoggedIn: state.isLoggedIn, - sessionView: buildSessionView(state) - }) - }, - handlePrimaryAction() { - if (this.data.isLoggedIn) { - wx.navigateTo({ - url: '/packages/demo/pages/workbench/index' - }) - return - } + handleEncyclopediaTap(event) { + const { route, title } = event.currentTarget.dataset - wx.navigateTo({ - url: '/pages/login/index' - }) + handleHomeEntry(route, title) }, - handleLogout() { - sessionStore.clearSession() - wx.showToast({ - title: '已清理登录态', - icon: 'success' - }) + + handlePortalTap(event) { + navigateToRoute(event.currentTarget.dataset.route) }, - handleEntryAction(event) { - const { path } = event.detail - if (!path) { - return - } + handleToolTap(event) { + const { route, title } = event.currentTarget.dataset - wx.navigateTo({ - url: path - }) + handleHomeEntry(route, title) + }, + + handleWellnessTap(event) { + const { route, title } = event.currentTarget.dataset + + handleHomeEntry(route, title) + }, + + handleClassicActionTap() { + handleHomeEntry(this.data.classicsActionRoute, this.data.classicsActionText) + }, + + handleClassicTap(event) { + const { route, title } = event.currentTarget.dataset + + handleHomeEntry(route, title) } }) + +module.exports = { + HOME_PAGE_BLUEPRINT, + createHomePageData +} diff --git a/pages/home/index.json b/pages/home/index.json index 93aea9c..76e1d51 100644 --- a/pages/home/index.json +++ b/pages/home/index.json @@ -1,7 +1,5 @@ { - "navigationBarTitleText": "首页", - "usingComponents": { - "app-button": "../../components/base/app-button/index", - "entry-card": "../../components/biz/entry-card/index" - } + "navigationBarTitleText": "玄知中医", + "navigationBarBackgroundColor": "#f8ecd8", + "navigationBarTextStyle": "black" } diff --git a/pages/home/index.wxml b/pages/home/index.wxml index bfb5836..3fd2535 100644 --- a/pages/home/index.wxml +++ b/pages/home/index.wxml @@ -1,65 +1,105 @@ - - - Stable Native Architecture - 玄志小程序基础骨架 - - 原生小程序 + JS + npm + TDesign + 分包 + service/store 分层,保留微信原生性能和长期维护边界。 - - - + + {{brandName}} + {{greeting}} + {{subtitle}} + + + + {{searchPlaceholder}} + {{searchBadge}} - - 当前会话 - - - 运行环境 - {{envName}} - - - API 地址 - {{apiBaseUrl}} - - - 登录状态 - {{sessionView.statusLabel}} - - - 当前用户 - {{sessionView.userName}} - - - Token - {{sessionView.tokenLabel}} - - - 权限 - {{sessionView.permissionsLabel}} + + {{encyclopediaTitle}} + + + + {{item.status}} + {{item.icon}} + {{item.title}} + - - + + {{portalTitle}} + + + + {{item.badge}} + {{item.title}} + {{item.subtitle}} + + + + + + + {{toolsTitle}} + + + + {{item.icon}} + {{item.title}} + + + + + + + {{wellnessTitle}} + + + + {{item.status}} + + {{item.icon}} + + {{item.title}} + + + + + + + + {{classicsTitle}} + {{classicsActionText}} → + + + + + + {{item.coverText}} + + {{item.title}} + + + diff --git a/pages/home/index.wxss b/pages/home/index.wxss index 558f081..47262c5 100644 --- a/pages/home/index.wxss +++ b/pages/home/index.wxss @@ -1,68 +1,323 @@ -.hero { - display: flex; - flex-direction: column; - gap: 24rpx; - padding: 36rpx 32rpx; - background: linear-gradient(160deg, #0f172a 0%, #1e293b 100%); - color: #f8fafc; +page { + min-height: 100%; + background: + linear-gradient(180deg, #f8edd6 0%, #f9f0de 24%, #f6ead4 100%); } -.hero__eyebrow { - font-size: 22rpx; - letter-spacing: 4rpx; - text-transform: uppercase; - color: #93c5fd; +.home-page { + position: relative; + min-height: 100vh; + box-sizing: border-box; + padding: 12rpx 18rpx 36rpx; + background: + linear-gradient(135deg, rgba(255, 255, 255, 0.65) 0%, rgba(255, 248, 236, 0) 36%), + linear-gradient(225deg, rgba(255, 245, 227, 0.7) 0%, rgba(255, 245, 227, 0) 44%); } -.hero__title { - font-size: 48rpx; +.home-page__brand { + display: inline-flex; + padding: 12rpx 20rpx; + border-radius: 999rpx; + background: rgba(241, 222, 190, 0.95); + color: #946739; + font-size: 26rpx; + line-height: 1; + box-shadow: 0 12rpx 24rpx rgba(173, 120, 56, 0.08); +} + +.home-page__greeting { + display: block; + margin-top: 12rpx; + font-family: 'STSong', 'Songti SC', serif; + font-size: 60rpx; font-weight: 700; + line-height: 1.1; + color: #2f1f12; +} + +.home-page__subtitle { + display: block; + margin-top: 10rpx; + font-family: 'STSong', 'Songti SC', serif; + font-size: 24rpx; + line-height: 1.65; + color: #8f6a42; +} + +.search-card { + display: flex; + align-items: center; + margin-top: 16rpx; + padding: 16rpx 18rpx 16rpx 20rpx; + border: 1rpx solid rgba(182, 151, 112, 0.24); + border-radius: 30rpx; + background: rgba(255, 251, 245, 0.96); + box-shadow: 0 16rpx 30rpx rgba(185, 152, 108, 0.14); +} + +.search-card__leading { + margin-right: 12rpx; + width: 40rpx; + height: 40rpx; + border-radius: 50%; + background: linear-gradient(135deg, #78c7dd 0%, #7b60bf 100%); + color: #ffffff; + font-size: 22rpx; + font-weight: 700; + line-height: 40rpx; + text-align: center; + box-shadow: 0 10rpx 18rpx rgba(123, 96, 191, 0.22); +} + +.search-card__placeholder { + flex: 1; + margin-right: 12rpx; + font-size: 26rpx; + color: #a18463; +} + +.search-card__badge { + flex-shrink: 0; + min-width: 80rpx; + padding: 10rpx 18rpx; + border-radius: 999rpx; + background: linear-gradient(135deg, #b67b35 0%, #8f5e21 100%); + color: #fff4de; + font-family: 'Georgia', 'Times New Roman', serif; + font-size: 24rpx; + font-weight: 700; + line-height: 1; + text-align: center; +} + +.section-card { + margin-top: 16rpx; + padding: 24rpx 18rpx 16rpx; + border: 1rpx solid rgba(189, 155, 105, 0.14); + border-radius: 30rpx; + background: rgba(255, 250, 243, 0.94); + box-shadow: 0 18rpx 36rpx rgba(178, 144, 99, 0.12); +} + +.section-card--last { + margin-bottom: 0; +} + +.section-card__header { + display: flex; + align-items: flex-end; + justify-content: space-between; + margin-bottom: 18rpx; + padding: 0 4rpx; +} + +.section-card__header .section-card__title { + margin-bottom: 0; + padding-left: 0; +} + +.section-card__title { + display: block; + margin-bottom: 18rpx; + padding-left: 4rpx; + font-family: 'STSong', 'Songti SC', serif; + font-size: 42rpx; + font-weight: 700; + line-height: 1.2; + color: #2f1f12; +} + +.section-card__action { + color: #be8b4a; + font-family: 'STSong', 'Songti SC', serif; + font-size: 24rpx; line-height: 1.2; } -.hero__summary { - font-size: 26rpx; - line-height: 1.8; - color: rgba(248, 250, 252, 0.8); +.portal-grid { + overflow: hidden; + margin: 0 -5rpx; } -.session-card { +.portal-grid__item { + box-sizing: border-box; + float: left; + width: 50%; + padding: 0 5rpx 10rpx; +} + +.portal-card { + min-height: 164rpx; + padding: 18rpx 18rpx 16rpx; + border: 1rpx solid rgba(191, 169, 140, 0.46); + border-radius: 24rpx; + background: linear-gradient(180deg, rgba(255, 255, 255, 0.78) 0%, rgba(250, 243, 232, 0.96) 100%); + box-shadow: 0 10rpx 20rpx rgba(182, 150, 107, 0.1); +} + +.portal-card__badge { + display: inline-block; + padding: 6rpx 14rpx; + border-radius: 999rpx; + background: rgba(246, 234, 214, 0.94); + color: #b67c31; + font-size: 18rpx; + line-height: 1; +} + +.portal-card__title { + display: block; + margin-top: 18rpx; + font-family: 'STSong', 'Songti SC', serif; + font-size: 28rpx; + font-weight: 700; + line-height: 1.2; + color: #38261a; +} + +.portal-card__subtitle { + display: block; + margin-top: 10rpx; + font-size: 22rpx; + line-height: 1.55; + color: #8f6a42; +} + +.feature-grid { display: flex; - flex-direction: column; - gap: 24rpx; - padding: 32rpx; + flex-wrap: wrap; + margin: 0 -4rpx; } -.session-card__rows { +.feature-grid__item { + box-sizing: border-box; + padding: 0 4rpx 10rpx; +} + +.feature-grid--four .feature-grid__item { + width: 25%; +} + +.feature-grid--three .feature-grid__item { + width: 33.3333%; +} + +.feature-card { + position: relative; display: flex; + box-sizing: border-box; + width: 100%; flex-direction: column; - gap: 20rpx; + align-items: center; + justify-content: center; + padding: 18rpx 8rpx; + border: 1rpx solid rgba(191, 169, 140, 0.46); + border-radius: 22rpx; + background: linear-gradient(180deg, rgba(255, 255, 255, 0.7) 0%, rgba(250, 243, 232, 0.92) 100%); } -.session-card__label { - flex-shrink: 0; +.feature-grid--four .feature-card { + min-height: 152rpx; +} + +.feature-grid--three .feature-card { + min-height: 182rpx; +} + +.feature-card__status { + position: absolute; + top: 8rpx; + right: 8rpx; + padding: 6rpx 12rpx; + border-radius: 999rpx; + background: rgba(246, 234, 214, 0.94); + color: #c39d6a; + font-size: 18rpx; + line-height: 1; +} + +.feature-card__icon { + width: 60rpx; + height: 60rpx; + border-radius: 20rpx; + background: linear-gradient(180deg, #f5ebdc 0%, #efe1cc 100%); + color: #b67c31; + font-size: 28rpx; + font-weight: 700; + line-height: 60rpx; + text-align: center; + box-shadow: inset 0 -8rpx 16rpx rgba(189, 150, 95, 0.08); +} + +.feature-card__emoji-box { + display: flex; + align-items: center; + justify-content: center; + width: 72rpx; + height: 72rpx; + border-radius: 20rpx; + background: linear-gradient(180deg, #f7efe3 0%, #efe0ca 100%); + box-shadow: inset 0 -8rpx 16rpx rgba(189, 150, 95, 0.06); +} + +.feature-card__emoji { + font-size: 36rpx; + line-height: 1; +} + +.feature-card__icon + .feature-card__title { + margin-top: 12rpx; +} + +.feature-card__title { + font-family: 'STSong', 'Songti SC', serif; + font-size: 22rpx; + font-weight: 700; + line-height: 1.35; + color: #38261a; + text-align: center; +} + +.feature-card__title--wellness { + margin-top: 16rpx; font-size: 24rpx; - color: #64748b; } -.session-card__value { - flex: 1; - text-align: right; - font-size: 24rpx; - color: #0f172a; +.feature-card--classic { + justify-content: flex-start; + padding-top: 18rpx; + padding-bottom: 14rpx; } -.session-card__value--mono { - font-family: 'Cascadia Code', 'Courier New', monospace; - word-break: break-all; +.feature-card__book { + position: relative; + width: 76rpx; + height: 112rpx; + border-radius: 8rpx; + background: + linear-gradient(90deg, rgba(138, 93, 48, 0.44) 0, rgba(138, 93, 48, 0.44) 10rpx, transparent 10rpx, transparent 100%), + linear-gradient(180deg, #cfaa75 0%, #c79d67 100%); + box-shadow: 0 10rpx 18rpx rgba(173, 128, 70, 0.12); } -.entry-list { - display: flex; - flex-direction: column; - gap: 24rpx; +.feature-card__book-char { + position: absolute; + top: 50%; + left: 10rpx; + right: 0; + transform: translateY(-50%); + color: #fff7eb; + font-family: 'STSong', 'Songti SC', serif; + font-size: 28rpx; + font-weight: 700; + line-height: 1; + text-align: center; } -.meta-row--top { - align-items: flex-start; +.feature-card__title--classic { + margin-top: 14rpx; + min-height: 56rpx; + padding: 0 4rpx; + font-size: 20rpx; + line-height: 1.45; } diff --git a/pages/home/tab-home-active.png b/pages/home/tab-home-active.png new file mode 100644 index 0000000..ff9a151 Binary files /dev/null and b/pages/home/tab-home-active.png differ diff --git a/pages/home/tab-home.png b/pages/home/tab-home.png new file mode 100644 index 0000000..90ee771 Binary files /dev/null and b/pages/home/tab-home.png differ diff --git a/pages/library/index.js b/pages/library/index.js new file mode 100644 index 0000000..420748b --- /dev/null +++ b/pages/library/index.js @@ -0,0 +1,350 @@ +const { ROUTES, openStaticRoute } = require('../../utils/static-ux/route-map') + +const LIBRARY_CATEGORIES = Object.freeze([ + { id: 'featured', name: '精选' }, + { id: 'annotation', name: '注解' }, + { id: 'classic-theory', name: '经论' }, + { id: 'cold-damage', name: '伤寒金匮' }, + { id: 'formula', name: '医方' }, + { id: 'materia-medica', name: '本草' }, + { id: 'acupuncture', name: '针灸' }, + { id: 'medical-record', name: '医案' }, + { id: 'comprehensive', name: '综合' } +]) + +const LIBRARY_BOOKS = Object.freeze({ + featured: [ + { + id: 'huangdi-neijing-suwen', + coverText: '典', + title: '黄帝内经素问', + author: '王冰次', + dynasty: '唐', + period: '公元762年' + }, + { + id: 'shang-han-lun', + coverText: '典', + title: '伤寒论', + author: '张仲景', + dynasty: '东汉', + period: '公元25-220年' + }, + { + id: 'wen-bing-tiao-bian', + coverText: '典', + title: '温病条辨', + author: '吴鞠通', + dynasty: '清', + period: '公元1644-1911年' + }, + { + id: 'bencao-gangmu-bieming-lu', + coverText: '典', + title: '本草纲目别名录', + author: '李时珍', + dynasty: '明', + period: '公元1368-1644年' + }, + { + id: 'huangdi-neijing-jizhu', + coverText: '典', + title: '黄帝内经灵枢集注', + author: '张志聪', + dynasty: '清', + period: '公元1672年' + }, + { + id: 'jingui-yaolue-fanglun', + coverText: '典', + title: '金匮要略方论', + author: '张仲景', + dynasty: '汉', + period: '公元219年' + } + ], + annotation: [ + { + id: 'huangdi-neijing-suwen-jizhu', + coverText: '典', + title: '黄帝内经素问集注', + author: '张志聪', + dynasty: '清', + period: '公元1672年' + }, + { + id: 'shennong-bencao-jizhu', + coverText: '典', + title: '神农本草经辑注', + author: '孙星衍', + dynasty: '清', + period: '公元1796年' + }, + { + id: 'nanjing-jizhu', + coverText: '典', + title: '难经集注', + author: '王九思', + dynasty: '明', + period: '公元1506年' + } + ], + 'classic-theory': [ + { + id: 'nan-jing', + coverText: '典', + title: '难经', + author: '扁鹊', + dynasty: '战国', + period: '约公元前475-221年' + }, + { + id: 'lei-jing', + coverText: '典', + title: '类经', + author: '张介宾', + dynasty: '明', + period: '公元1624年' + }, + { + id: 'yi-zong-bi-du', + coverText: '典', + title: '医宗必读', + author: '李中梓', + dynasty: '明', + period: '公元1637年' + } + ], + 'cold-damage': [ + { + id: 'jin-kui-yao-lve', + coverText: '典', + title: '金匮要略', + author: '张仲景', + dynasty: '东汉', + period: '公元219年' + }, + { + id: 'zhu-jie-shang-han-lun', + coverText: '典', + title: '注解伤寒论', + author: '成无己', + dynasty: '金', + period: '公元1144年' + }, + { + id: 'shang-han-ming-li-lun', + coverText: '典', + title: '伤寒明理论', + author: '成无己', + dynasty: '金', + period: '公元1156年' + } + ], + formula: [ + { + id: 'tai-ping-hui-min-he-ji-ju-fang', + coverText: '典', + title: '太平惠民和剂局方', + author: '陈师文', + dynasty: '宋', + period: '公元1107年' + }, + { + id: 'pu-ji-fang', + coverText: '典', + title: '普济方', + author: '朱橚', + dynasty: '明', + period: '公元1406年' + }, + { + id: 'yi-fang-kao', + coverText: '典', + title: '医方考', + author: '吴昆', + dynasty: '明', + period: '公元1584年' + } + ], + 'materia-medica': [ + { + id: 'shennong-bencao-jing', + coverText: '典', + title: '神农本草经', + author: '佚名', + dynasty: '汉', + period: '约公元1-2世纪' + }, + { + id: 'bencao-gangmu', + coverText: '典', + title: '本草纲目', + author: '李时珍', + dynasty: '明', + period: '公元1578年' + }, + { + id: 'bencao-cong-xin', + coverText: '典', + title: '本草从新', + author: '吴仪洛', + dynasty: '清', + period: '公元1757年' + } + ], + acupuncture: [ + { + id: 'zhen-jiu-jia-yi-jing', + coverText: '典', + title: '针灸甲乙经', + author: '皇甫谧', + dynasty: '西晋', + period: '公元282年' + }, + { + id: 'zhen-jiu-da-cheng', + coverText: '典', + title: '针灸大成', + author: '杨继洲', + dynasty: '明', + period: '公元1601年' + }, + { + id: 'tong-ren-shu-xue-zhen-jiu-tu-jing', + coverText: '典', + title: '铜人腧穴针灸图经', + author: '王惟一', + dynasty: '宋', + period: '公元1026年' + } + ], + 'medical-record': [ + { + id: 'lin-zheng-zhi-nan-yi-an', + coverText: '典', + title: '临证指南医案', + author: '叶天士', + dynasty: '清', + period: '公元1746年' + }, + { + id: 'shi-an-de-xiao-fang', + coverText: '典', + title: '石案得效方', + author: '危亦林', + dynasty: '元', + period: '公元1337年' + }, + { + id: 'wang-meng-ying-yi-an', + coverText: '典', + title: '王孟英医案', + author: '王孟英', + dynasty: '清', + period: '公元1862年' + } + ], + comprehensive: [ + { + id: 'yi-xue-ru-men', + coverText: '典', + title: '医学入门', + author: '李梴', + dynasty: '明', + period: '公元1575年' + }, + { + id: 'jing-yue-quan-shu', + coverText: '典', + title: '景岳全书', + author: '张介宾', + dynasty: '明', + period: '公元1624年' + }, + { + id: 'yi-zong-jin-jian', + coverText: '典', + title: '医宗金鉴', + author: '吴谦', + dynasty: '清', + period: '公元1742年' + } + ] +}) + +function cloneItems(items) { + return items.map(item => ({ ...item })) +} + +function getCategoryById(categoryId) { + return LIBRARY_CATEGORIES.find(item => item.id === categoryId) || LIBRARY_CATEGORIES[0] +} + +function getBooksByCategory(categoryId) { + const activeCategory = getCategoryById(categoryId) + return cloneItems(LIBRARY_BOOKS[activeCategory.id] || []) +} + +function createLibraryPageData(categoryId) { + const activeCategory = getCategoryById(categoryId) + + return { + title: '典籍', + searchButtonText: '搜索', + domainBridge: { + title: '进入易学阁', + subtitle: '切换到命理与经典的静态学习入口', + route: ROUTES.mingli.hall + }, + categories: cloneItems(LIBRARY_CATEGORIES), + activeCategoryId: activeCategory.id, + currentCategoryName: activeCategory.name, + visibleBooks: getBooksByCategory(activeCategory.id) + } +} + +function showNavigate(route) { + if (openStaticRoute(route, wx)) { + return + } + + if (typeof wx?.showToast === 'function') { + wx.showToast({ + title: '页面暂不可达', + icon: 'none' + }) + } +} + +Page({ + data: createLibraryPageData(), + + handleCategoryTap(event) { + const categoryId = event.currentTarget.dataset.categoryId + + this.setData(createLibraryPageData(categoryId)) + }, + + handleSearchTap() { + if (!wx || typeof wx.showToast !== 'function') { + return + } + + wx.showToast({ + title: '搜索功能暂未开放', + icon: 'none' + }) + }, + + handleDomainBridgeTap() { + showNavigate(ROUTES.mingli.hall) + } +}) + +module.exports = { + LIBRARY_CATEGORIES, + LIBRARY_BOOKS, + createLibraryPageData, + getBooksByCategory +} diff --git a/pages/library/index.json b/pages/library/index.json new file mode 100644 index 0000000..33b7698 --- /dev/null +++ b/pages/library/index.json @@ -0,0 +1,5 @@ +{ + "navigationBarTitleText": "典籍", + "navigationBarBackgroundColor": "#f8ecd8", + "navigationBarTextStyle": "black" +} diff --git a/pages/library/index.wxml b/pages/library/index.wxml new file mode 100644 index 0000000..a9fffa9 --- /dev/null +++ b/pages/library/index.wxml @@ -0,0 +1,43 @@ + + + {{title}} + {{searchButtonText}} + + + + + + {{item.name}} + + + + + + {{domainBridge.title}} + {{domainBridge.subtitle}} + + + 当前分类: {{currentCategoryName}} + + + + + + {{item.coverText}} + + + + {{item.title}} + {{item.author}} / {{item.dynasty}} / {{item.period}} + + + + + + diff --git a/pages/library/index.wxss b/pages/library/index.wxss new file mode 100644 index 0000000..769aed7 --- /dev/null +++ b/pages/library/index.wxss @@ -0,0 +1,205 @@ +page { + min-height: 100%; + background: + linear-gradient(180deg, #f8edd6 0%, #f9f0de 24%, #f6ead4 100%); +} + +.library-page { + box-sizing: border-box; + min-height: 100vh; + padding: 36rpx 20rpx 80rpx; + background: + linear-gradient(135deg, rgba(255, 255, 255, 0.58) 0%, rgba(255, 248, 236, 0) 36%), + linear-gradient(225deg, rgba(255, 245, 227, 0.68) 0%, rgba(255, 245, 227, 0) 48%); +} + +.library-page__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 18rpx 2rpx 30rpx; +} + +.library-page__title { + font-family: 'STSong', 'Songti SC', serif; + font-size: 60rpx; + font-weight: 700; + line-height: 1.2; + color: #2f1f12; +} + +.library-page__search { + min-width: 112rpx; + padding: 18rpx 26rpx; + border-radius: 999rpx; + background: rgba(240, 225, 201, 0.9); + box-shadow: 0 12rpx 24rpx rgba(173, 120, 56, 0.08); + color: #a56d2d; + font-family: 'STSong', 'Songti SC', serif; + font-size: 30rpx; + line-height: 1; + text-align: center; +} + +.library-page__content { + display: flex; + align-items: stretch; +} + +.library-page__sidebar { + box-sizing: border-box; + width: 168rpx; + flex-shrink: 0; + padding: 16rpx 14rpx; + border: 1rpx solid rgba(189, 155, 105, 0.12); + border-radius: 34rpx; + background: rgba(255, 250, 243, 0.86); + box-shadow: 0 16rpx 32rpx rgba(178, 144, 99, 0.08); +} + +.library-page__category { + padding: 22rpx 16rpx; + border-radius: 24rpx; +} + +.library-page__category + .library-page__category { + margin-top: 14rpx; +} + +.library-page__category--active { + background: linear-gradient(180deg, rgba(240, 228, 207, 0.98) 0%, rgba(236, 221, 197, 0.98) 100%); + box-shadow: inset 0 -8rpx 18rpx rgba(186, 149, 99, 0.08); +} + +.library-page__category-label { + display: block; + color: #8f6a42; + font-family: 'STSong', 'Songti SC', serif; + font-size: 32rpx; + line-height: 1.5; +} + +.library-page__category--active .library-page__category-label { + color: #7b501f; + font-weight: 700; +} + +.library-page__main { + flex: 1; + margin-left: 16rpx; +} + +.library-page__bridge { + padding: 26rpx 26rpx 24rpx; + border: 1rpx solid rgba(191, 154, 103, 0.2); + border-radius: 34rpx; + background: linear-gradient(135deg, rgba(255, 249, 239, 0.96) 0%, rgba(245, 233, 214, 0.98) 100%); + box-shadow: 0 16rpx 28rpx rgba(176, 136, 84, 0.1); +} + +.library-page__bridge-title { + display: block; + color: #6f451c; + font-family: 'STSong', 'Songti SC', serif; + font-size: 36rpx; + font-weight: 700; + line-height: 1.35; +} + +.library-page__bridge-subtitle { + display: block; + margin-top: 10rpx; + color: #97714a; + font-size: 26rpx; + line-height: 1.7; +} + +.library-page__current { + margin-top: 16rpx; + padding: 30rpx 26rpx; + border: 1rpx solid rgba(189, 155, 105, 0.12); + border-radius: 34rpx 34rpx 0 0; + background: rgba(255, 250, 243, 0.88); + color: #9c6d35; + font-family: 'STSong', 'Songti SC', serif; + font-size: 30rpx; + line-height: 1.4; +} + +.library-page__book-list { + padding: 0 0 8rpx; + border: 1rpx solid rgba(189, 155, 105, 0.08); + border-top: 0; + border-radius: 0 0 34rpx 34rpx; + background: rgba(255, 250, 243, 0.72); +} + +.book-card { + display: flex; + align-items: center; + margin: 18rpx; + padding: 22rpx 20rpx; + border: 1rpx solid rgba(192, 159, 117, 0.14); + border-radius: 28rpx; + background: rgba(255, 252, 247, 0.96); + box-shadow: 0 12rpx 24rpx rgba(181, 145, 97, 0.08); +} + +.book-card__cover { + position: relative; + width: 100rpx; + height: 136rpx; + flex-shrink: 0; + border-radius: 22rpx; + background: linear-gradient(180deg, #d5b07b 0%, #c99f67 100%); + box-shadow: 0 10rpx 18rpx rgba(173, 128, 70, 0.14); +} + +.book-card__cover-spine { + position: absolute; + top: 0; + left: 0; + width: 12rpx; + height: 100%; + border-top-left-radius: 22rpx; + border-bottom-left-radius: 22rpx; + background: rgba(138, 93, 48, 0.4); +} + +.book-card__cover-char { + position: absolute; + top: 50%; + left: 12rpx; + right: 0; + transform: translateY(-50%); + color: #fff7eb; + font-family: 'STSong', 'Songti SC', serif; + font-size: 42rpx; + font-weight: 700; + line-height: 1; + text-align: center; +} + +.book-card__body { + min-width: 0; + flex: 1; + margin-left: 18rpx; +} + +.book-card__title { + display: block; + color: #2f1f12; + font-family: 'STSong', 'Songti SC', serif; + font-size: 42rpx; + font-weight: 700; + line-height: 1.35; +} + +.book-card__meta { + display: block; + margin-top: 14rpx; + color: #8f6a42; + font-family: 'STSong', 'Songti SC', serif; + font-size: 28rpx; + line-height: 1.6; +} diff --git a/pages/library/tab-library-active.png b/pages/library/tab-library-active.png new file mode 100644 index 0000000..28eeb64 Binary files /dev/null and b/pages/library/tab-library-active.png differ diff --git a/pages/library/tab-library.png b/pages/library/tab-library.png new file mode 100644 index 0000000..563ad0e Binary files /dev/null and b/pages/library/tab-library.png differ diff --git a/pages/login/index.js b/pages/login/index.js index 9bcfee2..3c975f0 100644 --- a/pages/login/index.js +++ b/pages/login/index.js @@ -1,75 +1,58 @@ -const { sessionStore } = require('../../stores') -const { formatDateTime } = require('../../utils/util') +const { ROUTES, openStaticRoute } = require('../../utils/static-ux/route-map') + +function createLoginPageData() { + return { + title: '欢迎来到玄志', + subtitle: '当前阶段先提供静态登录页,后续会接入新的认证方案与后端接口。', + highlight: 'Static Login', + introTitle: '先把页面走通', + introDescription: '本页只保留 UI 和静态交互,用于承接首页、我的页和后续学习入口的跳转。', + notes: [ + '不会写入 session、token 或本地用户信息', + '不会复用旧 frontend 的认证 API 和表单结构', + '保留一个“先看看静态页面”的直接入口' + ], + actions: [ + { + key: 'wechat', + title: '微信授权登录', + description: '静态按钮,占位等待新认证方案', + actionType: 'toast' + }, + { + key: 'browse', + title: '先看看静态页面', + description: '直接返回首页,继续浏览已迁移页面', + actionType: 'route', + route: ROUTES.tabs.home + } + ] + } +} + +function showComingSoon() { + if (typeof wx?.showToast === 'function') { + wx.showToast({ + title: '登录功能建设中', + icon: 'none' + }) + } +} Page({ - data: { - submitting: false, - form: { - nickname: '', - mobile: '' - }, - mockHint: `请求层已接好,可把接口替换为真实后端。当前时间:${formatDateTime(new Date())}` - }, - handleNicknameChange(event) { - this.setData({ - 'form.nickname': event.detail.value - }) - }, - handleMobileChange(event) { - this.setData({ - 'form.mobile': event.detail.value - }) - }, - handleMockLogin() { - const { nickname, mobile } = this.data.form + data: createLoginPageData(), - if (!nickname.trim()) { - wx.showToast({ - title: '请输入昵称', - icon: 'none' - }) + handleActionTap(event) { + const { actionType, route } = event.currentTarget.dataset + + if (actionType === 'route' && openStaticRoute(route, wx)) { return } - this.setData({ - submitting: true - }) - - sessionStore.setSession({ - token: `mock_${Date.now()}`, - userInfo: { - nickname: nickname.trim(), - mobile: mobile.trim() - }, - permissions: ['workbench:view', 'profile:update'] - }) - - wx.showToast({ - title: '模拟登录成功', - icon: 'success' - }) - - this.setData({ - submitting: false - }) - - setTimeout(() => { - wx.reLaunch({ - url: '/pages/home/index' - }) - }, 300) - }, - handleClearSession() { - sessionStore.clearSession() - this.setData({ - form: { - nickname: '', - mobile: '' - } - }) - wx.showToast({ - title: '已清理', - icon: 'success' - }) + showComingSoon() } }) + +module.exports = { + createLoginPageData +} diff --git a/pages/login/index.json b/pages/login/index.json index 9e105ac..d240ade 100644 --- a/pages/login/index.json +++ b/pages/login/index.json @@ -1,8 +1,5 @@ { "navigationBarTitleText": "登录", - "usingComponents": { - "app-button": "../../components/base/app-button/index", - "t-input": "tdesign-miniprogram/input/input", - "t-tag": "tdesign-miniprogram/tag/tag" - } + "navigationBarBackgroundColor": "#f8ecd8", + "navigationBarTextStyle": "black" } diff --git a/pages/login/index.wxml b/pages/login/index.wxml index 9899a03..a7e5912 100644 --- a/pages/login/index.wxml +++ b/pages/login/index.wxml @@ -1,39 +1,30 @@ - -