fix: 新增考试批次
This commit is contained in:
parent
58c0a36f3e
commit
e78dc1963c
@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { http } from '@/utils/request'
|
||||
import type { ScoreResult, ApiResponse } from '@/types'
|
||||
import type { ScoreResult, ApiResponse, ExamBatch, PaginatedData } from '@/types'
|
||||
|
||||
/**
|
||||
* 后端返回的原始数据结构接口
|
||||
@ -19,27 +19,6 @@ interface RawScoreData {
|
||||
extraDataJson: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 考试批次接口
|
||||
*/
|
||||
interface ExamBatch {
|
||||
id: string
|
||||
name: string
|
||||
status: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页数据接口
|
||||
*/
|
||||
interface PaginatedData<T> {
|
||||
current: number
|
||||
size: number
|
||||
totalPage: number
|
||||
totalRows: number
|
||||
rows: T[]
|
||||
rainbow: number[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取考试批次列表
|
||||
* @param status - 状态过滤,0表示启用
|
||||
|
||||
@ -96,3 +96,33 @@ export interface ErrorInfo {
|
||||
/** 错误消息 */
|
||||
message: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 考试批次接口
|
||||
*/
|
||||
export interface ExamBatch {
|
||||
/** 批次ID */
|
||||
id: string
|
||||
/** 批次名称 */
|
||||
name: string
|
||||
/** 状态:0-启用 */
|
||||
status: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页数据接口
|
||||
*/
|
||||
export interface PaginatedData<T> {
|
||||
/** 当前页 */
|
||||
current: number
|
||||
/** 每页大小 */
|
||||
size: number
|
||||
/** 总页数 */
|
||||
totalPage: number
|
||||
/** 总行数 */
|
||||
totalRows: number
|
||||
/** 数据列表 */
|
||||
rows: T[]
|
||||
/** 页码彩虹 */
|
||||
rainbow: number[]
|
||||
}
|
||||
|
||||
@ -3,16 +3,25 @@
|
||||
* 首页组件
|
||||
* 成绩查询入口页面,包含身份证输入和查询功能
|
||||
*/
|
||||
import { ref, computed } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { queryScoreByIdCard, getExamBatchList } from '@/api/score'
|
||||
import Toast from '@/components/common/Toast.vue'
|
||||
import PuzzleCaptcha from '@/components/common/PuzzleCaptcha.vue'
|
||||
import type { ExamBatch } from '@/types'
|
||||
|
||||
// 路由实例
|
||||
const router = useRouter()
|
||||
|
||||
// 响应式状态
|
||||
/** 考试批次列表 */
|
||||
const examBatchList = ref<ExamBatch[]>([])
|
||||
/** 选中的考试批次ID */
|
||||
const selectedBatchId = ref('')
|
||||
/** 是否正在加载批次列表 */
|
||||
const isBatchLoading = ref(false)
|
||||
/** 下拉框是否展开 */
|
||||
const isDropdownOpen = ref(false)
|
||||
/** 身份证号输入值 */
|
||||
const idCard = ref('')
|
||||
/** 学生姓名输入值 */
|
||||
@ -33,11 +42,75 @@ const toastType = ref<'info' | 'success' | 'warning' | 'error'>('error')
|
||||
/** 是否显示验证码 */
|
||||
const showCaptcha = ref(false)
|
||||
|
||||
/**
|
||||
* 获取选中的批次名称
|
||||
*/
|
||||
const selectedBatchName = computed(() => {
|
||||
const batch = examBatchList.value.find(b => b.id === selectedBatchId.value)
|
||||
return batch?.name || '请选择考试批次'
|
||||
})
|
||||
|
||||
/**
|
||||
* 页面加载时获取考试批次列表
|
||||
*/
|
||||
onMounted(async () => {
|
||||
await loadExamBatchList()
|
||||
})
|
||||
|
||||
/**
|
||||
* 加载考试批次列表
|
||||
*/
|
||||
const loadExamBatchList = async () => {
|
||||
isBatchLoading.value = true
|
||||
try {
|
||||
const response = await getExamBatchList()
|
||||
if (response.code === 200 && response.data?.rows) {
|
||||
examBatchList.value = response.data.rows
|
||||
// 默认选中第一个批次
|
||||
if (response.data.rows.length > 0) {
|
||||
selectedBatchId.value = response.data.rows[0].id
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load exam batch list:', error)
|
||||
showMessage('加载考试批次失败', 'error')
|
||||
} finally {
|
||||
isBatchLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择考试批次
|
||||
*/
|
||||
const selectBatch = (batch: ExamBatch) => {
|
||||
selectedBatchId.value = batch.id
|
||||
isDropdownOpen.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换下拉框展开状态
|
||||
*/
|
||||
const toggleDropdown = () => {
|
||||
if (!isBatchLoading.value && examBatchList.value.length > 0) {
|
||||
isDropdownOpen.value = !isDropdownOpen.value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击外部关闭下拉框
|
||||
*/
|
||||
const closeDropdown = () => {
|
||||
isDropdownOpen.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
* 按钮是否可点击
|
||||
*/
|
||||
const isButtonDisabled = computed(() => {
|
||||
return isLoading.value || idCard.value.trim().length === 0 || studentName.value.trim().length === 0
|
||||
return isLoading.value ||
|
||||
idCard.value.trim().length === 0 ||
|
||||
studentName.value.trim().length === 0 ||
|
||||
!selectedBatchId.value
|
||||
})
|
||||
|
||||
/**
|
||||
@ -97,6 +170,13 @@ const showMessage = (message: string, type: 'info' | 'success' | 'warning' | 'er
|
||||
* 处理查询点击
|
||||
*/
|
||||
const handleQueryClick = () => {
|
||||
// 检查考试批次
|
||||
if (!selectedBatchId.value) {
|
||||
errorMessage.value = '请选择考试批次'
|
||||
showMessage(errorMessage.value, 'warning')
|
||||
return
|
||||
}
|
||||
|
||||
// 简单非空检查
|
||||
if (!studentName.value.trim()) {
|
||||
errorMessage.value = '请输入学生姓名'
|
||||
@ -123,20 +203,8 @@ const handleCaptchaSuccess = async () => {
|
||||
errorMessage.value = ''
|
||||
|
||||
try {
|
||||
// 1. 先获取考试批次列表
|
||||
const batchResponse = await getExamBatchList()
|
||||
|
||||
if (batchResponse.code !== 200 || !batchResponse.data || !batchResponse.data.rows || batchResponse.data.rows.length === 0) {
|
||||
errorMessage.value = '暂无考试数据'
|
||||
showMessage(errorMessage.value, 'warning')
|
||||
return
|
||||
}
|
||||
|
||||
// 默认取第一个批次的ID
|
||||
const examBatchId = batchResponse.data.rows[0].id
|
||||
|
||||
// 2. 调用查询接口
|
||||
const response = await queryScoreByIdCard(idCard.value, studentName.value, examBatchId)
|
||||
// 使用选中的考试批次ID进行查询
|
||||
const response = await queryScoreByIdCard(idCard.value, studentName.value, selectedBatchId.value)
|
||||
|
||||
if (response.code === 200 && response.data) {
|
||||
// 查询成功,存储数据并跳转
|
||||
@ -202,6 +270,92 @@ const handleKeyPress = (event: KeyboardEvent) => {
|
||||
|
||||
<!-- 查询表单卡片 -->
|
||||
<div class="card">
|
||||
<!-- 考试批次选择 -->
|
||||
<div class="mb-5">
|
||||
<label class="block text-sm font-medium text-slate-700 mb-2">
|
||||
考试批次
|
||||
</label>
|
||||
<div class="relative" @click.stop>
|
||||
<!-- 下拉按钮 -->
|
||||
<button
|
||||
type="button"
|
||||
class="input-field w-full flex items-center justify-between cursor-pointer"
|
||||
:class="{ 'border-primary-400 ring-2 ring-primary-100': isDropdownOpen }"
|
||||
@click="toggleDropdown"
|
||||
>
|
||||
<span :class="selectedBatchId ? 'text-slate-800' : 'text-slate-400'">
|
||||
<template v-if="isBatchLoading">
|
||||
<span class="flex items-center gap-2">
|
||||
<svg class="w-4 h-4 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" />
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
|
||||
</svg>
|
||||
加载中...
|
||||
</span>
|
||||
</template>
|
||||
<template v-else-if="examBatchList.length === 0">
|
||||
暂无考试批次
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ selectedBatchName }}
|
||||
</template>
|
||||
</span>
|
||||
<!-- 下拉箭头 -->
|
||||
<svg
|
||||
class="w-5 h-5 text-slate-400 transition-transform duration-200"
|
||||
:class="{ 'rotate-180': isDropdownOpen }"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<!-- 下拉列表 -->
|
||||
<Transition name="dropdown">
|
||||
<div
|
||||
v-if="isDropdownOpen"
|
||||
class="absolute z-20 mt-1 w-full bg-white border border-slate-200 rounded-xl shadow-lg overflow-hidden"
|
||||
>
|
||||
<div class="max-h-60 overflow-y-auto scrollbar-thin">
|
||||
<button
|
||||
v-for="batch in examBatchList"
|
||||
:key="batch.id"
|
||||
type="button"
|
||||
class="w-full px-4 py-3 text-left text-sm transition-colors cursor-pointer
|
||||
hover:bg-primary-50 active:bg-primary-100"
|
||||
:class="batch.id === selectedBatchId
|
||||
? 'bg-primary-50 text-primary-600 font-medium'
|
||||
: 'text-slate-700'"
|
||||
@click="selectBatch(batch)"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<span class="line-clamp-2">{{ batch.name }}</span>
|
||||
<!-- 选中标记 -->
|
||||
<svg
|
||||
v-if="batch.id === selectedBatchId"
|
||||
class="w-5 h-5 text-primary-500 flex-shrink-0 ml-2"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<!-- 点击外部关闭遮罩 -->
|
||||
<div
|
||||
v-if="isDropdownOpen"
|
||||
class="fixed inset-0 z-10"
|
||||
@click="closeDropdown"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-5">
|
||||
<label class="block text-sm font-medium text-slate-700 mb-2">
|
||||
学生姓名
|
||||
@ -332,3 +486,48 @@ const handleKeyPress = (event: KeyboardEvent) => {
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 下拉动画 */
|
||||
.dropdown-enter-active,
|
||||
.dropdown-leave-active {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.dropdown-enter-from,
|
||||
.dropdown-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
|
||||
/* 多行文本截断 */
|
||||
.line-clamp-2 {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 自定义滚动条样式 */
|
||||
.scrollbar-thin {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #cbd5e1 transparent;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb {
|
||||
background-color: #cbd5e1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.scrollbar-thin::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #94a3b8;
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user