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:
@@ -13,6 +13,9 @@ const { toAvatarPath } = require('../../utils/util.js')
|
||||
|
||||
const MBTI_OPTIONS = ['INTJ', 'INFP', 'INTP', 'ENTP', 'ENFP', 'ENTJ', 'ENFJ', 'INFJ', 'ISTJ', 'ISFJ', 'ESTJ', 'ESFJ', 'ISTP', 'ISFP', 'ESTP', 'ESFP']
|
||||
|
||||
/** 首次分步完善完成后写入;与手机号+昵称齐全时自动写入,老用户免向导 */
|
||||
const PROFILE_WIZARD_DONE_KEY = 'profile_wizard_v1_done'
|
||||
|
||||
Page({
|
||||
data: {
|
||||
statusBarHeight: 44,
|
||||
@@ -41,9 +44,17 @@ Page({
|
||||
loading: true,
|
||||
showPrivacyModal: false,
|
||||
nicknameInputFocus: false,
|
||||
/** 首次完善:分 3 步;full=1 或未达标资料时为单页 */
|
||||
wizardMode: false,
|
||||
wizardStep: 1,
|
||||
totalWizardSteps: 3,
|
||||
},
|
||||
|
||||
onLoad(options) {
|
||||
this._wizardRouteOpts = {
|
||||
full: options?.full === '1',
|
||||
wizardOff: options?.wizard === '0',
|
||||
}
|
||||
this.setData({
|
||||
statusBarHeight: app.globalData.statusBarHeight || 44,
|
||||
fromVip: options?.from === 'vip',
|
||||
@@ -98,6 +109,7 @@ Page({
|
||||
projectIntro: v('projectIntro'),
|
||||
loading: false,
|
||||
})
|
||||
this._applyWizardModeFromProfile(d)
|
||||
setTimeout(() => this.generateShareCard(), 200)
|
||||
} else {
|
||||
this.setData({ loading: false })
|
||||
@@ -109,6 +121,49 @@ Page({
|
||||
|
||||
goBack() { getApp().goBackOrToHome() },
|
||||
|
||||
/**
|
||||
* 是否走三步向导:资料未「手机号+昵称」齐全且未标记完成,且非 full=1、非 VIP 开通页强制单页。
|
||||
* 老用户已齐全则自动写 DONE,避免重复向导。
|
||||
*/
|
||||
_applyWizardModeFromProfile(d) {
|
||||
const ro = this._wizardRouteOpts || {}
|
||||
const forceFull = ro.full === true
|
||||
const forceNoWizard = ro.wizardOff === true
|
||||
const phoneNum = String(d.phone || '').replace(/\D/g, '')
|
||||
const phoneOk = /^1[3-9]\d{9}$/.test(phoneNum)
|
||||
const nickOk = !!(d.nickname && String(d.nickname).trim())
|
||||
if (phoneOk && nickOk && !wx.getStorageSync(PROFILE_WIZARD_DONE_KEY)) {
|
||||
wx.setStorageSync(PROFILE_WIZARD_DONE_KEY, '1')
|
||||
}
|
||||
const wizardMode = !forceFull && !forceNoWizard && !wx.getStorageSync(PROFILE_WIZARD_DONE_KEY)
|
||||
this.setData({ wizardMode, wizardStep: 1 })
|
||||
},
|
||||
|
||||
onWizardPrev() {
|
||||
if (this.data.wizardStep > 1) {
|
||||
this.setData({ wizardStep: this.data.wizardStep - 1 })
|
||||
}
|
||||
},
|
||||
|
||||
onWizardNext() {
|
||||
if (this.data.saving) return
|
||||
const { wizardMode, wizardStep } = this.data
|
||||
if (!wizardMode) return
|
||||
if (wizardStep === 1) {
|
||||
if (!(this.data.nickname || '').trim()) {
|
||||
wx.showToast({ title: '请填写昵称', icon: 'none' })
|
||||
return
|
||||
}
|
||||
this.setData({ wizardStep: 2 })
|
||||
return
|
||||
}
|
||||
if (wizardStep === 2) {
|
||||
this.setData({ wizardStep: 3 })
|
||||
return
|
||||
}
|
||||
this._doSaveProfile({ wizardComplete: true })
|
||||
},
|
||||
|
||||
onNicknameAreaTouch() {
|
||||
if (typeof wx.requirePrivacyAuthorize !== 'function') return
|
||||
wx.requirePrivacyAuthorize({
|
||||
@@ -365,7 +420,11 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
async saveProfile() {
|
||||
saveProfile() {
|
||||
this._doSaveProfile({ wizardComplete: false })
|
||||
},
|
||||
|
||||
async _doSaveProfile({ wizardComplete }) {
|
||||
const userId = app.globalData.userInfo?.id
|
||||
if (!userId) {
|
||||
wx.showToast({ title: '请先登录', icon: 'none' })
|
||||
@@ -373,7 +432,6 @@ Page({
|
||||
}
|
||||
const s = (v) => (v || '').toString().trim()
|
||||
const isVip = this.data.isVip
|
||||
// 手机号必填,格式校验(支持带空格/连字符输入)
|
||||
const phoneRaw = s(this.data.phone)
|
||||
if (!phoneRaw) {
|
||||
wx.showToast({ title: '请输入手机号', icon: 'none' })
|
||||
@@ -428,8 +486,17 @@ Page({
|
||||
if (app.globalData.userInfo) {
|
||||
if (payload.nickname) app.globalData.userInfo.nickname = payload.nickname
|
||||
if (res?.data?.avatar) app.globalData.userInfo.avatar = res.data.avatar
|
||||
if (payload.phone) app.globalData.userInfo.phone = payload.phone
|
||||
wx.setStorageSync('userInfo', app.globalData.userInfo)
|
||||
}
|
||||
if (wizardComplete) {
|
||||
wx.setStorageSync(PROFILE_WIZARD_DONE_KEY, '1')
|
||||
this.setData({ wizardMode: false, saving: false })
|
||||
setTimeout(() => {
|
||||
wx.redirectTo({ url: `/pages/member-detail/member-detail?id=${userId}` })
|
||||
}, 400)
|
||||
return
|
||||
}
|
||||
setTimeout(() => getApp().goBackOrToHome(), 800)
|
||||
} catch (e) {
|
||||
wx.showToast({ title: e.message || '保存失败', icon: 'none' })
|
||||
|
||||
Reference in New Issue
Block a user