fix: 仪表盘与用户管理、分析/测试接口、双端结果页与个人中心展示调整
- admin:Dashboard/Users 与超管 Users 联动后端统计 - api:Dashboard、Analyze、Test、超管 AppUser 逻辑更新 - 微信/抖音小程序:result、profile 页面与样式 - 新增 admin 公共组件与需求文档截图 Made-with: Cursor
This commit is contained in:
@@ -2,6 +2,47 @@
|
||||
const app = getApp()
|
||||
const payment = require('../../utils/payment')
|
||||
const { hasPhone, bindPhoneByCode, isProfileComplete } = require('../../utils/phoneAuth.js')
|
||||
const { mbtiDescriptions } = require('../../utils/descriptions')
|
||||
|
||||
function buildSceneFallback(baseResult) {
|
||||
const mbti = baseResult.mbti || ''
|
||||
const d = mbtiDescriptions[mbti] || {}
|
||||
const jobs = (d.careers || []).slice(0, 2).join('、') || '专业岗位'
|
||||
const name = d.name || baseResult.title || ''
|
||||
return {
|
||||
careerDevelopment: `结合 ${mbti}${name ? '(' + name + ')' : ''} 特质,${jobs} 等与系统性、长期主义更契合的路径往往更容易做出成绩;建议前 1~3 年夯实基本功与协作习惯,再向骨干或专家角色过渡。`,
|
||||
familyParenting: '家庭互动中可减少「对错评判」、增加情感确认;给孩子清晰边界的同时也留出讨论与试错空间,更利于信任感与自驱力。',
|
||||
partnerCofounder: '寻找合伙人时建议重点考察责任感与信息透明度,角色分工、决策机制与退出规则尽量书面化;互补型搭档通常比同特质堆叠更有效。'
|
||||
}
|
||||
}
|
||||
|
||||
/** 仅在后端已下发完整面相等字段时合并;预览脱敏接口不下发,避免未付费用户看到正文 */
|
||||
function mergeSceneBlocks(apiData, baseResult) {
|
||||
const trim = (s) => (s && String(s).trim()) || ''
|
||||
const apiC = trim(apiData.careerDevelopment)
|
||||
const apiF = trim(apiData.familyParenting)
|
||||
const apiP = trim(apiData.partnerCofounder)
|
||||
const fa = apiData.faceAnalysis
|
||||
const bone = apiData.boneAnalysis
|
||||
const rel = typeof apiData.relationship === 'string' ? apiData.relationship.trim() : ''
|
||||
const hasUnlockedReport =
|
||||
rel.length > 8 ||
|
||||
(typeof fa === 'string' && fa.length > 40) ||
|
||||
(fa && typeof fa === 'object' && !Array.isArray(fa)) ||
|
||||
(typeof apiData.faceAnalysisText === 'string' && apiData.faceAnalysisText.length > 40) ||
|
||||
(typeof bone === 'string' && bone.length > 40) ||
|
||||
(bone && typeof bone === 'object' && !Array.isArray(bone)) ||
|
||||
(typeof apiData.boneAnalysisText === 'string' && apiData.boneAnalysisText.length > 40)
|
||||
if (!hasUnlockedReport) {
|
||||
return { careerDevelopment: '', familyParenting: '', partnerCofounder: '' }
|
||||
}
|
||||
const fb = buildSceneFallback(baseResult)
|
||||
return {
|
||||
careerDevelopment: apiC || fb.careerDevelopment,
|
||||
familyParenting: apiF || fb.familyParenting,
|
||||
partnerCofounder: apiP || fb.partnerCofounder
|
||||
}
|
||||
}
|
||||
const { getEnterpriseIdForApiPayload } = require('../../utils/enterpriseContext.js')
|
||||
|
||||
Page({
|
||||
@@ -43,7 +84,10 @@ Page({
|
||||
portrait: null,
|
||||
hrView: null,
|
||||
bossView: null,
|
||||
resumeHighlights: ''
|
||||
resumeHighlights: '',
|
||||
careerDevelopment: '',
|
||||
familyParenting: '',
|
||||
partnerCofounder: ''
|
||||
},
|
||||
// 当前这次AI分析对应的测试记录ID(mbti_test_results.id)
|
||||
testResultId: null,
|
||||
@@ -237,7 +281,7 @@ Page({
|
||||
|
||||
// 处理API返回结果
|
||||
processResult(apiData) {
|
||||
const result = {
|
||||
const base = {
|
||||
mbti: apiData.mbti?.type || '',
|
||||
title: apiData.mbti?.title || '',
|
||||
summary: apiData.personalitySummary || apiData.overview || '',
|
||||
@@ -251,7 +295,12 @@ Page({
|
||||
boneAnalysisText: typeof apiData.boneAnalysis === 'string' ? apiData.boneAnalysis : '',
|
||||
careers: Array.isArray(apiData.careers) ? apiData.careers : [],
|
||||
relationship: apiData.relationship || '',
|
||||
gallupTop3: Array.isArray(apiData.gallupTop3) ? apiData.gallupTop3 : [],
|
||||
gallupTop3: Array.isArray(apiData.gallupTop3) ? apiData.gallupTop3 : []
|
||||
}
|
||||
const scene = mergeSceneBlocks(apiData, base)
|
||||
const result = {
|
||||
...base,
|
||||
...scene,
|
||||
faceAnalysis: null,
|
||||
boneAnalysis: null,
|
||||
portrait: apiData.portrait || null,
|
||||
@@ -282,17 +331,21 @@ Page({
|
||||
// 优先使用后端 /api/analyze 返回的价格信息,避免二次请求
|
||||
if (apiData._payment) {
|
||||
const p = apiData._payment || {}
|
||||
let amountYuan = typeof p.amountYuan === 'number'
|
||||
? p.amountYuan
|
||||
: (p.amountFen ? (p.amountFen / 100) : 0)
|
||||
if (p.requiresPayment && amountYuan <= 0) amountYuan = 1
|
||||
this.setData({
|
||||
payInfo: {
|
||||
requiresPayment: !!p.requiresPayment,
|
||||
isPaid: false,
|
||||
amountYuan: typeof p.amountYuan === 'number'
|
||||
? p.amountYuan
|
||||
: (p.amountFen ? (p.amountFen / 100) : 0)
|
||||
amountYuan
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// 拍照直连分析未带 _payment 时,拉 runtime 定价(与历史详情入口互不覆盖)
|
||||
this.initPayInfoFromRuntime()
|
||||
}
|
||||
// 无 _payment 时(如从历史详情进入)不在这里调 initPayInfoFromRuntime,避免异步 getRuntimeConfig 后覆盖详情接口返回的 needPaymentToUnlock;由调用方用 detail 的 payload 单独设置 payInfo
|
||||
},
|
||||
|
||||
|
||||
@@ -322,6 +375,7 @@ Page({
|
||||
isPaid: !!detailPayload.isPaid,
|
||||
amountYuan: needPay ? amountYuan : 0
|
||||
},
|
||||
hasPhone: hasPhone(),
|
||||
isProfileComplete: isProfileComplete()
|
||||
})
|
||||
return
|
||||
@@ -336,18 +390,25 @@ Page({
|
||||
: Number(facePriceRaw || 0)
|
||||
|
||||
const requiresByConfig = !!(reportRequires && reportRequires.face)
|
||||
const requiresPayment =
|
||||
let requiresPayment =
|
||||
typeof recordRequires === 'boolean' ? recordRequires : requiresByConfig
|
||||
// 系统设置需付款但金额为0 则直接可查看,不展示付费墙
|
||||
const needPay = requiresPayment && facePrice > 0
|
||||
const amountYuan = facePrice > 0 ? facePrice : 0
|
||||
let amountYuan = facePrice > 0 ? facePrice : 0
|
||||
let needPay = requiresPayment && amountYuan > 0
|
||||
// 后台开启人脸报告付费但未配有效单价时,按 ¥1 展示并走支付
|
||||
if (requiresByConfig && !needPay) {
|
||||
amountYuan = 1
|
||||
needPay = true
|
||||
requiresPayment = true
|
||||
}
|
||||
|
||||
this.setData({
|
||||
payInfo: {
|
||||
requiresPayment: needPay,
|
||||
isPaid: !!recordIsPaid,
|
||||
amountYuan
|
||||
}
|
||||
amountYuan: needPay ? amountYuan : 0
|
||||
},
|
||||
hasPhone: hasPhone(),
|
||||
isProfileComplete: isProfileComplete()
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
@@ -356,7 +417,9 @@ Page({
|
||||
requiresPayment: false,
|
||||
isPaid: !!recordIsPaid,
|
||||
amountYuan: 0
|
||||
}
|
||||
},
|
||||
hasPhone: hasPhone(),
|
||||
isProfileComplete: isProfileComplete()
|
||||
})
|
||||
})
|
||||
},
|
||||
@@ -365,10 +428,14 @@ Page({
|
||||
wx.navigateTo({ url: '/pages/user-profile/index' })
|
||||
},
|
||||
|
||||
// 解锁完整报告:发起人脸测试付费
|
||||
/** 付费墙主按钮:先支付,支付后再引导手机号与资料 */
|
||||
onTapUnlockPay() {
|
||||
this.unlockFullReport()
|
||||
},
|
||||
|
||||
// 解锁完整报告:发起人脸测试付费(不要求先完善资料)
|
||||
unlockFullReport() {
|
||||
const { payInfo, testResultId, hasReloadedAfterPay, isProfileComplete } = this.data
|
||||
if (!isProfileComplete) return
|
||||
const { payInfo, testResultId, hasReloadedAfterPay } = this.data
|
||||
if (!payInfo.requiresPayment || payInfo.isPaid) return
|
||||
|
||||
app.ensureLogin().then((logged) => {
|
||||
@@ -381,12 +448,12 @@ Page({
|
||||
success: () => {
|
||||
wx.showToast({ title: '已解锁完整报告', icon: 'success' })
|
||||
|
||||
// 本地先标记已付费,避免按钮仍然提示“需要解锁”
|
||||
this.setData({
|
||||
'payInfo.isPaid': true
|
||||
'payInfo.isPaid': true,
|
||||
hasPhone: hasPhone(),
|
||||
isProfileComplete: isProfileComplete()
|
||||
})
|
||||
|
||||
// 避免重复触发刷新:只在还没刷新的情况下,延迟 0.5s 拉一次详情
|
||||
if (testResultId && !hasReloadedAfterPay) {
|
||||
this.setData({ hasReloadedAfterPay: true })
|
||||
setTimeout(() => {
|
||||
@@ -394,14 +461,31 @@ Page({
|
||||
}, 500)
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
// 支付失败或取消,这里不做额外处理
|
||||
}
|
||||
fail: () => {}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// AI结果页付费按钮:就地触发微信手机号授权,然后调用 unlockFullReport
|
||||
/** 支付成功后:仅绑定手机号,不重复发起支付 */
|
||||
onPostPayBindPhone(e) {
|
||||
const { code, errMsg } = e.detail || {}
|
||||
if (errMsg && errMsg.indexOf('getPhoneNumber:fail') === 0) {
|
||||
wx.showToast({ title: '需要手机号以便保存报告', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (!code) {
|
||||
wx.showToast({ title: '获取手机号失败', icon: 'none' })
|
||||
return
|
||||
}
|
||||
bindPhoneByCode(code)
|
||||
.then(() => {
|
||||
this.setData({ hasPhone: true, isProfileComplete: isProfileComplete() })
|
||||
wx.showToast({ title: '已绑定手机号', icon: 'success' })
|
||||
})
|
||||
.catch(() => {})
|
||||
},
|
||||
|
||||
// 兼容旧版:付费前绑手机(现流程已改为先付费,此方法可不再使用)
|
||||
onGetPhoneNumberForFacePay(e) {
|
||||
const { code, errMsg } = e.detail || {}
|
||||
if (errMsg && errMsg.indexOf('getPhoneNumber:fail') === 0) {
|
||||
|
||||
@@ -44,6 +44,21 @@
|
||||
<text class="status-text">{{hasError ? '分析出现异常' : '分析完成,以下是您的个性化报告'}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 支付成功后:引导手机号 → 完善资料(不挡报告阅读) -->
|
||||
<view class="post-pay-guide" wx:if="{{!hasError && payInfo.isPaid && (!hasPhone || !isProfileComplete)}}">
|
||||
<text class="post-pay-guide-title">再完成两步,报告更好归属到您的账号</text>
|
||||
<text class="post-pay-guide-desc">① 授权手机号 ② 完善头像与昵称(「我的」- 个人资料)</text>
|
||||
<view class="post-pay-guide-actions">
|
||||
<button
|
||||
wx:if="{{!hasPhone}}"
|
||||
class="post-pay-guide-btn phone"
|
||||
open-type="getPhoneNumber"
|
||||
bindgetphonenumber="onPostPayBindPhone"
|
||||
>授权手机号</button>
|
||||
<view wx:if="{{hasPhone && !isProfileComplete}}" class="post-pay-guide-btn profile" bindtap="goToCompleteProfile">去完善资料</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 错误内容 -->
|
||||
<view class="error-content" wx:if="{{hasError}}">
|
||||
<text class="error-title">错误信息</text>
|
||||
@@ -56,23 +71,30 @@
|
||||
<!-- 报告预览区:未解锁时整区加盖高遮罩,避免概述/盖洛普等全文外露 -->
|
||||
<view class="report-gate-wrap" wx:if="{{!hasError}}">
|
||||
<view
|
||||
class="card personality-card {{(!isProfileComplete || (isProfileComplete && payInfo.requiresPayment && !payInfo.isPaid)) ? 'personality-card--locked' : ''}}"
|
||||
class="card personality-card {{(payInfo.requiresPayment && !payInfo.isPaid) ? 'personality-card--locked' : ''}}"
|
||||
>
|
||||
<text class="card-title">性格类型分析</text>
|
||||
<text class="personality-card-sub">MBTI · PDP · DISC 联合速览</text>
|
||||
|
||||
<view class="personality-type">
|
||||
<text class="type-code">{{result.mbti}}</text>
|
||||
<text class="type-name">{{result.title}}</text>
|
||||
<view class="type-chip-row" wx:if="{{result.pdp || result.disc}}">
|
||||
<text class="type-chip" wx:if="{{result.pdp}}">{{result.pdpEmoji}}{{result.pdp}}</text>
|
||||
<text class="type-chip type-chip--disc" wx:if="{{result.disc}}">DISC {{result.disc}}</text>
|
||||
</view>
|
||||
<text class="type-desc">{{result.summary}}</text>
|
||||
</view>
|
||||
|
||||
<view class="type-details">
|
||||
<view class="type-box pdp-box">
|
||||
<text class="type-emoji" wx:if="{{result.pdpEmoji}}">{{result.pdpEmoji}}</text>
|
||||
<text class="type-label">PDP主性格</text>
|
||||
<text class="type-value">{{result.pdp || '--'}}</text>
|
||||
<text class="type-sub" wx:if="{{result.pdpAux}}">辅助: {{result.pdpAux}}</text>
|
||||
</view>
|
||||
<view class="type-box disc-box">
|
||||
<text class="type-emoji type-emoji--disc">◎</text>
|
||||
<text class="type-label">DISC类型</text>
|
||||
<text class="type-value">{{result.disc || '--'}}型</text>
|
||||
<text class="type-sub" wx:if="{{result.discAux}}">辅助: {{result.discAux}}</text>
|
||||
@@ -95,7 +117,7 @@
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="card report-gate-teaser {{(!isProfileComplete || (isProfileComplete && payInfo.requiresPayment && !payInfo.isPaid)) ? 'report-gate-teaser--locked' : ''}}"
|
||||
class="card report-gate-teaser {{(payInfo.requiresPayment && !payInfo.isPaid) ? 'report-gate-teaser--locked' : ''}}"
|
||||
wx:if="{{result.summary}}"
|
||||
>
|
||||
<text class="card-title">性格概述</text>
|
||||
@@ -103,7 +125,7 @@
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="card gallup-card report-gate-teaser {{(!isProfileComplete || (isProfileComplete && payInfo.requiresPayment && !payInfo.isPaid)) ? 'report-gate-teaser--locked' : ''}}"
|
||||
class="card gallup-card report-gate-teaser {{(payInfo.requiresPayment && !payInfo.isPaid) ? 'report-gate-teaser--locked' : ''}}"
|
||||
wx:if="{{result.gallupTop3.length > 0}}"
|
||||
>
|
||||
<text class="card-title">盖洛普前三大优势</text>
|
||||
@@ -115,51 +137,67 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 未解锁:三大生活场景仅标题 + 占位行(正文付费后见) -->
|
||||
<view class="card scene-teaser-bundle" wx:if="{{payInfo.requiresPayment && !payInfo.isPaid}}">
|
||||
<text class="scene-bundle-hint">以下版块解锁后展示完整正文</text>
|
||||
<view class="scene-teaser-item">
|
||||
<text class="scene-teaser-title">职业发展方向</text>
|
||||
<view class="scene-teaser-lines">
|
||||
<view class="scene-teaser-line"></view>
|
||||
<view class="scene-teaser-line scene-teaser-line--short"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="scene-teaser-item">
|
||||
<text class="scene-teaser-title">家庭亲子关系</text>
|
||||
<view class="scene-teaser-lines">
|
||||
<view class="scene-teaser-line"></view>
|
||||
<view class="scene-teaser-line scene-teaser-line--short"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="scene-teaser-item scene-teaser-item--last">
|
||||
<text class="scene-teaser-title">寻找合伙人</text>
|
||||
<view class="scene-teaser-lines">
|
||||
<view class="scene-teaser-line"></view>
|
||||
<view class="scene-teaser-line scene-teaser-line--short"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 全幅遮罩 + 底部操作(覆盖性格概述、盖洛普等主要预览,仅留顶部少量「悬念」) -->
|
||||
<view
|
||||
class="report-gate-overlay"
|
||||
wx:if="{{!isProfileComplete || (isProfileComplete && payInfo.requiresPayment && !payInfo.isPaid)}}"
|
||||
wx:if="{{payInfo.requiresPayment && !payInfo.isPaid}}"
|
||||
>
|
||||
<view class="report-gate-gradient"></view>
|
||||
<!-- 底部白区内:标题 + 说明 + 按钮作为一组上下居中 -->
|
||||
<view class="report-gate-cta-wrap">
|
||||
<view class="report-gate-panel">
|
||||
<text class="paywall-fake-title">完整版性格深度解析</text>
|
||||
<text class="paywall-tip">解锁后将展示完整报告内容</text>
|
||||
<view
|
||||
class="paywall-btn paywall-btn-inline"
|
||||
wx:if="{{!isProfileComplete}}"
|
||||
bindtap="goToCompleteProfile"
|
||||
>
|
||||
<text class="paywall-btn-main">完善资料</text>
|
||||
<text class="paywall-fake-title">完整版深度解析</text>
|
||||
<text class="paywall-tip">含面相、骨相、人际与职业 / 家庭 / 合伙三大场景</text>
|
||||
<view class="paywall-btn paywall-btn-inline paywall-btn--compact" bindtap="onTapUnlockPay">
|
||||
<text class="paywall-btn-main">{{payInfo.amountYuan || 1}} 元解锁</text>
|
||||
</view>
|
||||
<block wx:elif="{{isProfileComplete && payInfo.requiresPayment && !payInfo.isPaid}}">
|
||||
<button
|
||||
class="paywall-btn paywall-btn-inline"
|
||||
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 paywall-btn-inline"
|
||||
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 paywall-tip-panel" wx:if="{{isProfileComplete}}">一次性解锁本次{{reportTitle || '分析报告'}},永久保存在「历史记录」中</text>
|
||||
<text class="paywall-tip paywall-tip-panel">解锁后可在「历史记录」再次查看;绑定手机与资料见上方绿色提示条</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- ========== 详细分析区(解锁后可见) ========== -->
|
||||
<block wx:if="{{!hasError && isProfileComplete && (!payInfo.requiresPayment || payInfo.isPaid)}}">
|
||||
<block wx:if="{{!hasError && (!payInfo.requiresPayment || payInfo.isPaid)}}">
|
||||
|
||||
<!-- 生活场景深度分析(AI 或本地兜底) -->
|
||||
<view class="card scene-detail-card" wx:if="{{result.careerDevelopment}}">
|
||||
<text class="card-title">职业发展方向</text>
|
||||
<text class="card-text">{{result.careerDevelopment}}</text>
|
||||
</view>
|
||||
<view class="card scene-detail-card" wx:if="{{result.familyParenting}}">
|
||||
<text class="card-title">家庭亲子关系</text>
|
||||
<text class="card-text">{{result.familyParenting}}</text>
|
||||
</view>
|
||||
<view class="card scene-detail-card" wx:if="{{result.partnerCofounder}}">
|
||||
<text class="card-title">寻找合伙人</text>
|
||||
<text class="card-text">{{result.partnerCofounder}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 面相分析 -->
|
||||
<block wx:if="{{result.faceAnalysis}}">
|
||||
@@ -304,18 +342,5 @@
|
||||
</view>
|
||||
|
||||
</block>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="action-section" wx:if="{{!hasError}}">
|
||||
<button class="btn btn-primary" open-type="share">
|
||||
<text class="btn-text">分享我的分析报告</text>
|
||||
</button>
|
||||
<view class="btn btn-outline" bindtap="retake">
|
||||
<text class="btn-text-outline">重新分析</text>
|
||||
</view>
|
||||
<view class="btn btn-outline" bindtap="goHome">
|
||||
<text class="btn-text-outline">返回首页</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -323,17 +323,79 @@
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
/* 高透明白雾,避免大块灰底发闷 */
|
||||
/* 约前 40% 区域保持清晰预览,以下渐强为「约 60% 可读」观感 */
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0.22) 10%,
|
||||
rgba(255, 255, 255, 0.48) 28%,
|
||||
rgba(255, 255, 255, 0.72) 48%,
|
||||
rgba(255, 255, 255, 0.88) 100%
|
||||
rgba(255, 255, 255, 0) 38%,
|
||||
rgba(255, 255, 255, 0.2) 52%,
|
||||
rgba(255, 255, 255, 0.55) 68%,
|
||||
rgba(255, 255, 255, 0.82) 88%,
|
||||
rgba(255, 255, 255, 0.92) 100%
|
||||
);
|
||||
}
|
||||
|
||||
/* 支付成功后:手机号 / 资料引导条 */
|
||||
.post-pay-guide {
|
||||
margin: 16rpx 24rpx 0;
|
||||
padding: 24rpx 28rpx;
|
||||
background: linear-gradient(135deg, #ecfdf5 0%, #d1fae5 100%);
|
||||
border-radius: 20rpx;
|
||||
border: 1rpx solid rgba(16, 185, 129, 0.35);
|
||||
}
|
||||
|
||||
.post-pay-guide-title {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #065f46;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.post-pay-guide-desc {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
color: #047857;
|
||||
line-height: 1.45;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.post-pay-guide-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.post-pay-guide-btn.phone {
|
||||
margin: 0;
|
||||
padding: 0 36rpx;
|
||||
height: 72rpx;
|
||||
line-height: 72rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
background: linear-gradient(135deg, #10b981 0%, #34d399 100%);
|
||||
border-radius: 36rpx;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.post-pay-guide-btn.phone::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.post-pay-guide-btn.profile {
|
||||
padding: 0 36rpx;
|
||||
height: 72rpx;
|
||||
line-height: 72rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #065f46;
|
||||
background: #fff;
|
||||
border-radius: 36rpx;
|
||||
border: 2rpx solid #10b981;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.report-gate-panel {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
@@ -362,23 +424,102 @@
|
||||
max-width: 640rpx;
|
||||
margin: 28rpx auto 0;
|
||||
box-sizing: border-box;
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
align-items: center !important;
|
||||
padding: 28rpx 24rpx !important;
|
||||
}
|
||||
|
||||
.paywall-btn-inline .paywall-btn-price {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-top: 10rpx;
|
||||
font-size: 22rpx;
|
||||
line-height: 1.45;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* 主按钮单行:仅金额 +「元解锁」 */
|
||||
.paywall-btn--compact {
|
||||
flex-direction: row !important;
|
||||
justify-content: center !important;
|
||||
padding: 32rpx 40rpx !important;
|
||||
}
|
||||
|
||||
.paywall-btn--compact .paywall-btn-main {
|
||||
font-size: 34rpx;
|
||||
letter-spacing: 2rpx;
|
||||
}
|
||||
|
||||
.report-gate-panel .paywall-btn-inline::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* 未解锁:三大场景标题预览 */
|
||||
.scene-teaser-bundle {
|
||||
text-align: left;
|
||||
padding-bottom: 28rpx !important;
|
||||
}
|
||||
|
||||
.scene-bundle-hint {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
color: #94a3b8;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.scene-teaser-item {
|
||||
padding: 20rpx 0;
|
||||
border-bottom: 1rpx solid #f1f5f9;
|
||||
}
|
||||
|
||||
.scene-teaser-item--last {
|
||||
border-bottom: none;
|
||||
padding-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.scene-teaser-title {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #334155;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.scene-teaser-lines {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.scene-teaser-line {
|
||||
height: 14rpx;
|
||||
border-radius: 8rpx;
|
||||
background: linear-gradient(90deg, #e2e8f0 0%, #f1f5f9 100%);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scene-teaser-line--short {
|
||||
width: 62%;
|
||||
}
|
||||
|
||||
.scene-detail-card {
|
||||
border-left: 6rpx solid #e63946;
|
||||
padding-left: 28rpx !important;
|
||||
}
|
||||
|
||||
/* 未解锁:限制概述与盖洛普可见行数 */
|
||||
.report-gate-teaser--locked .card-text {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 5;
|
||||
-webkit-line-clamp: 8;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.report-gate-teaser--locked .gallup-list {
|
||||
max-height: 96rpx;
|
||||
max-height: 140rpx;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
@@ -428,6 +569,48 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.personality-card-sub {
|
||||
display: block;
|
||||
font-size: 22rpx;
|
||||
color: #94a3b8;
|
||||
margin: -8rpx 0 24rpx;
|
||||
letter-spacing: 1rpx;
|
||||
}
|
||||
|
||||
.type-chip-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 12rpx;
|
||||
margin: 12rpx 0 8rpx;
|
||||
}
|
||||
|
||||
.type-chip {
|
||||
font-size: 22rpx;
|
||||
color: #64748b;
|
||||
background: #f8fafc;
|
||||
border: 1rpx solid #e2e8f0;
|
||||
padding: 8rpx 20rpx;
|
||||
border-radius: 999rpx;
|
||||
}
|
||||
|
||||
.type-chip--disc {
|
||||
color: #2563eb;
|
||||
background: #eff6ff;
|
||||
border-color: #bfdbfe;
|
||||
}
|
||||
|
||||
.type-emoji {
|
||||
display: block;
|
||||
font-size: 36rpx;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
|
||||
.type-emoji--disc {
|
||||
opacity: 0.35;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.personality-type {
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ Page({
|
||||
discType: '',
|
||||
pdpType: '',
|
||||
aiType: '',
|
||||
/** 面相记录中的盖洛普前三摘要(/api/test/recent 或本地 aiResult) */
|
||||
gallupPreview: '',
|
||||
mbtiTime: '',
|
||||
discTime: '',
|
||||
pdpTime: '',
|
||||
@@ -158,6 +160,7 @@ Page({
|
||||
// DISC resultText 后端已含「型」,type badge 只显示字母,去掉「型」
|
||||
const discType = r.disc ? r.disc.resultText.replace(/型$/, '') : ''
|
||||
|
||||
const gallupPreview = (r.ai && r.ai.gallupPreview) ? String(r.ai.gallupPreview) : ''
|
||||
this.setData({
|
||||
testCount: totalCount,
|
||||
hasResults: !!(r.mbti || r.disc || r.pdp || r.ai),
|
||||
@@ -165,6 +168,7 @@ Page({
|
||||
discType,
|
||||
pdpType: r.pdp ? r.pdp.resultText : '',
|
||||
aiType: r.ai ? r.ai.resultText : '',
|
||||
gallupPreview,
|
||||
mbtiTime: r.mbti ? r.mbti.testTime : '',
|
||||
discTime: r.disc ? r.disc.testTime : '',
|
||||
pdpTime: r.pdp ? r.pdp.testTime : '',
|
||||
@@ -209,6 +213,11 @@ Page({
|
||||
const pdpResult = wx.getStorageSync('pdpResult')
|
||||
const aiResult = wx.getStorageSync('aiResult')
|
||||
|
||||
let gallupPreview = ''
|
||||
if (aiResult && Array.isArray(aiResult.gallupTop3) && aiResult.gallupTop3.length) {
|
||||
gallupPreview = aiResult.gallupTop3.slice(0, 3).map(String).join('、')
|
||||
}
|
||||
|
||||
let testCount = 0
|
||||
if (mbtiResult) testCount++
|
||||
if (discResult) testCount++
|
||||
@@ -228,7 +237,8 @@ Page({
|
||||
mbtiType: mbtiResult ? getTypeOnly(mbtiResult, 'mbti') : '',
|
||||
discType: discResult ? getTypeOnly(discResult, 'disc') : '',
|
||||
pdpType: pdpResult ? getTypeOnly(pdpResult, 'pdp') : '',
|
||||
aiType: aiResult ? (aiResult.mbtiType || aiResult.type || '') : '',
|
||||
aiType: aiResult ? (aiResult.mbti || aiResult.mbtiType || aiResult.type || '') : '',
|
||||
gallupPreview,
|
||||
mbtiTime: _fmt(mbtiResult && (mbtiResult.createdAt || mbtiResult.timestamp || mbtiResult.testTime)),
|
||||
discTime: _fmt(discResult && (discResult.createdAt || discResult.timestamp || discResult.testTime)),
|
||||
pdpTime: _fmt(pdpResult && (pdpResult.createdAt || pdpResult.timestamp || pdpResult.testTime)),
|
||||
@@ -282,19 +292,34 @@ Page({
|
||||
},
|
||||
viewMBTI() {
|
||||
const id = this.data.mbtiResultId
|
||||
wx.navigateTo({ url: id ? `/pages/result/mbti?id=${id}&type=mbti` : '/pages/result/mbti' })
|
||||
if (id) wx.navigateTo({ url: `/pages/result/mbti?id=${id}&type=mbti` })
|
||||
else wx.navigateTo({ url: '/pages/test/mbti' })
|
||||
},
|
||||
viewDISC() {
|
||||
const id = this.data.discResultId
|
||||
wx.navigateTo({ url: id ? `/pages/result/disc?id=${id}&type=disc` : '/pages/result/disc' })
|
||||
if (id) wx.navigateTo({ url: `/pages/result/disc?id=${id}&type=disc` })
|
||||
else wx.navigateTo({ url: '/pages/test/disc' })
|
||||
},
|
||||
viewPDP() {
|
||||
const id = this.data.pdpResultId
|
||||
wx.navigateTo({ url: id ? `/pages/result/pdp?id=${id}&type=pdp` : '/pages/result/pdp' })
|
||||
if (id) wx.navigateTo({ url: `/pages/result/pdp?id=${id}&type=pdp` })
|
||||
else wx.navigateTo({ url: '/pages/test/pdp' })
|
||||
},
|
||||
/** 盖洛普随面相报告:有记录看报告,否则去拍摄 */
|
||||
viewGallup() {
|
||||
const id = this.data.aiResultId
|
||||
if (id) wx.navigateTo({ url: `/pages/index/result?id=${id}&type=ai` })
|
||||
else wx.switchTab({ url: '/pages/index/camera' })
|
||||
},
|
||||
viewAI() {
|
||||
const id = this.data.aiResultId
|
||||
wx.navigateTo({ url: id ? `/pages/index/result?id=${id}&type=ai` : '/pages/index/result' })
|
||||
if (id) wx.navigateTo({ url: `/pages/index/result?id=${id}&type=ai` })
|
||||
else wx.switchTab({ url: '/pages/index/camera' })
|
||||
},
|
||||
/** 动作测试:进入底部「拍摄」页 */
|
||||
goToActionTest() {
|
||||
try { require('../../utils/analytics').track('tap_action_test_camera', {}) } catch (e) {}
|
||||
wx.switchTab({ url: '/pages/index/camera' })
|
||||
},
|
||||
|
||||
logout() {
|
||||
|
||||
@@ -79,52 +79,70 @@
|
||||
<text class="depth-header-chevron">›</text>
|
||||
</view>
|
||||
</view>
|
||||
<block wx:if="{{hasResults}}">
|
||||
<scroll-view scroll-x class="cards-scroll cards-scroll--in-card" enhanced show-scrollbar="{{false}}">
|
||||
<view class="cards-row cards-row--in-card">
|
||||
<view class="result-card card-purple" wx:if="{{mbtiType}}" bindtap="viewMBTI">
|
||||
<view class="card-deco"></view>
|
||||
<view class="card-icon-wrap card-icon-purple">
|
||||
<text class="card-icon">🧠</text>
|
||||
</view>
|
||||
<text class="card-label">MBTI性格</text>
|
||||
<text class="card-value">{{mbtiType}}</text>
|
||||
<text class="card-time">{{mbtiTime}}</text>
|
||||
</view>
|
||||
<view class="result-card card-blue" wx:if="{{discType}}" bindtap="viewDISC">
|
||||
<view class="card-deco"></view>
|
||||
<view class="card-icon-wrap card-icon-blue">
|
||||
<text class="card-icon">📊</text>
|
||||
</view>
|
||||
<text class="card-label">DISC测评</text>
|
||||
<text class="card-value">{{discType}}型</text>
|
||||
<text class="card-time">{{discTime}}</text>
|
||||
</view>
|
||||
<view class="result-card card-orange" wx:if="{{pdpType}}" bindtap="viewPDP">
|
||||
<view class="card-deco"></view>
|
||||
<view class="card-icon-wrap card-icon-orange">
|
||||
<text class="card-icon">🦁</text>
|
||||
</view>
|
||||
<text class="card-label">PDP行为</text>
|
||||
<text class="card-value">{{pdpType}}</text>
|
||||
<text class="card-time">{{pdpTime}}</text>
|
||||
</view>
|
||||
<view class="result-card card-rose" wx:if="{{aiType && !reviewMode}}" bindtap="viewAI">
|
||||
<view class="card-deco"></view>
|
||||
<view class="card-icon-wrap card-icon-rose">
|
||||
<text class="card-icon">👁️</text>
|
||||
</view>
|
||||
<text class="card-label">面相分析</text>
|
||||
<text class="card-value">{{aiType}}</text>
|
||||
<text class="card-time">{{aiTime}}</text>
|
||||
<scroll-view scroll-x class="cards-scroll cards-scroll--in-card" enhanced show-scrollbar="{{false}}">
|
||||
<view class="cards-row cards-row--in-card">
|
||||
<view class="result-card card-purple {{mbtiType ? '' : 'result-card--placeholder'}}" bindtap="viewMBTI">
|
||||
<view class="card-deco"></view>
|
||||
<view class="card-icon-wrap card-icon-purple">
|
||||
<text class="card-icon">🧠</text>
|
||||
</view>
|
||||
<text class="card-label">MBTI性格</text>
|
||||
<text class="card-value">{{mbtiType || '未测评'}}</text>
|
||||
<text class="card-time">{{mbtiType ? mbtiTime : '点击进入问卷'}}</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</block>
|
||||
<view wx:else class="depth-empty-hint">
|
||||
<text>完成任一测评后,此处展示最近一次结果;右上角可查看全部历史。</text>
|
||||
<view class="result-card card-orange {{pdpType ? '' : 'result-card--placeholder'}}" bindtap="viewPDP">
|
||||
<view class="card-deco"></view>
|
||||
<view class="card-icon-wrap card-icon-orange">
|
||||
<text class="card-icon">🦁</text>
|
||||
</view>
|
||||
<text class="card-label">PDP行为</text>
|
||||
<text class="card-value">{{pdpType || '未测评'}}</text>
|
||||
<text class="card-time">{{pdpType ? pdpTime : '点击进入问卷'}}</text>
|
||||
</view>
|
||||
<view class="result-card card-blue {{discType ? '' : 'result-card--placeholder'}}" bindtap="viewDISC">
|
||||
<view class="card-deco"></view>
|
||||
<view class="card-icon-wrap card-icon-blue">
|
||||
<text class="card-icon">📊</text>
|
||||
</view>
|
||||
<text class="card-label">DISC测评</text>
|
||||
<text class="card-value">{{discType ? discType + '型' : '未测评'}}</text>
|
||||
<text class="card-time">{{discType ? discTime : '点击进入问卷'}}</text>
|
||||
</view>
|
||||
<view class="result-card card-teal {{(gallupPreview || aiType) ? '' : 'result-card--placeholder'}}" bindtap="viewGallup" wx:if="{{!reviewMode}}">
|
||||
<view class="card-deco"></view>
|
||||
<view class="card-icon-wrap card-icon-teal">
|
||||
<text class="card-icon">⭐</text>
|
||||
</view>
|
||||
<text class="card-label">盖洛普优势</text>
|
||||
<text class="card-value card-value--small">{{gallupPreview || (aiType ? '见面相报告' : '未测评')}}</text>
|
||||
<text class="card-time">{{gallupPreview ? aiTime : (aiType ? '已拍面相可查看' : '先完成面相拍摄')}}</text>
|
||||
</view>
|
||||
<view class="result-card card-rose {{aiType ? '' : 'result-card--placeholder'}}" bindtap="viewAI" wx:if="{{!reviewMode}}">
|
||||
<view class="card-deco"></view>
|
||||
<view class="card-icon-wrap card-icon-rose">
|
||||
<text class="card-icon">👁️</text>
|
||||
</view>
|
||||
<text class="card-label">面相分析</text>
|
||||
<text class="card-value">{{aiType || '未测评'}}</text>
|
||||
<text class="card-time">{{aiType ? aiTime : '去拍摄分析'}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="depth-empty-hint depth-empty-hint--compact">
|
||||
<text>灰色为未完成项,点击即可进入测评或拍摄;右上方可查看全部历史。</text>
|
||||
</view>
|
||||
<view class="depth-inner-divider"></view>
|
||||
<view class="menu-item menu-item--flat" bindtap="goToActionTest" wx:if="{{!reviewMode}}">
|
||||
<view class="menu-icon-wrap menu-icon-amber">
|
||||
<text class="menu-icon">📷</text>
|
||||
</view>
|
||||
<view class="menu-content">
|
||||
<text class="menu-title">动作测试(拍摄)</text>
|
||||
<text class="menu-sub">拍摄人像照片,生成面相、骨相与盖洛普等解析</text>
|
||||
</view>
|
||||
<text class="menu-chevron">›</text>
|
||||
</view>
|
||||
<view class="menu-divider menu-divider--in-card" wx:if="{{!reviewMode}}"></view>
|
||||
<view class="menu-item menu-item--flat" bindtap="goToDeepService">
|
||||
<view class="menu-icon-wrap menu-icon-purple">
|
||||
<text class="menu-icon">✨</text>
|
||||
|
||||
@@ -325,6 +325,11 @@ custom-tab-bar {
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.depth-empty-hint--compact {
|
||||
padding-top: 4rpx;
|
||||
padding-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.depth-inner-divider {
|
||||
height: 1rpx;
|
||||
background: #F3F4F6;
|
||||
@@ -391,6 +396,30 @@ custom-tab-bar {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 未完成测评:灰阶可点击 */
|
||||
.result-card--placeholder {
|
||||
background: #f3f4f6 !important;
|
||||
border-color: #e5e7eb !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.result-card--placeholder .card-deco {
|
||||
opacity: 0.12 !important;
|
||||
}
|
||||
|
||||
.result-card--placeholder .card-label,
|
||||
.result-card--placeholder .card-time {
|
||||
color: #9ca3af !important;
|
||||
}
|
||||
|
||||
.result-card--placeholder .card-value {
|
||||
color: #6b7280 !important;
|
||||
}
|
||||
|
||||
.result-card--placeholder .card-icon-wrap {
|
||||
opacity: 0.65;
|
||||
}
|
||||
|
||||
.card-deco {
|
||||
position: absolute;
|
||||
top: -24rpx;
|
||||
@@ -405,6 +434,7 @@ custom-tab-bar {
|
||||
.card-blue .card-deco { background: #BFDBFE; }
|
||||
.card-orange .card-deco { background: #FDE68A; }
|
||||
.card-rose .card-deco { background: #FECDD3; }
|
||||
.card-teal .card-deco { background: #99f6e4; }
|
||||
|
||||
.card-icon-wrap {
|
||||
width: 64rpx;
|
||||
@@ -419,6 +449,7 @@ custom-tab-bar {
|
||||
.card-icon-blue { background: #DBEAFE; }
|
||||
.card-icon-orange { background: #FEF3C7; }
|
||||
.card-icon-rose { background: #FFE4E6; }
|
||||
.card-icon-teal { background: #ccfbf1; }
|
||||
|
||||
.card-icon {
|
||||
font-size: 32rpx;
|
||||
@@ -440,6 +471,17 @@ custom-tab-bar {
|
||||
.card-blue .card-value { color: #2563EB; }
|
||||
.card-orange .card-value { color: #D97706; }
|
||||
.card-rose .card-value { color: #E11D48; }
|
||||
.card-teal .card-value { color: #0f766e; }
|
||||
|
||||
.card-value--small {
|
||||
font-size: 26rpx !important;
|
||||
font-weight: 700 !important;
|
||||
line-height: 1.35;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-time {
|
||||
font-size: 20rpx;
|
||||
|
||||
Reference in New Issue
Block a user