feat: 小程序与管理端迭代(神仙AI、了解自己CRM、AI测试入口、报表与分润等)

Made-with: Cursor
This commit is contained in:
卡若
2026-04-17 19:46:48 +08:00
parent cf835ea585
commit 7108f28280
239 changed files with 21061 additions and 2175 deletions

View File

@@ -3,15 +3,23 @@ const app = getApp()
const payment = require('../../utils/payment')
const { getTypeOnly } = require('../../utils/resultFormat')
const { isReportProfileComplete } = require('../../utils/phoneAuth.js')
function toProfileLockedPdp(full) {
if (!full) return full
const desc = full.description || {}
return {
description: { type: desc.type || '', emoji: desc.emoji || '' },
locked: true
}
}
const {
slicePreviewText,
slicePreviewList,
openTimelineShareHint
} = require('../../utils/resultProfileGate.js')
const {
getPdpInsight,
getPdpTags,
buildPdpDimensions
} = require('../../utils/pdpInsights.js')
const { decorateCareers } = require('../../utils/mbtiInsights.js')
const {
computeJourney,
markShared,
markCamera
} = require('../../utils/resultJourneyState.js')
const resultScrollSync = require('../../utils/resultSectionScrollSync.js')
const PDP_KEYS = ['Tiger', 'Peacock', 'Koala', 'Owl', 'Chameleon']
@@ -46,10 +54,54 @@ Page({
testResultId: null,
shareToken: '',
hasReloadedAfterPay: false,
fromShare: false
fromShare: false,
profileGate: false,
previewPdpDescription: '',
previewPdpStrengths: [],
pdpDimensions: [],
pdpInsight: null,
pdpTags: [],
pdpCareerItems: [],
journey: { step1Unlocked: false, step2Unlocked: false, step3Unlocked: false, activeStep: 1 },
sectionNav: [
{ id: 'sec-hero', label: 'PDP 画像', emoji: '🦁' },
{ id: 'sec-insight', label: '深度洞察', emoji: '🧠' },
{ id: 'sec-dim', label: '五维得分', emoji: '📊' },
{ id: 'sec-trait', label: '优势与注意', emoji: '✨' },
{ id: 'sec-team', label: '团队角色', emoji: '🤝' },
{ id: 'sec-career', label: '推荐职业', emoji: '💼' },
{ id: 'sec-cta', label: '深度方案', emoji: '💎' }
],
scrollTarget: '',
activeSection: ''
},
onTapSectionNav(e) {
const id = e && e.detail && e.detail.id
if (!id) return
this.setData({ scrollTarget: '', activeSection: id }, () => {
this.setData({ scrollTarget: id })
})
},
onSectionScroll(e) {
resultScrollSync.onScroll(this, e)
},
_syncJourney() {
this.setData({
journey: computeJourney({
profileGate: !!this.data.profileGate,
payRequired: !!(this.data.payInfo && this.data.payInfo.requiresPayment),
isPaid: !!(this.data.payInfo && this.data.payInfo.isPaid)
})
})
},
onLoad(options) {
try {
wx.showShareMenu({ withShareTicket: true, menus: ['shareAppMessage', 'shareTimeline'] })
} catch (e) {}
const fromShareFs =
options && (String(options.fs) === '1' || options.from === 'share')
const id = options && options.id != null && options.id !== '' ? String(options.id) : ''
@@ -72,6 +124,7 @@ Page({
result: withPercentagesInt(result),
typeSummaryLine: getTypeOnly(result, 'pdp')
})
this._syncPdpGate()
this.initPayInfoFromRuntime('pdp')
} else {
wx.showToast({ title: '暂无测试结果', icon: 'none' })
@@ -90,6 +143,7 @@ Page({
typeSummaryLine: getTypeOnly(data, 'pdp'),
shareToken: payload.shareToken || ''
})
this._syncPdpGate()
const payInfo = {
requiresPayment: needPaymentToUnlock,
isPaid,
@@ -100,6 +154,7 @@ Page({
patch.testResultId = String(payload.id)
}
this.setData(patch)
this._reportPaywallOnce('pdp', payInfo)
},
loadDetail(id) {
@@ -148,20 +203,100 @@ Page({
},
onShow() {
this._syncJourney()
if (this.data.testResultId) return
const raw = wx.getStorageSync('pdpResult')
if (!raw) return
const gated = isReportProfileComplete() ? raw : toProfileLockedPdp(raw)
this.setData({
result: withPercentagesInt(gated),
typeSummaryLine: getTypeOnly(gated, 'pdp')
result: withPercentagesInt(raw),
typeSummaryLine: getTypeOnly(raw, 'pdp')
})
this._syncPdpGate()
},
_syncPdpGate() {
const r = this.data.result
const fromShare = !!this.data.fromShare
const profileGate = !fromShare && !isReportProfileComplete()
const desc = (r && r.description) || {}
const code = (r && (r.dominantType || (desc && desc.type))) || ''
this.setData({
profileGate,
previewPdpDescription: slicePreviewText(desc.description || '', 0.3),
previewPdpStrengths: slicePreviewList(desc.strengths || [], 0.3),
pdpDimensions: r ? buildPdpDimensions(r) : [],
pdpInsight: getPdpInsight(code),
pdpTags: getPdpTags(code),
pdpCareerItems: decorateCareers(desc.careers || [])
})
this._syncJourney()
},
onTapDeepService() {
try { require('../../utils/analytics').track('tap_deep_service_from_pdp', {}) } catch (e) {}
wx.navigateTo({ url: '/pages/purchase/index' })
},
onTapPromoCenter() {
try { require('../../utils/analytics').track('tap_promo_from_pdp', {}) } catch (e) {}
wx.navigateTo({ url: '/pages/promo/index' })
},
onTapReadFull() {
try { require('../../utils/analytics').track('tap_read_full', { type: 'pdp' }) } catch (e) {}
if (this.data.profileGate) {
this.goCompleteProfile()
return
}
if (this.data.payInfo.requiresPayment && !this.data.payInfo.isPaid) {
this.unlockFullReport()
return
}
wx.showToast({ title: '当前已是完整报告', icon: 'none' })
},
onTapShareMoment() {
try { require('../../utils/analytics').track('tap_share_moment', { type: 'pdp' }) } catch (e) {}
if (!this.data.journey.step1Unlocked) {
wx.showToast({ title: '请先解锁全文', icon: 'none' })
this.onTapReadFull()
return
}
markShared()
this._syncJourney()
openTimelineShareHint()
},
onTapFaceCamera() {
try { require('../../utils/analytics').track('tap_face_camera', { from: 'pdp' }) } catch (e) {}
if (!this.data.journey.step2Unlocked) {
wx.showToast({ title: '请先分享朋友圈', icon: 'none' })
return
}
markCamera()
this._syncJourney()
wx.switchTab({ url: '/pages/index/camera' })
},
goReadFullFromShare() {
try { require('../../utils/analytics').track('tap_read_full', { type: 'pdp', from: 'share' }) } catch (e) {}
wx.switchTab({ url: '/pages/profile/index' })
},
goCompleteProfile() {
try { require('../../utils/analytics').track('tap_complete_profile', { from: 'pdp' }) } catch (e) {}
wx.navigateTo({ url: '/pages/user-profile/index' })
},
_reportPaywallOnce(testType, payInfo) {
if (!payInfo || !payInfo.requiresPayment || payInfo.isPaid) return
if (this._paywallReported) return
this._paywallReported = true
try {
require('../../utils/analytics').track('paywall_view', { type: testType, amountYuan: payInfo.amountYuan })
} catch (e) {}
},
initPayInfoFromRuntime(testType) {
app.getRuntimeConfig()
.then((cfg) => {
@@ -169,9 +304,10 @@ Page({
const pricing = cfg.pricing || {}
const requiresPayment = !!(reportRequires && reportRequires[testType])
const amountYuan = Number(pricing[testType]) || (requiresPayment ? 1.98 : 0)
this.setData({
payInfo: { requiresPayment, isPaid: false, amountYuan }
})
const payInfo = { requiresPayment, isPaid: false, amountYuan }
this.setData({ payInfo })
this._reportPaywallOnce(testType, payInfo)
this._syncJourney()
})
.catch(() => this.setData({ payInfo: { requiresPayment: false, isPaid: false, amountYuan: 0 } }))
},
@@ -179,6 +315,7 @@ Page({
unlockFullReport() {
const { payInfo, testResultId, hasReloadedAfterPay } = this.data
if (!payInfo.requiresPayment || payInfo.isPaid) return
try { require('../../utils/analytics').track('tap_unlock_full', { type: 'pdp', amountYuan: payInfo.amountYuan }) } catch (e) {}
app.ensureLogin && app.ensureLogin().then((logged) => {
if (!logged) { wx.showToast({ title: '请先登录', icon: 'none' }); return }
payment.purchasePdpTest({
@@ -186,6 +323,7 @@ Page({
success: () => {
wx.showToast({ title: '已解锁完整报告', icon: 'success' })
this.setData({ 'payInfo.isPaid': true })
this._syncJourney()
if (testResultId && !hasReloadedAfterPay) {
this.setData({ hasReloadedAfterPay: true })
setTimeout(() => this.loadDetail(testResultId), 500)