diff --git a/miniprogram/README.md b/miniprogram/README.md
index 06118089..2cc11dbf 100644
--- a/miniprogram/README.md
+++ b/miniprogram/README.md
@@ -35,7 +35,8 @@ miniprogram/
│ ├── purchases/ # 订单页
│ └── settings/ # 设置页
├── utils/
-│ └── util.js # 工具函数
+│ ├── util.js # 工具函数
+│ └── payment.js # 支付工具
├── assets/
│ └── icons/ # 图标资源
├── project.config.json # 项目配置
diff --git a/miniprogram/app.js b/miniprogram/app.js
index 92a9ca12..e6522742 100644
--- a/miniprogram/app.js
+++ b/miniprogram/app.js
@@ -88,10 +88,7 @@ App({
isSinglePageMode: false,
// 更新检测:上次检测时间戳,避免频繁请求
- lastUpdateCheck: 0,
-
- // 审核模式:后端 /api/miniprogram/config 返回 auditMode=true 时隐藏所有支付相关UI
- auditMode: false
+ lastUpdateCheck: 0
},
onLaunch(options) {
@@ -363,7 +360,6 @@ App({
this.globalData.mchId = mpConfig.mchId || this.globalData.mchId
this.globalData.withdrawSubscribeTmplId = mpConfig.withdrawSubscribeTmplId || this.globalData.withdrawSubscribeTmplId
this.globalData.supportWechat = mpConfig.supportWechat || mpConfig.customerWechat || mpConfig.serviceWechat || ''
- this.globalData.auditMode = mpConfig.auditMode || false
try {
wx.setStorageSync('apiBaseUrl', this.globalData.baseUrl)
} catch (_) {}
@@ -513,7 +509,7 @@ App({
},
fail: (err) => {
const msg = (err && err.errMsg) ? (err.errMsg.indexOf('timeout') !== -1 ? '请求超时,请重试' : '网络异常,请重试') : '网络异常,请重试'
- console.warn('[App] request fail:', url, msg)
+ showError(msg)
reject(new Error(msg))
}
})
@@ -531,6 +527,7 @@ App({
})
if (!loginRes || !loginRes.code) {
console.warn('[App] wx.login 未返回 code')
+ wx.showToast({ title: '获取登录态失败,请重试', icon: 'none' })
return null
}
try {
@@ -572,13 +569,16 @@ App({
return res.data
}
} catch (apiError) {
- console.warn('[App] API登录失败:', apiError.message)
+ console.log('[App] API登录失败:', apiError.message)
+ // 不使用模拟登录,提示用户网络问题
+ wx.showToast({ title: '网络异常,请重试', icon: 'none' })
return null
}
return null
} catch (e) {
- console.warn('[App] 登录失败:', e)
+ console.error('[App] 登录失败:', e)
+ wx.showToast({ title: '登录失败,请重试', icon: 'none' })
return null
}
},
@@ -630,7 +630,7 @@ App({
return res.data.openId
}
} catch (e) {
- console.warn('[App] 获取openId失败:', e)
+ console.error('[App] 获取openId失败:', e)
}
return null
@@ -646,7 +646,7 @@ App({
wx.login({ success: resolve, fail: reject })
})
if (!loginRes.code) {
- console.warn('[App] loginWithPhone: wx.login 未返回 code')
+ wx.showToast({ title: '获取登录态失败', icon: 'none' })
return null
}
const res = await this.request('/api/miniprogram/phone-login', {
@@ -677,7 +677,8 @@ App({
return res.data
}
} catch (e) {
- console.warn('[App] 手机号登录失败:', e)
+ console.log('[App] 手机号登录失败:', e)
+ wx.showToast({ title: '登录失败,请重试', icon: 'none' })
}
return null
diff --git a/miniprogram/app.json b/miniprogram/app.json
index dcb27c14..9e9f1075 100644
--- a/miniprogram/app.json
+++ b/miniprogram/app.json
@@ -6,6 +6,7 @@
"pages/my/my",
"pages/read/read",
"pages/link-preview/link-preview",
+ "pages/about/about",
"pages/agreement/agreement",
"pages/privacy/privacy",
"pages/referral/referral",
@@ -15,16 +16,13 @@
"pages/addresses/addresses",
"pages/addresses/edit",
"pages/withdraw-records/withdraw-records",
- "pages/wallet/wallet",
"pages/vip/vip",
"pages/member-detail/member-detail",
"pages/mentors/mentors",
"pages/mentor-detail/mentor-detail",
"pages/profile-show/profile-show",
"pages/profile-edit/profile-edit",
- "pages/avatar-nickname/avatar-nickname",
- "pages/gift-pay/detail",
- "pages/gift-pay/list"
+ "pages/wallet/wallet"
],
"window": {
"backgroundTextStyle": "light",
diff --git a/miniprogram/pages/agreement/agreement.wxml b/miniprogram/pages/agreement/agreement.wxml
index a1987185..4060b50c 100644
--- a/miniprogram/pages/agreement/agreement.wxml
+++ b/miniprogram/pages/agreement/agreement.wxml
@@ -31,7 +31,7 @@
我们可能适时修订本协议,修订后将在小程序内公示。若您继续使用服务,即视为接受修订后的协议。
七、联系我们
- 如有疑问,请通过 Soul 派对房与我们联系。
+ 如有疑问,请通过小程序内「关于作者」或 Soul 派对房与我们联系。
diff --git a/miniprogram/pages/chapters/chapters.js b/miniprogram/pages/chapters/chapters.js
index 0d20b06d..b7762e22 100644
--- a/miniprogram/pages/chapters/chapters.js
+++ b/miniprogram/pages/chapters/chapters.js
@@ -31,10 +31,7 @@ Page({
appendixList: [],
// 每日新增章节
- dailyChapters: [],
-
- // 审核模式
- auditMode: false
+ dailyChapters: []
},
onLoad() {
@@ -186,7 +183,6 @@ Page({
tabBar.setData({ selected: 1 })
}
}
- this.setData({ auditMode: app.globalData.auditMode })
this.updateUserStatus()
this.loadVipStatus()
},
diff --git a/miniprogram/pages/chapters/chapters.wxml b/miniprogram/pages/chapters/chapters.wxml
index 9f734b15..9c25e577 100644
--- a/miniprogram/pages/chapters/chapters.wxml
+++ b/miniprogram/pages/chapters/chapters.wxml
@@ -5,7 +5,7 @@
-
+
🔍
@@ -17,14 +17,8 @@
-
-
-
- 加载目录中...
-
-
-
+
📚
@@ -39,34 +33,9 @@
-
-
-
-
-
-
-
-
- {{item.title}}
- {{item.dateStr}} · ¥{{item.price}}
-
- ›
-
-
-
-
-
-
+
+
+
📖
序言|为什么我每天早上6点在Soul开播?
@@ -90,15 +59,14 @@
- {{item.chapters.length || item.chapterCount}}章
+ {{item.chapters.length}}章
→
-
+
- 加载中...
@@ -113,7 +81,6 @@
免费
已解锁
- 待开放
¥{{section.price}}
›
@@ -126,8 +93,8 @@
-
-
+
+
📖
尾声|这本书的真实目的
@@ -148,7 +115,6 @@
wx:key="id"
bindtap="goToRead"
data-id="{{item.id}}"
- data-mid="{{item.mid}}"
>
{{item.title}}
→
diff --git a/miniprogram/pages/chapters/chapters.wxss b/miniprogram/pages/chapters/chapters.wxss
index dbf5d2b7..890390e8 100644
--- a/miniprogram/pages/chapters/chapters.wxss
+++ b/miniprogram/pages/chapters/chapters.wxss
@@ -75,34 +75,6 @@
width: 100%;
}
-/* ===== 目录加载中 ===== */
-.parts-loading {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 120rpx 0;
- gap: 24rpx;
-}
-
-.parts-loading-spinner {
- width: 64rpx;
- height: 64rpx;
- border: 6rpx solid rgba(255, 255, 255, 0.1);
- border-top-color: #00CED1;
- border-radius: 50%;
- animation: parts-spin 0.8s linear infinite;
-}
-
-.parts-loading-text {
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.5);
-}
-
-@keyframes parts-spin {
- to { transform: rotate(360deg); }
-}
-
/* ===== 书籍信息卡 ===== */
.book-info-card {
display: flex;
@@ -174,89 +146,6 @@
box-sizing: border-box;
}
-/* ===== 每日新增 ===== */
-.daily-section {
- margin-bottom: 32rpx;
- padding: 24rpx;
- background: #1c1c1e;
- border-radius: 24rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.05);
-}
-
-.daily-header {
- display: flex;
- align-items: center;
- gap: 16rpx;
- margin-bottom: 24rpx;
-}
-
-.daily-title {
- font-size: 30rpx;
- font-weight: 600;
- color: #ffffff;
-}
-
-.daily-badge {
- font-size: 22rpx;
- padding: 4rpx 12rpx;
- background: #F6AD55;
- color: #ffffff;
- border-radius: 20rpx;
-}
-
-.daily-list {
- display: flex;
- flex-direction: column;
- gap: 0;
-}
-
-.daily-item {
- display: flex;
- align-items: center;
- padding: 16rpx 0;
- border-bottom: 1rpx solid rgba(255, 255, 255, 0.06);
-}
-
-.daily-item:last-child {
- border-bottom: none;
-}
-
-.daily-dot {
- width: 12rpx;
- height: 12rpx;
- border-radius: 50%;
- background: rgba(0, 206, 209, 0.6);
- margin-right: 20rpx;
- flex-shrink: 0;
-}
-
-.daily-content {
- flex: 1;
- min-width: 0;
- display: flex;
- flex-direction: column;
- gap: 4rpx;
-}
-
-.daily-item-title {
- font-size: 26rpx;
- color: #ffffff;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.daily-item-meta {
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.4);
-}
-
-.daily-arrow {
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.4);
- margin-left: 16rpx;
-}
-
/* ===== 章节项 ===== */
.chapter-item {
display: flex;
@@ -450,12 +339,6 @@
margin-left: 16rpx;
}
-.chapters-loading {
- padding: 24rpx;
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.5);
-}
-
.chapter-group {
background: rgba(28, 28, 30, 0.5);
border-radius: 16rpx;
diff --git a/miniprogram/pages/index/index.js b/miniprogram/pages/index/index.js
index 64ceaa3d..d637900c 100644
--- a/miniprogram/pages/index/index.js
+++ b/miniprogram/pages/index/index.js
@@ -4,9 +4,9 @@
* 技术支持: 存客宝
*/
-console.log('[Index] ===== 首页文件开始加载 =====')
-
const app = getApp()
+const { trackClick } = require('../../utils/trackClick')
+const { checkAndExecute } = require('../../utils/ruleEngine')
Page({
data: {
@@ -20,15 +20,13 @@ Page({
readCount: 0,
// 书籍数据
- totalSections: 62,
+ totalSections: 0,
bookData: [],
- // 推荐章节
- featuredSections: [
- { id: '1.1', title: '荷包:电动车出租的被动收入模式', tag: '免费', tagClass: 'tag-free', part: '真实的人' },
- { id: '3.1', title: '3000万流水如何跑出来', tag: '热门', tagClass: 'tag-pink', part: '真实的行业' },
- { id: '8.1', title: '流量杠杆:抖音、Soul、飞书', tag: '推荐', tagClass: 'tag-purple', part: '真实的赚钱' }
- ],
+ // 精选推荐(按热度排行,默认显示3篇,可展开更多)
+ featuredSections: [],
+ featuredSectionsAll: [],
+ featuredExpanded: false,
// 最新章节(动态计算)
latestSection: null,
@@ -47,9 +45,10 @@ Page({
superMembers: [],
superMembersLoading: true,
- // 最新新增章节(完整列表 + 展示列表,用于展开/折叠)
+ // 最新新增章节
latestChapters: [],
- displayLatestChapters: [],
+ latestChaptersExpanded: false,
+ latestChaptersAll: [],
// 篇章数(从 bookData 计算)
partCount: 0,
@@ -57,26 +56,12 @@ Page({
// 加载状态
loading: true,
- // 审核模式
- auditMode: false,
-
// 链接卡若 - 留资弹窗
showLeadModal: false,
- leadPhone: '',
-
- // 展开状态(首页精选/最新)
- featuredExpanded: false,
- latestExpanded: false,
- featuredSectionsFull: [], // 展开时用 book/hot 加载的完整列表
- featuredExpandedLoading: false,
-
- // 功能配置(搜索开关)
- searchEnabled: true
+ leadPhone: ''
},
onLoad(options) {
- console.log('[Index] ===== onLoad 触发 =====')
-
// 获取系统信息
this.setData({
statusBarHeight: app.globalData.statusBarHeight,
@@ -85,26 +70,19 @@ Page({
// 处理分享参数(推荐码绑定)
if (options && options.ref) {
- console.log('[Index] 检测到推荐码:', options.ref)
app.handleReferralCode({ query: options })
}
wx.showShareMenu({ withShareTimeline: true })
- this.loadFeatureConfig()
this.initData()
},
onShow() {
- console.log('[Index] onShow 触发')
-
// 设置TabBar选中状态
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
const tabBar = this.getTabBar()
- console.log('[Index] TabBar 组件:', tabBar ? '已找到' : '未找到')
-
// 主动触发配置加载
if (tabBar && tabBar.loadFeatureConfig) {
- console.log('[Index] 主动调用 TabBar.loadFeatureConfig()')
tabBar.loadFeatureConfig()
}
@@ -114,24 +92,24 @@ Page({
} else if (tabBar) {
tabBar.setData({ selected: 0 })
}
- } else {
- console.log('[Index] TabBar 组件未找到或 getTabBar 方法不存在')
}
- // 同步审核模式
- this.setData({ auditMode: app.globalData.auditMode })
-
// 更新用户状态
this.updateUserStatus()
+
+ // 规则引擎:首页展示时检查(填头像、分享引导等)
+ checkAndExecute('page_show', this)
},
- // 初始化数据:首次进页面并行异步加载,加快首屏展示
initData() {
- this.setData({ loading: false })
- this.loadBookData()
- this.loadFeaturedFromServer()
- this.loadSuperMembers()
- this.loadLatestChapters()
+ Promise.all([
+ this.loadBookData(),
+ this.loadFeaturedFromServer(),
+ this.loadSuperMembers(),
+ this.loadLatestChapters()
+ ]).finally(() => {
+ this.setData({ loading: false })
+ })
},
async loadSuperMembers() {
@@ -149,13 +127,8 @@ Page({
avatar: u.avatar || '',
isVip: true
}))
- if (members.length > 0) {
- console.log('[Index] 超级个体加载成功:', members.length, '人')
- }
}
- } catch (e) {
- console.log('[Index] vip/members 请求失败:', e)
- }
+ } catch (e) {}
// 不足 4 个则用有头像的普通用户补充
if (members.length < 4) {
try {
@@ -172,73 +145,36 @@ Page({
}
this.setData({ superMembers: members, superMembersLoading: false })
} catch (e) {
- console.log('[Index] 加载超级个体失败:', e)
this.setData({ superMembersLoading: false })
}
},
- // 从服务端获取精选推荐、最新更新(stitch_soul:book/recommended、book/latest-chapters)
+ // 从服务端获取精选推荐(按热度排行)和最新更新
async loadFeaturedFromServer() {
try {
- // 1. 精选推荐:优先用 book/recommended(按阅读量+算法,带 热门/推荐/精选 标签)
- let featured = []
+ // 1. 精选推荐:从 book/hot 获取热度排行数据
try {
- const recRes = await app.request({ url: '/api/miniprogram/book/recommended', silent: true })
- if (recRes && recRes.success && Array.isArray(recRes.data) && recRes.data.length > 0) {
- featured = recRes.data.map((s, i) => ({
+ const hotRes = await app.request({ url: '/api/miniprogram/book/hot?limit=50', silent: true })
+ if (hotRes && hotRes.success && Array.isArray(hotRes.data) && hotRes.data.length > 0) {
+ const tagClassMap = { '热门': 'tag-hot', '推荐': 'tag-rec', '精选': 'tag-rec' }
+ const all = hotRes.data.map((s, i) => ({
id: s.id || s.section_id,
mid: s.mid ?? s.MID ?? 0,
title: s.sectionTitle || s.section_title || s.title || s.chapterTitle || '',
part: (s.partTitle || s.part_title || '').replace(/[_||]/g, ' ').trim(),
- tag: s.tag || ['热门', '推荐', '精选'][i] || '精选',
- tagClass: ['tag-hot', 'tag-rec', 'tag-rec'][i] || 'tag-rec'
+ tag: s.tag || '',
+ tagClass: tagClassMap[s.tag] || 'tag-rec',
+ hotScore: s.hotScore || s.hot_score || 0,
+ hotRank: s.hotRank || (i + 1),
+ price: s.price ?? 1,
}))
- this.setData({ featuredSections: featured })
+ this.setData({
+ featuredSectionsAll: all,
+ featuredSections: all.slice(0, 3),
+ featuredExpanded: false,
+ })
}
- } catch (e) { console.log('[Index] book/recommended 失败:', e) }
-
- // 兜底:无 recommended 时从 book/hot 取前3
- if (featured.length === 0) {
- try {
- const hotRes = await app.request({ url: '/api/miniprogram/book/hot?limit=10', silent: true })
- const hotList = (hotRes && hotRes.data) ? hotRes.data : []
- if (hotList.length > 0) {
- const tagMap = ['热门', '推荐', '精选']
- featured = hotList.slice(0, 3).map((s, i) => ({
- id: s.id || s.section_id,
- mid: s.mid ?? s.MID ?? 0,
- title: s.sectionTitle || s.section_title || s.title || s.chapterTitle || '',
- part: (s.partTitle || s.part_title || '').replace(/[_||]/g, ' ').trim(),
- tag: tagMap[i] || '精选',
- tagClass: ['tag-hot', 'tag-rec', 'tag-rec'][i] || 'tag-rec'
- }))
- this.setData({ featuredSections: featured })
- }
- } catch (e) { console.log('[Index] book/hot 兜底失败:', e) }
- }
- if (featured.length === 0) {
- const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
- const chapters = (res && res.data) || (res && res.chapters) || []
- const valid = chapters.filter(c => {
- const pt = (c.part_title || c.partTitle || '').toLowerCase()
- return !pt.includes('序言') && !pt.includes('尾声') && !pt.includes('附录')
- })
- if (valid.length > 0) {
- const tagMap = ['热门', '推荐', '精选']
- featured = valid
- .sort((a, b) => new Date(b.updated_at || b.updatedAt || 0) - new Date(a.updated_at || a.updatedAt || 0))
- .slice(0, 3)
- .map((s, i) => ({
- id: s.id,
- mid: s.mid ?? s.MID ?? 0,
- title: s.section_title || s.sectionTitle || s.title || s.chapterTitle || '',
- part: (s.part_title || s.partTitle || '').replace(/[_||]/g, ' ').trim(),
- tag: tagMap[i] || '精选',
- tagClass: ['tag-hot', 'tag-rec', 'tag-rec'][i] || 'tag-rec'
- }))
- this.setData({ featuredSections: featured })
- }
- }
+ } catch (e) {}
// 2. 最新更新:用 book/latest-chapters 取第1条(排除「序言」「尾声」「附录」)
try {
@@ -260,7 +196,6 @@ Page({
})
}
} catch (e) {
- // 兜底:从 all-chapters 取
const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
const chapters = (res && res.data) || (res && res.chapters) || []
const valid = chapters.filter(c => {
@@ -280,9 +215,7 @@ Page({
})
}
}
- } catch (e) {
- console.log('[Index] 从服务端加载推荐失败:', e)
- }
+ } catch (e) {}
},
async loadBookData() {
@@ -293,7 +226,7 @@ Page({
const partIds = new Set(chapters.map(c => c.partId || c.part_id || '').filter(Boolean))
this.setData({
bookData: chapters,
- totalSections: res.total || chapters.length || 62,
+ totalSections: res.total || chapters.length || app.globalData.totalSections || 0,
partCount: partIds.size || 5
})
}
@@ -305,7 +238,7 @@ Page({
// 更新用户状态(已读数 = 用户实际打开过的章节数,仅统计有权限阅读的)
updateUserStatus() {
const { isLoggedIn, hasFullBook, purchasedSections } = app.globalData
- const readCount = Math.min(app.getReadCount(), this.data.totalSections || 62)
+ const readCount = Math.min(app.getReadCount(), this.data.totalSections || app.globalData.totalSections || 0)
this.setData({
isLoggedIn,
hasFullBook,
@@ -315,35 +248,20 @@ Page({
// 跳转到目录
goToChapters() {
+ trackClick('home', 'nav_click', '目录')
wx.switchTab({ url: '/pages/chapters/chapters' })
},
- async loadFeatureConfig() {
- try {
- if (app.globalData.features && typeof app.globalData.features.searchEnabled === 'boolean') {
- this.setData({ searchEnabled: app.globalData.features.searchEnabled })
- return
- }
- const res = await app.request({ url: '/api/miniprogram/config', silent: true })
- const features = (res && res.features) || {}
- const searchEnabled = features.searchEnabled !== false
- if (!app.globalData.features) app.globalData.features = {}
- app.globalData.features.searchEnabled = searchEnabled
- this.setData({ searchEnabled })
- } catch (e) {
- this.setData({ searchEnabled: true })
- }
- },
-
// 跳转到搜索页
goToSearch() {
- if (!this.data.searchEnabled) return
+ trackClick('home', 'nav_click', '搜索')
wx.navigateTo({ url: '/pages/search/search' })
},
// 跳转到阅读页(优先传 mid,与分享逻辑一致)
goToRead(e) {
const id = e.currentTarget.dataset.id
+ trackClick('home', 'card_click', e.currentTarget.dataset.id || '章节')
const mid = e.currentTarget.dataset.mid || app.getSectionMid(id)
const q = mid ? `mid=${mid}` : `id=${id}`
wx.navigateTo({ url: `/pages/read/read?${q}` })
@@ -355,10 +273,16 @@ Page({
},
goToVip() {
+ trackClick('home', 'btn_click', 'VIP')
wx.navigateTo({ url: '/pages/vip/vip' })
},
+ goToAbout() {
+ wx.navigateTo({ url: '/pages/about/about' })
+ },
+
async onLinkKaruo() {
+ trackClick('home', 'btn_click', '链接卡若')
const app = getApp()
if (!app.globalData.isLoggedIn || !app.globalData.userInfo) {
wx.showModal({
@@ -373,23 +297,31 @@ Page({
return
}
const userId = app.globalData.userInfo.id
- // 2 分钟内只能点一次(与后端限频一致)
- const leadLastTs = wx.getStorageSync('lead_last_submit_ts') || 0
- if (Date.now() - leadLastTs < 2 * 60 * 1000) {
- wx.showToast({ title: '操作太频繁,请2分钟后再试', icon: 'none' })
- return
- }
let phone = (app.globalData.userInfo.phone || '').trim()
let wechatId = (app.globalData.userInfo.wechatId || app.globalData.userInfo.wechat_id || '').trim()
+ let avatar = (app.globalData.userInfo.avatar || app.globalData.userInfo.avatarUrl || '').trim()
if (!phone && !wechatId) {
try {
const profileRes = await app.request({ url: `/api/miniprogram/user/profile?userId=${userId}`, silent: true })
if (profileRes?.success && profileRes.data) {
phone = (profileRes.data.phone || '').trim()
wechatId = (profileRes.data.wechatId || profileRes.data.wechat_id || '').trim()
+ if (!avatar) avatar = (profileRes.data.avatar || '').trim()
}
} catch (e) {}
}
+ if ((!phone && !wechatId) || !avatar) {
+ wx.showModal({
+ title: '完善资料',
+ content: !avatar ? '请先设置头像和填写联系方式,以便对方联系您' : '请先填写手机号或微信号,以便对方联系您',
+ confirmText: '去填写',
+ cancelText: '取消',
+ success: (res) => {
+ if (res.confirm) wx.navigateTo({ url: '/pages/profile-edit/profile-edit' })
+ }
+ })
+ return
+ }
if (phone || wechatId) {
wx.showLoading({ title: '提交中...', mask: true })
try {
@@ -405,8 +337,12 @@ Page({
})
wx.hideLoading()
if (res && res.success) {
- wx.setStorageSync('lead_last_submit_ts', Date.now())
- wx.showToast({ title: res.message || '提交成功', icon: 'success' })
+ wx.showModal({
+ title: '提交成功',
+ content: '卡若会主动添加你微信,请注意你的微信消息',
+ showCancel: false,
+ confirmText: '好的'
+ })
} else {
wx.showToast({ title: (res && res.message) || '提交失败', icon: 'none' })
}
@@ -469,11 +405,6 @@ Page({
wx.showToast({ title: '请输入正确的手机号', icon: 'none' })
return
}
- const leadLastTs = wx.getStorageSync('lead_last_submit_ts') || 0
- if (Date.now() - leadLastTs < 2 * 60 * 1000) {
- wx.showToast({ title: '操作太频繁,请2分钟后再试', icon: 'none' })
- return
- }
const app = getApp()
const userId = app.globalData.userInfo?.id
wx.showLoading({ title: '提交中...', mask: true })
@@ -490,7 +421,6 @@ Page({
wx.hideLoading()
this.setData({ showLeadModal: false, leadPhone: '' })
if (res && res.success) {
- wx.setStorageSync('lead_last_submit_ts', Date.now())
// 同步手机号到用户资料
try {
if (userId) {
@@ -519,6 +449,7 @@ Page({
},
async submitLead() {
+ trackClick('home', 'btn_click', '提交留资')
const phone = (this.data.leadPhone || '').trim().replace(/\s/g, '')
if (!phone) {
wx.showToast({ title: '请输入手机号', icon: 'none' })
@@ -531,75 +462,76 @@ Page({
wx.switchTab({ url: '/pages/match/match' })
},
- // 精选推荐:展开/折叠
- async toggleFeaturedExpanded() {
- if (this.data.featuredExpandedLoading) return
- if (this.data.featuredExpanded) {
- const collapsed = this.data.featuredSectionsFull.length > 0 ? this.data.featuredSectionsFull.slice(0, 3) : this.data.featuredSections
- this.setData({ featuredExpanded: false, featuredSections: collapsed })
- return
- }
- if (this.data.featuredSectionsFull.length > 0) {
- this.setData({ featuredExpanded: true, featuredSections: this.data.featuredSectionsFull })
- return
- }
- this.setData({ featuredExpandedLoading: true })
- try {
- const res = await app.request({ url: '/api/miniprogram/book/hot?limit=50', silent: true })
- const list = (res && res.data) ? res.data : []
- const tagMap = ['热门', '推荐', '精选']
- const full = list.map((s, i) => ({
- id: s.id || s.section_id,
- mid: s.mid ?? s.MID ?? 0,
- title: s.sectionTitle || s.section_title || s.title || s.chapterTitle || '',
- part: (s.partTitle || s.part_title || '').replace(/[_||]/g, ' ').trim(),
- tag: tagMap[i % 3] || '精选',
- tagClass: ['tag-hot', 'tag-rec', 'tag-rec'][i % 3] || 'tag-rec'
- }))
- this.setData({
- featuredSectionsFull: full,
- featuredSections: full,
- featuredExpanded: true,
- featuredExpandedLoading: false
- })
- } catch (e) {
- console.log('[Index] 加载精选更多失败:', e)
- this.setData({ featuredExpandedLoading: false })
- }
- },
-
- // 最新新增:展开/折叠(默认 5 条,点击展开剩余)
- toggleLatestExpanded() {
- const expanded = !this.data.latestExpanded
- const display = expanded ? this.data.latestChapters : this.data.latestChapters.slice(0, 5)
- this.setData({ latestExpanded: expanded, displayLatestChapters: display })
- },
-
- // 最新新增:用 latest-chapters 接口(后端按 updated_at 取前 N 条),不拉全量,支持万级文章
async loadLatestChapters() {
try {
- const res = await app.request({ url: '/api/miniprogram/book/latest-chapters', silent: true })
- const list = (res && res.data) ? res.data : []
+ let chapters = app.globalData.bookData || []
+ if (!Array.isArray(chapters) || chapters.length === 0) {
+ const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
+ chapters = (res && res.data) || (res && res.chapters) || []
+ }
const pt = (c) => (c.partTitle || c.part_title || '').toLowerCase()
const exclude = c => !pt(c).includes('序言') && !pt(c).includes('尾声') && !pt(c).includes('附录')
- const latest = list
- .filter(exclude)
- .slice(0, 20)
- .map(c => {
- const d = new Date(c.updatedAt || c.updated_at || Date.now())
- const title = c.section_title || c.sectionTitle || c.title || c.chapterTitle || ''
- return {
- id: c.id,
- mid: c.mid ?? c.MID ?? 0,
- title,
- desc: '', // latest-chapters 不返回 content,避免大表全量加载
- price: c.price ?? 1,
- dateStr: `${d.getMonth() + 1}/${d.getDate()}`
- }
- })
- const display = this.data.latestExpanded ? latest : latest.slice(0, 5)
- this.setData({ latestChapters: latest, displayLatestChapters: display })
- } catch (e) { console.log('[Index] 加载最新新增失败:', e) }
+ let candidates = chapters.filter(c => (c.isNew || c.is_new) === true && exclude(c))
+ if (candidates.length === 0) {
+ candidates = chapters.filter(exclude)
+ }
+ const sessionNum = (c) => {
+ const title = c.section_title || c.sectionTitle || c.title || c.chapterTitle || ''
+ const m = title.match(/第\s*(\d+)\s*场/) || title.match(/第(\d+)场/)
+ if (m) return parseInt(m[1], 10)
+ const id = c.id != null ? String(c.id) : ''
+ if (/^\d+$/.test(id)) return parseInt(id, 10)
+ return 0
+ }
+ const mapChapter = (c) => {
+ const d = new Date(c.updatedAt || c.updated_at || Date.now())
+ const title = c.section_title || c.sectionTitle || c.title || c.chapterTitle || ''
+ const rawContent = (c.content || '').replace(/<[^>]+>/g, '').trim()
+ let desc = ''
+ if (rawContent && rawContent.length > 0) {
+ const clean = rawContent.replace(/^#[\d.]+\s*/, '').trim()
+ desc = clean.length > 36 ? clean.slice(0, 36) + '...' : clean
+ }
+ return {
+ id: c.id,
+ mid: c.mid ?? c.MID ?? 0,
+ title,
+ desc,
+ price: c.price ?? 1,
+ dateStr: `${d.getMonth() + 1}/${d.getDate()}`
+ }
+ }
+ const sorted = candidates.sort((a, b) => {
+ const na = sessionNum(a)
+ const nb = sessionNum(b)
+ if (na !== nb) return nb - na
+ return new Date(b.updatedAt || b.updated_at || 0) - new Date(a.updatedAt || a.updated_at || 0)
+ })
+ const latestAll = sorted.slice(0, 10).map(mapChapter)
+ this.setData({
+ latestChaptersAll: latestAll,
+ latestChapters: latestAll.slice(0, 5),
+ latestChaptersExpanded: false,
+ })
+ } catch (e) {}
+ },
+
+ toggleLatestExpand() {
+ const all = this.data.latestChaptersAll || []
+ if (this.data.latestChaptersExpanded) {
+ this.setData({ latestChapters: all.slice(0, 5), latestChaptersExpanded: false })
+ } else {
+ this.setData({ latestChapters: all, latestChaptersExpanded: true })
+ }
+ },
+
+ toggleFeaturedExpand() {
+ const all = this.data.featuredSectionsAll || []
+ if (this.data.featuredExpanded) {
+ this.setData({ featuredSections: all.slice(0, 3), featuredExpanded: false })
+ } else {
+ this.setData({ featuredSections: all, featuredExpanded: true })
+ }
},
goToMemberDetail(e) {
diff --git a/miniprogram/pages/index/index.wxml b/miniprogram/pages/index/index.wxml
index def50a7b..d510110e 100644
--- a/miniprogram/pages/index/index.wxml
+++ b/miniprogram/pages/index/index.wxml
@@ -24,8 +24,8 @@
-
-
+
+
🔍
@@ -38,31 +38,18 @@
- 最新更新
+ 推荐
{{latestSection.title}}
- 开始阅读
+ 点击阅读
→
- 最新更新
+ 推荐
加载中...
- 开始阅读→
-
-
-
-
-
-
-
-
-
-
+ 点击阅读→
@@ -100,18 +87,14 @@
成为会员,展示你的项目
- 加入创业派对 →
+ 加入创业派对 →
-
-
+
+
- {{item.id}}
- {{item.tag || '精选'}}
+ {{item.tag}}
{{item.title}}
›
+
+ {{featuredExpanded ? '收起' : '展开更多'}}
+ {{featuredExpanded ? '∧' : '∨'}}
+
-
+
diff --git a/miniprogram/pages/index/index.wxss b/miniprogram/pages/index/index.wxss
index 0462e791..625d679a 100644
--- a/miniprogram/pages/index/index.wxss
+++ b/miniprogram/pages/index/index.wxss
@@ -689,12 +689,6 @@
margin-bottom: 32rpx;
}
-.section-header-right {
- display: flex;
- align-items: center;
- gap: 16rpx;
-}
-
.daily-badge-wrap {
display: inline-flex;
align-items: center;
diff --git a/miniprogram/pages/match/match.js b/miniprogram/pages/match/match.js
index 11a67471..813e7e96 100644
--- a/miniprogram/pages/match/match.js
+++ b/miniprogram/pages/match/match.js
@@ -5,7 +5,8 @@
*/
const app = getApp()
-const { checkAndExecute } = require('../../utils/ruleEngine.js')
+const { trackClick } = require('../../utils/trackClick')
+const { checkAndExecute } = require('../../utils/ruleEngine')
// 默认匹配类型配置
// 找伙伴:真正的匹配功能,匹配数据库中的真实用户
@@ -75,16 +76,13 @@ Page({
// 匹配价格(可配置)
matchPrice: 1,
- extraMatches: 0,
-
- auditMode: false
+ extraMatches: 0
},
onLoad() {
wx.showShareMenu({ withShareTimeline: true })
this.setData({
- statusBarHeight: app.globalData.statusBarHeight || 44,
- auditMode: app.globalData.auditMode
+ statusBarHeight: app.globalData.statusBarHeight || 44
})
this.loadMatchConfig()
this.loadStoredContact()
@@ -107,9 +105,7 @@ Page({
// 加载匹配配置
async loadMatchConfig() {
try {
- const res = await app.request({ url: '/api/miniprogram/match/config', silent: true, method: 'GET',
- method: 'GET'
- })
+ const res = await app.request({ url: '/api/miniprogram/match/config', silent: true, method: 'GET' })
if (res.success && res.data) {
// 更新全局配置,导师顾问类型强制显示「导师顾问」
@@ -200,6 +196,7 @@ Page({
// 选择匹配类型
selectType(e) {
+ trackClick('match', 'tab_click', e.currentTarget.dataset.type || '类型选择')
const typeId = e.currentTarget.dataset.type
const type = MATCH_TYPES.find(t => t.id === typeId)
this.setData({
@@ -210,6 +207,7 @@ Page({
// 点击匹配按钮
async handleMatchClick() {
+ trackClick('match', 'btn_click', '开始匹配')
const currentType = MATCH_TYPES.find(t => t.id === this.data.selectedType)
// 导师顾问:先播匹配动画,动画完成后再跳转(不在此处直接跳)
@@ -311,7 +309,7 @@ Page({
confirmText: '去购买',
success: (res) => {
if (res.confirm) {
- wx.switchTab({ url: '/pages/catalog/catalog' })
+ wx.switchTab({ url: '/pages/chapters/chapters' })
}
}
})
@@ -367,7 +365,7 @@ Page({
}, 500)
// 1.5-3秒后:导师顾问→跳转;其他类型→弹窗
- const delay = Math.random() * 1500 + 1500
+ const delay = Math.random() * 7000 + 3000
setTimeout(() => {
clearInterval(timer)
this.setData({ isMatching: false })
@@ -416,14 +414,15 @@ Page({
// 从数据库获取真实用户匹配
let matchedUser = null
try {
- const res = await app.request({ url: '/api/miniprogram/match/users', silent: true,
+ const res = await app.request({
+ url: '/api/miniprogram/match/users',
+ silent: true,
method: 'POST',
data: {
matchType: this.data.selectedType,
userId: app.globalData.userInfo?.id || ''
}
})
-
if (res.success && res.data) {
matchedUser = res.data
console.log('[Match] 从数据库匹配到用户:', matchedUser.nickname)
@@ -433,8 +432,8 @@ Page({
}
// 延迟显示结果(模拟匹配过程)
- const delay = Math.random() * 2000 + 2000
- setTimeout(() => {
+ const delay = Math.random() * 7000 + 3000
+ const timeoutId = setTimeout(() => {
clearInterval(timer)
// 如果没有匹配到用户,提示用户
@@ -464,39 +463,9 @@ Page({
// 上报匹配行为到存客宝
this.reportMatch(matchedUser)
-
}, delay)
},
- // 生成模拟匹配数据
- generateMockMatch() {
- const nicknames = ['创业先锋', '资源整合者', '私域专家', '导师顾问', '连续创业者']
- const concepts = [
- '专注私域流量运营5年,帮助100+品牌实现从0到1的增长。',
- '连续创业者,擅长商业模式设计和资源整合。',
- '在Soul分享真实创业故事,希望找到志同道合的合作伙伴。'
- ]
- const wechats = ['soul_partner_1', 'soul_business_2024', 'soul_startup_fan']
-
- const index = Math.floor(Math.random() * nicknames.length)
- const currentType = MATCH_TYPES.find(t => t.id === this.data.selectedType)
-
- return {
- id: `user_${Date.now()}`,
- nickname: nicknames[index],
- avatar: `https://picsum.photos/200/200?random=${Date.now()}`,
- tags: ['创业者', '私域运营', currentType?.label || '创业合伙'],
- matchScore: Math.floor(Math.random() * 20) + 80,
- concept: concepts[index % concepts.length],
- wechat: wechats[index % wechats.length],
- commonInterests: [
- { icon: '📚', text: '都在读《创业派对》' },
- { icon: '💼', text: '对私域运营感兴趣' },
- { icon: '🎯', text: '相似的创业方向' }
- ]
- }
- },
-
// 上报匹配行为
async reportMatch(matchedUser) {
try {
@@ -515,6 +484,15 @@ Page({
}
}
})
+ // 记录匹配行为到 user_tracks
+ const uid = app.globalData.userInfo?.id
+ if (uid) {
+ app.request('/api/miniprogram/track', {
+ method: 'POST',
+ data: { userId: uid, action: 'match', target: matchedUser?.id || '', extraData: { matchType: this.data.selectedType } },
+ silent: true
+ }).catch(() => {})
+ }
// 匹配后规则:引导填写 MBTI/行业信息
checkAndExecute('after_match', this)
} catch (e) {
@@ -534,6 +512,7 @@ Page({
// 添加微信好友
handleAddWechat() {
+ trackClick('match', 'btn_click', '加好友')
if (!this.data.currentMatch) return
wx.setClipboardData({
@@ -584,6 +563,7 @@ Page({
// 提交加入
async handleJoinSubmit() {
+ trackClick('match', 'btn_click', '加入提交')
const { contactType, phoneNumber, wechatId, joinType, isJoining, canHelp, needHelp } = this.data
if (isJoining) return
@@ -639,18 +619,16 @@ Page({
this.setData({ showJoinModal: false, joinSuccess: false })
}, 2000)
} else {
- // 即使API返回失败,也模拟成功(因为已保存本地)
- this.setData({ joinSuccess: true })
- setTimeout(() => {
- this.setData({ showJoinModal: false, joinSuccess: false })
- }, 2000)
+ this.setData({
+ joinSuccess: false,
+ joinError: res.error || '提交失败,请稍后重试'
+ })
}
} catch (e) {
- // 网络错误时也模拟成功
- this.setData({ joinSuccess: true })
- setTimeout(() => {
- this.setData({ showJoinModal: false, joinSuccess: false })
- }, 2000)
+ this.setData({
+ joinSuccess: false,
+ joinError: e.message || '网络异常,请稍后重试'
+ })
} finally {
this.setData({ isJoining: false })
}
@@ -674,6 +652,7 @@ Page({
// 购买匹配次数
async buyMatchCount() {
+ trackClick('match', 'btn_click', '购买次数')
this.setData({ showUnlockModal: false })
try {
@@ -727,19 +706,7 @@ Page({
if (e.errMsg && e.errMsg.includes('cancel')) {
wx.showToast({ title: '已取消', icon: 'none' })
} else {
- // 测试模式
- wx.showModal({
- title: '支付服务暂不可用',
- content: '是否使用测试模式购买?',
- success: (res) => {
- if (res.confirm) {
- const extraMatches = (wx.getStorageSync('extra_match_count') || 0) + 1
- wx.setStorageSync('extra_match_count', extraMatches)
- wx.showToast({ title: '测试购买成功', icon: 'success' })
- this.initUserStatus()
- }
- }
- })
+ wx.showToast({ title: e.message || '支付失败,请稍后重试', icon: 'none' })
}
}
},
@@ -750,11 +717,6 @@ Page({
wx.switchTab({ url: '/pages/chapters/chapters' })
},
- // 打开资料修改页(找伙伴右上角图标)
- openSettings() {
- wx.navigateTo({ url: '/pages/profile-edit/profile-edit' })
- },
-
// 阻止事件冒泡
preventBubble() {},
diff --git a/miniprogram/pages/match/match.wxml b/miniprogram/pages/match/match.wxml
index 9f602d3a..1d689120 100644
--- a/miniprogram/pages/match/match.wxml
+++ b/miniprogram/pages/match/match.wxml
@@ -15,15 +15,11 @@
-
+
⚡
今日免费次数已用完
购买次数
-
- ⚡
- 今日免费次数已用完,明天再来
-
@@ -39,16 +35,11 @@
-
+
⚡
购买次数
¥1 = 1次匹配
-
- ⏳
- 明天再来
- 今日次数已用完
-
👥
开始匹配
@@ -305,8 +296,8 @@
-
-
+
+
⚡
购买匹配次数
diff --git a/miniprogram/pages/my/my.js b/miniprogram/pages/my/my.js
index 69c1b188..657966d9 100644
--- a/miniprogram/pages/my/my.js
+++ b/miniprogram/pages/my/my.js
@@ -77,9 +77,6 @@ Page({
// 我的代付链接
giftList: [],
-
- // 审核模式
- auditMode: false,
},
onLoad() {
@@ -95,7 +92,6 @@ Page({
},
onShow() {
- this.setData({ auditMode: app.globalData.auditMode })
// 设置TabBar选中状态(根据 matchEnabled 动态设置)
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
const tabBar = this.getTabBar()
@@ -267,8 +263,6 @@ Page({
const newList = list.filter(x => x.id !== item.id)
this.setData({ pendingConfirmList: newList })
this.loadPendingConfirm()
- this.loadMyEarnings()
- this.loadWalletBalance()
}
if (hasPackage) {
@@ -389,8 +383,6 @@ Page({
wx.hideLoading()
this.setData({ receivingAll: false })
this.loadPendingConfirm()
- this.loadMyEarnings()
- this.loadWalletBalance()
}
},
diff --git a/miniprogram/pages/my/my.wxml b/miniprogram/pages/my/my.wxml
index bf489f94..54742ec7 100644
--- a/miniprogram/pages/my/my.wxml
+++ b/miniprogram/pages/my/my.wxml
@@ -34,9 +34,9 @@
{{userInfo.nickname || '点击设置昵称'}}
- {{isVip ? '会员中心' : '成为会员'}}
+ {{isVip ? '会员中心' : '成为会员'}}
-
+
会员
匹配
排行
@@ -53,11 +53,11 @@
{{referralCount}}
推荐好友
-
+
{{earnings === '-' ? '--' : earnings}}
我的收益
-
+
{{walletBalance > 0 ? '¥' + walletBalance : '0'}}
我的余额
@@ -68,7 +68,7 @@
-
+
@@ -143,7 +143,7 @@
-
+