21
This commit is contained in:
parent
e1dc8c37ee
commit
e85cd41a2d
|
|
@ -0,0 +1,137 @@
|
||||||
|
/**
|
||||||
|
* AssessmentRecord API - 测评记录管理 API
|
||||||
|
* @module api/business/assessmentRecord
|
||||||
|
* @description 提供测评记录相关接口,包括记录列表、详情、报告和导出功能
|
||||||
|
*/
|
||||||
|
import { request, type ApiResponse } from '@/utils/request'
|
||||||
|
import type { PagedRequest, PagedResult } from '@/types/common'
|
||||||
|
|
||||||
|
// ==================== 类型定义 ====================
|
||||||
|
|
||||||
|
/** 测评记录列表项 */
|
||||||
|
export interface AssessmentRecordItem {
|
||||||
|
id: number
|
||||||
|
userId: number
|
||||||
|
userNickname: string | null
|
||||||
|
orderId: number
|
||||||
|
orderNo: string | null
|
||||||
|
assessmentTypeId: number
|
||||||
|
assessmentTypeName: string | null
|
||||||
|
name: string
|
||||||
|
phone: string
|
||||||
|
gender: number
|
||||||
|
genderName: string
|
||||||
|
age: number
|
||||||
|
educationStage: number
|
||||||
|
educationStageName: string
|
||||||
|
province: string
|
||||||
|
city: string
|
||||||
|
district: string
|
||||||
|
status: number
|
||||||
|
statusName: string
|
||||||
|
startTime: string | null
|
||||||
|
submitTime: string | null
|
||||||
|
completeTime: string | null
|
||||||
|
createTime: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 答案详情 */
|
||||||
|
export interface AnswerDetail {
|
||||||
|
id: number
|
||||||
|
questionId: number
|
||||||
|
questionNo: number
|
||||||
|
questionContent: string
|
||||||
|
answerValue: number
|
||||||
|
createTime: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 结果详情 */
|
||||||
|
export interface ResultDetail {
|
||||||
|
id: number
|
||||||
|
categoryId: number
|
||||||
|
categoryName: string
|
||||||
|
categoryTypeName: string
|
||||||
|
score: number
|
||||||
|
maxScore: number
|
||||||
|
percentage: number
|
||||||
|
rank: number
|
||||||
|
starLevel: number
|
||||||
|
createTime: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 测评记录详情(含答案和结果) */
|
||||||
|
export interface AssessmentRecordDetail extends AssessmentRecordItem {
|
||||||
|
answers: AnswerDetail[]
|
||||||
|
results: ResultDetail[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 报告分类项 */
|
||||||
|
export interface ReportCategoryItem {
|
||||||
|
categoryId: number
|
||||||
|
categoryName: string
|
||||||
|
score: number
|
||||||
|
maxScore: number
|
||||||
|
percentage: number
|
||||||
|
starLevel: number
|
||||||
|
conclusionContent: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 报告分类组 */
|
||||||
|
export interface ReportCategoryGroup {
|
||||||
|
categoryTypeId: number
|
||||||
|
categoryTypeName: string
|
||||||
|
items: ReportCategoryItem[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 测评报告 */
|
||||||
|
export interface AssessmentReport extends AssessmentRecordItem {
|
||||||
|
resultGroups: ReportCategoryGroup[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 测评记录查询参数 */
|
||||||
|
export interface AssessmentRecordQuery extends PagedRequest {
|
||||||
|
userId?: number
|
||||||
|
assessmentTypeId?: number
|
||||||
|
status?: number
|
||||||
|
startDate?: string
|
||||||
|
endDate?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== API ====================
|
||||||
|
|
||||||
|
/** 获取测评记录列表 */
|
||||||
|
export function getRecordList(params: AssessmentRecordQuery): Promise<ApiResponse<PagedResult<AssessmentRecordItem>>> {
|
||||||
|
return request<PagedResult<AssessmentRecordItem>>({
|
||||||
|
url: '/admin/assessmentRecord/getList',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取测评记录详情 */
|
||||||
|
export function getRecordDetail(id: number): Promise<ApiResponse<AssessmentRecordDetail>> {
|
||||||
|
return request<AssessmentRecordDetail>({
|
||||||
|
url: '/admin/assessmentRecord/getDetail',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取测评报告 */
|
||||||
|
export function getRecordReport(id: number): Promise<ApiResponse<AssessmentReport>> {
|
||||||
|
return request<AssessmentReport>({
|
||||||
|
url: '/admin/assessmentRecord/getReport',
|
||||||
|
method: 'get',
|
||||||
|
params: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导出测评记录 */
|
||||||
|
export function exportRecords(params: AssessmentRecordQuery): Promise<ApiResponse<Blob>> {
|
||||||
|
return request<Blob>({
|
||||||
|
url: '/admin/assessmentRecord/export',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -118,6 +118,12 @@ export const businessRoutes: RouteRecordRaw[] = [
|
||||||
name: 'ScoreOption',
|
name: 'ScoreOption',
|
||||||
component: () => import('@/views/business/assessment/scoreOption/index.vue'),
|
component: () => import('@/views/business/assessment/scoreOption/index.vue'),
|
||||||
meta: { title: '评分标准', permission: 'assessment:view', keepAlive: true }
|
meta: { title: '评分标准', permission: 'assessment:view', keepAlive: true }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'record',
|
||||||
|
name: 'AssessmentRecord',
|
||||||
|
component: () => import('@/views/business/assessment/record/index.vue'),
|
||||||
|
meta: { title: '测评记录', permission: 'assessmentRecord:view', keepAlive: true }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,638 @@
|
||||||
|
<template>
|
||||||
|
<div class="record-container">
|
||||||
|
<!-- 页面标题和操作栏 -->
|
||||||
|
<el-card class="page-header">
|
||||||
|
<div class="header-content">
|
||||||
|
<div class="header-left">
|
||||||
|
<h2 class="page-title">测评记录</h2>
|
||||||
|
<span class="page-description">查看用户测评记录、答案详情和测评报告</span>
|
||||||
|
</div>
|
||||||
|
<div class="header-right">
|
||||||
|
<el-button type="success" @click="handleExport" :loading="state.exportLoading">
|
||||||
|
<el-icon><Download /></el-icon>
|
||||||
|
导出Excel
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 搜索表单 -->
|
||||||
|
<el-card class="search-card">
|
||||||
|
<el-form :model="queryParams" inline>
|
||||||
|
<el-form-item label="用户ID">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.userId"
|
||||||
|
placeholder="请输入用户ID"
|
||||||
|
clearable
|
||||||
|
@keyup.enter="handleSearch"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态">
|
||||||
|
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
|
||||||
|
<el-option label="待测评" :value="1" />
|
||||||
|
<el-option label="测评中" :value="2" />
|
||||||
|
<el-option label="生成中" :value="3" />
|
||||||
|
<el-option label="已完成" :value="4" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="创建时间">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
:shortcuts="dateShortcuts"
|
||||||
|
@change="handleDateRangeChange"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="handleSearch">
|
||||||
|
<el-icon><Search /></el-icon>
|
||||||
|
搜索
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="handleReset">
|
||||||
|
<el-icon><Refresh /></el-icon>
|
||||||
|
重置
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 数据表格 -->
|
||||||
|
<el-card v-loading="state.loading" class="table-card">
|
||||||
|
<el-table :data="state.tableData" row-key="id" stripe>
|
||||||
|
<el-table-column prop="id" label="ID" width="80" />
|
||||||
|
<el-table-column label="用户" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div>{{ row.userNickname || '-' }}</div>
|
||||||
|
<div class="sub-text">ID: {{ row.userId }}</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="被测评人" min-width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div>{{ row.name }}</div>
|
||||||
|
<div class="sub-text">{{ row.phone }}</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="assessmentTypeName" label="测评类型" width="120" show-overflow-tooltip />
|
||||||
|
<el-table-column label="性别/年龄" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.genderName }} / {{ row.age }}岁
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="educationStageName" label="学历阶段" width="110" align="center" />
|
||||||
|
<el-table-column label="地区" min-width="130" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.province }}{{ row.city }}{{ row.district }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="状态" width="90" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="getStatusTagType(row.status)" size="small">
|
||||||
|
{{ row.statusName }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="orderNo" label="订单号" width="170" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="submitTime" label="提交时间" width="170" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.submitTime || '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="createTime" label="创建时间" width="170" align="center" />
|
||||||
|
<el-table-column label="操作" width="160" fixed="right" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button type="primary" link size="small" @click="handleViewDetail(row)">
|
||||||
|
<el-icon><View /></el-icon>
|
||||||
|
详情
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="row.status === 4"
|
||||||
|
type="success"
|
||||||
|
link
|
||||||
|
size="small"
|
||||||
|
@click="handleViewReport(row)"
|
||||||
|
>
|
||||||
|
<el-icon><Document /></el-icon>
|
||||||
|
报告
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<div class="pagination-wrapper">
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="queryParams.page"
|
||||||
|
v-model:page-size="queryParams.pageSize"
|
||||||
|
:page-sizes="[10, 20, 50, 100]"
|
||||||
|
:total="state.total"
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 详情抽屉 -->
|
||||||
|
<el-drawer
|
||||||
|
v-model="state.detailVisible"
|
||||||
|
title="测评记录详情"
|
||||||
|
size="650px"
|
||||||
|
:close-on-click-modal="true"
|
||||||
|
>
|
||||||
|
<div v-loading="state.detailLoading" class="record-detail">
|
||||||
|
<template v-if="state.detail">
|
||||||
|
<!-- 基本信息 -->
|
||||||
|
<div class="detail-section">
|
||||||
|
<h4 class="section-title">基本信息</h4>
|
||||||
|
<el-descriptions :column="2" border>
|
||||||
|
<el-descriptions-item label="被测评人">{{ state.detail.name }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="手机号">{{ state.detail.phone }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="性别">{{ state.detail.genderName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="年龄">{{ state.detail.age }}岁</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="学历阶段">{{ state.detail.educationStageName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="地区">{{ state.detail.province }}{{ state.detail.city }}{{ state.detail.district }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="测评类型">{{ state.detail.assessmentTypeName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="状态">
|
||||||
|
<el-tag :type="getStatusTagType(state.detail.status)" size="small">
|
||||||
|
{{ state.detail.statusName }}
|
||||||
|
</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="订单号" :span="2">{{ state.detail.orderNo || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="开始时间">{{ state.detail.startTime || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="提交时间">{{ state.detail.submitTime || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="完成时间">{{ state.detail.completeTime || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="创建时间">{{ state.detail.createTime }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 答案列表 -->
|
||||||
|
<div v-if="state.detail.answers && state.detail.answers.length > 0" class="detail-section">
|
||||||
|
<h4 class="section-title">答案列表(共{{ state.detail.answers.length }}题)</h4>
|
||||||
|
<el-table :data="state.detail.answers" stripe size="small" max-height="400">
|
||||||
|
<el-table-column prop="questionNo" label="题号" width="60" align="center" />
|
||||||
|
<el-table-column prop="questionContent" label="题目内容" min-width="250" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="answerValue" label="答案" width="70" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag size="small" type="primary">{{ row.answerValue }}</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 结果列表 -->
|
||||||
|
<div v-if="state.detail.results && state.detail.results.length > 0" class="detail-section">
|
||||||
|
<h4 class="section-title">测评结果</h4>
|
||||||
|
<el-table :data="state.detail.results" stripe size="small" max-height="400">
|
||||||
|
<el-table-column prop="categoryTypeName" label="分类类型" width="120" />
|
||||||
|
<el-table-column prop="categoryName" label="分类名称" min-width="120" />
|
||||||
|
<el-table-column label="得分" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.score }} / {{ row.maxScore }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="百分比" width="80" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.percentage }}%
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="星级" width="130" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-rate v-model="row.starLevel" disabled />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</el-drawer>
|
||||||
|
|
||||||
|
<!-- 报告抽屉 -->
|
||||||
|
<el-drawer
|
||||||
|
v-model="state.reportVisible"
|
||||||
|
title="测评报告"
|
||||||
|
size="700px"
|
||||||
|
:close-on-click-modal="true"
|
||||||
|
>
|
||||||
|
<div v-loading="state.reportLoading" class="record-detail">
|
||||||
|
<template v-if="state.report">
|
||||||
|
<!-- 被测评人信息 -->
|
||||||
|
<div class="detail-section">
|
||||||
|
<h4 class="section-title">被测评人信息</h4>
|
||||||
|
<el-descriptions :column="3" border>
|
||||||
|
<el-descriptions-item label="姓名">{{ state.report.name }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="性别">{{ state.report.genderName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="年龄">{{ state.report.age }}岁</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="学历阶段">{{ state.report.educationStageName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="地区" :span="2">{{ state.report.province }}{{ state.report.city }}{{ state.report.district }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 按分类分组展示结果 -->
|
||||||
|
<div
|
||||||
|
v-for="group in state.report.resultGroups"
|
||||||
|
:key="group.categoryTypeId"
|
||||||
|
class="detail-section"
|
||||||
|
>
|
||||||
|
<h4 class="section-title">{{ group.categoryTypeName }}</h4>
|
||||||
|
<el-table :data="group.items" stripe size="small">
|
||||||
|
<el-table-column prop="categoryName" label="分类" min-width="120" />
|
||||||
|
<el-table-column label="得分" width="100" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.score }} / {{ row.maxScore }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="百分比" width="80" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ row.percentage }}%
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="星级" width="130" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-rate v-model="row.starLevel" disabled />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="conclusionContent" label="结论" min-width="200" show-overflow-tooltip />
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</el-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
/**
|
||||||
|
* 测评记录管理页面
|
||||||
|
* @description 查看用户测评记录、答案详情和测评报告,支持搜索、导出
|
||||||
|
*/
|
||||||
|
import { reactive, ref, onMounted } from 'vue'
|
||||||
|
import { Search, Refresh, View, Download, Document } from '@element-plus/icons-vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import {
|
||||||
|
getRecordList,
|
||||||
|
getRecordDetail,
|
||||||
|
getRecordReport,
|
||||||
|
exportRecords,
|
||||||
|
type AssessmentRecordItem,
|
||||||
|
type AssessmentRecordDetail,
|
||||||
|
type AssessmentReport,
|
||||||
|
type AssessmentRecordQuery
|
||||||
|
} from '@/api/business/assessmentRecord'
|
||||||
|
|
||||||
|
// ============ Constants ============
|
||||||
|
|
||||||
|
/** 日期快捷选项 */
|
||||||
|
const dateShortcuts = [
|
||||||
|
{
|
||||||
|
text: '最近一周',
|
||||||
|
value: () => {
|
||||||
|
const end = new Date()
|
||||||
|
const start = new Date()
|
||||||
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
|
||||||
|
return [start, end]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '最近一个月',
|
||||||
|
value: () => {
|
||||||
|
const end = new Date()
|
||||||
|
const start = new Date()
|
||||||
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
|
||||||
|
return [start, end]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '最近三个月',
|
||||||
|
value: () => {
|
||||||
|
const end = new Date()
|
||||||
|
const start = new Date()
|
||||||
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
|
||||||
|
return [start, end]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// ============ Types ============
|
||||||
|
|
||||||
|
interface RecordPageState {
|
||||||
|
loading: boolean
|
||||||
|
tableData: AssessmentRecordItem[]
|
||||||
|
total: number
|
||||||
|
detailVisible: boolean
|
||||||
|
detailLoading: boolean
|
||||||
|
detail: AssessmentRecordDetail | null
|
||||||
|
reportVisible: boolean
|
||||||
|
reportLoading: boolean
|
||||||
|
report: AssessmentReport | null
|
||||||
|
exportLoading: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ Refs ============
|
||||||
|
|
||||||
|
const dateRange = ref<[string, string] | null>(null)
|
||||||
|
|
||||||
|
// ============ State ============
|
||||||
|
|
||||||
|
const queryParams = reactive({
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
userId: '',
|
||||||
|
status: undefined as number | undefined,
|
||||||
|
startDate: undefined as string | undefined,
|
||||||
|
endDate: undefined as string | undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
const state = reactive<RecordPageState>({
|
||||||
|
loading: false,
|
||||||
|
tableData: [],
|
||||||
|
total: 0,
|
||||||
|
detailVisible: false,
|
||||||
|
detailLoading: false,
|
||||||
|
detail: null,
|
||||||
|
reportVisible: false,
|
||||||
|
reportLoading: false,
|
||||||
|
report: null,
|
||||||
|
exportLoading: false
|
||||||
|
})
|
||||||
|
|
||||||
|
// ============ Helper Functions ============
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取状态标签类型
|
||||||
|
* 待测评=info, 测评中=primary, 生成中=warning, 已完成=success
|
||||||
|
*/
|
||||||
|
function getStatusTagType(status: number): 'info' | 'primary' | 'warning' | 'success' {
|
||||||
|
switch (status) {
|
||||||
|
case 1: return 'info'
|
||||||
|
case 2: return 'primary'
|
||||||
|
case 3: return 'warning'
|
||||||
|
case 4: return 'success'
|
||||||
|
default: return 'info'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ API Functions ============
|
||||||
|
|
||||||
|
/** 加载测评记录列表 */
|
||||||
|
async function loadRecordList() {
|
||||||
|
state.loading = true
|
||||||
|
try {
|
||||||
|
const params: AssessmentRecordQuery = {
|
||||||
|
page: queryParams.page,
|
||||||
|
pageSize: queryParams.pageSize
|
||||||
|
}
|
||||||
|
if (queryParams.userId) {
|
||||||
|
params.userId = Number(queryParams.userId)
|
||||||
|
}
|
||||||
|
if (queryParams.status !== undefined) {
|
||||||
|
params.status = queryParams.status
|
||||||
|
}
|
||||||
|
if (queryParams.startDate) {
|
||||||
|
params.startDate = queryParams.startDate
|
||||||
|
}
|
||||||
|
if (queryParams.endDate) {
|
||||||
|
params.endDate = queryParams.endDate
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await getRecordList(params)
|
||||||
|
if (res.code === 0) {
|
||||||
|
state.tableData = res.data?.list || []
|
||||||
|
state.total = res.data?.total || 0
|
||||||
|
} else {
|
||||||
|
throw new Error(res.message || '获取测评记录列表失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : '获取测评记录列表失败'
|
||||||
|
ElMessage.error(message)
|
||||||
|
} finally {
|
||||||
|
state.loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 加载测评记录详情 */
|
||||||
|
async function loadRecordDetail(id: number) {
|
||||||
|
state.detailLoading = true
|
||||||
|
try {
|
||||||
|
const res = await getRecordDetail(id)
|
||||||
|
if (res.code === 0) {
|
||||||
|
state.detail = res.data
|
||||||
|
} else {
|
||||||
|
throw new Error(res.message || '获取测评记录详情失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : '获取测评记录详情失败'
|
||||||
|
ElMessage.error(message)
|
||||||
|
} finally {
|
||||||
|
state.detailLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 加载测评报告 */
|
||||||
|
async function loadRecordReport(id: number) {
|
||||||
|
state.reportLoading = true
|
||||||
|
try {
|
||||||
|
const res = await getRecordReport(id)
|
||||||
|
if (res.code === 0) {
|
||||||
|
state.report = res.data
|
||||||
|
} else {
|
||||||
|
throw new Error(res.message || '获取测评报告失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : '获取测评报告失败'
|
||||||
|
ElMessage.error(message)
|
||||||
|
} finally {
|
||||||
|
state.reportLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ Event Handlers ============
|
||||||
|
|
||||||
|
function handleSearch() {
|
||||||
|
queryParams.page = 1
|
||||||
|
loadRecordList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleReset() {
|
||||||
|
queryParams.userId = ''
|
||||||
|
queryParams.status = undefined
|
||||||
|
queryParams.startDate = undefined
|
||||||
|
queryParams.endDate = undefined
|
||||||
|
dateRange.value = null
|
||||||
|
queryParams.page = 1
|
||||||
|
loadRecordList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDateRangeChange(val: [string, string] | null) {
|
||||||
|
if (val) {
|
||||||
|
queryParams.startDate = val[0]
|
||||||
|
queryParams.endDate = val[1]
|
||||||
|
} else {
|
||||||
|
queryParams.startDate = undefined
|
||||||
|
queryParams.endDate = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSizeChange(size: number) {
|
||||||
|
queryParams.pageSize = size
|
||||||
|
queryParams.page = 1
|
||||||
|
loadRecordList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCurrentChange(page: number) {
|
||||||
|
queryParams.page = page
|
||||||
|
loadRecordList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleViewDetail(row: AssessmentRecordItem) {
|
||||||
|
state.detailVisible = true
|
||||||
|
state.detail = null
|
||||||
|
loadRecordDetail(row.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleViewReport(row: AssessmentRecordItem) {
|
||||||
|
state.reportVisible = true
|
||||||
|
state.report = null
|
||||||
|
loadRecordReport(row.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleExport() {
|
||||||
|
state.exportLoading = true
|
||||||
|
try {
|
||||||
|
const params: AssessmentRecordQuery = {
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10000
|
||||||
|
}
|
||||||
|
if (queryParams.userId) {
|
||||||
|
params.userId = Number(queryParams.userId)
|
||||||
|
}
|
||||||
|
if (queryParams.status !== undefined) {
|
||||||
|
params.status = queryParams.status
|
||||||
|
}
|
||||||
|
if (queryParams.startDate) {
|
||||||
|
params.startDate = queryParams.startDate
|
||||||
|
}
|
||||||
|
if (queryParams.endDate) {
|
||||||
|
params.endDate = queryParams.endDate
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await exportRecords(params)
|
||||||
|
|
||||||
|
// 创建下载链接
|
||||||
|
const blob = res.data instanceof Blob ? res.data : new Blob([res.data as BlobPart])
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.download = `测评记录_${new Date().toISOString().slice(0, 10)}.xlsx`
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
|
||||||
|
ElMessage.success('导出成功')
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : '导出失败'
|
||||||
|
ElMessage.error(message)
|
||||||
|
} finally {
|
||||||
|
state.exportLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============ Lifecycle ============
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadRecordList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.record-container {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary, #303133);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-description {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text-secondary, #909399);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-card {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-card :deep(.el-card__body) {
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-card {
|
||||||
|
min-height: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-text {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--text-secondary, #909399);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 详情/报告抽屉样式 */
|
||||||
|
.record-detail {
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-section {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
margin: 0 0 16px 0;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary, #303133);
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 1px solid var(--border-lighter, #ebeef5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格样式 */
|
||||||
|
:deep(.el-table) {
|
||||||
|
--el-table-border-color: var(--border-lighter, #ebeef5);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table th.el-table__cell) {
|
||||||
|
background-color: var(--bg-light, #f5f7fa);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-descriptions) {
|
||||||
|
--el-descriptions-item-bordered-label-background: var(--bg-light, #f5f7fa);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue
Block a user