Files
school-developer/src/views/basic/graduverify/index.vue
吴红兵 94c3473958 fix
2026-03-07 01:34:48 +08:00

451 lines
15 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>