// pages/history/index.js - 测试历史记录(一次拉取全部,时间行右侧展示企业名) const app = getApp() const { getEffectiveEnterpriseId } = require('../../utils/enterpriseContext.js') Page({ data: { activeTab: 'all', tabName: '', gaokaoOnly: false, list: [], total: 0, page: 1, hasMore: false, loading: false, loadingMore: false, isEnterprise: false, reviewMode: false, permMbti: true, permSbti: true, permPdp: true, permDisc: true, permFace: true }, _checkIsEnterprise() { const gd = app.globalData || {} const storedUser = wx.getStorageSync('userInfo') || null const scope = gd.appScope || 'personal' const enterpriseId = getEffectiveEnterpriseId() return scope === 'enterprise' || !!enterpriseId }, _syncPermsAndTab() { if (this.data.gaokaoOnly) { this.setData({ permMbti: false, permSbti: false, permPdp: false, permDisc: false, permFace: false, reviewMode: !!app.globalData.reviewMode, activeTab: 'all', tabName: '高考' }) return } const p = app.globalData.enterprisePermissions const permMbti = !p || p.mbti !== false const permSbti = !p || p.sbti !== false const permPdp = !p || p.pdp !== false const permDisc = !p || p.disc !== false const permFace = !p || p.face !== false const reviewMode = !!app.globalData.reviewMode const names = { all: '', mbti: 'MBTI', sbti: 'SBTI', pdp: 'PDP', disc: 'DISC', ai: '面相', resume: '简历' } let { activeTab } = this.data if (activeTab === 'mbti' && !permMbti) activeTab = 'all' if (activeTab === 'sbti' && !permSbti) activeTab = 'all' if (activeTab === 'pdp' && !permPdp) activeTab = 'all' if (activeTab === 'disc' && !permDisc) activeTab = 'all' if (activeTab === 'ai' && (!permFace || reviewMode)) activeTab = 'all' this.setData({ permMbti, permSbti, permPdp, permDisc, permFace, reviewMode, activeTab, tabName: names[activeTab] || '' }) }, onLoad(options) { const gaokaoOnly = options && String(options.gaokaoOnly || '') === '1' if (gaokaoOnly) { this.setData({ gaokaoOnly: true, tabName: '高考' }) } this.setData({ isEnterprise: this._checkIsEnterprise() }) this._syncPermsAndTab() this.loadAll() }, onShow() { this.setData({ isEnterprise: this._checkIsEnterprise() }) this._syncPermsAndTab() this.loadAll() if (typeof this.getTabBar === 'function' && this.getTabBar()) { const tb = this.getTabBar() if (typeof tb.updateSelected === 'function') tb.updateSelected() } }, /** 首屏/切换 Tab:接口默认 pageSize=10,更多走 loadMore */ loadAll() { if (this.data.loading) return this.setData({ loading: true, list: [], page: 1, hasMore: false, loadingMore: false }) this._fetchHistoryPage(1, false) }, /** 触底加载下一页 */ loadMore() { if (!this.data.hasMore || this.data.loadingMore || this.data.loading) return const next = this.data.page + 1 this.setData({ loadingMore: true }) this._fetchHistoryPage(next, true) }, onReachBottom() { this.loadMore() }, _fetchHistoryPage(pageNum, append) { const token = app.globalData.token || wx.getStorageSync('token') const apiBase = app.globalData.apiBase const { activeTab, gaokaoOnly } = this.data const typeParam = gaokaoOnly ? '&type=gaokao' : (activeTab === 'all' ? '' : `&type=${activeTab}`) if (!token || !apiBase) { this.setData({ loading: false, loadingMore: false }) this.loadFromStorage() return } wx.request({ url: `${apiBase}/api/test/history?page=${pageNum}&pageSize=10${typeParam}&scope=all`, method: 'GET', header: { Authorization: `Bearer ${token}` }, success: (res) => { if (res.statusCode === 200 && res.data && res.data.data) { const payload = res.data.data const rawList = payload.list || [] const formatted = this.formatList(rawList) const total = typeof payload.total === 'number' ? payload.total : formatted.length const hasMore = !!payload.hasMore if (append) { this.setData({ list: this.data.list.concat(formatted), total, hasMore, page: pageNum, loadingMore: false, loading: false }) } else { this.setData({ list: formatted, total, hasMore, page: pageNum, loading: false, loadingMore: false }) } } else { this.setData({ loading: false, loadingMore: false }) if (!append) this.loadFromStorage() } }, fail: () => { this.setData({ loading: false, loadingMore: false }) if (!append) this.loadFromStorage() } }) }, formatList(rawList) { const typeNames = { mbti: 'MBTI性格测试', sbti: 'SBTI性格测试', disc: 'DISC性格测试', pdp: 'PDP行为偏好测试', ai: '面相分析', resume: '简历综合分析', gaokao: '高考志愿' } const emojis = { mbti: '🧠', sbti: '🎭', disc: '📊', pdp: '🦁', ai: '👁️', resume: '📋', gaokao: '🎓' } return rawList.map((item, idx) => { const testType = String(item.testType || item.type || 'mbti').toLowerCase() if (testType === 'gaokao') { const rawBadge = String(item.resultText || '').trim() const shortBadge = rawBadge === '已生成' || rawBadge === '志愿报告' || rawBadge === '查看报告' ? rawBadge : '已生成' return { ...item, type: 'gaokao', testType: 'gaokao', key: item.key || 'gaokao_' + (item.id || idx), emoji: item.emoji || '🎓', typeName: '高考志愿', resultText: shortBadge, enterpriseName: item.enterpriseName || '' } } if (item.typeName) { const t = String(item.testType || item.type || 'mbti').toLowerCase() return { ...item, type: t, enterpriseName: item.enterpriseName || '' } } const ts = item.createdAt || item.testTime || item.timestamp return { ...item, type: testType, key: testType + '_' + (item.id || idx), emoji: emojis[testType] || '📋', typeName: typeNames[testType] || '测试', testTime: ts ? this.formatTime(typeof ts === 'number' ? ts * 1000 : ts) : '', enterpriseName: item.enterpriseName || '' } }) }, // 本地缓存回退 loadFromStorage() { const { permMbti, permSbti, permPdp, permDisc, permFace, reviewMode } = this.data const mbtiResult = wx.getStorageSync('mbtiResult') const sbtiResult = wx.getStorageSync('sbtiResult') const discResult = wx.getStorageSync('discResult') const pdpResult = wx.getStorageSync('pdpResult') const aiResult = wx.getStorageSync('aiResult') const list = [] if (mbtiResult && permMbti) list.push({ type: 'mbti', key: 'mbti', emoji: '🧠', typeName: 'MBTI性格测试', resultText: mbtiResult.mbtiType || '未知', testTime: this.formatTime(mbtiResult.timestamp), data: mbtiResult }) if (sbtiResult && permSbti) { const rt = sbtiResult.sbtiType || (sbtiResult.finalType && sbtiResult.finalType.code) || '未知' const cn = sbtiResult.sbtiCn || (sbtiResult.finalType && sbtiResult.finalType.cn) || '' list.push({ type: 'sbti', key: 'sbti', emoji: '🎭', typeName: 'SBTI性格测试', resultText: cn ? `${rt}(${cn})` : rt, testTime: this.formatTime(sbtiResult.timestamp || sbtiResult.completedAt), data: sbtiResult }) } if (pdpResult && permPdp) list.push({ type: 'pdp', key: 'pdp', emoji: pdpResult.description?.emoji || '🦁', typeName: 'PDP行为偏好测试', resultText: pdpResult.description?.type || '未知', testTime: this.formatTime(pdpResult.timestamp || pdpResult.completedAt), data: pdpResult }) if (discResult && permDisc) list.push({ type: 'disc', key: 'disc', emoji: '📊', typeName: 'DISC性格测试', resultText: (discResult.dominantType || '未知') + '型', testTime: this.formatTime(discResult.timestamp || discResult.completedAt), data: discResult }) if (aiResult && permFace && !reviewMode) list.push({ type: 'ai', key: 'ai', emoji: '👁️', typeName: '面相分析', resultText: aiResult.mbti || '未知', testTime: this.formatTime(aiResult.timestamp || aiResult.completedAt), data: aiResult }) this.setData({ list, total: list.length, page: 1, hasMore: false, loading: false, loadingMore: false }) }, changeTab(e) { const tab = e.currentTarget.dataset.tab const names = { all: '', mbti: 'MBTI', sbti: 'SBTI', pdp: 'PDP', disc: 'DISC', ai: '面相', resume: '简历' } this.setData({ activeTab: tab, tabName: names[tab] || '' }) this.loadAll() }, formatTime(timestamp) { if (!timestamp) return '未知时间' const date = new Date(timestamp) const y = date.getFullYear() const m = String(date.getMonth() + 1).padStart(2, '0') const d = String(date.getDate()).padStart(2, '0') const h = String(date.getHours()).padStart(2, '0') const min = String(date.getMinutes()).padStart(2, '0') return `${y}-${m}-${d} ${h}:${min}` }, viewDetail(e) { const type = e.currentTarget.dataset.type const id = e.currentTarget.dataset.id const routes = { mbti: '/pages/result/mbti', sbti: '/pages/result/sbti', disc: '/pages/result/disc', pdp: '/pages/result/pdp', ai: '/pages/index/result', resume: '/pages/result/resume', gaokao: '/pages/gaokao/report' } const base = routes[type] if (!base) return if ((type === 'ai' || type === 'resume' || type === 'gaokao') && !id) return const query = id ? `?id=${id}&type=${type}` : '' wx.navigateTo({ url: query ? base + query : base }) }, goToTest() { if (this.data.gaokaoOnly) { wx.navigateTo({ url: '/pages/gaokao/task-center' }) return } const routes = { mbti: '/pages/test/mbti', sbti: '/pages/test/sbti', disc: '/pages/test/disc', pdp: '/pages/test/pdp', ai: '/pages/index/camera', resume: '/pages/enterprise/index' } const url = routes[this.data.activeTab] if (url) wx.navigateTo({ url }) else wx.switchTab({ url: '/pages/index/index' }) }, })