mi-assessment/uniapp/pages/assessment/history/index.vue
zpc f8a9aaf71f
Some checks failed
continuous-integration/drone/push Build is failing
feat(assessment): Add retest status for equal score detection
- Add new assessment status 7 ("需重测") for cases where all scores are equal
- Create AllScoresEqualException to handle scenarios where 8 intelligences or 40 ability dimensions have identical scores
- Implement CheckAllScoresEqual validation in ReportGenerationService to detect and prevent invalid report generation
- Add UpdateRecordStatusToRetestAsync method in ReportQueueConsumer to handle retest status updates
- Update admin UI status tag mapping to display retest status with warning indicator
- Add user-friendly message for retest status in AssessmentService
- Update status description mappings across services to include new retest status
- Prevent PDF generation when all scores are equal, prompting users to retake the assessment
2026-03-31 15:08:23 +08:00

389 lines
9.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
/**
* 往期测评页面
* 展示用户的历史测评记录
*/
import { ref, computed, onMounted } from 'vue'
import { onShow, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
import { useUserStore } from '@/store/user.js'
import { useAuth } from '@/composables/useAuth.js'
import { getHistoryList } from '@/api/assessment.js'
import Empty from '@/components/Empty/index.vue'
import Loading from '@/components/Loading/index.vue'
const userStore = useUserStore()
const { checkLogin } = useAuth()
// 测评状态常量(与后端一致)
const ASSESSMENT_STATUS = {
PENDING: 1, // 待测评
TESTING: 2, // 测评中
GENERATING: 3, // 生成中
COMPLETED: 4, // 已完成
FAILED: 5, // 生成失败
DATA_READY: 6, // 数据已就绪PDF生成中
NEED_RETEST: 7 // 需重测(全同分)
}
// 状态
const loading = ref(false)
const refreshing = ref(false)
const historyList = ref([])
const page = ref(1)
const pageSize = ref(10)
const total = ref(0)
const noMore = ref(false)
// 计算属性
const isEmpty = computed(() => !loading.value && historyList.value.length === 0)
/**
* 获取测评状态样式类
*/
function getStatusClass(status) {
const classMap = {
[ASSESSMENT_STATUS.PENDING]: 'status-pending',
[ASSESSMENT_STATUS.TESTING]: 'status-testing',
[ASSESSMENT_STATUS.GENERATING]: 'status-generating',
[ASSESSMENT_STATUS.COMPLETED]: 'status-completed',
[ASSESSMENT_STATUS.FAILED]: 'status-failed',
[ASSESSMENT_STATUS.DATA_READY]: 'status-generating',
[ASSESSMENT_STATUS.NEED_RETEST]: 'status-retest'
}
return classMap[status] || ''
}
/**
* 加载测评历史列表
*/
async function loadHistoryList(isRefresh = false) {
if (loading.value) return
if (isRefresh) {
page.value = 1
noMore.value = false
}
loading.value = true
try {
const res = await getHistoryList({
page: page.value,
pageSize: pageSize.value
})
if (res.code === 0 && res.data) {
const list = res.data.list || []
total.value = res.data.total || 0
if (isRefresh) {
historyList.value = list
} else {
historyList.value = [...historyList.value, ...list]
}
// 判断是否还有更多
noMore.value = historyList.value.length >= total.value
} else {
uni.showToast({
title: res.message || '获取测评记录失败',
icon: 'none'
})
}
} catch (error) {
console.error('获取测评历史失败:', error)
uni.showToast({
title: '网络错误,请重试',
icon: 'none'
})
} finally {
loading.value = false
refreshing.value = false
uni.stopPullDownRefresh()
}
}
/**
* 下拉刷新
*/
onPullDownRefresh(() => {
refreshing.value = true
loadHistoryList(true)
})
/**
* 上拉加载更多
*/
onReachBottom(() => {
if (!noMore.value && !loading.value) {
page.value++
loadHistoryList()
}
})
/**
* 查看测评结果
*/
function viewResult(record) {
// 已完成 - 查看报告
if (record.status === ASSESSMENT_STATUS.COMPLETED) {
uni.navigateTo({
url: `/pages/assessment/result/index?recordId=${record.id}`
})
return
}
// 数据已就绪PDF生成中- 可查看网页报告
if (record.status === ASSESSMENT_STATUS.DATA_READY) {
uni.navigateTo({
url: `/pages/assessment/result/index?recordId=${record.id}`
})
return
}
// 生成中 - 提示等待
if (record.status === ASSESSMENT_STATUS.GENERATING) {
uni.showToast({
title: '报告生成中,请稍后查看',
icon: 'none'
})
return
}
// 待测评/测评中 - 跳转到答题页继续
if (record.status === ASSESSMENT_STATUS.PENDING || record.status === ASSESSMENT_STATUS.TESTING) {
uni.navigateTo({
url: `/pages/assessment/info/index?typeId=${record.assessmentTypeId || 1}`
})
return
}
// 需重测 - 跳转到测评首页重新开始
if (record.status === ASSESSMENT_STATUS.NEED_RETEST) {
uni.navigateTo({
url: `/pages/assessment/info/index?typeId=${record.assessmentTypeId || 1}`
})
return
}
}
/**
* 页面显示时检查登录状态并加载数据
*/
onShow(() => {
userStore.restoreFromStorage()
if (checkLogin()) {
loadHistoryList(true)
}
})
/**
* 页面加载
*/
onMounted(() => {
userStore.restoreFromStorage()
})
</script>
<template>
<view class="history-page">
<!-- 页面加载中 -->
<Loading type="page" :loading="loading && historyList.length === 0" />
<!-- 测评记录列表 -->
<view class="history-list" v-if="!isEmpty">
<view
class="history-card"
v-for="record in historyList"
:key="record.id"
@click="viewResult(record)"
>
<!-- 卡片头部:测评名称 + 状态 -->
<view class="card-header">
<view class="assessment-name">{{ record.assessmentName || '多元智能测评' }}</view>
<view class="assessment-status" :class="getStatusClass(record.status)">
{{ record.statusText }}
</view>
</view>
<!-- 卡片内容:测评人 + 测评日期 -->
<view class="card-content">
<view class="info-row">
<text class="info-label">测评人</text>
<text class="info-value">{{ record.name || '--' }}</text>
</view>
<view class="info-row">
<text class="info-label">测评日期</text>
<text class="info-value">{{ record.testDate || '--' }}</text>
</view>
</view>
<!-- 卡片底部:已完成显示查看报告,需重测显示重新测试 -->
<view class="card-footer" v-if="record.status === ASSESSMENT_STATUS.COMPLETED || record.status === ASSESSMENT_STATUS.DATA_READY">
<view class="view-btn">
<text>查看报告</text>
<view class="arrow-icon"></view>
</view>
</view>
<view class="card-footer" v-else-if="record.status === ASSESSMENT_STATUS.NEED_RETEST">
<view class="view-btn retest-btn">
<text>重新测试</text>
<view class="arrow-icon"></view>
</view>
</view>
</view>
<!-- 加载更多 -->
<Loading
type="more"
:loading="loading && historyList.length > 0"
:noMore="noMore"
noMoreText="没有更多测评记录了"
/>
</view>
<!-- 空状态 -->
<Empty
v-if="isEmpty"
text="暂无测评记录"
:showButton="true"
buttonText="去测评"
buttonUrl="/pages/index/index"
/>
</view>
</template>
<style lang="scss" scoped>
@import '@/styles/variables.scss';
.history-page {
min-height: 100vh;
background-color: $bg-color;
padding: $spacing-lg;
padding-bottom: calc(#{$spacing-lg} + env(safe-area-inset-bottom));
}
.history-list {
.history-card {
background-color: $bg-white;
border-radius: $border-radius-lg;
margin-bottom: $spacing-lg;
overflow: hidden;
box-shadow: $shadow-sm;
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: $spacing-md $spacing-lg;
border-bottom: 1rpx solid $border-light;
.assessment-name {
font-size: $font-size-lg;
font-weight: $font-weight-medium;
color: $text-color;
}
.assessment-status {
font-size: $font-size-sm;
padding: 4rpx 16rpx;
border-radius: $border-radius-sm;
// 待测评 - 橙色
&.status-pending {
color: $warning-color;
background-color: rgba(250, 173, 20, 0.1);
}
// 测评中 - 蓝色
&.status-testing {
color: $primary-color;
background-color: rgba(74, 144, 226, 0.1);
}
// 生成中 - 蓝色
&.status-generating {
color: $primary-color;
background-color: rgba(74, 144, 226, 0.1);
}
// 已完成 - 绿色
&.status-completed {
color: $success-color;
background-color: rgba(82, 196, 26, 0.1);
}
// 生成失败 - 红色
&.status-failed {
color: $error-color;
background-color: rgba(255, 77, 79, 0.1);
}
// 需重测 - 橙色
&.status-retest {
color: $warning-color;
background-color: rgba(250, 173, 20, 0.1);
}
}
}
.card-content {
padding: $spacing-md $spacing-lg;
.info-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: $spacing-xs 0;
.info-label {
font-size: $font-size-md;
color: $text-secondary;
}
.info-value {
font-size: $font-size-md;
color: $text-color;
}
}
}
.card-footer {
display: flex;
justify-content: flex-end;
padding: $spacing-md $spacing-lg;
border-top: 1rpx solid $border-light;
.view-btn {
display: flex;
align-items: center;
color: $primary-color;
font-size: $font-size-md;
&.retest-btn {
color: $warning-color;
.arrow-icon {
border-right-color: $warning-color;
border-bottom-color: $warning-color;
}
}
.arrow-icon {
width: 12rpx;
height: 12rpx;
border-right: 3rpx solid $primary-color;
border-bottom: 3rpx solid $primary-color;
transform: rotate(-45deg);
margin-left: 8rpx;
}
&:active {
opacity: 0.7;
}
}
}
}
}
</style>