feat: migrate static pages to native tabbar

This commit is contained in:
2026-04-23 21:25:24 +08:00
parent f3cd0c3a98
commit cd30f57f2c
116 changed files with 7143 additions and 311 deletions

View File

@@ -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
}

View File

@@ -0,0 +1,5 @@
{
"navigationBarTitleText": "学习资产",
"navigationBarBackgroundColor": "#f8ecd8",
"navigationBarTextStyle": "black"
}

View File

@@ -0,0 +1,36 @@
<view class="asset-page">
<view class="asset-page__header">
<text class="asset-page__title">{{title}}</text>
</view>
<view class="asset-page__filters">
<view
class="asset-page__filter {{activeKind === item.key ? 'asset-page__filter--active' : ''}}"
wx:for="{{filterItems}}"
wx:key="key"
data-kind="{{item.key}}"
bindtap="handleFilterTap"
>
<text class="asset-page__filter-label">{{item.label}}</text>
</view>
</view>
<view wx:if="{{items.length}}" class="asset-page__list">
<view
class="asset-card"
wx:for="{{items}}"
wx:key="key"
data-route="{{item.route}}"
bindtap="handleEntryTap"
>
<text class="asset-card__title">{{item.title}}</text>
<text class="asset-card__subtitle">{{item.subtitle}}</text>
<text class="asset-card__description">{{item.description}}</text>
</view>
</view>
<view wx:else class="asset-page__empty">
<text class="asset-page__empty-title">{{emptyTitle}}</text>
<text class="asset-page__empty-description">{{emptyDescription}}</text>
</view>
</view>

View File

@@ -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;
}