Files
school-developer/src/views/basic/graduverify/index.vue
2026-03-06 11:49:01 +08:00

495 lines
17 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.

<template>
<div class="modern-page-container">
<div class="page-wrapper">
<!-- 搜索表单卡片 -->
<el-card v-show="showSearch" class="search-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><Search /></el-icon>
筛选条件
</span>
</div>
</template>
<el-form :model="searchForm" ref="searchFormRef" :inline="true" @keyup.enter="handleSearch" class="search-form">
<el-form-item label="批次号" prop="batchNo">
<el-input
v-model="searchForm.batchNo"
placeholder="请输入批次号"
clearable
style="width: 180px" />
</el-form-item>
<el-form-item label="学号" prop="stuNo">
<el-input
v-model="searchForm.stuNo"
placeholder="请输入学号"
clearable
style="width: 150px" />
</el-form-item>
<el-form-item label="姓名" prop="systemRealName">
<el-input
v-model="searchForm.systemRealName"
placeholder="请输入姓名"
clearable
style="width: 120px" />
</el-form-item>
<el-form-item label="入学年份" prop="enterYear">
<el-date-picker
v-model="searchForm.enterYear"
type="year"
placeholder="选择年份"
value-format="YYYY"
style="width: 120px" />
</el-form-item>
<el-form-item label="是否一致" prop="isMatch">
<el-select
v-model="searchForm.isMatch"
placeholder="请选择"
clearable
style="width: 120px">
<el-option label="一致" value="1" />
<el-option label="不一致" value="0" />
</el-select>
</el-form-item>
<el-form-item label="核对状态" prop="verifyStatus">
<el-select
v-model="searchForm.verifyStatus"
placeholder="请选择核对状态"
clearable
style="width: 120px">
<el-option label="待核对" value="0" />
<el-option label="已核对" value="1" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleSearch">查询</el-button>
<el-button icon="Refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 内容卡片 -->
<el-card class="content-card" shadow="never">
<template #header>
<div class="card-header">
<span class="card-title">
<el-icon class="title-icon"><User /></el-icon>
毕业学生信息核对
</span>
<div class="header-actions">
<el-upload
:show-file-list="false"
:before-upload="beforeUpload"
:http-request="handleImport"
accept=".xlsx,.xls"
class="upload-btn">
<el-button type="primary" icon="Upload" :loading="importLoading">导入核对</el-button>
</el-upload>
<el-button type="success" icon="Download" @click="handleDownloadTemplate">下载模板</el-button>
<el-button type="warning" icon="Download" @click="handleExport">导出数据</el-button>
<el-button type="info" icon="Bell" @click="handleSend" :disabled="!searchForm.batchNo">下发班主任</el-button>
<right-toolbar
v-model:showSearch="showSearch"
class="ml10"
@queryTable="getDataList" />
</div>
</div>
</template>
<!-- 统计卡片 -->
<el-row :gutter="16" class="stat-row">
<el-col :span="4">
<el-statistic title="总核对数" :value="statistics.total" />
</el-col>
<el-col :span="4">
<el-statistic title="信息一致" :value="statistics.matched">
<template #suffix>
<span class="text-success"></span>
</template>
</el-statistic>
</el-col>
<el-col :span="4">
<el-statistic title="信息不一致" :value="statistics.mismatched">
<template #suffix>
<span class="text-danger"></span>
</template>
</el-statistic>
</el-col>
<el-col :span="4">
<el-statistic title="待核对" :value="statistics.pending" />
</el-col>
<el-col :span="4">
<el-statistic title="已核对" :value="statistics.verified" />
</el-col>
</el-row>
<!-- 表格 -->
<el-table
:data="dataList"
v-loading="loading"
stripe
:cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle"
class="modern-table">
<el-table-column type="index" label="序号" width="70" align="center">
<template #default="{ $index }">
{{ (page.currentPage - 1) * page.pageSize + $index + 1 }}
</template>
</el-table-column>
<el-table-column prop="batchNo" label="批次号" width="160" align="center" />
<el-table-column prop="stuNo" label="学号" width="120" align="center" />
<el-table-column label="姓名" width="180" align="center">
<template #default="{ row }">
<div>
<span v-if="row.systemRealName">系统{{ row.systemRealName }}</span>
<span v-if="row.systemRealName && row.enrollRealName && row.systemRealName !== row.enrollRealName"
class="text-danger">
<br />回流{{ row.enrollRealName }}
</span>
<span v-else-if="row.enrollRealName && !row.systemRealName">回流{{ row.enrollRealName }}</span>
</div>
</template>
</el-table-column>
<el-table-column label="身份证号" width="220" align="center">
<template #default="{ row }">
<div v-if="row.systemIdCard || row.enrollIdCard">
<div v-if="row.systemIdCard">系统{{ row.systemIdCard }}</div>
<div v-if="row.enrollIdCard && row.enrollIdCard !== row.systemIdCard" class="text-warning">
回流{{ row.enrollIdCard }}
</div>
</div>
</template>
</el-table-column>
<el-table-column prop="enrollNo" label="学籍号" width="180" align="center" />
<el-table-column prop="className" label="班级" width="150" />
<el-table-column prop="classMasterName" label="班主任" width="100" align="center" />
<el-table-column prop="enterYear" label="入学年份" width="100" align="center" />
<el-table-column prop="isMatch" label="是否一致" width="100" align="center">
<template #default="{ row }">
<el-tag :type="row.isMatch === '1' ? 'success' : 'danger'" size="small">
{{ row.isMatch === '1' ? '一致' : '不一致' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="verifyStatus" label="核对状态" width="100" align="center">
<template #default="{ row }">
<el-tag :type="row.verifyStatus === '1' ? 'success' : 'warning'" size="small">
{{ row.verifyStatus === '1' ? '已核对' : '待核对' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="verifyResult" label="核对结果" width="150" show-overflow-tooltip />
<el-table-column prop="remarks" label="备注" width="150" show-overflow-tooltip />
<el-table-column label="操作" width="120" align="center" fixed="right">
<template #default="{ row }">
<el-button
v-if="row.verifyStatus === '0'"
icon="Edit"
link
type="primary"
@click="handleVerify(row)">
核对
</el-button>
</template>
</el-table-column>
<template #empty>
<el-empty description="暂无数据" :image-size="120" />
</template>
</el-table>
<!-- 分页 -->
<el-pagination
v-model:current-page="page.currentPage"
v-model:page-size="page.pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="page.total"
layout="total, sizes, prev, pager, next, jumper"
class="pagination"
@size-change="handleSizeChange"
@current-change="handleCurrentChange" />
</el-card>
</div>
<!-- 核对弹窗 -->
<el-dialog
v-model="verifyDialogVisible"
title="毕业学生信息核对"
width="600px"
:close-on-click-modal="false">
<el-descriptions :column="2" border>
<el-descriptions-item label="学号">{{ verifyData.stuNo }}</el-descriptions-item>
<el-descriptions-item label="入学年份">{{ verifyData.enterYear }}</el-descriptions-item>
<el-descriptions-item label="系统姓名">{{ verifyData.systemRealName }}</el-descriptions-item>
<el-descriptions-item label="回流姓名">{{ verifyData.enrollRealName }}</el-descriptions-item>
<el-descriptions-item label="系统身份证">{{ verifyData.systemIdCard }}</el-descriptions-item>
<el-descriptions-item label="回流身份证">{{ verifyData.enrollIdCard }}</el-descriptions-item>
<el-descriptions-item label="学籍号">{{ verifyData.enrollNo }}</el-descriptions-item>
<el-descriptions-item label="班级">{{ verifyData.className }}</el-descriptions-item>
<el-descriptions-item label="信息是否一致" :span="2">
<el-tag :type="verifyData.isMatch === '1' ? 'success' : 'danger'">
{{ verifyData.isMatch === '1' ? '一致' : '不一致' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item v-if="verifyData.remarks" label="备注" :span="2">{{ verifyData.remarks }}</el-descriptions-item>
</el-descriptions>
<el-form :model="verifyForm" label-width="80px" class="verify-form">
<el-form-item label="核对结果">
<el-radio-group v-model="verifyForm.verifyResult">
<el-radio label="信息无误">信息无误</el-radio>
<el-radio label="需修改系统">需修改系统</el-radio>
<el-radio label="需修改回流">需修改回流</el-radio>
<el-radio label="其他问题">其他问题</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="verifyDialogVisible = false">取消</el-button>
<el-button type="primary" @click="submitVerify" :loading="verifyLoading">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts" name="GraduVerify">
import { reactive, ref, onMounted } from 'vue'
import { fetchList, importData, exportData, sendToClassMaster, submitVerify as submitVerifyApi, downloadTemplate } from '/@/api/basic/graduverify'
import { useMessage, useMessageBox } from '/@/hooks/message'
import { Search, User, Upload, Download, Bell, Edit } from '@element-plus/icons-vue'
// 定义变量内容
const searchFormRef = ref()
const showSearch = ref(true)
const loading = ref(false)
const importLoading = ref(false)
const verifyLoading = ref(false)
const dataList = ref<any[]>([])
const verifyDialogVisible = ref(false)
const verifyData = ref<any>({})
const verifyForm = reactive({
id: '',
verifyResult: '信息无误'
})
// 统计数据
const statistics = reactive({
total: 0,
matched: 0,
mismatched: 0,
pending: 0,
verified: 0
})
// 分页
const page = reactive({
currentPage: 1,
pageSize: 20,
total: 0
})
// 搜索表单
const searchForm = reactive({
batchNo: '',
stuNo: '',
systemRealName: '',
enterYear: undefined as number | undefined,
isMatch: '',
verifyStatus: ''
})
// 表格样式
const tableStyle = {
cellStyle: { textAlign: 'center' },
headerCellStyle: {
textAlign: 'center',
background: 'var(--el-table-row-hover-bg-color)',
color: 'var(--el-text-color-primary)'
}
}
// 查询
const handleSearch = () => {
page.currentPage = 1
getDataList()
}
// 重置
const handleReset = () => {
searchFormRef.value?.resetFields()
getDataList()
}
// 分页
const handleSizeChange = (val: number) => {
page.pageSize = val
getDataList()
}
const handleCurrentChange = (val: number) => {
page.currentPage = val
getDataList()
}
// 获取数据列表
const getDataList = async () => {
loading.value = true
try {
const res = await fetchList({
current: page.currentPage,
size: page.pageSize,
...searchForm,
enterYear: searchForm.enterYear ? parseInt(searchForm.enterYear as any) : undefined
})
if (res.data && res.data.records) {
dataList.value = res.data.records
page.total = res.data.total
// 计算统计数据
statistics.total = res.data.total
statistics.matched = dataList.value.filter((item: any) => item.isMatch === '1').length
statistics.mismatched = dataList.value.filter((item: any) => item.isMatch === '0').length
statistics.pending = dataList.value.filter((item: any) => item.verifyStatus === '0').length
statistics.verified = dataList.value.filter((item: any) => item.verifyStatus === '1').length
} else {
dataList.value = []
page.total = 0
}
} catch (err: any) {
useMessage().error(err.msg || '获取数据失败')
} finally {
loading.value = false
}
}
// 上传前验证
const beforeUpload = (file: File) => {
const isExcel = file.name.endsWith('.xlsx') || file.name.endsWith('.xls')
if (!isExcel) {
useMessage().error('只能上传Excel文件')
return false
}
return true
}
// 导入核对
const handleImport = async (options: any) => {
importLoading.value = true
try {
const res = await importData(options.file)
useMessage().success(res.msg || '导入核对成功')
if (res.data) {
searchForm.batchNo = ''
}
getDataList()
} catch (err: any) {
useMessage().error(err.msg || '导入失败')
} finally {
importLoading.value = false
}
}
// 下载模板
const handleDownloadTemplate = async () => {
try {
const res = await downloadTemplate()
const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = '毕业学籍回流导入模板.xlsx'
link.click()
window.URL.revokeObjectURL(url)
} catch (err: any) {
useMessage().error(err.msg || '下载失败')
}
}
// 导出
const handleExport = async () => {
try {
const res = await exportData(searchForm)
const blob = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = '毕业学生核对数据.xlsx'
link.click()
window.URL.revokeObjectURL(url)
} catch (err: any) {
useMessage().error(err.msg || '导出失败')
}
}
// 下发班主任
const handleSend = async () => {
if (!searchForm.batchNo) {
useMessage().warning('请先选择批次号')
return
}
try {
await useMessageBox().confirm('确定要将该批次数据下发至班主任核对吗?')
await sendToClassMaster(searchForm.batchNo)
useMessage().success('下发成功')
} catch (err: any) {
if (err !== 'cancel') {
useMessage().error(err.msg || '下发失败')
}
}
}
// 核对
const handleVerify = (row: any) => {
verifyData.value = row
verifyForm.id = row.id
verifyForm.verifyResult = '信息无误'
verifyDialogVisible.value = true
}
// 提交核对
const submitVerify = async () => {
verifyLoading.value = true
try {
await submitVerifyApi(verifyForm.id, verifyForm.verifyResult)
useMessage().success('核对成功')
verifyDialogVisible.value = false
getDataList()
} catch (err: any) {
useMessage().error(err.msg || '核对失败')
} finally {
verifyLoading.value = false
}
}
// 初始化
onMounted(() => {
getDataList()
})
</script>
<style scoped lang="scss">
@import '/@/assets/styles/modern-page.scss';
.stat-row {
margin-bottom: 16px;
}
.upload-btn {
display: inline-block;
margin-right: 10px;
}
.text-danger {
color: var(--el-color-danger);
}
.text-warning {
color: var(--el-color-warning);
}
.text-success {
color: var(--el-color-success);
}
.verify-form {
margin-top: 16px;
}
</style>