mi-assessment/uniapp/pages/assessment/history/index.vue
18631081161 34030cf2ea
All checks were successful
continuous-integration/drone/push Build is passing
界面优化
2026-04-07 19:58:30 +08:00

422 lines
11 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, retestRecord } 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()
}
})
/**
* 查看测评结果
*/
async 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) {
await handleRetestFromHistory(record)
return
}
// 生成失败 - 调用重测接口后跳转答题页(免费重测)
if (record.status === ASSESSMENT_STATUS.FAILED) {
await handleRetestFromHistory(record)
return
}
}
/**
* 从往期测评列表发起重测(免费)
*/
async function handleRetestFromHistory(record) {
try {
uni.showLoading({ title: '重置中...' })
const res = await retestRecord(record.id)
uni.hideLoading()
if (res && res.code === 0) {
uni.navigateTo({
url: `/pages/assessment/questions/index?typeId=${record.assessmentTypeId || 1}&recordId=${record.id}`
})
} else {
uni.showToast({ title: res?.message || '重置失败,请重试', icon: 'none' })
}
} catch (error) {
uni.hideLoading()
console.error('重测重置失败:', error)
uni.showToast({ title: '重置失败,请重试', icon: 'none' })
}
}
/**
* 页面显示时检查登录状态并加载数据
*/
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 class="card-footer" v-else-if="record.status === ASSESSMENT_STATUS.FAILED">
<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>