fix: 新增考试批次

This commit is contained in:
阿梦 2026-02-01 21:13:23 +08:00
parent 58c0a36f3e
commit e78dc1963c
3 changed files with 246 additions and 38 deletions

View File

@ -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

View File

@ -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[]
}

View File

@ -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>