feat: 小程序超级个体/个人资料/CKB获客;VIP列表展示过滤;管理端与API联调

- 超级个体:去掉首位特例;列表仅展示有头像且非微信默认昵称(vip.go)
- 个人资料:居中头像、低调联系方式、点头像优先走存客宝 lead(ckbLeadToken)
- 阅读页分享朋友圈复制与 toast 去重
- soul-api: miniprogram users 带 ckbLeadToken;其它 handler 与路由调整
- 脚本:content_upload、miniprogram 上传辅助等

Made-with: Cursor
This commit is contained in:
卡若
2026-03-22 08:34:28 +08:00
parent 17ce20c8ee
commit 5724fba877
119 changed files with 8198 additions and 4369 deletions

View File

@@ -3,20 +3,13 @@
* 账号绑定功能
*/
const app = getApp()
const { toAvatarPath } = require('../../utils/util.js')
Page({
data: {
statusBarHeight: 44,
isLoggedIn: false,
userInfo: null,
version: '1.0.0',
isDevMode: false, // 是否开发版(用于显示切换账号入口)
// 切换账号(开发)
showSwitchAccountModal: false,
switchAccountUserId: '',
switchAccountLoading: false,
version: '',
// 绑定信息
phoneNumber: '',
@@ -38,16 +31,27 @@ Page({
wx.showShareMenu({ withShareTimeline: true })
const accountInfo = wx.getAccountInfoSync ? wx.getAccountInfoSync() : null
const envVersion = accountInfo?.miniProgram?.envVersion || ''
const wxPkgVersion = (accountInfo?.miniProgram?.version || '').trim()
const displayVersion =
wxPkgVersion ||
(app.globalData.appDisplayVersion || '1.7.1')
this.setData({
statusBarHeight: app.globalData.statusBarHeight,
isLoggedIn: app.globalData.isLoggedIn,
userInfo: app.globalData.userInfo,
isDevMode: envVersion === 'develop'
isDevMode: envVersion === 'develop',
version: displayVersion
})
this.loadBindingInfo()
},
onShow() {
const accountInfo = wx.getAccountInfoSync ? wx.getAccountInfoSync() : null
const wxPkgVersion = (accountInfo?.miniProgram?.version || '').trim()
const displayVersion =
wxPkgVersion ||
(app.globalData.appDisplayVersion || '1.7.1')
this.setData({ version: displayVersion })
this.loadBindingInfo()
},
@@ -247,92 +251,6 @@ Page({
}
},
// 获取微信头像(新版授权)
async getWechatAvatar() {
try {
const res = await wx.getUserProfile({
desc: '用于完善会员资料'
})
if (res.userInfo) {
const { nickName, avatarUrl: tempAvatarUrl } = res.userInfo
wx.showLoading({ title: '上传中...', mask: true })
// 1. 先上传图片到服务器
console.log('[Settings] 开始上传头像:', tempAvatarUrl)
const uploadRes = await new Promise((resolve, reject) => {
wx.uploadFile({
url: app.globalData.baseUrl + '/api/miniprogram/upload',
filePath: tempAvatarUrl,
name: 'file',
formData: {
folder: 'avatars'
},
success: (uploadResult) => {
try {
const data = JSON.parse(uploadResult.data)
if (data.success) {
resolve(data)
} else {
reject(new Error(data.error || '上传失败'))
}
} catch (err) {
reject(new Error('解析响应失败'))
}
},
fail: (err) => {
reject(err)
}
})
})
// 2. 获取上传后的完整URL显示用保存时只传路径
let avatarUrl = uploadRes.data?.url || uploadRes.url
if (avatarUrl && !avatarUrl.startsWith('http')) {
avatarUrl = app.globalData.baseUrl + avatarUrl
}
console.log('[Settings] 头像上传成功:', avatarUrl)
// 3. 更新本地
this.setData({
userInfo: {
...this.data.userInfo,
nickname: nickName,
avatar: avatarUrl
}
})
// 4. 同步到服务器数据库(只保存路径,不含域名)
const userId = app.globalData.userInfo?.id
if (userId) {
await app.request('/api/miniprogram/user/profile', {
method: 'POST',
data: { userId, nickname: nickName, avatar: toAvatarPath(avatarUrl) }
})
}
// 5. 更新全局
if (app.globalData.userInfo) {
app.globalData.userInfo.nickname = nickName
app.globalData.userInfo.avatar = avatarUrl
wx.setStorageSync('userInfo', app.globalData.userInfo)
}
wx.hideLoading()
wx.showToast({ title: '头像更新成功', icon: 'success' })
}
} catch (e) {
wx.hideLoading()
console.error('[Settings] 获取头像失败:', e)
wx.showToast({
title: e.message || '获取头像失败',
icon: 'none'
})
}
},
// 微信隐私协议同意getPhoneNumber 需先同意)
onAgreePrivacyForPhone() {
if (app._privacyResolve) {
@@ -402,71 +320,6 @@ Page({
this.setData({ showBindModal: false })
},
// 跳转账户密码登录页(开发)
goToDevLogin() {
wx.navigateTo({ url: '/pages/dev-login/dev-login' })
},
// 打开切换账号弹窗(开发)
openSwitchAccountModal() {
this.setData({
showSwitchAccountModal: true,
switchAccountUserId: app.globalData.userInfo?.id || ''
})
},
// 关闭切换账号弹窗
closeSwitchAccountModal() {
if (this.data.switchAccountLoading) return
this.setData({ showSwitchAccountModal: false, switchAccountUserId: '' })
},
// 切换账号 userId 输入
onSwitchAccountUserIdInput(e) {
this.setData({ switchAccountUserId: e.detail.value.trim() })
},
// 确认切换账号
async confirmSwitchAccount() {
const userId = this.data.switchAccountUserId.trim()
if (!userId || this.data.switchAccountLoading) return
this.setData({ switchAccountLoading: true })
try {
const res = await app.request('/api/miniprogram/dev/login-as', {
method: 'POST',
data: { userId }
})
if (res && res.success && res.data) {
const { token, user } = res.data
const openId = res.data.openId || ''
wx.setStorageSync('token', token)
wx.setStorageSync('userInfo', user)
app.globalData.userInfo = user
app.globalData.isLoggedIn = true
app.globalData.purchasedSections = user.purchasedSections || []
app.globalData.hasFullBook = user.hasFullBook || false
app.globalData.isVip = user.isVip || false
app.globalData.vipExpireDate = user.vipExpireDate || ''
if (openId) {
app.globalData.openId = openId
wx.setStorageSync('openId', openId)
}
this.setData({
showSwitchAccountModal: false,
switchAccountUserId: '',
switchAccountLoading: false
})
this.loadBindingInfo()
wx.showToast({ title: '已切换为 ' + (user.nickname || userId), icon: 'success' })
} else {
throw new Error(res?.error || '切换失败')
}
} catch (e) {
this.setData({ switchAccountLoading: false })
wx.showToast({ title: e.message || '切换失败', icon: 'none' })
}
},
// 清除缓存
clearCache() {
wx.showModal({

View File

@@ -113,21 +113,7 @@
<text class="tip-text">提示:绑定微信号才能使用提现功能</text>
</view>
<!-- 开发专用:切换账号(仅开发版显示) -->
<view class="dev-switch-card" wx:if="{{isDevMode}}" bindtap="openSwitchAccountModal">
<view class="dev-switch-inner">
<icon name="wrench" size="40" color="#8e8e93" customClass="dev-switch-icon"></icon>
<text class="dev-switch-text">切换账号(开发)</text>
<text class="dev-switch-desc">输入 userId 切换为其他账号调试</text>
</view>
</view>
<view class="dev-switch-card" wx:if="{{isDevMode}}" bindtap="goToDevLogin">
<view class="dev-switch-inner">
<icon name="smartphone" size="40" color="#8e8e93" customClass="dev-switch-icon"></icon>
<text class="dev-switch-text">账户密码登录</text>
<text class="dev-switch-desc">输入对方手机号登录,密码可留空</text>
</view>
</view>
<!-- 开发专用入口已移除 -->
<view class="logout-btn" wx:if="{{isLoggedIn}}" bindtap="handleLogout">退出登录</view>
</view>
@@ -164,28 +150,4 @@
</view>
</view>
<!-- 切换账号弹窗(开发) -->
<view class="modal-overlay" wx:if="{{showSwitchAccountModal}}" bindtap="closeSwitchAccountModal">
<view class="modal-content" catchtap="stopPropagation">
<view class="modal-header">
<text class="modal-title">切换账号(开发)</text>
<view class="modal-close" bindtap="closeSwitchAccountModal"><icon name="x" size="36" color="#8e8e93"></icon></view>
</view>
<view class="modal-body">
<view class="input-wrapper">
<input
class="form-input"
placeholder="请输入目标用户的 userId如 ogpTW5fmXRGNpoUbXB3UEqnVe5Tg"
placeholder-class="input-placeholder"
value="{{switchAccountUserId}}"
bindinput="onSwitchAccountUserIdInput"
/>
</view>
<text class="bind-tip">从管理端或数据库获取要调试的用户 ID切换后将以该用户身份操作</text>
<view class="btn-primary {{!switchAccountUserId || switchAccountLoading ? 'btn-disabled' : ''}}" bindtap="confirmSwitchAccount">
{{switchAccountLoading ? '切换中...' : '确认切换'}}
</view>
</view>
</view>
</view>
</view>