feat: 小程序阅读记录与资料链路、管理端用户规则、API/VIP/推荐与运营脚本
- miniprogram: reading-records、imageUrl/mpNavigate、多页资料与 VIP 展示调整 - soul-admin: Users/Settings/UserDetailModal、dist 构建产物更新 - soul-api: user/vip/referral/ckb/db、MBTI 头像管理、user_rule_completion、迁移 SQL - .cursor: karuo-party 与飞书文档;.gitignore 忽略 .tmp_skill_bundle Made-with: Cursor
This commit is contained in:
@@ -26,6 +26,7 @@ Page({
|
||||
|
||||
// 展开状态
|
||||
expandedPart: null,
|
||||
bookCollapsed: false,
|
||||
|
||||
// 已加载的篇章章节缓存 { partId: chapters }
|
||||
_loadedChapters: {},
|
||||
@@ -47,7 +48,11 @@ Page({
|
||||
partsLoading: true,
|
||||
|
||||
// 功能配置(搜索开关)
|
||||
searchEnabled: true
|
||||
searchEnabled: true,
|
||||
|
||||
// mp_config.mpUi.chaptersPage
|
||||
chaptersBookTitle: '一场SOUL的创业实验场',
|
||||
chaptersBookSubtitle: '来自Soul派对房的真实商业故事'
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
@@ -62,10 +67,20 @@ Page({
|
||||
this.loadFeatureConfig()
|
||||
},
|
||||
|
||||
_applyChaptersMpUi() {
|
||||
const c = app.globalData.configCache?.mpConfig?.mpUi?.chaptersPage || {}
|
||||
this.setData({
|
||||
chaptersBookTitle: String(c.bookTitle || '一场SOUL的创业实验场').trim() || '一场SOUL的创业实验场',
|
||||
chaptersBookSubtitle: String(c.bookSubtitle || '来自Soul派对房的真实商业故事').trim() ||
|
||||
'来自Soul派对房的真实商业故事'
|
||||
})
|
||||
},
|
||||
|
||||
async loadFeatureConfig() {
|
||||
try {
|
||||
if (app.globalData.features && typeof app.globalData.features.searchEnabled === 'boolean') {
|
||||
this.setData({ searchEnabled: app.globalData.features.searchEnabled })
|
||||
this._applyChaptersMpUi()
|
||||
return
|
||||
}
|
||||
const res = await app.getConfig()
|
||||
@@ -74,8 +89,10 @@ Page({
|
||||
if (!app.globalData.features) app.globalData.features = {}
|
||||
app.globalData.features.searchEnabled = searchEnabled
|
||||
this.setData({ searchEnabled })
|
||||
this._applyChaptersMpUi()
|
||||
} catch (e) {
|
||||
this.setData({ searchEnabled: true })
|
||||
this._applyChaptersMpUi()
|
||||
}
|
||||
},
|
||||
|
||||
@@ -92,7 +109,6 @@ Page({
|
||||
totalSections = res.totalSections ?? 0
|
||||
fixedSections = res.fixedSections || []
|
||||
}
|
||||
const numbers = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二']
|
||||
const fixedMap = {}
|
||||
fixedSections.forEach(f => { fixedMap[f.id] = f.mid })
|
||||
const appendixList = [
|
||||
@@ -100,13 +116,14 @@ Page({
|
||||
{ id: 'appendix-2', title: '附录2|创业者自检清单', mid: fixedMap['appendix-2'] },
|
||||
{ id: 'appendix-3', title: '附录3|本书提到的工具和资源', mid: fixedMap['appendix-3'] }
|
||||
]
|
||||
const bookData = parts.map((p, idx) => ({
|
||||
const bookData = parts.map((p) => ({
|
||||
id: p.id,
|
||||
number: numbers[idx] || String(idx + 1),
|
||||
icon: p.icon || '',
|
||||
title: p.title,
|
||||
subtitle: p.subtitle || '',
|
||||
chapterCount: p.chapterCount || 0,
|
||||
chapters: [] // 展开时懒加载
|
||||
chapters: [],
|
||||
alwaysShow: (p.title || '').indexOf('每日派对干货') > -1
|
||||
}))
|
||||
app.globalData.totalSections = totalSections
|
||||
this.setData({
|
||||
@@ -186,6 +203,7 @@ Page({
|
||||
},
|
||||
|
||||
onShow() {
|
||||
this._applyChaptersMpUi()
|
||||
// 设置TabBar选中状态
|
||||
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
|
||||
const tabBar = this.getTabBar()
|
||||
@@ -225,7 +243,11 @@ Page({
|
||||
this.setData({ isLoggedIn, hasFullBook, purchasedSections, isVip })
|
||||
},
|
||||
|
||||
// 切换展开状态,展开时懒加载该篇章章节
|
||||
toggleBookCollapse() {
|
||||
trackClick('chapters', 'btn_click', '折叠书名')
|
||||
this.setData({ bookCollapsed: !this.data.bookCollapsed })
|
||||
},
|
||||
|
||||
async togglePart(e) {
|
||||
trackClick('chapters', 'tab_click', e.currentTarget.dataset.id || '篇章')
|
||||
const partId = e.currentTarget.dataset.id
|
||||
|
||||
@@ -34,18 +34,21 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 书籍信息卡 -->
|
||||
<view class="book-info-card card-gradient" wx:if="{{!partsLoading}}">
|
||||
<!-- 书籍信息卡(点击折叠/展开除"每日派对干货"外的篇章) -->
|
||||
<view class="book-info-card card-gradient" wx:if="{{!partsLoading}}" bindtap="toggleBookCollapse">
|
||||
<view class="book-icon">
|
||||
<view class="book-icon-inner"><icon name="book-open" size="56" color="#ffffff"></icon></view>
|
||||
</view>
|
||||
<view class="book-info">
|
||||
<text class="book-title">一场SOUL的创业实验场</text>
|
||||
<text class="book-subtitle">来自Soul派对房的真实商业故事</text>
|
||||
<text class="book-title">{{chaptersBookTitle}}</text>
|
||||
<text class="book-subtitle">{{chaptersBookSubtitle}}</text>
|
||||
</view>
|
||||
<view class="book-count">
|
||||
<text class="count-value brand-color">{{totalSections}}</text>
|
||||
<text class="count-label">章节</text>
|
||||
<view class="book-right-area">
|
||||
<view class="book-count">
|
||||
<text class="count-value brand-color">{{totalSections}}</text>
|
||||
<text class="count-label">章节</text>
|
||||
</view>
|
||||
<text class="book-collapse-hint">{{bookCollapsed ? '展开 ▸' : '折叠 ▾'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -65,11 +68,12 @@
|
||||
|
||||
<!-- 篇章列表 -->
|
||||
<view class="part-list">
|
||||
<view class="part-item" wx:for="{{bookData}}" wx:key="id">
|
||||
<view class="part-item" wx:for="{{bookData}}" wx:key="id" wx:if="{{!bookCollapsed || item.alwaysShow}}">
|
||||
<!-- 篇章标题 -->
|
||||
<view class="part-header" bindtap="togglePart" data-id="{{item.id}}">
|
||||
<view class="part-left">
|
||||
<view class="part-icon">{{item.number}}</view>
|
||||
<image wx:if="{{item.icon}}" class="part-icon-img" src="{{item.icon}}" mode="aspectFit"/>
|
||||
<view wx:else class="part-icon">{{item.title[0] || '篇'}}</view>
|
||||
<view class="part-info">
|
||||
<text class="part-title">{{item.title}}</text>
|
||||
<text class="part-subtitle">{{item.subtitle}}</text>
|
||||
|
||||
@@ -195,20 +195,11 @@
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.book-count {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.count-value {
|
||||
font-size: 40rpx;
|
||||
font-weight: 700;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.count-label {
|
||||
font-size: 20rpx;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
.book-right-area { display: flex; flex-direction: column; align-items: flex-end; gap: 8rpx; }
|
||||
.book-count { text-align: right; }
|
||||
.count-value { font-size: 40rpx; font-weight: 700; display: block; }
|
||||
.count-label { font-size: 20rpx; color: rgba(255, 255, 255, 0.4); }
|
||||
.book-collapse-hint { font-size: 20rpx; color: #00CED1; opacity: 0.7; }
|
||||
|
||||
/* ===== 目录内容 ===== */
|
||||
.chapters-content {
|
||||
@@ -365,6 +356,9 @@
|
||||
color: #ffffff;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.part-icon-img {
|
||||
width: 64rpx; height: 64rpx; border-radius: 16rpx; flex-shrink: 0;
|
||||
}
|
||||
|
||||
.part-info {
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user