项目提交

This commit is contained in:
Ghost
2026-03-20 18:20:47 +08:00
parent 2f82ee3b23
commit 79512b2fc6
26 changed files with 268 additions and 175 deletions

View File

@@ -8,6 +8,7 @@ App({
token: null,
siteTitle: '神仙团队AI性格测试',
textConfig: null, // 从 /api/config/runtime 动态加载analyzingTitle, startButtonText, reportTitle, aiAnalysisText 等
maintenanceMode: undefined, // 审核模式undefined=未加载,等 getRuntimeConfig 后再决定,避免 tabBar 闪烁
// 当前使用范围personal 个人版 / enterprise 企业版(影响定价与 enterpriseId 写入)
appScope: 'personal',
// 扫码进入企业页时 scene 解析出的企业IDe_123提交测试/分析时优先使用
@@ -35,10 +36,11 @@ App({
// 静默登录获取openId
this.silentLogin()
// 预加载站点/小程序名称(供导航栏展示)
// 预加载站点/小程序名称、审核模式(供导航栏展示)
this.getRuntimeConfig().then((cfg) => {
if (cfg && cfg.siteTitle) {
this.globalData.siteTitle = cfg.siteTitle
if (cfg) {
if (cfg.siteTitle) this.globalData.siteTitle = cfg.siteTitle
if (cfg.maintenanceMode !== undefined) this.globalData.maintenanceMode = !!cfg.maintenanceMode
}
}).catch(() => {})
},
@@ -332,6 +334,7 @@ App({
const data = res.data.data || {}
if (data.siteTitle) this.globalData.siteTitle = data.siteTitle
if (data.textConfig) this.globalData.textConfig = data.textConfig
if (data.maintenanceMode !== undefined) this.globalData.maintenanceMode = !!data.maintenanceMode
resolve(data)
} else {
reject(new Error(res.data && res.data.message ? res.data.message : '获取配置失败'))

View File

@@ -2,6 +2,7 @@
Component({
data: {
selected: 0,
maintenanceMode: true, // 默认隐藏,等配置加载后再决定是否显示,避免闪烁或残留
list: [
{ pagePath: '/pages/index/index', text: '首页', textKey: 'home', icon: 'home' },
{ pagePath: '/pages/index/camera', text: '查看报告', textKey: 'camera', icon: 'camera' },
@@ -33,6 +34,9 @@ Component({
const url = currentPage.route
let selected = 0
// 未加载配置时视为审核模式(隐藏按钮),避免残留或闪烁
const gd = getApp().globalData || {}
const maintenanceMode = gd.maintenanceMode === false ? false : true
if (url === 'pages/index/index' || url === 'pages/enterprise/index') {
selected = 0
@@ -42,7 +46,7 @@ Component({
selected = 2
}
this.setData({ selected })
this.setData({ selected, maintenanceMode })
} catch (error) {
console.error('updateSelected error:', error)
// 默认选中第一个

View File

@@ -12,6 +12,7 @@
<text class="tab-text">首页</text>
</view>
<view
wx:if="{{!maintenanceMode}}"
class="tab-item-center {{selected === 1 ? 'active' : ''}}"
data-index="1"
data-path="/pages/index/camera"

View File

@@ -8,7 +8,8 @@ Page({
navbarHeight: 88,
siteTitle: '神仙团队AI性格测试',
startButtonEnterprise: '开始面部测试',
aiAnalysisText: '智能分析'
aiAnalysisText: '智能分析',
maintenanceMode: false
},
onLoad(options) {
@@ -59,12 +60,14 @@ Page({
const statusBarHeightRpx = (statusBarHeight * 750) / screenWidth
const navbarHeightRpx = statusBarHeightRpx + 88
const gd = app.globalData
const maintenanceMode = !!(gd.maintenanceMode)
this.setData({
statusBarHeight: statusBarHeightRpx,
navbarHeight: navbarHeightRpx,
siteTitle: gd.siteTitle || '神仙团队AI性格测试',
startButtonEnterprise: (gd.textConfig && gd.textConfig.startButtonEnterprise) || '开始面部测试',
aiAnalysisText: (gd.textConfig && gd.textConfig.aiAnalysisText) || '智能分析'
startButtonEnterprise: maintenanceMode ? '开始性格测试' : ((gd.textConfig && gd.textConfig.startButtonEnterprise) || '开始面部测试'),
aiAnalysisText: (gd.textConfig && gd.textConfig.aiAnalysisText) || '智能分析',
maintenanceMode
})
// 未绑定企业的用户:若从邀请码扫码进入(有 enterpriseIdFromScene也允许使用企业版
@@ -78,9 +81,18 @@ Page({
if (!fromInvite) {
if (userInfo.hasEnterprise === true) {
app.getRuntimeConfig().then((cfg) => {
if (cfg && cfg.siteTitle) {
app.globalData.siteTitle = cfg.siteTitle
this.setData({ siteTitle: cfg.siteTitle })
if (cfg) {
if (cfg.siteTitle) {
app.globalData.siteTitle = cfg.siteTitle
this.setData({ siteTitle: cfg.siteTitle })
}
const maintenanceMode = !!(cfg.maintenanceMode)
if (cfg.maintenanceMode !== undefined) app.globalData.maintenanceMode = maintenanceMode
this.setData({
startButtonEnterprise: maintenanceMode ? '开始性格测试' : (cfg.textConfig && cfg.textConfig.startButtonEnterprise || '开始面部测试'),
aiAnalysisText: (cfg.textConfig && cfg.textConfig.aiAnalysisText) || '智能分析',
maintenanceMode
})
}
}).catch(() => {})
return
@@ -98,13 +110,14 @@ Page({
app.globalData.siteTitle = cfg.siteTitle
this.setData({ siteTitle: cfg.siteTitle })
}
if (cfg.textConfig) {
app.globalData.textConfig = cfg.textConfig
this.setData({
startButtonEnterprise: cfg.textConfig.startButtonEnterprise || '开始面部测试',
aiAnalysisText: cfg.textConfig.aiAnalysisText || '智能分析'
})
}
if (cfg.textConfig) app.globalData.textConfig = cfg.textConfig
const maintenanceMode = !!(cfg && cfg.maintenanceMode)
if (cfg.maintenanceMode !== undefined) app.globalData.maintenanceMode = maintenanceMode
this.setData({
startButtonEnterprise: maintenanceMode ? '开始性格测试' : (cfg.textConfig && cfg.textConfig.startButtonEnterprise || '开始面部测试'),
aiAnalysisText: (cfg.textConfig && cfg.textConfig.aiAnalysisText) || '智能分析',
maintenanceMode
})
}
if ((cfg && cfg.pricingType) !== 'enterprise' && !app.globalData.enterpriseIdFromScene) {
redirectBack()
@@ -136,14 +149,17 @@ Page({
onShow() {
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
this.getTabBar().setData({ selected: 0 })
const tabBar = this.getTabBar()
tabBar.setData({ selected: 0, maintenanceMode: !!getApp().globalData.maintenanceMode })
}
try { getApp().globalData.appScope = 'enterprise' } catch (e) {}
const gd = getApp().globalData
const maintenanceMode = !!(gd.maintenanceMode)
this.setData({
siteTitle: gd.siteTitle || '神仙团队AI性格测试',
startButtonEnterprise: (gd.textConfig && gd.textConfig.startButtonEnterprise) || '开始面部测试',
aiAnalysisText: (gd.textConfig && gd.textConfig.aiAnalysisText) || '智能分析'
startButtonEnterprise: maintenanceMode ? '开始性格测试' : ((gd.textConfig && gd.textConfig.startButtonEnterprise) || '开始面部测试'),
aiAnalysisText: (gd.textConfig && gd.textConfig.aiAnalysisText) || '智能分析',
maintenanceMode
})
},
@@ -154,8 +170,12 @@ Page({
})
},
// 开始AI面部测试先校验是否已上传简历再跳转相机
// 开始AI面部测试先校验是否已上传简历再跳转相机;审核模式下直接跳转 test-select
startAITest() {
if (this.data.maintenanceMode) {
wx.navigateTo({ url: '/pages/test-select/index' })
return
}
const eid = (app.globalData && app.globalData.enterpriseIdFromScene) || (app.globalData && app.globalData.userInfo && app.globalData.userInfo.enterpriseId) || (wx.getStorageSync('userInfo') || {}).enterpriseId || null
const query = eid ? `?enterpriseId=${eid}&pageSize=1` : '?pageSize=1'
request({

View File

@@ -33,13 +33,13 @@
<view class="step-item">
<view class="step-circle active">1</view>
<text class="step-label">STEP1</text>
<text class="step-text">拍摄照片</text>
<text class="step-text">{{maintenanceMode ? '选择测试' : '拍摄照片'}}</text>
</view>
<view class="step-line"></view>
<view class="step-item">
<view class="step-circle">2</view>
<text class="step-label">STEP2</text>
<text class="step-text">{{aiAnalysisText || '智能分析'}}</text>
<text class="step-text">{{maintenanceMode ? '回答题目' : (aiAnalysisText || '智能分析')}}</text>
</view>
<view class="step-line"></view>
<view class="step-item">

View File

@@ -9,7 +9,8 @@
display: flex;
flex-direction: column;
box-sizing: border-box;
padding-bottom: calc(100rpx + env(safe-area-inset-bottom) + 40rpx);
/* 底部留足空间,避免开始按钮遮挡 tabBar 中间浮起圆钮(上浮 56rpx */
padding-bottom: calc(156rpx + env(safe-area-inset-bottom) + 40rpx);
}
/* 背景装饰圆形 */

View File

@@ -1,6 +1,6 @@
// pages/index/camera.js - 面相分析拍照页,拍完后上传到服务器再跳转结果页
const app = getApp()
const { hasPhone, bindPhoneByCode, ensureProfileCompleteAndRedirect } = require('../../utils/phoneAuth.js')
const { hasPhone, bindPhoneByCode } = require('../../utils/phoneAuth.js')
Page({
data: {
@@ -29,10 +29,8 @@ Page({
},
onShow() {
if (!ensureProfileCompleteAndRedirect()) return
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
this.getTabBar().setData({ selected: 1 })
}
const tabBar = typeof this.getTabBar === 'function' && this.getTabBar()
if (tabBar) tabBar.setData({ selected: 1, maintenanceMode: !!app.globalData.maintenanceMode })
this.setData({ needPhoneAuth: !hasPhone() })
const tc = app.globalData.textConfig
if (tc && tc.aiAnalysisText) {
@@ -115,14 +113,8 @@ Page({
})
},
// 完成拍照:先上传 3 张图到服务器,拿到 URL 后再跳转结果页
// 完成拍照:先上传 3 张图到服务器,拿到 URL 后再跳转结果页(不强制完善资料)
completeCapture() {
if (!ensureProfileCompleteAndRedirect()) return
if (!hasPhone()) {
wx.showToast({ title: '请先授权手机号', icon: 'none' })
this.setData({ needPhoneAuth: true })
return
}
const photos = this.data.photos
if (!photos || photos.length === 0) {
wx.showToast({ title: '请先拍摄照片', icon: 'none' })

View File

@@ -59,11 +59,4 @@
</view>
</view>
<!-- 手机号授权入口:仅在未授权时显示,就地弹出微信系统手机号授权 -->
<view class="phone-auth-section" wx:if="{{needPhoneAuth}}">
<text class="phone-auth-tip">为保障服务与联系,请先授权手机号。</text>
<button class="phone-auth-btn" open-type="getPhoneNumber" bindgetphonenumber="onGetPhoneNumber">
授权手机号后开始查看报告
</button>
</view>
</view>

View File

@@ -9,7 +9,8 @@ Page({
showEnterpriseEntry: false,
siteTitle: '神仙团队AI性格测试',
startButtonText: '开始面相测试',
aiAnalysisText: '智能分析'
aiAnalysisText: '智能分析',
maintenanceMode: false
},
onLoad(options) {
@@ -23,13 +24,15 @@ Page({
const userInfo = getApp().globalData.userInfo || wx.getStorageSync('userInfo') || {}
const gd = getApp().globalData
const maintenanceMode = !!(gd.maintenanceMode)
this.setData({
statusBarHeight: statusBarHeightRpx,
navbarHeight: navbarHeightRpx,
showEnterpriseEntry: userInfo.hasEnterprise === true,
siteTitle: gd.siteTitle || '神仙团队AI性格测试',
startButtonText: (gd.textConfig && gd.textConfig.startButtonText) || '开始面相测试',
aiAnalysisText: (gd.textConfig && gd.textConfig.aiAnalysisText) || '智能分析'
startButtonText: maintenanceMode ? '开始性格测试' : ((gd.textConfig && gd.textConfig.startButtonText) || '开始面相测试'),
aiAnalysisText: (gd.textConfig && gd.textConfig.aiAnalysisText) || '智能分析',
maintenanceMode
})
// 预加载站点名称与文案配置
app.getRuntimeConfig().then((cfg) => {
@@ -40,11 +43,16 @@ Page({
}
if (cfg.textConfig) {
getApp().globalData.textConfig = cfg.textConfig
this.setData({
startButtonText: cfg.textConfig.startButtonText || '开始面相测试',
aiAnalysisText: cfg.textConfig.aiAnalysisText || '智能分析'
})
}
const maintenanceMode = !!(cfg && cfg.maintenanceMode)
if (cfg.maintenanceMode !== undefined) getApp().globalData.maintenanceMode = maintenanceMode
this.setData({
startButtonText: maintenanceMode ? '开始性格测试' : (cfg.textConfig && cfg.textConfig.startButtonText || '开始面相测试'),
aiAnalysisText: (cfg.textConfig && cfg.textConfig.aiAnalysisText) || '智能分析',
maintenanceMode
})
const tabBar = typeof this.getTabBar === 'function' && this.getTabBar()
if (tabBar) tabBar.setData({ maintenanceMode })
}
}).catch(() => {})
// ── 解析入参(兼容扫码 scene / 分享链接 options──
@@ -83,19 +91,20 @@ Page({
},
onShow() {
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
this.getTabBar().setData({ selected: 0 })
}
const tabBar = typeof this.getTabBar === 'function' && this.getTabBar()
if (tabBar) tabBar.setData({ selected: 0, maintenanceMode: !!getApp().globalData.maintenanceMode })
// 个人版首页:固定 scope=personal并清除企业来源上下文
try {
getApp().globalData.appScope = 'personal'
getApp().globalData.enterpriseIdFromScene = null
} catch (e) {}
const gd = getApp().globalData
const maintenanceMode = !!(gd.maintenanceMode)
this.setData({
siteTitle: gd.siteTitle || '神仙团队AI性格测试',
startButtonText: (gd.textConfig && gd.textConfig.startButtonText) || '开始面相测试',
aiAnalysisText: (gd.textConfig && gd.textConfig.aiAnalysisText) || '智能分析'
startButtonText: maintenanceMode ? '开始性格测试' : ((gd.textConfig && gd.textConfig.startButtonText) || '开始面相测试'),
aiAnalysisText: (gd.textConfig && gd.textConfig.aiAnalysisText) || '智能分析',
maintenanceMode
})
const userInfo = getApp().globalData.userInfo || wx.getStorageSync('userInfo') || {}
this.setData({ showEnterpriseEntry: userInfo.hasEnterprise === true })
@@ -117,9 +126,13 @@ Page({
}
},
// 开始拍照(个人版入口:强制本次链路为个人定价)
// 开始拍照(个人版入口:强制本次链路为个人定价);审核模式下跳转 test-select
startCamera() {
try { getApp().globalData.appScope = 'personal' } catch (e) {}
if (this.data.maintenanceMode) {
wx.navigateTo({ url: '/pages/test-select/index' })
return
}
wx.switchTab({
url: '/pages/index/camera'
})

View File

@@ -33,13 +33,13 @@
<view class="step-item">
<view class="step-circle active">1</view>
<text class="step-label">STEP1</text>
<text class="step-text">拍摄照片</text>
<text class="step-text">{{maintenanceMode ? '选择测试' : '拍摄照片'}}</text>
</view>
<view class="step-line"></view>
<view class="step-item">
<view class="step-circle">2</view>
<text class="step-label">STEP2</text>
<text class="step-text">{{aiAnalysisText || '智能分析'}}</text>
<text class="step-text">{{maintenanceMode ? '回答题目' : (aiAnalysisText || '智能分析')}}</text>
</view>
<view class="step-line"></view>
<view class="step-item">
@@ -54,8 +54,6 @@
<text class="button-text">{{startButtonText || '开始面相测试'}}</text>
</view>
<view class="ai-generated-tip">人工智能生成</view>
<!-- 自定义底部导航 -->
<custom-tab-bar />
</view>

View File

@@ -9,7 +9,8 @@
display: flex;
flex-direction: column;
box-sizing: border-box;
padding-bottom: calc(100rpx + env(safe-area-inset-bottom) + 40rpx);
/* 底部留足空间,避免开始按钮遮挡 tabBar 中间浮起圆钮(上浮 56rpx */
padding-bottom: calc(156rpx + env(safe-area-inset-bottom) + 40rpx);
}
/* 背景装饰圆形(与企业版一致) */
@@ -260,14 +261,6 @@
font-weight: 600;
}
.ai-generated-tip {
text-align: center;
font-size: 24rpx;
color: #9CA3AF;
margin-top: 24rpx;
margin-bottom: 16rpx;
}
/* 确保底部导航显示 */
custom-tab-bar {
position: fixed;

View File

@@ -1,7 +1,7 @@
// pages/index/result.js - AI分析结果页按旧版模板重构
const app = getApp()
const payment = require('../../utils/payment')
const { hasPhone, bindPhoneByCode, ensureProfileCompleteAndRedirect } = require('../../utils/phoneAuth.js')
const { hasPhone, bindPhoneByCode, isProfileComplete } = require('../../utils/phoneAuth.js')
Page({
data: {
@@ -50,6 +50,7 @@ Page({
hasReloadedAfterPay: false,
// 是否已在本地拥有手机号(决定是否还需要弹出微信手机号授权)
hasPhone: false,
isProfileComplete: false,
analyzingTitle: '正在分析中',
reportTitle: '分析报告',
aiAnalysisText: '智能分析'
@@ -134,8 +135,7 @@ Page({
},
onShow() {
if (!ensureProfileCompleteAndRedirect()) return
this.setData({ hasPhone: hasPhone() })
this.setData({ hasPhone: hasPhone(), isProfileComplete: isProfileComplete() })
const tc = app.globalData.textConfig
if (tc) {
this.setData({
@@ -324,7 +324,8 @@ Page({
requiresPayment: needPay,
isPaid: !!detailPayload.isPaid,
amountYuan: needPay ? amountYuan : 0
}
},
isProfileComplete: isProfileComplete()
})
return
}
@@ -363,12 +364,15 @@ Page({
})
},
goToCompleteProfile() {
wx.navigateTo({ url: '/pages/user-profile/index' })
},
// 解锁完整报告:发起人脸测试付费
unlockFullReport() {
const { payInfo, testResultId, hasReloadedAfterPay } = this.data
if (!payInfo.requiresPayment || payInfo.isPaid) {
return
}
const { payInfo, testResultId, hasReloadedAfterPay, isProfileComplete } = this.data
if (!isProfileComplete) return
if (!payInfo.requiresPayment || payInfo.isPaid) return
app.ensureLogin().then((logged) => {
if (!logged) {
@@ -402,14 +406,13 @@ Page({
// AI结果页付费按钮就地触发微信手机号授权然后调用 unlockFullReport
onGetPhoneNumberForFacePay(e) {
if (!ensureProfileCompleteAndRedirect()) return
const { code, errMsg } = e.detail || {}
if (errMsg && errMsg.indexOf('getPhoneNumber:fail') === 0) {
if (!hasPhone()) {
if (hasPhone()) {
this.unlockFullReport()
} else {
wx.showToast({ title: '需要授权手机号才能继续', icon: 'none' })
return
}
this.unlockFullReport()
return
}
if (!code) {

View File

@@ -111,36 +111,45 @@
</view>
</view>
<!-- 付费解锁完整报告(不展示虚拟假数据) -->
<view class="card paywall-card" wx:if="{{!hasError && payInfo.requiresPayment && !payInfo.isPaid}}">
<!-- 付费解锁:未完善资料显示「完善资料」;已完善且有价格显示「解锁完整报告」;已完善且无价格则不显示 -->
<view class="card paywall-card" wx:if="{{!hasError && (!isProfileComplete || (isProfileComplete && payInfo.requiresPayment && !payInfo.isPaid))}}">
<view class="paywall-content">
<text class="paywall-fake-title">完整版性格深度解析</text>
<text class="paywall-tip">解锁后将展示完整报告内容</text>
<!-- 中间解锁按钮:未有手机号时就地触发微信手机号授权 -->
<button
<!-- 未完善资料:用户点击后手动跳转 -->
<view
class="paywall-btn"
wx:if="{{!hasPhone}}"
open-type="getPhoneNumber"
bindgetphonenumber="onGetPhoneNumberForFacePay"
wx:if="{{!isProfileComplete}}"
bindtap="goToCompleteProfile"
>
<text class="paywall-btn-main">解锁完整报告</text>
<text class="paywall-btn-price">¥{{payInfo.amountYuan || 0}} / 次</text>
</button>
<!-- 已有手机号时:普通按钮,直接解锁 -->
<button
class="paywall-btn"
wx:elif="{{hasPhone}}"
bindtap="unlockFullReport"
>
<text class="paywall-btn-main">解锁完整报告</text>
<text class="paywall-btn-price">¥{{payInfo.amountYuan || 0}} / 次</text>
</button>
<text class="paywall-tip">一次性解锁本次{{reportTitle || '分析报告'}},永久保存在「历史记录」中</text>
<text class="paywall-btn-main">完善资料</text>
</view>
<!-- 已完善资料且有价格:解锁按钮 -->
<block wx:elif="{{isProfileComplete && payInfo.requiresPayment && !payInfo.isPaid}}">
<button
class="paywall-btn"
wx:if="{{!hasPhone}}"
open-type="getPhoneNumber"
bindgetphonenumber="onGetPhoneNumberForFacePay"
>
<text class="paywall-btn-main">解锁完整报告</text>
<text class="paywall-btn-price">¥{{payInfo.amountYuan || 0}} / 次</text>
</button>
<view
class="paywall-btn"
wx:elif="{{hasPhone}}"
bindtap="unlockFullReport"
>
<text class="paywall-btn-main">解锁完整报告</text>
<text class="paywall-btn-price">¥{{payInfo.amountYuan || 0}} / 次</text>
</view>
</block>
<text class="paywall-tip" wx:if="{{isProfileComplete}}">一次性解锁本次{{reportTitle || '分析报告'}},永久保存在「历史记录」中</text>
</view>
</view>
<!-- ========== 详细分析区(解锁后可见) ========== -->
<block wx:if="{{!hasError && (!payInfo.requiresPayment || payInfo.isPaid)}}">
<block wx:if="{{!hasError && isProfileComplete && (!payInfo.requiresPayment || payInfo.isPaid)}}">
<!-- 面相分析 -->
<block wx:if="{{result.faceAnalysis}}">
@@ -298,7 +307,5 @@
<text class="btn-text-outline">返回首页</text>
</view>
</view>
<view class="ai-generated-tip">人工智能生成</view>
</view>
</view>

View File

@@ -670,14 +670,6 @@
color: #374151;
}
.ai-generated-tip {
text-align: center;
font-size: 24rpx;
color: #9CA3AF;
margin-top: 24rpx;
padding-bottom: 40rpx;
}
/* ========== 操作按钮 ========== */
.action-section {
margin-top: 16rpx;

View File

@@ -1,6 +1,6 @@
// pages/index/camera.js - 面相分析拍照页,拍完后上传到服务器再跳转结果页
const app = getApp()
const { hasPhone, bindPhoneByCode, ensureProfileCompleteAndRedirect } = require('../../utils/phoneAuth.js')
const { hasPhone, bindPhoneByCode } = require('../../utils/phoneAuth.js')
Page({
data: {
@@ -34,7 +34,6 @@ Page({
},
onShow() {
if (!ensureProfileCompleteAndRedirect()) return
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
this.getTabBar().setData({ selected: 1 })
}
@@ -163,9 +162,8 @@ Page({
})
},
// 完成拍照:先上传 3 张图到服务器,拿到 URL 后再跳转结果页
// 完成拍照:先上传 3 张图到服务器,拿到 URL 后再跳转结果页(不强制完善资料)
completeCapture() {
if (!ensureProfileCompleteAndRedirect()) return
const urls = (this.data.uploadedUrls || []).filter(Boolean)
if (!urls.length) {
wx.showToast({ title: '请先上传至少一张照片', icon: 'none' })

View File

@@ -50,9 +50,8 @@ Page({
this.loadData()
}
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
this.getTabBar().setData({ selected: 2 })
}
const tabBar = typeof this.getTabBar === 'function' && this.getTabBar()
if (tabBar) tabBar.setData({ selected: 2, maintenanceMode: !!app.globalData.maintenanceMode })
},
/** 先确保登录完成(静默登录),再刷新页面数据 */

View File

@@ -1,7 +1,7 @@
// pages/purchase/index.js - 开通会员(深度服务价格:个人/企业区分,类目由后端配置可新增)
const app = getApp()
const payment = require('../../utils/payment')
const { hasPhone, bindPhoneByCode, ensureProfileCompleteAndRedirect } = require('../../utils/phoneAuth.js')
const { hasPhone, bindPhoneByCode } = require('../../utils/phoneAuth.js')
Page({
data: {
@@ -27,7 +27,6 @@ Page({
},
onShow() {
if (!ensureProfileCompleteAndRedirect()) return
this.setData({ hasPhone: hasPhone() })
},
@@ -82,9 +81,8 @@ Page({
this.handlePurchase(tab, index)
},
// 实际执行购买/咨询逻辑(已确保有手机号
// 实际执行购买/咨询逻辑(不强制完善资料
handlePurchase(tab, index) {
if (!ensureProfileCompleteAndRedirect()) return
if (index === undefined || index === null) return
const list = tab === 'enterprise' ? this.data.enterpriseCategories : this.data.personalCategories
const category = list[index]

View File

@@ -1,6 +1,7 @@
// pages/result/disc.js - DISC结果页支持付费墙 + 历史详情拉取)
const app = getApp()
const payment = require('../../utils/payment')
const { isProfileComplete } = require('../../utils/phoneAuth.js')
function toIntPercent(v) {
if (v == null) return 0
@@ -32,7 +33,14 @@ Page({
],
payInfo: { requiresPayment: false, isPaid: false, amountYuan: 0 },
testResultId: null,
hasReloadedAfterPay: false
hasReloadedAfterPay: false,
isProfileComplete: false,
maintenanceMode: false
},
onShow() {
const maintenanceMode = !!(getApp().globalData && getApp().globalData.maintenanceMode)
this.setData({ isProfileComplete: isProfileComplete(), maintenanceMode })
},
onLoad(options) {
@@ -77,7 +85,8 @@ Page({
isPaid,
amountYuan: needPaymentToUnlock ? amountYuan : 0
}
this.setData({ payInfo })
const maintenanceMode = !!(getApp().globalData && getApp().globalData.maintenanceMode)
this.setData({ payInfo, isProfileComplete: isProfileComplete(), maintenanceMode })
} else {
wx.showToast({ title: res.data?.message || '加载失败', icon: 'none' })
}
@@ -101,8 +110,13 @@ Page({
.catch(() => this.setData({ payInfo: { requiresPayment: false, isPaid: false, amountYuan: 0 } }))
},
goToCompleteProfile() {
wx.navigateTo({ url: '/pages/user-profile/index' })
},
unlockFullReport() {
const { payInfo, testResultId, hasReloadedAfterPay } = this.data
const { payInfo, testResultId, hasReloadedAfterPay, isProfileComplete } = this.data
if (!isProfileComplete) return
if (!payInfo.requiresPayment || payInfo.isPaid) return
app.ensureLogin && app.ensureLogin().then((logged) => {
if (!logged) { wx.showToast({ title: '请先登录', icon: 'none' }); return }

View File

@@ -9,7 +9,7 @@
<text class="type-description" wx:if="{{result.description && result.description.description}}">{{result.description.description}}</text>
</view>
<view class="paywall-card" wx:if="{{payInfo.requiresPayment && !payInfo.isPaid}}">
<view class="paywall-card" wx:if="{{(!maintenanceMode && !isProfileComplete) || ((maintenanceMode || isProfileComplete) && payInfo.requiresPayment && !payInfo.isPaid)}}">
<view class="paywall-content">
<view class="paywall-blur">
<text class="paywall-fake-title">完整DISC报告</text>
@@ -18,14 +18,25 @@
<text class="paywall-fake-line">• 职业匹配建议</text>
</view>
<view class="paywall-mask"></view>
<view class="paywall-btn" bindtap="unlockFullReport">
<view
class="paywall-btn"
wx:if="{{!maintenanceMode && !isProfileComplete}}"
bindtap="goToCompleteProfile"
>
<text class="paywall-btn-main">完善资料</text>
</view>
<view
wx:elif="{{payInfo.requiresPayment && !payInfo.isPaid}}"
class="paywall-btn"
bindtap="unlockFullReport"
>
<text class="paywall-btn-main">解锁完整报告</text>
<text class="paywall-btn-price">¥{{payInfo.amountYuan}} / 次</text>
</view>
</view>
</view>
<view class="scores-section" wx:if="{{!payInfo.requiresPayment || payInfo.isPaid}}">
<view class="scores-section" wx:if="{{(maintenanceMode || isProfileComplete) && (!payInfo.requiresPayment || payInfo.isPaid)}}">
<text class="section-title">DISC得分详情</text>
<view class="score-item" wx:for="{{typeList}}" wx:key="type">
<view class="score-header">
@@ -39,7 +50,7 @@
</view>
</view>
<view class="analysis-card" wx:if="{{!payInfo.requiresPayment || payInfo.isPaid}}">
<view class="analysis-card" wx:if="{{(maintenanceMode || isProfileComplete) && (!payInfo.requiresPayment || payInfo.isPaid)}}">
<text class="card-title">主要性格特征分析</text>
<view class="trait-section">
<text class="trait-title">优势</text>
@@ -57,7 +68,7 @@
</view>
</view>
<view class="analysis-card" wx:if="{{!payInfo.requiresPayment || payInfo.isPaid}}">
<view class="analysis-card" wx:if="{{(maintenanceMode || isProfileComplete) && (!payInfo.requiresPayment || payInfo.isPaid)}}">
<text class="card-title">职业匹配度分析</text>
<view class="career-item" wx:for="{{result.description.careers}}" wx:key="*this">
<view class="career-dot"></view>

View File

@@ -1,7 +1,7 @@
// pages/result/mbti.js - MBTI结果页支持付费墙 + 历史详情拉取)
const app = getApp()
const payment = require('../../utils/payment')
const { hasPhone, bindPhoneByCode, ensureProfileCompleteAndRedirect } = require('../../utils/phoneAuth.js')
const { hasPhone, bindPhoneByCode, isProfileComplete } = require('../../utils/phoneAuth.js')
Page({
data: {
@@ -22,7 +22,9 @@ Page({
},
testResultId: null,
hasReloadedAfterPay: false,
hasPhone: false
hasPhone: false,
isProfileComplete: false,
maintenanceMode: false
},
onLoad(options) {
@@ -46,8 +48,8 @@ Page({
},
onShow() {
if (!ensureProfileCompleteAndRedirect()) return
this.setData({ hasPhone: hasPhone() })
const maintenanceMode = !!(getApp().globalData && getApp().globalData.maintenanceMode)
this.setData({ hasPhone: hasPhone(), isProfileComplete: isProfileComplete(), maintenanceMode })
},
loadDetail(id) {
@@ -77,7 +79,8 @@ Page({
isPaid,
amountYuan: needPaymentToUnlock ? amountYuan : 0
}
this.setData({ payInfo })
const maintenanceMode = !!(getApp().globalData && getApp().globalData.maintenanceMode)
this.setData({ payInfo, isProfileComplete: isProfileComplete(), maintenanceMode })
} else {
wx.showToast({ title: res.data?.message || '加载失败', icon: 'none' })
}
@@ -157,16 +160,20 @@ Page({
})
},
// 完善资料:用户点击后手动跳转,不强制
goToCompleteProfile() {
wx.navigateTo({ url: '/pages/user-profile/index' })
},
// 付费解锁按钮:就地触发微信手机号授权,然后调用 unlockFullReport
onGetPhoneNumberForMbtiPay(e) {
if (!ensureProfileCompleteAndRedirect()) return
const { code, errMsg } = e.detail || {}
if (errMsg && errMsg.indexOf('getPhoneNumber:fail') === 0) {
if (!hasPhone()) {
if (hasPhone()) {
this.unlockFullReport()
} else {
wx.showToast({ title: '需要授权手机号才能继续', icon: 'none' })
return
}
this.unlockFullReport()
return
}
if (!code) {

View File

@@ -10,8 +10,8 @@
<text class="type-description">{{mbtiDesc.description}}</text>
</view>
<!-- 付费墙:未解锁时显示 -->
<view class="paywall-card" wx:if="{{payInfo.requiresPayment && !payInfo.isPaid}}">
<!-- 付费墙:审核模式下跳过资料检查;非审核模式下未完善资料显示「完善资料」 -->
<view class="paywall-card" wx:if="{{(!maintenanceMode && !isProfileComplete) || ((maintenanceMode || isProfileComplete) && payInfo.requiresPayment && !payInfo.isPaid)}}">
<view class="paywall-content">
<view class="paywall-blur">
<text class="paywall-fake-title">完整性格分析</text>
@@ -20,29 +20,38 @@
<text class="paywall-fake-line">• 职业匹配与人际关系建议</text>
</view>
<view class="paywall-mask"></view>
<!-- 未有手机号:使用微信系统手机号授权 -->
<button
<!-- 未完善资料(非审核模式):显示完善资料按钮,用户点击后手动跳转 -->
<view
class="paywall-btn"
wx:if="{{!hasPhone}}"
open-type="getPhoneNumber"
bindgetphonenumber="onGetPhoneNumberForMbtiPay"
wx:if="{{!maintenanceMode && !isProfileComplete}}"
bindtap="goToCompleteProfile"
>
<text class="paywall-btn-main">解锁完整报告</text>
<text class="paywall-btn-price">¥{{payInfo.amountYuan}} / 次</text>
</button>
<!-- 已有手机号:普通按钮,直接解锁 -->
<button
class="paywall-btn"
wx:elif="{{hasPhone}}"
bindtap="unlockFullReport"
>
<text class="paywall-btn-main">解锁完整报告</text>
<text class="paywall-btn-price">¥{{payInfo.amountYuan}} / 次</text>
</button>
<text class="paywall-btn-main">完善资料</text>
</view>
<!-- 有价格:解锁按钮 -->
<block wx:elif="{{payInfo.requiresPayment && !payInfo.isPaid}}">
<button
class="paywall-btn"
wx:if="{{!hasPhone}}"
open-type="getPhoneNumber"
bindgetphonenumber="onGetPhoneNumberForMbtiPay"
>
<text class="paywall-btn-main">解锁完整报告</text>
<text class="paywall-btn-price">¥{{payInfo.amountYuan}} / 次</text>
</button>
<view
class="paywall-btn"
wx:elif="{{hasPhone}}"
bindtap="unlockFullReport"
>
<text class="paywall-btn-main">解锁完整报告</text>
<text class="paywall-btn-price">¥{{payInfo.amountYuan}} / 次</text>
</view>
</block>
</view>
</view>
<view class="dimensions-section" wx:if="{{!payInfo.requiresPayment || payInfo.isPaid}}">
<view class="dimensions-section" wx:if="{{(maintenanceMode || isProfileComplete) && (!payInfo.requiresPayment || payInfo.isPaid)}}">
<view class="dimension-item" wx:for="{{dimensions}}" wx:key="key">
<view class="dimension-labels">
<text class="label-left">{{item.left}}</text>
@@ -59,7 +68,7 @@
</view>
</view>
<view class="analysis-card" wx:if="{{!payInfo.requiresPayment || payInfo.isPaid}}">
<view class="analysis-card" wx:if="{{(maintenanceMode || isProfileComplete) && (!payInfo.requiresPayment || payInfo.isPaid)}}">
<text class="card-title">性格特征分析</text>
<view class="trait-section">
<text class="trait-title">优势</text>
@@ -78,7 +87,7 @@
</view>
</view>
<view class="analysis-card" wx:if="{{!payInfo.requiresPayment || payInfo.isPaid}}">
<view class="analysis-card" wx:if="{{(maintenanceMode || isProfileComplete) && (!payInfo.requiresPayment || payInfo.isPaid)}}">
<text class="card-title">职业匹配度分析</text>
<view class="career-item" wx:for="{{mbtiDesc.careers}}" wx:key="*this">
<view class="career-dot"></view>
@@ -86,7 +95,7 @@
</view>
</view>
<view class="analysis-card" wx:if="{{(!payInfo.requiresPayment || payInfo.isPaid) && mbtiDesc.relationships}}">
<view class="analysis-card" wx:if="{{(maintenanceMode || isProfileComplete) && (!payInfo.requiresPayment || payInfo.isPaid) && mbtiDesc.relationships}}">
<text class="card-title">人际关系分析</text>
<text class="relationship-text">{{mbtiDesc.relationships}}</text>
</view>

View File

@@ -1,6 +1,7 @@
// pages/result/pdp.js - PDP结果页支持付费墙 + 历史详情拉取)
const app = getApp()
const payment = require('../../utils/payment')
const { isProfileComplete } = require('../../utils/phoneAuth.js')
const PDP_KEYS = ['Tiger', 'Peacock', 'Koala', 'Owl', 'Chameleon']
@@ -32,7 +33,14 @@ Page({
],
payInfo: { requiresPayment: false, isPaid: false, amountYuan: 0 },
testResultId: null,
hasReloadedAfterPay: false
hasReloadedAfterPay: false,
isProfileComplete: false,
maintenanceMode: false
},
onShow() {
const maintenanceMode = !!(getApp().globalData && getApp().globalData.maintenanceMode)
this.setData({ isProfileComplete: isProfileComplete(), maintenanceMode })
},
onLoad(options) {
@@ -77,7 +85,8 @@ Page({
isPaid,
amountYuan: needPaymentToUnlock ? amountYuan : 0
}
this.setData({ payInfo })
const maintenanceMode = !!(getApp().globalData && getApp().globalData.maintenanceMode)
this.setData({ payInfo, isProfileComplete: isProfileComplete(), maintenanceMode })
} else {
wx.showToast({ title: res.data?.message || '加载失败', icon: 'none' })
}
@@ -101,8 +110,13 @@ Page({
.catch(() => this.setData({ payInfo: { requiresPayment: false, isPaid: false, amountYuan: 0 } }))
},
goToCompleteProfile() {
wx.navigateTo({ url: '/pages/user-profile/index' })
},
unlockFullReport() {
const { payInfo, testResultId, hasReloadedAfterPay } = this.data
const { payInfo, testResultId, hasReloadedAfterPay, isProfileComplete } = this.data
if (!isProfileComplete) return
if (!payInfo.requiresPayment || payInfo.isPaid) return
app.ensureLogin && app.ensureLogin().then((logged) => {
if (!logged) { wx.showToast({ title: '请先登录', icon: 'none' }); return }

View File

@@ -10,7 +10,7 @@
<text class="type-description" wx:if="{{result.description.description}}">{{result.description.description}}</text>
</view>
<view class="paywall-card" wx:if="{{payInfo.requiresPayment && !payInfo.isPaid}}">
<view class="paywall-card" wx:if="{{(!maintenanceMode && !isProfileComplete) || ((maintenanceMode || isProfileComplete) && payInfo.requiresPayment && !payInfo.isPaid)}}">
<view class="paywall-content">
<view class="paywall-blur">
<text class="paywall-fake-title">完整PDP报告</text>
@@ -19,14 +19,25 @@
<text class="paywall-fake-line">• 推荐职业</text>
</view>
<view class="paywall-mask"></view>
<view class="paywall-btn" bindtap="unlockFullReport">
<view
class="paywall-btn"
wx:if="{{!maintenanceMode && !isProfileComplete}}"
bindtap="goToCompleteProfile"
>
<text class="paywall-btn-main">完善资料</text>
</view>
<view
wx:elif="{{payInfo.requiresPayment && !payInfo.isPaid}}"
class="paywall-btn"
bindtap="unlockFullReport"
>
<text class="paywall-btn-main">解锁完整报告</text>
<text class="paywall-btn-price">¥{{payInfo.amountYuan}} / 次</text>
</view>
</view>
</view>
<view class="scores-section" wx:if="{{!payInfo.requiresPayment || payInfo.isPaid}}">
<view class="scores-section" wx:if="{{(maintenanceMode || isProfileComplete) && (!payInfo.requiresPayment || payInfo.isPaid)}}">
<text class="section-title">PDP得分详情</text>
<view class="score-item" wx:for="{{typeList}}" wx:key="type">
<view class="score-header">
@@ -40,7 +51,7 @@
</view>
</view>
<view class="analysis-card" wx:if="{{!payInfo.requiresPayment || payInfo.isPaid}}">
<view class="analysis-card" wx:if="{{(maintenanceMode || isProfileComplete) && (!payInfo.requiresPayment || payInfo.isPaid)}}">
<text class="card-title">性格特征</text>
<view class="trait-section">
<text class="trait-title">优势</text>
@@ -58,12 +69,12 @@
</view>
</view>
<view class="analysis-card" wx:if="{{!payInfo.requiresPayment || payInfo.isPaid}}">
<view class="analysis-card" wx:if="{{(maintenanceMode || isProfileComplete) && (!payInfo.requiresPayment || payInfo.isPaid)}}">
<text class="card-title">团队角色</text>
<text class="relationship-text">{{result.description.teamRole}}</text>
</view>
<view class="analysis-card" wx:if="{{!payInfo.requiresPayment || payInfo.isPaid}}">
<view class="analysis-card" wx:if="{{(maintenanceMode || isProfileComplete) && (!payInfo.requiresPayment || payInfo.isPaid)}}">
<text class="card-title">推荐职业</text>
<view class="career-item" wx:for="{{result.description.careers}}" wx:key="*this">
<view class="career-dot"></view>