高考版优化

审核模式优化
This commit is contained in:
Ghost
2026-04-29 18:01:26 +08:00
parent 59419c4ce9
commit a411bf7b21
134 changed files with 19898 additions and 3371 deletions

View File

@@ -1,6 +1,4 @@
const gaokaoApi = require('../../utils/gaokao')
const app = getApp()
const { isAuditHideAiMode, redirectIfMiniprogramAudit } = require('../../utils/miniprogramAuditGate.js')
/** 科类/选科备选项:首项为占位,不可作为有效保存值 */
const SUBJECT_PLACEHOLDER = '请选择科类/选科'
@@ -121,18 +119,7 @@ Page({
/** 每次页面展示拉取(含从上级页返回),避免栈内页面不触发 onLoad 时看不到已保存内容 */
onShow() {
const run = () => {
if (isAuditHideAiMode(app.globalData)) {
redirectIfMiniprogramAudit('版本审核中暂不可用')
return
}
this.loadFormFromServer()
}
if (app.getRuntimeConfig) {
app.getRuntimeConfig().finally(run)
} else {
run()
}
this.loadFormFromServer()
},
loadFormFromServer() {

View File

@@ -1,120 +1,146 @@
const gaokaoApi = require('../../utils/gaokao')
const app = getApp()
const { isAuditHideAiMode, redirectIfMiniprogramAudit } = require('../../utils/miniprogramAuditGate.js')
Page({
data: {
loading: false,
tasks: {
mbti: { code: 'mbti', status: 'todo', resultText: '', testResultId: 0, id: 0, testType: 'mbti', typeName: 'MBTI性格', emoji: '🧠', testTime: '' },
pdp: { code: 'pdp', status: 'todo', resultText: '', testResultId: 0, id: 0, testType: 'pdp', typeName: 'PDP行为', emoji: '🦁', testTime: '' },
disc: { code: 'disc', status: 'todo', resultText: '', testResultId: 0, id: 0, testType: 'disc', typeName: 'DISC测评', emoji: '📊', testTime: '' },
face: { code: 'face', status: 'todo', resultText: '', testResultId: 0, id: 0, testType: 'ai', typeName: '拍照面相', emoji: '📷', testTime: '', recordTestType: 'ai' },
form: { code: 'form', status: 'todo', resultText: '', testResultId: 0, id: 0, testType: 'form', typeName: '高考信息表单', emoji: '📝', testTime: '' }
},
canAnalyze: false,
missingItems: [],
analyzing: false
statusBarHeight: 0,
navbarHeight: 88,
siteTitle: '高考版AI性格测试',
startButtonGaokao: '开始面部测试',
aiAnalysisText: '智能分析',
maintenanceMode: false,
reviewMode: false,
permFace: true,
showVersionPicker: false
},
_navigateByVersion(version) {
try {
getApp().globalData.appScope = version || 'personal'
getApp().globalData.selectedHomeVersion = version || 'personal'
} catch (e) {}
if (version === 'enterprise') {
wx.navigateTo({ url: '/pages/enterprise/index' })
return
}
if (version === 'gaokao') {
wx.navigateTo({ url: '/pages/gaokao/index' })
return
}
wx.switchTab({ url: '/pages/index/index' })
},
openVersionPicker() {
this.setData({ showVersionPicker: true })
},
closeVersionPicker() {
this.setData({ showVersionPicker: false })
},
noop() {},
selectVersion(e) {
const version = String(e.currentTarget.dataset.version || '')
this.setData({ showVersionPicker: false })
if (!version) return
this._navigateByVersion(version)
},
onLoad(options) {
try {
app.globalData.appScope = 'gaokao'
app.globalData.selectedHomeVersion = 'gaokao'
} catch (e) {}
try {
require('../../utils/thirdPartyContext.js').ingestThirdPartyOnPageLoad(options || {}, app)
} catch (e) {}
const systemInfo = wx.getSystemInfoSync()
const statusBarHeight = systemInfo.statusBarHeight || 0
const screenWidth = systemInfo.screenWidth || 375
const statusBarHeightRpx = (statusBarHeight * 750) / screenWidth
const navbarHeightRpx = statusBarHeightRpx + 88
const gd = app.globalData
const maintenanceMode = !!(gd.reviewMode || gd.maintenanceMode)
const ep = gd.enterprisePermissions
const pf = !ep || ep.face !== false
this.setData({
statusBarHeight: statusBarHeightRpx,
navbarHeight: navbarHeightRpx,
siteTitle: gd.siteTitle || '高考版AI性格测试',
startButtonGaokao: (maintenanceMode || !pf) ? '开始性格测试' : ((gd.textConfig && gd.textConfig.startButtonEnterprise) || '开始面部测试'),
aiAnalysisText: (gd.textConfig && gd.textConfig.aiAnalysisText) || '智能分析',
maintenanceMode,
reviewMode: maintenanceMode,
permFace: pf
})
},
onShow() {
const run = () => {
if (isAuditHideAiMode(app.globalData)) {
redirectIfMiniprogramAudit('版本审核中暂不可用')
return
}
this.refreshStatus()
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
const tabBar = this.getTabBar()
if (typeof tabBar.updateSelected === 'function') tabBar.updateSelected()
}
if (app.getRuntimeConfig) {
app.getRuntimeConfig().finally(run)
} else {
run()
}
},
refreshStatus() {
this.setData({ loading: true })
gaokaoApi
.getTaskStatus({ scene: 'gaokao_hub' })
.then((data) => {
try {
getApp().globalData.appScope = 'gaokao'
getApp().globalData.selectedHomeVersion = 'gaokao'
} catch (e) {}
app.ensureLogin()
.then(() => app.getRuntimeConfig())
.then((cfg) => {
if (cfg && cfg.siteTitle) {
app.globalData.siteTitle = cfg.siteTitle
}
const gd = app.globalData
const maintenanceMode = !!(gd.reviewMode || gd.maintenanceMode)
const ep = gd.enterprisePermissions
const pf = !ep || ep.face !== false
this.setData({
tasks: data.tasks || this.data.tasks,
canAnalyze: !!data.canAnalyze,
missingItems: data.missingItems || []
siteTitle: gd.siteTitle || '高考版AI性格测试',
startButtonGaokao: (maintenanceMode || !pf) ? '开始性格测试' : ((gd.textConfig && gd.textConfig.startButtonEnterprise) || '开始面部测试'),
aiAnalysisText: (gd.textConfig && gd.textConfig.aiAnalysisText) || '智能分析',
maintenanceMode,
reviewMode: maintenanceMode,
permFace: pf
})
})
.catch((e) => {
wx.showToast({ title: e.message || '加载失败', icon: 'none' })
})
.finally(() => this.setData({ loading: false }))
.catch(() => {})
},
goTask(e) {
const code = e.currentTarget.dataset.code
const resultId = Number(e.currentTarget.dataset.resultId || 0)
const done = (e.currentTarget.dataset.status || '') === 'done'
if (code === 'mbti') {
if (done && resultId > 0) {
wx.navigateTo({ url: `/pages/result/mbti?id=${resultId}&type=mbti` })
} else {
wx.navigateTo({ url: '/pages/test/mbti' })
}
switchVersion() {
const versions = (getApp().globalData.availableVersions || []).filter((v) => v === 'personal' || v === 'enterprise' || v === 'gaokao')
if (versions.length >= 3) {
this.openVersionPicker()
return
}
if (code === 'pdp') {
if (done && resultId > 0) {
wx.navigateTo({ url: `/pages/result/pdp?id=${resultId}&type=pdp` })
} else {
wx.navigateTo({ url: '/pages/test/pdp' })
}
this._navigateByVersion('personal')
},
startAITest() {
const ep = getApp().globalData.enterprisePermissions
if (this.data.maintenanceMode || (ep && ep.face === false)) {
wx.navigateTo({ url: '/pages/test-select/index' })
return
}
if (code === 'disc') {
if (done && resultId > 0) {
wx.navigateTo({ url: `/pages/result/disc?id=${resultId}&type=disc` })
} else {
wx.navigateTo({ url: '/pages/test/disc' })
}
return
}
if (code === 'face') {
const recType = String(e.currentTarget.dataset.recordType || 'ai').toLowerCase()
const typeParam = recType === 'face' ? 'face' : 'ai'
if (done && resultId > 0) {
wx.navigateTo({ url: `/pages/index/result?id=${resultId}&type=${typeParam}` })
} else {
wx.switchTab({ url: '/pages/index/camera' })
}
return
}
if (code === 'form') {
wx.navigateTo({ url: '/pages/gaokao/form' })
wx.switchTab({ url: '/pages/index/camera' })
},
openTaskCenter() {
wx.navigateTo({ url: '/pages/gaokao/task-center' })
},
onShareAppMessage() {
const { getSharePath } = require('../../utils/share')
return {
title: '神仙团队AI性格测试 (高考版)',
path: getSharePath('/pages/gaokao/index')
}
},
onAnalyzeTap() {
if (!this.data.canAnalyze) {
const nameMap = {
mbti: 'MBTI测试',
pdp: 'PDP测试',
disc: 'DISC测试',
face: '拍照面相',
form: '高考信息表单'
}
const msg = (this.data.missingItems || []).map((k) => nameMap[k] || k).join('、')
wx.showToast({
title: msg ? `请先完成:${msg}` : '请先完成全部任务',
icon: 'none',
duration: 2500
})
return
onShareTimeline() {
const { buildShareQuery } = require('../../utils/share')
return {
title: '神仙团队AI性格测试 (高考版)',
query: buildShareQuery()
}
this.setData({ analyzing: true })
wx.navigateTo({
url: '/pages/gaokao/report?pendingAnalyze=1',
complete: () => {
this.setData({ analyzing: false })
}
})
}
})

View File

@@ -1,4 +1,8 @@
{
"navigationBarTitleText": "高考志愿"
"navigationStyle": "custom",
"usingComponents": {
"custom-tab-bar": "/custom-tab-bar/index"
},
"enablePullDownRefresh": false
}

View File

@@ -1,90 +1,100 @@
<view class="page">
<view class="container">
<view class="header">
<view class="title">高考志愿任务中心</view>
<view class="desc">完成 MBTI、PDP、DISC、拍照面相与高考信息表单后即可生成综合分析若定价非 0请在报告页解锁全文与问卷类测评一致。</view>
</view>
<view class="task-list">
<view class="task-card" data-code="mbti" data-status="{{tasks.mbti.status}}" data-result-id="{{tasks.mbti.testResultId}}" bindtap="goTask">
<view class="left">
<view class="task-title-row">
<text class="task-emoji">{{tasks.mbti.emoji}}</text>
<text class="name">{{tasks.mbti.typeName}}</text>
</view>
<view class="result result--value" wx:if="{{tasks.mbti.status==='done'}}">{{tasks.mbti.resultText || '已完成'}}</view>
<view class="result todo" wx:else>未完成,请去测试</view>
<view class="task-time" wx:if="{{tasks.mbti.status==='done' && tasks.mbti.testTime}}">{{tasks.mbti.testTime}}</view>
<view class="container" style="padding-top: {{navbarHeight}}px;">
<view class="custom-navbar" style="padding-top: {{statusBarHeight}}rpx;">
<view class="navbar-content">
<view class="switch-version-btn" bindtap="switchVersion">
<text class="switch-version-icon">🎓</text>
<text class="switch-version-text">高考版</text>
</view>
<view class="status {{tasks.mbti.status==='done'?'done':''}}">{{tasks.mbti.status==='done'?'查看结果':'去完成'}}</view>
</view>
<view class="task-card" data-code="pdp" data-status="{{tasks.pdp.status}}" data-result-id="{{tasks.pdp.testResultId}}" bindtap="goTask">
<view class="left">
<view class="task-title-row">
<text class="task-emoji">{{tasks.pdp.emoji}}</text>
<text class="name">{{tasks.pdp.typeName}}</text>
</view>
<view class="result result--value" wx:if="{{tasks.pdp.status==='done'}}">{{tasks.pdp.resultText || '已完成'}}</view>
<view class="result todo" wx:else>未完成,请去测试</view>
<view class="task-time" wx:if="{{tasks.pdp.status==='done' && tasks.pdp.testTime}}">{{tasks.pdp.testTime}}</view>
</view>
<view class="status {{tasks.pdp.status==='done'?'done':''}}">{{tasks.pdp.status==='done'?'查看结果':'去完成'}}</view>
</view>
<view class="task-card" data-code="disc" data-status="{{tasks.disc.status}}" data-result-id="{{tasks.disc.testResultId}}" bindtap="goTask">
<view class="left">
<view class="task-title-row">
<text class="task-emoji">{{tasks.disc.emoji}}</text>
<text class="name">{{tasks.disc.typeName}}</text>
</view>
<view class="result result--value" wx:if="{{tasks.disc.status==='done'}}">{{tasks.disc.resultText || '已完成'}}</view>
<view class="result todo" wx:else>未完成,请去测试</view>
<view class="task-time" wx:if="{{tasks.disc.status==='done' && tasks.disc.testTime}}">{{tasks.disc.testTime}}</view>
</view>
<view class="status {{tasks.disc.status==='done'?'done':''}}">{{tasks.disc.status==='done'?'查看结果':'去完成'}}</view>
</view>
<view class="task-card" data-code="face" data-status="{{tasks.face.status}}" data-result-id="{{tasks.face.testResultId}}" data-record-type="{{tasks.face.recordTestType}}" bindtap="goTask">
<view class="left">
<view class="task-title-row">
<text class="task-emoji">{{tasks.face.emoji}}</text>
<text class="name">{{tasks.face.typeName}}</text>
</view>
<view class="result result--value" wx:if="{{tasks.face.status==='done'}}">{{tasks.face.resultText || '已完成'}}</view>
<view class="result todo" wx:else>未完成,请去拍摄</view>
<view class="task-time" wx:if="{{tasks.face.status==='done' && tasks.face.testTime}}">{{tasks.face.testTime}}</view>
</view>
<view class="status {{tasks.face.status==='done'?'done':''}}">{{tasks.face.status==='done'?'查看结果':'去完成'}}</view>
</view>
<view class="task-card" data-code="form" data-status="{{tasks.form.status}}" data-result-id="{{tasks.form.testResultId}}" bindtap="goTask">
<view class="left">
<view class="task-title-row">
<text class="task-emoji">{{tasks.form.emoji}}</text>
<text class="name">{{tasks.form.typeName}}</text>
</view>
<view class="result result--value" wx:if="{{tasks.form.status==='done'}}">
<block wx:if="{{tasks.form.resultText}}">已填写:{{tasks.form.resultText}}</block>
<block wx:else>已完成</block>
</view>
<view class="result todo" wx:else>未完成,请先填写</view>
<view class="task-time" wx:if="{{tasks.form.status==='done' && tasks.form.testTime}}">{{tasks.form.testTime}}</view>
</view>
<view class="status {{tasks.form.status==='done'?'done':''}}">{{tasks.form.status==='done'?'查看结果':'去完成'}}</view>
<view class="navbar-title">{{siteTitle || '高考版AI性格测试'}}</view>
<view class="navbar-placeholder"></view>
</view>
</view>
<view class="bg-decoration bg-top-right"></view>
<view class="bg-decoration bg-bottom-left"></view>
<view class="top-image-section">
<view class="image-container">
<view class="image-wrapper">
<image class="main-image" src="/images/mbti-team-image.png" mode="aspectFit"></image>
</view>
<view class="float-tag tag-1" wx:if="{{!reviewMode && permFace}}">面相分析</view>
<view class="float-tag tag-2" wx:if="{{!reviewMode && permFace}}">骨相分析</view>
<view class="float-tag tag-3">性格测评</view>
<view class="float-tag tag-1" wx:if="{{reviewMode}}">MBTI</view>
<view class="float-tag tag-2" wx:if="{{reviewMode}}">DISC</view>
</view>
</view>
<view class="bottom-actions">
<button
class="action-btn {{canAnalyze ? 'action-btn--primary' : 'action-btn--disabled'}}"
loading="{{analyzing}}"
bindtap="onAnalyzeTap"
disabled="{{analyzing}}"
>
{{canAnalyze ? '开始综合分析' : '综合分析(未完成)'}}
</button>
<view class="process-section">
<view class="section-title">测试流程</view>
<view class="process-steps" wx:if="{{!reviewMode && permFace}}">
<view class="step-item">
<view class="step-circle active">1</view>
<text class="step-label">STEP1</text>
<text class="step-text">拍摄照片</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>
</view>
<view class="step-line"></view>
<view class="step-item">
<view class="step-circle">3</view>
<text class="step-label">STEP3</text>
<text class="step-text">生成报告</text>
</view>
</view>
<view class="process-steps" wx:if="{{reviewMode || !permFace}}">
<view class="step-item">
<view class="step-circle active">1</view>
<text class="step-label">STEP1</text>
<text class="step-text">选择测试</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">回答问题</text>
</view>
<view class="step-line"></view>
<view class="step-item">
<view class="step-circle">3</view>
<text class="step-label">STEP3</text>
<text class="step-text">查看结果</text>
</view>
</view>
</view>
<view class="start-button" bindtap="startAITest">
<text class="button-text">{{startButtonGaokao || '开始性格测试'}}</text>
</view>
<view class="task-center-entry" bindtap="openTaskCenter">
<text class="task-center-entry__icon">🗂️</text>
<text class="task-center-entry__text">进入高考志愿任务中心(含历史记录)</text>
<text class="task-center-entry__arrow"></text>
</view>
<view class="version-picker-mask" wx:if="{{showVersionPicker}}" bindtap="closeVersionPicker">
<view class="version-picker-dialog" catchtap="noop">
<view class="version-picker-head">
<view class="version-picker-badge">版本选择</view>
<view class="version-picker-close" bindtap="closeVersionPicker">×</view>
</view>
<view class="version-picker-title">请选择使用版本</view>
<view class="version-picker-desc">当前企业支持三种版本,选择后将进入对应流程</view>
<view class="version-picker-actions">
<button class="version-btn version-btn-personal" data-version="personal" bindtap="selectVersion">个人版</button>
<button class="version-btn version-btn-enterprise" data-version="enterprise" bindtap="selectVersion">企业版</button>
<button class="version-btn version-btn-gaokao" data-version="gaokao" bindtap="selectVersion">高考版</button>
</view>
<view class="version-picker-foot">
<button class="version-cancel-btn" bindtap="closeVersionPicker">取消</button>
</view>
</view>
</view>
<custom-tab-bar />
</view>

View File

@@ -1,142 +1,380 @@
.page {
min-height: 100vh;
background: #f7f8fc;
box-sizing: border-box;
}
.container {
min-height: 100%;
background: #f7f8fc;
padding: 32rpx 24rpx;
/* 底部固定操作条占位(单主按钮 + 安全区) */
padding-bottom: calc(140rpx + env(safe-area-inset-bottom));
box-sizing: border-box;
}
.header {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 20rpx;
}
.title {
font-size: 36rpx;
font-weight: 700;
color: #222;
}
.desc {
font-size: 24rpx;
color: #666;
margin-top: 8rpx;
}
.task-list {
min-height: 100vh;
width: 100vw;
overflow-x: hidden;
overflow-y: auto;
background: #f3f6fb;
position: relative;
display: flex;
flex-direction: column;
gap: 16rpx;
padding-bottom: calc(100rpx + env(safe-area-inset-bottom) + 40rpx);
box-sizing: border-box;
}
.task-card {
/* 背景装饰圆形 */
.bg-decoration {
position: absolute;
width: 600rpx;
height: 600rpx;
border-radius: 50%;
pointer-events: none;
z-index: 0;
}
.bg-top-right {
top: -200rpx;
right: -200rpx;
background: radial-gradient(circle, rgba(37, 99, 235, 0.08) 0%, transparent 70%);
}
.bg-bottom-left {
bottom: 0;
left: 0;
background: radial-gradient(circle, rgba(37, 99, 235, 0.05) 0%, transparent 70%);
}
.custom-navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
background: #ffffff;
z-index: 10000;
width: 100%;
}
.navbar-content {
height: 88rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 40rpx;
position: relative;
background: #fff;
border-radius: 14rpx;
padding: 22rpx 20rpx;
min-height: 88rpx;
}
.navbar-title {
position: absolute;
left: 50%;
transform: translateX(-50%);
font-size: 36rpx;
font-weight: 700;
color: #1e3a8a;
text-align: center;
flex: 1;
}
.navbar-placeholder {
width: 140rpx;
flex-shrink: 0;
}
.switch-version-btn {
display: flex;
align-items: center;
gap: 8rpx;
padding: 12rpx 20rpx;
background: #e8f0ff;
border-radius: 30rpx;
border: 1rpx solid rgba(37, 99, 235, 0.3);
flex-shrink: 0;
z-index: 10;
}
.switch-version-icon {
font-size: 24rpx;
color: #1d4ed8;
}
.switch-version-text {
font-size: 24rpx;
color: #2563eb;
font-weight: 700;
}
.top-image-section {
width: 100%;
padding: 15rpx 40rpx 20rpx;
flex-shrink: 0;
box-sizing: border-box;
position: relative;
z-index: 1;
margin-top: 0;
}
.image-container {
position: relative;
width: 100%;
}
.image-wrapper {
position: relative;
width: 100%;
padding-top: 100%;
border-radius: 50%;
overflow: hidden;
background: #ffffff;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.12);
}
.main-image {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 85%;
height: 85%;
display: block;
}
.float-tag {
position: absolute;
padding: 12rpx 24rpx;
border-radius: 30rpx;
font-size: 24rpx;
font-weight: 600;
color: #2563eb;
background: #ffffff;
box-shadow: 0 4rpx 14rpx rgba(37, 99, 235, 0.12);
white-space: nowrap;
z-index: 10;
}
.tag-1 {
top: 15%;
right: 10rpx;
}
.tag-2 {
bottom: 25%;
left: 0;
}
.tag-3 {
bottom: 25%;
right: 0;
}
.process-section {
padding: 15rpx 40rpx 20rpx;
flex-shrink: 0;
position: relative;
z-index: 1;
}
.section-title {
font-size: 32rpx;
font-weight: 700;
color: #2563eb;
text-align: center;
margin-bottom: 25rpx;
}
.process-steps {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 10rpx;
}
.step-item {
display: flex;
flex-direction: column;
align-items: center;
flex: 1;
}
.step-circle {
width: 70rpx;
height: 70rpx;
border-radius: 50%;
background: #b8b9bc;
display: flex;
align-items: center;
justify-content: center;
font-size: 36rpx;
font-weight: 700;
color: #fff;
margin-bottom: 12rpx;
}
.step-circle.active {
background: #2563eb;
}
.step-label {
font-size: 20rpx;
color: #9ca3af;
margin-bottom: 4rpx;
}
.step-text {
font-size: 24rpx;
color: #374151;
font-weight: 600;
}
.step-line {
width: 50rpx;
height: 4rpx;
background: #e0e0e0;
margin: 0 6rpx 50rpx;
}
.start-button {
margin: 20rpx 40rpx 0;
background: #2563eb;
border-radius: 50rpx;
padding: 24rpx;
text-align: center;
box-shadow: 0 8rpx 24rpx rgba(37, 99, 235, 0.2);
position: relative;
z-index: 1;
}
.task-center-entry {
margin: 18rpx 36rpx 0;
background: #ffffff;
border: 1rpx solid #dbeafe;
border-radius: 18rpx;
padding: 22rpx 24rpx;
display: flex;
align-items: center;
box-shadow: 0 8rpx 18rpx rgba(37, 99, 235, 0.08);
}
.task-center-entry__icon {
font-size: 34rpx;
}
.task-center-entry__text {
flex: 1;
margin-left: 12rpx;
color: #1e3a8a;
font-size: 26rpx;
font-weight: 500;
}
.task-center-entry__arrow {
color: #93c5fd;
font-size: 34rpx;
}
.button-text {
font-size: 30rpx;
color: #fff;
font-weight: 700;
}
.version-picker-mask {
position: fixed;
inset: 0;
background: rgba(10, 15, 30, 0.52);
z-index: 20000;
display: flex;
align-items: center;
justify-content: center;
padding: 40rpx;
box-sizing: border-box;
}
.version-picker-dialog {
width: 100%;
max-width: 640rpx;
background: #fff;
border-radius: 30rpx;
padding: 26rpx 30rpx 24rpx;
box-shadow: 0 24rpx 70rpx rgba(16, 24, 40, 0.28);
}
.version-picker-head {
display: flex;
align-items: center;
justify-content: space-between;
}
.task-title-row {
display: flex;
align-items: center;
gap: 12rpx;
.version-picker-badge {
padding: 8rpx 18rpx;
border-radius: 999rpx;
background: #e8f0ff;
color: #1d4ed8;
font-size: 22rpx;
font-weight: 600;
}
.task-emoji {
font-size: 36rpx;
.version-picker-close {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
background: #f3f4f6;
color: #6b7280;
display: flex;
align-items: center;
justify-content: center;
font-size: 34rpx;
line-height: 1;
}
.name {
font-size: 30rpx;
color: #222;
font-weight: 600;
.version-picker-title {
text-align: center;
font-size: 36rpx;
font-weight: 700;
color: #111827;
margin-top: 12rpx;
}
.task-time {
margin-top: 6rpx;
font-size: 22rpx;
color: #9ca3af;
}
.result {
margin-top: 8rpx;
.version-picker-desc {
margin-top: 12rpx;
text-align: center;
font-size: 24rpx;
color: #666;
color: #6b7280;
line-height: 1.6;
}
.result--value {
font-size: 30rpx;
font-weight: 600;
color: #4f46e5;
}
.result.todo {
color: #d04848;
}
.status {
font-size: 24rpx;
color: #7c3aed;
}
.status.done {
color: #1f9d55;
}
/* 底部悬浮:全宽主操作 */
.bottom-actions {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 200;
width: 100%;
box-sizing: border-box;
padding: 16rpx 24rpx;
padding-bottom: calc(16rpx + env(safe-area-inset-bottom));
background: rgba(255, 255, 255, 0.98);
box-shadow: 0 -8rpx 32rpx rgba(15, 23, 42, 0.08);
border-top: 1rpx solid #eef0f4;
.version-picker-actions {
margin-top: 28rpx;
display: flex;
flex-direction: column;
gap: 16rpx;
}
.action-btn {
width: 100% !important;
margin: 0;
padding: 24rpx 32rpx;
line-height: 1.35;
border-radius: 16rpx;
font-size: 30rpx;
font-weight: 700;
border: none;
box-sizing: border-box;
}
.action-btn::after {
border: none;
}
.action-btn--primary {
background: #7c3aed;
.version-btn {
height: 84rpx;
line-height: 84rpx;
border-radius: 999rpx;
color: #fff;
font-size: 29rpx;
font-weight: 600;
border: none;
box-shadow: 0 6rpx 16rpx rgba(37, 99, 235, 0.16);
}
.action-btn--disabled {
background: #e5e7eb;
color: #9ca3af;
.version-btn-personal { background: #2563eb; }
.version-btn-enterprise { background: #2563eb; }
.version-btn-gaokao { background: #2563eb; }
.version-picker-foot {
margin-top: 14rpx;
}
.version-cancel-btn {
height: 76rpx;
line-height: 76rpx;
border-radius: 999rpx;
background: #f3f4f6;
color: #4b5563;
font-size: 27rpx;
font-weight: 600;
border: 1rpx solid #d1d5db;
}
custom-tab-bar {
display: block;
height: 0;
overflow: visible;
}

View File

@@ -13,7 +13,6 @@ const inviteCodeGate = require('../../utils/inviteCodeGate.js')
const { openTimelineShareHint } = require('../../utils/resultProfileGate.js')
const { computeJourney, markShared } = require('../../utils/gaokaoJourneyState.js')
const { getEnterpriseIdForApiPayload } = require('../../utils/enterpriseContext.js')
const { isAuditHideAiMode, redirectIfMiniprogramAudit } = require('../../utils/miniprogramAuditGate.js')
/** GET /api/test/detail | share-detail 返回体 -> mergeApiReport 入参 */
function mapTestDetailToReportPayload(detail) {
@@ -242,60 +241,55 @@ Page({
},
onLoad(options) {
const bootstrap = () => {
if (isAuditHideAiMode(app.globalData)) {
redirectIfMiniprogramAudit('版本审核中暂不可用')
return
}
try {
wx.showShareMenu({ withShareTicket: true, menus: ['shareAppMessage', 'shareTimeline'] })
} catch (e) {}
try {
wx.showShareMenu({ withShareTicket: true, menus: ['shareAppMessage', 'shareTimeline'] })
} catch (e) {}
const fromShareFs = options && (String(options.fs) === '1' || options.from === 'share')
const sid = options && options.id != null && options.id !== '' ? String(options.id) : ''
const st = options && options.st ? String(options.st).trim() : ''
const fromShareFs = options && (String(options.fs) === '1' || options.from === 'share')
const sid = options && options.id != null && options.id !== '' ? String(options.id) : ''
const st = options && options.st ? String(options.st).trim() : ''
if (sid && st) {
this.setData({ fromShare: true })
this.loadShareDetail(sid, st)
return
}
const ec =
typeof this.getOpenerEventChannel === 'function' ? this.getOpenerEventChannel() : null
if (ec && typeof ec.once === 'function') {
ec.once('gaokaoAnalyzeReport', (payload) => {
if (payload && payload.report) {
this.applyPayloadOnly({
id: payload.id,
createdAt: payload.createdAt || 0,
overview: payload.overview || '',
report: payload.report
})
}
})
}
const pendingAnalyze = options && String(options.pendingAnalyze) === '1'
if (pendingAnalyze) {
// 等 runtime 后再跑;用微任务启动分析,避免原 onReady 与异步 onLoad 竞态
setTimeout(() => this.beginAnalyzeFlow(), 0)
return
}
const rid = options && options.id != null ? parseInt(String(options.id), 10) : 0
this._detailReportId = rid > 0 && !Number.isNaN(rid) ? rid : 0
if (fromShareFs) {
this.setData({ fromShare: true })
}
const delay = options && options.fromAnalyze === '1' ? 400 : 0
setTimeout(() => this.load(), delay)
if (sid && st) {
this.setData({ fromShare: true })
this.loadShareDetail(sid, st)
return
}
if (app.getRuntimeConfig) {
app.getRuntimeConfig().then(bootstrap).catch(bootstrap)
} else {
bootstrap()
const ec =
typeof this.getOpenerEventChannel === 'function' ? this.getOpenerEventChannel() : null
if (ec && typeof ec.once === 'function') {
ec.once('gaokaoAnalyzeReport', (payload) => {
if (payload && payload.report) {
this.applyPayloadOnly({
id: payload.id,
createdAt: payload.createdAt || 0,
overview: payload.overview || '',
report: payload.report
})
}
})
}
const pendingAnalyze = options && String(options.pendingAnalyze) === '1'
if (pendingAnalyze) {
this._pendingAnalyze = true
return
}
const rid = options && options.id != null ? parseInt(String(options.id), 10) : 0
this._detailReportId = rid > 0 && !Number.isNaN(rid) ? rid : 0
if (fromShareFs) {
this.setData({ fromShare: true })
}
const delay = options && options.fromAnalyze === '1' ? 400 : 0
setTimeout(() => this.load(), delay)
},
onReady() {
if (this._pendingAnalyze) {
this._pendingAnalyze = false
this.beginAnalyzeFlow()
}
},

View File

@@ -0,0 +1,116 @@
const gaokaoApi = require('../../utils/gaokao')
Page({
data: {
loading: false,
tasks: {
mbti: { code: 'mbti', status: 'todo', resultText: '', testResultId: 0, id: 0, testType: 'mbti', typeName: 'MBTI性格', emoji: '🧠', testTime: '' },
pdp: { code: 'pdp', status: 'todo', resultText: '', testResultId: 0, id: 0, testType: 'pdp', typeName: 'PDP行为', emoji: '🦁', testTime: '' },
disc: { code: 'disc', status: 'todo', resultText: '', testResultId: 0, id: 0, testType: 'disc', typeName: 'DISC测评', emoji: '📊', testTime: '' },
face: { code: 'face', status: 'todo', resultText: '', testResultId: 0, id: 0, testType: 'ai', typeName: '拍照面相', emoji: '📷', testTime: '', recordTestType: 'ai' },
form: { code: 'form', status: 'todo', resultText: '', testResultId: 0, id: 0, testType: 'form', typeName: '高考信息表单', emoji: '📝', testTime: '' }
},
canAnalyze: false,
missingItems: [],
analyzing: false
},
onShow() {
try {
const app = getApp()
app.globalData.appScope = 'gaokao'
app.globalData.selectedHomeVersion = 'gaokao'
} catch (e) {}
this.refreshStatus()
},
refreshStatus() {
this.setData({ loading: true })
gaokaoApi
.getTaskStatus({ scene: 'gaokao_hub' })
.then((res) => {
this.setData({
tasks: res.tasks || this.data.tasks,
canAnalyze: !!res.canAnalyze,
missingItems: res.missingItems || []
})
})
.catch((e) => {
wx.showToast({ title: e.message || '加载失败', icon: 'none' })
})
.finally(() => this.setData({ loading: false }))
},
goTask(e) {
const code = e.currentTarget.dataset.code
const resultId = Number(e.currentTarget.dataset.resultId || 0)
const done = (e.currentTarget.dataset.status || '') === 'done'
if (code === 'mbti') {
if (done && resultId > 0) {
wx.navigateTo({ url: `/pages/result/mbti?id=${resultId}&type=mbti&vscope=gaokao` })
} else {
wx.navigateTo({ url: '/pages/test/mbti' })
}
return
}
if (code === 'pdp') {
if (done && resultId > 0) {
wx.navigateTo({ url: `/pages/result/pdp?id=${resultId}&type=pdp&vscope=gaokao` })
} else {
wx.navigateTo({ url: '/pages/test/pdp' })
}
return
}
if (code === 'disc') {
if (done && resultId > 0) {
wx.navigateTo({ url: `/pages/result/disc?id=${resultId}&type=disc&vscope=gaokao` })
} else {
wx.navigateTo({ url: '/pages/test/disc' })
}
return
}
if (code === 'face') {
const recType = String(e.currentTarget.dataset.recordType || 'ai').toLowerCase()
const typeParam = recType === 'face' ? 'face' : 'ai'
if (done && resultId > 0) {
wx.navigateTo({ url: `/pages/index/result?id=${resultId}&type=${typeParam}&vscope=gaokao` })
} else {
wx.switchTab({ url: '/pages/index/camera' })
}
return
}
if (code === 'form') {
wx.navigateTo({ url: '/pages/gaokao/form' })
}
},
onAnalyzeTap() {
if (!this.data.canAnalyze) {
const nameMap = {
mbti: 'MBTI测试',
pdp: 'PDP测试',
disc: 'DISC测试',
face: '拍照面相',
form: '高考信息表单'
}
const msg = (this.data.missingItems || []).map((k) => nameMap[k] || k).join('、')
wx.showToast({
title: msg ? `请先完成:${msg}` : '请先完成全部任务',
icon: 'none',
duration: 2500
})
return
}
this.setData({ analyzing: true })
wx.navigateTo({
url: '/pages/gaokao/report?pendingAnalyze=1',
complete: () => {
this.setData({ analyzing: false })
}
})
},
goHistoryTests() {
wx.navigateTo({ url: '/pages/history/index?gaokaoOnly=1' })
}
})

View File

@@ -0,0 +1,4 @@
{
"navigationBarTitleText": "高考志愿任务中心",
"enablePullDownRefresh": true
}

View File

@@ -0,0 +1,93 @@
<view class="page">
<view class="container">
<view class="header">
<view class="title">高考志愿任务中心</view>
<view class="desc">完成 MBTI、PDP、DISC、拍照面相与高考信息表单后即可生成综合分析若定价非 0请在报告页解锁全文与问卷类测评一致。</view>
</view>
<view class="task-list">
<view class="task-card" data-code="mbti" data-status="{{tasks.mbti.status}}" data-result-id="{{tasks.mbti.testResultId}}" bindtap="goTask">
<view class="left">
<view class="task-title-row">
<text class="task-emoji">{{tasks.mbti.emoji}}</text>
<text class="name">{{tasks.mbti.typeName}}</text>
</view>
<view class="result result--value" wx:if="{{tasks.mbti.status==='done'}}">{{tasks.mbti.resultText || '已完成'}}</view>
<view class="result todo" wx:else>未完成,请去测试</view>
<view class="task-time" wx:if="{{tasks.mbti.status==='done' && tasks.mbti.testTime}}">{{tasks.mbti.testTime}}</view>
</view>
<view class="status {{tasks.mbti.status==='done'?'done':''}}">{{tasks.mbti.status==='done'?'查看结果':'去完成'}}</view>
</view>
<view class="task-card" data-code="pdp" data-status="{{tasks.pdp.status}}" data-result-id="{{tasks.pdp.testResultId}}" bindtap="goTask">
<view class="left">
<view class="task-title-row">
<text class="task-emoji">{{tasks.pdp.emoji}}</text>
<text class="name">{{tasks.pdp.typeName}}</text>
</view>
<view class="result result--value" wx:if="{{tasks.pdp.status==='done'}}">{{tasks.pdp.resultText || '已完成'}}</view>
<view class="result todo" wx:else>未完成,请去测试</view>
<view class="task-time" wx:if="{{tasks.pdp.status==='done' && tasks.pdp.testTime}}">{{tasks.pdp.testTime}}</view>
</view>
<view class="status {{tasks.pdp.status==='done'?'done':''}}">{{tasks.pdp.status==='done'?'查看结果':'去完成'}}</view>
</view>
<view class="task-card" data-code="disc" data-status="{{tasks.disc.status}}" data-result-id="{{tasks.disc.testResultId}}" bindtap="goTask">
<view class="left">
<view class="task-title-row">
<text class="task-emoji">{{tasks.disc.emoji}}</text>
<text class="name">{{tasks.disc.typeName}}</text>
</view>
<view class="result result--value" wx:if="{{tasks.disc.status==='done'}}">{{tasks.disc.resultText || '已完成'}}</view>
<view class="result todo" wx:else>未完成,请去测试</view>
<view class="task-time" wx:if="{{tasks.disc.status==='done' && tasks.disc.testTime}}">{{tasks.disc.testTime}}</view>
</view>
<view class="status {{tasks.disc.status==='done'?'done':''}}">{{tasks.disc.status==='done'?'查看结果':'去完成'}}</view>
</view>
<view class="task-card" data-code="face" data-status="{{tasks.face.status}}" data-result-id="{{tasks.face.testResultId}}" data-record-type="{{tasks.face.recordTestType}}" bindtap="goTask">
<view class="left">
<view class="task-title-row">
<text class="task-emoji">{{tasks.face.emoji}}</text>
<text class="name">{{tasks.face.typeName}}</text>
</view>
<view class="result result--value" wx:if="{{tasks.face.status==='done'}}">{{tasks.face.resultText || '已完成'}}</view>
<view class="result todo" wx:else>未完成,请去拍摄</view>
<view class="task-time" wx:if="{{tasks.face.status==='done' && tasks.face.testTime}}">{{tasks.face.testTime}}</view>
</view>
<view class="status {{tasks.face.status==='done'?'done':''}}">{{tasks.face.status==='done'?'查看结果':'去完成'}}</view>
</view>
<view class="task-card" data-code="form" data-status="{{tasks.form.status}}" data-result-id="{{tasks.form.testResultId}}" bindtap="goTask">
<view class="left">
<view class="task-title-row">
<text class="task-emoji">{{tasks.form.emoji}}</text>
<text class="name">{{tasks.form.typeName}}</text>
</view>
<view class="result result--value" wx:if="{{tasks.form.status==='done'}}">
<block wx:if="{{tasks.form.resultText}}">已填写:{{tasks.form.resultText}}</block>
<block wx:else>已完成</block>
</view>
<view class="result todo" wx:else>未完成,请先填写</view>
<view class="task-time" wx:if="{{tasks.form.status==='done' && tasks.form.testTime}}">{{tasks.form.testTime}}</view>
</view>
<view class="status {{tasks.form.status==='done'?'done':''}}">{{tasks.form.status==='done'?'查看结果':'去完成'}}</view>
</view>
</view>
<view class="history-entry" bindtap="goHistoryTests">
<text class="history-entry__text">历史测评记录</text>
</view>
</view>
<view class="bottom-actions">
<button
class="action-btn {{canAnalyze ? 'action-btn--primary' : 'action-btn--disabled'}}"
loading="{{analyzing}}"
bindtap="onAnalyzeTap"
disabled="{{analyzing}}"
>
{{canAnalyze ? '开始综合分析' : '综合分析(未完成)'}}
</button>
</view>
</view>

View File

@@ -0,0 +1,154 @@
.page {
min-height: 100vh;
background: #f7f8fc;
box-sizing: border-box;
}
.container {
min-height: 100%;
background: #f7f8fc;
padding: 32rpx 24rpx;
/* 底部固定操作条占位(单主按钮 + 安全区) */
padding-bottom: calc(140rpx + env(safe-area-inset-bottom));
box-sizing: border-box;
}
.header {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 20rpx;
}
.title {
font-size: 36rpx;
font-weight: 700;
color: #222;
}
.desc {
font-size: 24rpx;
color: #666;
margin-top: 8rpx;
}
.task-list {
display: flex;
flex-direction: column;
gap: 16rpx;
}
.task-card {
background: #fff;
border-radius: 14rpx;
padding: 22rpx 20rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.task-title-row {
display: flex;
align-items: center;
gap: 12rpx;
}
.task-emoji {
font-size: 36rpx;
line-height: 1;
}
.name {
font-size: 30rpx;
color: #222;
font-weight: 600;
}
.task-time {
margin-top: 6rpx;
font-size: 22rpx;
color: #9ca3af;
}
.result {
margin-top: 8rpx;
font-size: 24rpx;
color: #666;
}
.result--value {
font-size: 30rpx;
font-weight: 600;
color: #4f46e5;
}
.result.todo {
color: #d04848;
}
.status {
font-size: 24rpx;
color: #7c3aed;
}
.status.done {
color: #1f9d55;
}
.history-entry {
margin: 18rpx auto 0;
width: 100%;
padding: 8rpx 0;
text-align: center;
}
.history-entry__text {
font-size: 24rpx;
color: #9ca3af;
font-weight: 500;
}
/* 底部悬浮:全宽主操作 */
.bottom-actions {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 200;
width: 100%;
box-sizing: border-box;
padding: 16rpx 24rpx;
padding-bottom: calc(16rpx + env(safe-area-inset-bottom));
background: rgba(255, 255, 255, 0.98);
box-shadow: 0 -8rpx 32rpx rgba(15, 23, 42, 0.08);
border-top: 1rpx solid #eef0f4;
display: flex;
flex-direction: column;
gap: 16rpx;
}
.action-btn {
width: 100% !important;
margin: 0;
padding: 24rpx 32rpx;
line-height: 1.35;
border-radius: 16rpx;
font-size: 30rpx;
font-weight: 700;
border: none;
box-sizing: border-box;
}
.action-btn::after {
border: none;
}
.action-btn--primary {
background: #7c3aed;
color: #fff;
}
.action-btn--disabled {
background: #e5e7eb;
color: #9ca3af;
}