feat: 小程序与管理端迭代(神仙AI、了解自己CRM、AI测试入口、报表与分润等)
Made-with: Cursor
This commit is contained in:
@@ -9,6 +9,9 @@ const MBTI_TIME_SEC = 30 * 60 // 30 分钟
|
||||
Page({
|
||||
data: {
|
||||
loading: true,
|
||||
loadError: false,
|
||||
loadErrorMsg: '',
|
||||
usingLocalFallback: false,
|
||||
questions: [],
|
||||
currentIndex: 0,
|
||||
currentQuestion: null,
|
||||
@@ -30,26 +33,37 @@ Page({
|
||||
try {
|
||||
require('../../utils/thirdPartyContext.js').ingestThirdPartyOnPageLoad(options || {}, app)
|
||||
} catch (e) {}
|
||||
// 分享直达本页时 silentLogin 可能尚未完成,须先 ensureLogin 再拉题,否则 401 → 白屏
|
||||
app.ensureLogin()
|
||||
.then((ok) => {
|
||||
if (!ok) {
|
||||
this.setData({ loading: false })
|
||||
wx.showToast({ title: '登录失败,请重试', icon: 'none' })
|
||||
return Promise.reject(new Error('login'))
|
||||
}
|
||||
return loadQuestions('mbti', {})
|
||||
})
|
||||
this._loadOptions = options || {}
|
||||
this._startLoadQuestions()
|
||||
},
|
||||
|
||||
retryLoad() {
|
||||
this._startLoadQuestions()
|
||||
},
|
||||
|
||||
/**
|
||||
* 登录与拉题解耦:即使登录失败,仍尝试拉题(线上失败会走 401/network 降级本地)
|
||||
*/
|
||||
_startLoadQuestions() {
|
||||
this.setData({ loading: true, loadError: false, loadErrorMsg: '', usingLocalFallback: false })
|
||||
// 先尝试登录,但不阻塞题库;拉题失败会降级本地 fallback
|
||||
const ensure = (typeof app.ensureLogin === 'function')
|
||||
? app.ensureLogin().catch(() => false)
|
||||
: Promise.resolve(false)
|
||||
ensure
|
||||
.then(() => loadQuestions('mbti', { allowLocalFallback: true }))
|
||||
.then((questions) => {
|
||||
if (!questions) return
|
||||
const total = questions.length
|
||||
if (!total) {
|
||||
wx.showToast({ title: '暂无题目', icon: 'none' })
|
||||
this.setData({ loading: false })
|
||||
if (!questions || !questions.length) {
|
||||
this.setData({ loading: false, loadError: true, loadErrorMsg: '暂无题目,请稍后再试' })
|
||||
return
|
||||
}
|
||||
const total = questions.length
|
||||
// 判断是否为本地题(id 以 'L-' 前缀)
|
||||
const isLocal = !!(questions[0] && typeof questions[0].id === 'string' && questions[0].id.indexOf('L-') === 0)
|
||||
this.setData({
|
||||
loading: false,
|
||||
loadError: false,
|
||||
usingLocalFallback: isLocal,
|
||||
questions,
|
||||
currentQuestion: questions[0],
|
||||
canAccess: true,
|
||||
@@ -60,7 +74,7 @@ Page({
|
||||
formatTime: '30:00'
|
||||
})
|
||||
try {
|
||||
require('../../utils/analytics').track('test_start', { type: 'mbti', total })
|
||||
require('../../utils/analytics').track('test_start', { type: 'mbti', total, source: isLocal ? 'local' : 'remote' })
|
||||
} catch (e) {}
|
||||
try {
|
||||
wx.showShareMenu({ withShareTicket: true, menus: ['shareAppMessage', 'shareTimeline'] })
|
||||
@@ -68,9 +82,8 @@ Page({
|
||||
this.startTimer()
|
||||
})
|
||||
.catch((err) => {
|
||||
if (err && err.message === 'login') return
|
||||
this.setData({ loading: false })
|
||||
wx.showToast({ title: (err && err.message) || '加载失败', icon: 'none' })
|
||||
const msg = (err && err.message) || '加载失败,请检查网络后重试'
|
||||
this.setData({ loading: false, loadError: true, loadErrorMsg: msg })
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
@@ -3,7 +3,18 @@
|
||||
<view wx:if="{{loading}}" class="test-loading">
|
||||
<text class="test-loading-text">加载题目…</text>
|
||||
</view>
|
||||
<view wx:elif="{{loadError}}" class="test-error-state">
|
||||
<view class="test-error-ic">⚠️</view>
|
||||
<text class="test-error-title">加载失败</text>
|
||||
<text class="test-error-msg">{{loadErrorMsg || '网络异常,请检查后重试'}}</text>
|
||||
<view class="test-error-btn" bindtap="retryLoad">重新加载</view>
|
||||
<text class="test-error-hint">若持续失败,请在微信中关闭小程序后再次打开</text>
|
||||
</view>
|
||||
<block wx:elif="{{currentQuestion}}">
|
||||
<view wx:if="{{usingLocalFallback}}" class="fallback-banner">
|
||||
<text class="fallback-banner-ic">📶</text>
|
||||
<text class="fallback-banner-text">当前使用本地题库(网络异常自动切换),结果仍会在本机完成</text>
|
||||
</view>
|
||||
<view class="progress-section">
|
||||
<view class="progress-info">
|
||||
<text class="question-count">问题 {{currentIndex + 1}}/{{total}}</text>
|
||||
|
||||
@@ -20,6 +20,74 @@
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 错误态:加载失败 + 重试按钮 */
|
||||
.test-error-state {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 80rpx 60rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.test-error-ic {
|
||||
font-size: 84rpx;
|
||||
line-height: 1;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
.test-error-title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
.test-error-msg {
|
||||
font-size: 26rpx;
|
||||
color: #6b7280;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 40rpx;
|
||||
max-width: 520rpx;
|
||||
}
|
||||
.test-error-btn {
|
||||
padding: 20rpx 56rpx;
|
||||
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
||||
color: #fff;
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
border-radius: 48rpx;
|
||||
box-shadow: 0 10rpx 24rpx rgba(99, 102, 241, 0.3);
|
||||
margin-bottom: 28rpx;
|
||||
}
|
||||
.test-error-btn:active {
|
||||
transform: scale(0.97);
|
||||
opacity: 0.9;
|
||||
}
|
||||
.test-error-hint {
|
||||
font-size: 22rpx;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
/* 本地 fallback 题库提示条 */
|
||||
.fallback-banner {
|
||||
margin: 16rpx 24rpx 0;
|
||||
padding: 14rpx 20rpx;
|
||||
background: #fef3c7;
|
||||
border: 1rpx solid #fcd34d;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10rpx;
|
||||
}
|
||||
.fallback-banner-ic {
|
||||
font-size: 24rpx;
|
||||
}
|
||||
.fallback-banner-text {
|
||||
font-size: 22rpx;
|
||||
color: #92400e;
|
||||
line-height: 1.4;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.progress-section {
|
||||
padding: 32rpx;
|
||||
border-bottom: 1rpx solid #e5e5e5;
|
||||
|
||||
Reference in New Issue
Block a user