diff --git a/.cursor/agent/小程序开发工程师/evolution/2026-03-03.md b/.cursor/agent/小程序开发工程师/evolution/2026-03-03.md
new file mode 100644
index 00000000..fa6a9b29
--- /dev/null
+++ b/.cursor/agent/小程序开发工程师/evolution/2026-03-03.md
@@ -0,0 +1,27 @@
+# 2026-03-03 经验
+
+## 我的页面卡片区边距优化
+
+### 问题/场景
+
+用户反馈「我的」页面的卡片区域左右边距过大,内容区在水平方向上显得较窄,未充分利用屏幕横向空间。
+
+### 解决方案
+
+将卡片区域及上下关联区块的左右边距统一缩小:
+
+| 区块 | 修改前 | 修改后 |
+|------|--------|--------|
+| `.main-content` | 32rpx | 16rpx |
+| `.header-block` | 40rpx | 16rpx |
+| `.guest-block` | 48rpx | 16rpx |
+| `.card` padding | 40rpx | 32rpx |
+| `.card` margin-bottom | 32rpx | 24rpx |
+
+### 提炼规则
+
+个人中心、设置类页面(如「我的」)的卡片区左右边距宜紧凑,**推荐 16rpx**;卡片内边距 32rpx、卡片间距 24rpx,以充分利用横向空间。
+
+### 适用
+
+- `miniprogram/pages/my/` 及类似个人中心、设置页
diff --git a/.cursor/agent/小程序开发工程师/evolution/索引.md b/.cursor/agent/小程序开发工程师/evolution/索引.md
index eaf1ff56..6bdf6290 100644
--- a/.cursor/agent/小程序开发工程师/evolution/索引.md
+++ b/.cursor/agent/小程序开发工程师/evolution/索引.md
@@ -3,3 +3,4 @@
| 日期 | 摘要 | 文件 |
|------|------|------|
| 2026-02-28 | input 边距口诀、match 资源对接弹窗修正 | [2026-02-28.md](./2026-02-28.md) |
+| 2026-03-03 | 我的页面卡片区边距优化,16rpx 推荐值 | [2026-03-03.md](./2026-03-03.md) |
diff --git a/.cursor/agent/开发助理/经验清单.md b/.cursor/agent/开发助理/经验清单.md
index f8697f96..a4bf4ab5 100644
--- a/.cursor/agent/开发助理/经验清单.md
+++ b/.cursor/agent/开发助理/经验清单.md
@@ -24,6 +24,7 @@
|------|------|------|------------|------|
| 2026-02-27 | 小程序、团队 | 最佳实践 | SKILL-小程序开发 §6、SKILL-管理端开发 §4.1 | 输入框 padding 用 view/div 包裹 |
| 2026-02-28 | 小程序、管理端 | 最佳实践 | miniprogram §6、admin §4.1 | input 边距口诀「外边包 view、内部 width 100%」;match 弹窗已修正 |
+| 2026-03-03 | 小程序 | 最佳实践 | miniprogram §8 | 我的页面卡片区边距 16rpx,个人中心类页面布局规范 |
---
@@ -34,4 +35,4 @@
---
-**最后更新**:2026-02-28
+**最后更新**:2026-03-03
diff --git a/.cursor/agent/开发助理/项目索引/小程序.md b/.cursor/agent/开发助理/项目索引/小程序.md
index 9874bcd4..59665cf4 100644
--- a/.cursor/agent/开发助理/项目索引/小程序.md
+++ b/.cursor/agent/开发助理/项目索引/小程序.md
@@ -20,9 +20,10 @@
| 2026-02-27 | 吸收经验:输入框 padding 用 view 包裹,已升级 SKILL-小程序开发 §6 | 已完成 |
| 2026-02-28 | stitch_soul 需求评审:首页/目录/导师/会员/资料五类页面,待需求与接口确定后分阶段实现 | 待续 |
| 2026-02-28 | 吸收经验:input 边距口诀「外边包 view、内部 width 100%」写入 Skill §6;match 资源对接弹窗已按规范修正 | 已完成 |
+| 2026-03-03 | 吸收经验:我的页面卡片区边距优化,16rpx 为个人中心类页面推荐值,已升级 SKILL §8 | 已完成 |
> **格式说明**:每次开发后在此追加一行,日期格式 YYYY-MM-DD,状态用:已完成 / 进行中 / 待续 / 搁置
---
-**最后更新**:2026-02-28
+**最后更新**:2026-03-03
diff --git a/.cursor/skills/miniprogram-dev/SKILL.md b/.cursor/skills/miniprogram-dev/SKILL.md
index 393567ec..a4eea193 100644
--- a/.cursor/skills/miniprogram-dev/SKILL.md
+++ b/.cursor/skills/miniprogram-dev/SKILL.md
@@ -68,11 +68,20 @@ description: Soul 创业派对小程序开发规范。在 miniprogram/ 下编辑
---
-## 7. 何时使用本 Skill
+## 7. 布局与边距(个人中心/设置类页面)
+
+- **卡片区左右边距**:宜紧凑,**推荐 16rpx**,避免内容区过窄。
+- **卡片**:内边距 32rpx,卡片间距 24rpx。
+- **适用**:`pages/my/`、设置页等个人中心类页面。
+
+---
+
+## 8. 何时使用本 Skill
- 在 **miniprogram/** 下新增或修改页面、组件、utils 时。
- 在小程序内新增或修改任何网络请求路径时(必须保持 `/api/miniprogram/...`)。
- 做阅读、支付、推荐、提现等与 soul-api 对接的功能时。
- 做表单、input/textarea 样式时(遵循 §6,用 view 包裹,padding 写在 view 上)。
+- 做个人中心、设置页布局时(遵循 §7,卡片区边距 16rpx)。
遵循本 Skill 可保证小程序只与 soul-api 的 miniprogram 路由组对接,避免与管理端或 next-project 接口混用。
diff --git a/miniprogram/app.js b/miniprogram/app.js
index aacfcca8..553d1ce2 100644
--- a/miniprogram/app.js
+++ b/miniprogram/app.js
@@ -8,7 +8,7 @@ const { parseScene } = require('./utils/scene.js')
App({
globalData: {
// API基础地址 - 连接真实后端
- baseUrl: 'https://soulapi.quwanzhi.com',
+ baseUrl: 'https://soulapi.quwanzhi.com',
// baseUrl: 'https://souldev.quwanzhi.com',
// baseUrl: 'http://localhost:8080',
@@ -56,7 +56,10 @@ App({
navBarHeight: 88,
// TabBar相关
- currentTab: 0
+ currentTab: 0,
+
+ // 更新检测:上次检测时间戳,避免频繁请求
+ lastUpdateCheck: 0
},
onLaunch(options) {
@@ -77,9 +80,10 @@ App({
this.handleReferralCode(options)
},
- // 小程序显示时也检查分享参数
+ // 小程序显示时:处理分享参数、检测更新(从后台切回时)
onShow(options) {
this.handleReferralCode(options)
+ this.checkUpdate()
},
// 处理推荐码绑定:官方以 options.scene 接收扫码参数(可同时带 mid/id + ref),与 utils/scene 解析闭环
@@ -238,21 +242,30 @@ App({
}
},
- // 检查更新
+ /**
+ * 小程序更新检测(基于 wx.getUpdateManager)
+ * - 启动时检测;从后台切回前台时也检测(间隔至少 5 分钟,避免频繁请求)
+ */
checkUpdate() {
- if (wx.canIUse('getUpdateManager')) {
+ try {
+ if (!wx.canIUse('getUpdateManager')) return
+ const now = Date.now()
+ const lastCheck = this.globalData.lastUpdateCheck || 0
+ if (lastCheck && now - lastCheck < 5 * 60 * 1000) return // 5 分钟内不重复检测
+ this.globalData.lastUpdateCheck = now
+
const updateManager = wx.getUpdateManager()
-
updateManager.onCheckForUpdate((res) => {
if (res.hasUpdate) {
- console.log('发现新版本')
+ console.log('[App] 发现新版本,正在下载...')
}
})
-
updateManager.onUpdateReady(() => {
wx.showModal({
title: '更新提示',
- content: '新版本已准备好,是否重启应用?',
+ content: '新版本已准备好,重启后即可使用',
+ confirmText: '立即重启',
+ cancelText: '稍后',
success: (res) => {
if (res.confirm) {
updateManager.applyUpdate()
@@ -260,13 +273,15 @@ App({
}
})
})
-
updateManager.onUpdateFailed(() => {
wx.showToast({
title: '更新失败,请稍后重试',
- icon: 'none'
+ icon: 'none',
+ duration: 2500
})
})
+ } catch (e) {
+ console.warn('[App] checkUpdate failed:', e)
}
},
diff --git a/miniprogram/assets/icons/book-arrow-teal.svg b/miniprogram/assets/icons/book-arrow-teal.svg
new file mode 100644
index 00000000..6a861630
--- /dev/null
+++ b/miniprogram/assets/icons/book-arrow-teal.svg
@@ -0,0 +1,5 @@
+
diff --git a/miniprogram/assets/icons/book-arrow.svg b/miniprogram/assets/icons/book-arrow.svg
new file mode 100644
index 00000000..2b1320d0
--- /dev/null
+++ b/miniprogram/assets/icons/book-arrow.svg
@@ -0,0 +1,5 @@
+
diff --git a/miniprogram/assets/icons/book-open-teal.svg b/miniprogram/assets/icons/book-open-teal.svg
new file mode 100644
index 00000000..482f4093
--- /dev/null
+++ b/miniprogram/assets/icons/book-open-teal.svg
@@ -0,0 +1,4 @@
+
diff --git a/miniprogram/assets/icons/clock-teal.svg b/miniprogram/assets/icons/clock-teal.svg
new file mode 100644
index 00000000..96a928e3
--- /dev/null
+++ b/miniprogram/assets/icons/clock-teal.svg
@@ -0,0 +1,4 @@
+
diff --git a/miniprogram/assets/icons/clock.svg b/miniprogram/assets/icons/clock.svg
new file mode 100644
index 00000000..547f24a7
--- /dev/null
+++ b/miniprogram/assets/icons/clock.svg
@@ -0,0 +1,4 @@
+
diff --git a/miniprogram/assets/icons/eye-off.svg b/miniprogram/assets/icons/eye-off.svg
new file mode 100644
index 00000000..05b73282
--- /dev/null
+++ b/miniprogram/assets/icons/eye-off.svg
@@ -0,0 +1,6 @@
+
diff --git a/miniprogram/assets/icons/eye-teal.svg b/miniprogram/assets/icons/eye-teal.svg
new file mode 100644
index 00000000..cdd62e60
--- /dev/null
+++ b/miniprogram/assets/icons/eye-teal.svg
@@ -0,0 +1,4 @@
+
diff --git a/miniprogram/assets/icons/eye.svg b/miniprogram/assets/icons/eye.svg
new file mode 100644
index 00000000..10ff508d
--- /dev/null
+++ b/miniprogram/assets/icons/eye.svg
@@ -0,0 +1,4 @@
+
diff --git a/miniprogram/assets/icons/folder-teal.svg b/miniprogram/assets/icons/folder-teal.svg
new file mode 100644
index 00000000..28003510
--- /dev/null
+++ b/miniprogram/assets/icons/folder-teal.svg
@@ -0,0 +1,3 @@
+
diff --git a/miniprogram/assets/icons/folder.svg b/miniprogram/assets/icons/folder.svg
new file mode 100644
index 00000000..5e5b7042
--- /dev/null
+++ b/miniprogram/assets/icons/folder.svg
@@ -0,0 +1,3 @@
+
diff --git a/miniprogram/assets/icons/info-blue.svg b/miniprogram/assets/icons/info-blue.svg
new file mode 100644
index 00000000..0f5bb1d2
--- /dev/null
+++ b/miniprogram/assets/icons/info-blue.svg
@@ -0,0 +1,5 @@
+
diff --git a/miniprogram/assets/icons/info.svg b/miniprogram/assets/icons/info.svg
new file mode 100644
index 00000000..11d0f224
--- /dev/null
+++ b/miniprogram/assets/icons/info.svg
@@ -0,0 +1,5 @@
+
diff --git a/miniprogram/assets/icons/settings-gray.svg b/miniprogram/assets/icons/settings-gray.svg
new file mode 100644
index 00000000..d7098fc1
--- /dev/null
+++ b/miniprogram/assets/icons/settings-gray.svg
@@ -0,0 +1,4 @@
+
diff --git a/miniprogram/assets/icons/users-teal.svg b/miniprogram/assets/icons/users-teal.svg
new file mode 100644
index 00000000..ed97706f
--- /dev/null
+++ b/miniprogram/assets/icons/users-teal.svg
@@ -0,0 +1,6 @@
+
diff --git a/miniprogram/assets/images/author-avatar.png b/miniprogram/assets/images/author-avatar.png
new file mode 100644
index 00000000..c02ae73f
Binary files /dev/null and b/miniprogram/assets/images/author-avatar.png differ
diff --git a/miniprogram/pages/about/about.js b/miniprogram/pages/about/about.js
index ff164288..2dc3a222 100644
--- a/miniprogram/pages/about/about.js
+++ b/miniprogram/pages/about/about.js
@@ -7,23 +7,15 @@ const app = getApp()
Page({
data: {
statusBarHeight: 44,
+ authorLoading: true,
author: {
name: '卡若',
avatar: 'K',
- title: 'Soul派对房主理人 · 私域运营专家',
- bio: '每天早上6点到9点,在Soul派对房分享真实的创业故事。专注私域运营与项目变现,用"云阿米巴"模式帮助创业者构建可持续的商业体系。本书记录了62个真实商业案例,涵盖电商、内容、传统行业等多个领域。',
- stats: [
- { label: '商业案例', value: '62' },
- { label: '连续直播', value: '365天' },
- { label: '派对分享', value: '1000+' }
- ],
- // 联系方式已移至后台配置
- contact: null,
- highlights: [
- '5年私域运营经验',
- '帮助100+品牌从0到1增长',
- '连续创业者,擅长商业模式设计'
- ]
+ avatarImg: '/assets/images/author-avatar.png',
+ title: '',
+ bio: '',
+ stats: [],
+ highlights: []
},
bookInfo: {
title: '一场Soul的创业实验',
@@ -44,22 +36,68 @@ Page({
this.setData({
statusBarHeight: app.globalData.statusBarHeight
})
+ this.loadAuthor()
this.loadBookStats()
},
+
+ async loadAuthor() {
+ this.setData({ authorLoading: true })
+ try {
+ const res = await app.request({ url: '/api/miniprogram/about/author', silent: true })
+ if (res?.success && res.data) {
+ const d = res.data
+ let avatarImg = d.avatarImg || ''
+ if (avatarImg && !avatarImg.startsWith('http')) {
+ const base = (app.globalData.baseUrl || '').replace(/\/$/, '')
+ avatarImg = base ? base + (avatarImg.startsWith('/') ? avatarImg : '/' + avatarImg) : avatarImg
+ }
+ this.setData({
+ author: {
+ name: d.name || '卡若',
+ avatar: d.avatar || 'K',
+ avatarImg: avatarImg || '/assets/images/author-avatar.png',
+ title: d.title || '',
+ bio: d.bio || '',
+ stats: Array.isArray(d.stats) ? d.stats : [
+ { label: '商业案例', value: '62' },
+ { label: '连续直播', value: '365天' },
+ { label: '派对分享', value: '1000+' }
+ ],
+ highlights: Array.isArray(d.highlights) ? d.highlights : []
+ },
+ authorLoading: false
+ })
+ } else {
+ this.setData({ authorLoading: false })
+ }
+ } catch (e) {
+ console.log('[About] 加载作者配置失败,使用默认')
+ this.setData({ authorLoading: false })
+ }
+ },
- // 加载书籍统计
+ // 加载书籍统计(合并到作者统计第一项「商业案例」)
async loadBookStats() {
try {
- const res = await app.request('/api/miniprogram/book/stats')
- if (res && res.success) {
- this.setData({
- 'bookInfo.totalChapters': res.data?.totalChapters || 62,
- 'author.stats': [
- { label: '商业案例', value: String(res.data?.totalChapters || 62) },
- { label: '连续直播', value: '365天' },
- { label: '派对分享', value: '1000+' }
- ]
- })
+ const res = await app.request({ url: '/api/miniprogram/book/stats', silent: true })
+ if (res?.success && res.data) {
+ const total = res.data?.totalChapters || 62
+ this.setData({ 'bookInfo.totalChapters': total })
+ const stats = this.data.author?.stats || []
+ const idx = stats.findIndex((s) => s && (s.label === '商业案例' || s.label === '章节'))
+ if (idx >= 0 && stats[idx]) {
+ const next = [...stats]
+ next[idx] = { ...stats[idx], value: String(total) }
+ this.setData({ 'author.stats': next })
+ } else if (stats.length === 0) {
+ this.setData({
+ 'author.stats': [
+ { label: '商业案例', value: String(total) },
+ { label: '连续直播', value: '365天' },
+ { label: '派对分享', value: '1000+' }
+ ]
+ })
+ }
}
} catch (e) {
console.log('[About] 加载书籍统计失败,使用默认值')
diff --git a/miniprogram/pages/about/about.wxml b/miniprogram/pages/about/about.wxml
index 598e9464..3ddb3660 100644
--- a/miniprogram/pages/about/about.wxml
+++ b/miniprogram/pages/about/about.wxml
@@ -8,9 +8,13 @@
+ 加载中...
-
- {{author.avatar}}
+
+
+
+ {{author.avatar}}
+
{{author.name}}
{{author.title}}
{{author.bio}}
@@ -33,7 +37,7 @@
-
+
📚 {{bookInfo.title}}
@@ -58,7 +62,7 @@
-
+
联系作者
🎉
diff --git a/miniprogram/pages/about/about.wxss b/miniprogram/pages/about/about.wxss
index 337aa041..06ad5a00 100644
--- a/miniprogram/pages/about/about.wxss
+++ b/miniprogram/pages/about/about.wxss
@@ -4,8 +4,11 @@
.nav-title { font-size: 36rpx; font-weight: 600; color: #00CED1; }
.nav-placeholder { width: 72rpx; }
.content { padding: 32rpx; }
+.loading-row { text-align: center; color: rgba(255,255,255,0.6); font-size: 28rpx; padding: 48rpx 0; }
.author-card { background: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%); border-radius: 32rpx; padding: 48rpx; text-align: center; margin-bottom: 24rpx; border: 2rpx solid rgba(0,206,209,0.2); }
-.author-avatar { width: 160rpx; height: 160rpx; border-radius: 50%; background: linear-gradient(135deg, #00CED1, #20B2AA); display: flex; align-items: center; justify-content: center; margin: 0 auto 24rpx; font-size: 64rpx; color: #fff; font-weight: 700; border: 4rpx solid rgba(0,206,209,0.3); }
+.author-avatar-wrap { width: 160rpx; height: 160rpx; margin: 0 auto 24rpx; overflow: hidden; border-radius: 50%; border: 4rpx solid rgba(0,206,209,0.3); flex-shrink: 0; }
+.author-avatar-img { width: 100%; height: 100%; display: block; }
+.author-avatar { width: 100%; height: 100%; border-radius: 50%; background: linear-gradient(135deg, #00CED1, #20B2AA); display: flex; align-items: center; justify-content: center; font-size: 64rpx; color: #fff; font-weight: 700; }
.author-name { font-size: 40rpx; font-weight: 700; color: #fff; display: block; margin-bottom: 8rpx; }
.author-title { font-size: 26rpx; color: #00CED1; display: block; margin-bottom: 24rpx; }
.author-bio { font-size: 26rpx; color: rgba(255,255,255,0.7); line-height: 1.8; display: block; margin-bottom: 32rpx; }
diff --git a/miniprogram/pages/chapters/chapters.js b/miniprogram/pages/chapters/chapters.js
index 9106b80a..154ffa44 100644
--- a/miniprogram/pages/chapters/chapters.js
+++ b/miniprogram/pages/chapters/chapters.js
@@ -212,29 +212,31 @@ Page({
navBarHeight: app.globalData.navBarHeight
})
this.updateUserStatus()
- this.loadBookDataFromServer()
- this.loadDailyChapters()
- this.loadTotalFromServer()
+ this.loadChaptersOnce()
},
- async loadTotalFromServer() {
- try {
- const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
- if (res && (res.total || (res.data && res.data.length))) {
- this.setData({ totalSections: res.total || (res.data || []).length })
- }
- } catch (e) {}
+ // 固定模块(序言、尾声、附录)不参与中间篇章
+ _isFixedPart(pt) {
+ if (!pt) return false
+ const p = String(pt).toLowerCase().replace(/[_\s||]/g, '')
+ return p.includes('序言') || p.includes('尾声') || p.includes('附录')
},
- // stitch_soul P0-8:从服务端加载目录,按 part 聚合,带 isNew、免费/¥1
- async loadBookDataFromServer() {
+ // 一次请求拉取全量目录,同时更新 totalSections / bookData / dailyChapters
+ async loadChaptersOnce() {
try {
const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
const rows = (res && res.data) || (res && res.chapters) || []
if (rows.length === 0) return
+
+ // 1. totalSections
+ const totalSections = res.total ?? rows.length
+
+ // 2. bookData(过滤序言/尾声/附录,中间篇章按 part 聚合)
+ const filtered = rows.filter(r => !this._isFixedPart(r.partTitle || r.part_title))
const partMap = new Map()
const numbers = ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二']
- rows.forEach((r, idx) => {
+ filtered.forEach((r) => {
const pid = r.partId || r.part_id || 'part-1'
const cid = r.chapterId || r.chapter_id || 'chapter-1'
if (!partMap.has(pid)) {
@@ -270,9 +272,28 @@ Page({
chapters: Array.from(p.chapters.values())
}))
const firstPart = bookData[0] && bookData[0].id
+
+ // 3. dailyChapters(sort_order > 62 的新增章节,按更新时间取前20)
+ const baseSort = 62
+ const daily = rows
+ .filter(r => (r.sectionOrder ?? r.sort_order ?? 0) > baseSort)
+ .sort((a, b) => new Date(b.updatedAt || b.updated_at || 0) - new Date(a.updatedAt || a.updated_at || 0))
+ .slice(0, 20)
+ .map(c => {
+ const d = new Date(c.updatedAt || c.updated_at || Date.now())
+ return {
+ id: c.id,
+ mid: c.mid ?? c.MID ?? 0,
+ title: c.section_title || c.title || c.sectionTitle,
+ price: c.price ?? 1,
+ dateStr: `${d.getMonth() + 1}/${d.getDate()}`
+ }
+ })
+
this.setData({
bookData,
- totalSections: rows.length,
+ totalSections,
+ dailyChapters: daily,
expandedPart: firstPart || this.data.expandedPart
})
} catch (e) { console.log('[Chapters] 加载目录失败:', e) }
@@ -324,30 +345,6 @@ Page({
wx.switchTab({ url: '/pages/index/index' })
},
- async loadDailyChapters() {
- try {
- const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
- const chapters = (res && res.data) || (res && res.chapters) || []
- const daily = chapters
- .filter(c => (c.sectionOrder || c.sort_order || 0) > 62)
- .sort((a, b) => new Date(b.updatedAt || b.updated_at || 0) - new Date(a.updatedAt || a.updated_at || 0))
- .slice(0, 20)
- .map(c => {
- const d = new Date(c.updatedAt || c.updated_at || Date.now())
- return {
- id: c.id,
- mid: c.mid ?? c.MID ?? 0,
- title: c.section_title || c.title || c.sectionTitle,
- price: c.price || 1,
- dateStr: `${d.getMonth()+1}/${d.getDate()}`
- }
- })
- if (daily.length > 0) {
- this.setData({ dailyChapters: daily, totalSections: 62 + daily.length })
- }
- } catch (e) { console.log('[Chapters] 加载最新新增失败:', e) }
- },
-
// 跳转到搜索页
goToSearch() {
wx.navigateTo({ url: '/pages/search/search' })
diff --git a/miniprogram/pages/index/index.js b/miniprogram/pages/index/index.js
index 35b27f0c..9fb60ee6 100644
--- a/miniprogram/pages/index/index.js
+++ b/miniprogram/pages/index/index.js
@@ -104,19 +104,13 @@ Page({
this.updateUserStatus()
},
- // 初始化数据
- async initData() {
- this.setData({ loading: true })
- try {
- await this.loadBookData()
- await this.loadFeaturedFromServer()
- this.loadSuperMembers()
- this.loadLatestChapters()
- } catch (e) {
- console.error('初始化失败:', e)
- } finally {
- this.setData({ loading: false })
- }
+ // 初始化数据:首次进页面并行异步加载,加快首屏展示
+ initData() {
+ this.setData({ loading: false })
+ this.loadBookData()
+ this.loadFeaturedFromServer()
+ this.loadSuperMembers()
+ this.loadLatestChapters()
},
async loadSuperMembers() {
@@ -358,9 +352,14 @@ Page({
wx.switchTab({ url: '/pages/my/my' })
},
- // 下拉刷新
+ // 下拉刷新(等待各异步加载完成后再结束)
async onPullDownRefresh() {
- await this.initData()
+ await Promise.all([
+ this.loadBookData(),
+ this.loadFeaturedFromServer(),
+ this.loadSuperMembers(),
+ this.loadLatestChapters()
+ ])
this.updateUserStatus()
wx.stopPullDownRefresh()
},
diff --git a/miniprogram/pages/index/index.wxml b/miniprogram/pages/index/index.wxml
index 53bc16a7..04cd2438 100644
--- a/miniprogram/pages/index/index.wxml
+++ b/miniprogram/pages/index/index.wxml
@@ -4,7 +4,7 @@
-
+
@@ -38,8 +36,8 @@
-
-
+
+
最新更新
{{latestSection.title}}
@@ -49,6 +47,13 @@
→
+
+
+ 最新更新
+ 加载中...
+
+ 开始阅读→
+
@@ -81,14 +86,10 @@
-
+
@@ -124,14 +125,10 @@
-
+
a + c.charCodeAt(0), 0)
- const mock = MOCK_ENRICHMENT[hash % MOCK_ENRICHMENT.length]
+ // 将空值、「未填写」、纯空格均视为未填写(用于隐藏对应项)
+ _emptyIfPlaceholder(v) {
+ if (v == null || v === undefined) return ''
+ const s = String(v).trim()
+ return (s === '' || s === '未填写') ? '' : s
+ },
+ enrichAndFormat(raw) {
+ const e = (v) => this._emptyIfPlaceholder(v)
const merged = {
id: raw.id,
name: raw.name || raw.vipName || raw.vip_name || raw.nickname || '创业者',
avatar: raw.avatar || raw.vipAvatar || raw.vip_avatar || '',
- isVip: raw.isVip || raw.is_vip === 1,
- mbti: raw.mbti || mock.mbti,
- region: raw.region || mock.region,
- industry: raw.industry || mock.industry,
- position: raw.position || mock.position,
- businessScale: raw.businessScale || raw.business_scale || mock.businessScale,
- skills: raw.skills || mock.skills,
- contactRaw: raw.contactRaw || raw.vipContact || raw.vip_contact || raw.phone || mock.contactRaw,
- wechatRaw: raw.wechatRaw || raw.wechatId || raw.wechat_id || mock.wechatRaw,
- bestMonth: raw.bestMonth || raw.storyBestMonth || raw.story_best_month || mock.bestMonth,
- achievement: raw.achievement || raw.storyAchievement || raw.story_achievement || mock.achievement,
- turningPoint: raw.turningPoint || raw.storyTurning || raw.story_turning || mock.turningPoint,
- canHelp: raw.canHelp || raw.helpOffer || raw.help_offer || mock.canHelp,
- needHelp: raw.needHelp || raw.helpNeed || raw.help_need || mock.needHelp,
- project: raw.project || raw.vipProject || raw.vip_project || raw.projectIntro || raw.project_intro || mock.project
+ isVip: !!(raw.isVip || raw.is_vip),
+ mbti: e(raw.mbti),
+ region: e(raw.region),
+ industry: e(raw.industry),
+ position: e(raw.position),
+ businessScale: e(raw.businessScale || raw.business_scale),
+ skills: e(raw.skills),
+ contactRaw: raw.contactRaw || raw.vipContact || raw.vip_contact || raw.phone || '',
+ wechatRaw: raw.wechatRaw || raw.wechatId || raw.wechat_id || '',
+ bestMonth: e(raw.bestMonth || raw.storyBestMonth || raw.story_best_month),
+ achievement: e(raw.achievement || raw.storyAchievement || raw.story_achievement),
+ turningPoint: e(raw.turningPoint || raw.storyTurning || raw.story_turning),
+ canHelp: e(raw.canHelp || raw.helpOffer || raw.help_offer),
+ needHelp: e(raw.needHelp || raw.helpNeed || raw.help_need),
+ project: e(raw.project || raw.vipProject || raw.vip_project || raw.projectIntro || raw.project_intro)
}
const contact = merged.contactRaw || ''
diff --git a/miniprogram/pages/member-detail/member-detail.wxml b/miniprogram/pages/member-detail/member-detail.wxml
index 553c52bb..0b1730c5 100644
--- a/miniprogram/pages/member-detail/member-detail.wxml
+++ b/miniprogram/pages/member-detail/member-detail.wxml
@@ -6,10 +6,7 @@
‹
个人资料
-
- ⋯
- ●
-
+
@@ -18,21 +15,23 @@
-
-
- {{member.name[0] || '创'}}
+
+
+
+ {{member.name[0] || '创'}}
+
VIP
{{member.name}}
-
+
{{member.mbti}}
📍{{member.region}}
-
-
+
+
👤
基本信息
@@ -55,26 +54,30 @@
我擅长
{{member.skills}}
-
+
联系方式
- {{member.contactDisplay || '未填写'}}
- 🔒
- 📋
+ {{member.contactDisplay || member.contactRaw}}
+
+
+
+ 📋
-
+
微信号
- {{member.wechatDisplay || '未填写'}}
- 🔒
- 📋
+ {{member.wechatDisplay || member.wechatRaw}}
+
+
+
+ 📋
-
+
💡
@@ -85,12 +88,12 @@
🏆最赚钱的一个月做的是什么
{{member.bestMonth}}
-
+
⭐最有成就感的一件事
{{member.achievement}}
-
+
🔄人生的转折点
{{member.turningPoint}}
@@ -98,7 +101,7 @@
-
+
🤝
diff --git a/miniprogram/pages/member-detail/member-detail.wxss b/miniprogram/pages/member-detail/member-detail.wxss
index 1151f00f..c6f547b2 100644
--- a/miniprogram/pages/member-detail/member-detail.wxss
+++ b/miniprogram/pages/member-detail/member-detail.wxss
@@ -35,12 +35,16 @@
pointer-events: none;
}
.profile-body { position: relative; z-index: 1; display: flex; flex-direction: column; align-items: center; }
-.avatar-wrap {
+.avatar-outer {
position: relative;
width: 176rpx; height: 176rpx;
+ margin-bottom: 32rpx;
+}
+.avatar-wrap {
+ position: relative;
+ width: 100%; height: 100%;
border-radius: 50%;
overflow: hidden;
- margin-bottom: 32rpx;
border: 2rpx solid rgba(255, 255, 255, 0.1);
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.4);
}
@@ -62,10 +66,12 @@
font-size: 56rpx; color: #5EEAD4; font-weight: 700;
}
.vip-tag {
- position: absolute; bottom: 4rpx; right: 4rpx;
+ position: absolute; bottom: -4rpx; right: -4rpx;
background: linear-gradient(135deg, #F59E0B, #e8920d);
color: #000; font-size: 20rpx; font-weight: 800;
- padding: 6rpx 16rpx; border-radius: 20rpx;
+ padding: 6rpx 14rpx; border-radius: 16rpx;
+ z-index: 2;
+ box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.3);
}
.profile-name { font-size: 40rpx; font-weight: 700; color: #fff; margin-bottom: 24rpx; letter-spacing: 2rpx; }
.profile-tags { display: flex; align-items: center; justify-content: center; gap: 24rpx; flex-wrap: wrap; }
@@ -96,6 +102,8 @@
.f-val.mono { font-family: ui-monospace, monospace; letter-spacing: 2rpx; }
.f-row { display: flex; align-items: center; gap: 16rpx; }
.icon-copy { font-size: 36rpx; color: #94A3B8; opacity: 0.6; padding: 8rpx; }
+.icon-eye-off { display: flex; align-items: center; justify-content: center; }
+.icon-eye-off .icon-img { width: 40rpx; height: 40rpx; }
.divider { height: 1rpx; background: rgba(255, 255, 255, 0.05); margin: 32rpx 0; }
diff --git a/miniprogram/pages/my/my.wxml b/miniprogram/pages/my/my.wxml
index c333a623..6fad4513 100644
--- a/miniprogram/pages/my/my.wxml
+++ b/miniprogram/pages/my/my.wxml
@@ -1,8 +1,11 @@
-
+
-
+
我的
+
+
+
@@ -16,77 +19,69 @@
点击登录
-
-