| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 |
- <template>
- <!-- 成绩查询 - 成绩单 -->
- <ReportModule
- ref="reportModuleRef"
- :showTitle="false"
- :showDescribe="false"
- :showPrintBtn="false"
- tableOrChart="table"
- :currentPage="state.pageInfo.pageNum"
- :pageSize="state.pageInfo.pageSize"
- :pageSizes="[50, 100]"
- :total="state.pageInfo.total"
- @ChangePageSize="ChangePageSize"
- @ChangeCurrentPage="ChangeCurrentPage"
- @ExportExcel="ExportExcel"
- >
- <template #title_left>
- <el-input
- v-model="state.keyWord"
- clearable
- style="width: 200px"
- placeholder="请输入学号或姓名"
- class="input_with"
- @input="HandleSearch"
- >
- <template #append>
- <el-button :icon="Search" @click="HandleSearch" />
- </template>
- </el-input>
- <span class="count_item"
- >应考:{{ state.tableCount.examStudentCount }}人</span
- >
- <span class="count_item">实考:{{ state.tableCount.normalCount }}人</span>
- <span class="count_item orange"
- >缺考:{{ state.tableCount.missExamCount }}人</span
- >
- </template>
- <template #module_table_chart>
- <el-table
- :data="state.tableData"
- border
- stripe
- ref="tableRef"
- :height="state.tableHeight"
- v-loading="state.tableLoading"
- :element-loading-text="state.loadingText"
- :element-loading-spinner="loadingSvgIcon"
- element-loading-background="#ffffff"
- >
- <el-table-column
- type="index"
- :index="GetIndexNumber"
- align="center"
- width="70"
- label="序号"
- fixed="left"
- ></el-table-column>
- <el-table-column
- v-for="item in state.staticHeaderData"
- :key="item.prop"
- :prop="item.prop"
- :label="item.label"
- min-width="80"
- fixed="left"
- >
- <template #default="scope">
- <span
- v-if="
- scope.row.score != '缺考' &&
- scope.row.score != '违纪' &&
- item.prop === 'studentUserName'
- "
- @click="OpenStudentPaper(scope.row)"
- style="cursor: pointer; color: #2e64fa"
- >
- {{ scope.row[item.prop] }}
- </span>
- <template v-else>
- {{ scope.row?.[item.prop] || "-" }}
- </template>
- </template>
- </el-table-column>
- <el-table-column
- v-for="(item, index) in state.answerDataTitle"
- :key="item.prop"
- :prop="item.prop"
- :label="item.label"
- min-width="100"
- >
- <template #default="scope">{{
- scope.row?.dynamicsData?.answerData?.[index] || "-"
- }}</template>
- </el-table-column>
- </el-table>
- </template>
- </ReportModule>
- <StudentPaper
- :modelValue="state.showStudentPaperDialog"
- :paperInfo="state.paperInfo"
- :pageTitle="state.paperTitle"
- @updateModelValue="UpdateModelValue"
- ></StudentPaper>
- </template>
- <script lang="ts" setup>
- import ReportModule from "@/components/ReportModule.vue";
- import StudentPaper from "@/components/StudentPaper.vue"; //学生答题卡组件
- import {
- studentTranscriptTitle,
- queryJointStudentStatistics,
- studentTranscript,
- studentTranscriptExcel,
- } from "@/api/analysis";
- import { useAnalysisStore } from "@/store/analysis";
- import { Search } from "@element-plus/icons-vue";
- import { downloadExcel } from "@/utils/exportExcel";
- import { loadingSvgIcon } from "@/utils/common";
- import {
- onMounted,
- onUnmounted,
- reactive,
- ref,
- computed,
- nextTick,
- watch,
- } from "vue";
- import { throttle } from "lodash";
- interface TableColumn {
- prop: string;
- label: string;
- display?: boolean;
- [key: string]: any;
- }
- interface TableCount {
- examStudentCount: number | string;
- normalCount: number | string;
- missExamCount: number | string;
- [key: string]: any; // 防止后端返回额外字段导致类型校验报错
- }
- interface PageInfo {
- pageSize: number;
- pageNum: number;
- total: number;
- }
- interface PaperInfo {
- examPaperId: string | number;
- platformNumber: string;
- questionId: string;
- }
- interface State {
- keyWord: string;
- tableHeight: Number;
- tableData: any[];
- staticHeaderData: TableColumn[];
- answerDataTitle: TableColumn[];
- tableCount: TableCount;
- pageInfo: PageInfo;
- tableLoading: boolean; // 修复:使用小写 boolean
- loadingText: string; // 修复:使用小写 string
- paperInfo: PaperInfo;
- paperTitle: string;
- showStudentPaperDialog: boolean;
- }
- const analysisStore = useAnalysisStore();
- const getExamName = computed(() => {
- return analysisStore.analysisExamInfo.examName || "";
- });
- const tableRef = ref<any>(null);
- const reportModuleRef = ref<any>(null);
- const state = reactive<State>({
- keyWord: "",
- tableHeight: 1000, //表格高度
- tableData: [],
- staticHeaderData: [],
- answerDataTitle: [],
- tableCount: {
- examStudentCount: 0,
- normalCount: 0,
- missExamCount: 0,
- },
- pageInfo: {
- pageSize: 50,
- pageNum: 1,
- total: 0,
- },
- tableLoading: true,
- loadingText: "加载中,请稍后...",
- paperInfo: {
- examPaperId: "", //考试科目id
- platformNumber: "", //学籍号平台号
- questionId: "", //题目id
- }, //学生试卷信息
- paperTitle: "", //学生试卷标题
- showStudentPaperDialog: false, //是否显示学生答题卡弹窗
- });
- /** 获取表头 */
- const GetStudentTranscriptTitle = async () => {
- if (!analysisStore.filterObject) return;
- try {
- const res = await studentTranscriptTitle({
- ...analysisStore.filterObject,
- topicControl: 0,
- });
- if (res.code === 200) {
- const staticHeaderData = res.data?.title?.staticHeaderData || [];
- state.staticHeaderData = staticHeaderData.filter(
- (item: TableColumn) => item.display, // 优化:使用已定义的接口类型
- );
- state.answerDataTitle =
- res.data?.title?.dynamicsHeaderData?.answerDataTitle || [];
- }
- } catch (err) {
- console.error("获取表头失败:", err);
- }
- };
- /** 获取顶部统计 */
- const GetTableCount = async () => {
- if (!analysisStore.filterObject) return;
- try {
- const res = await queryJointStudentStatistics({
- ...analysisStore.filterObject,
- topicControl: 0,
- queryStr: state.keyWord,
- });
- if (res.code === 200) {
- state.tableCount = res.data || {};
- }
- } catch (err) {
- console.error("获取统计数据失败:", err);
- }
- };
- /** 获取表格数据 */
- const GetTableData = async (isInit: boolean = false) => {
- if (!analysisStore.filterObject) return;
- try {
- state.tableLoading = true;
- const res = await studentTranscript({
- ...analysisStore.filterObject,
- topicControl: 0,
- queryStr: state.keyWord,
- pageParam: {
- pageNum: state.pageInfo.pageNum,
- pageSize: state.pageInfo.pageSize,
- },
- });
- if (res.code === 200) {
- state.tableData = res.data?.listData || [];
- state.pageInfo.total = Number(res.data?.total || 0);
- }
- } catch (err) {
- console.error("获取表格数据失败:", err);
- } finally {
- state.tableLoading = false;
- // 数据加载完成后重置滚动
- nextTick(() => {
- TableScrollTop();
- if (isInit) {
- SetTableHeight();
- }
- });
- }
- };
- //获取序号
- const GetIndexNumber = (index: number) => {
- let indexCount =
- (state.pageInfo.pageNum - 1) * state.pageInfo.pageSize + index + 1;
- return indexCount;
- };
- // 当前页码事件
- const ChangeCurrentPage = (val: number) => {
- state.pageInfo.pageNum = val;
- GetTableData();
- };
- // 每页显示个数事件
- const ChangePageSize = (val: number) => {
- state.pageInfo.pageSize = val;
- state.pageInfo.pageNum = 1;
- GetTableData();
- };
- // 导出Excel
- const ExportExcel = () => {
- // 1. 设置加载状态
- reportModuleRef.value?.SetExportLoading?.(true);
- // 2. 参数
- const params = {
- ...analysisStore.filterObject,
- topicControl: 0,
- topicControlName: "选项明细",
- queryStr: state.keyWord,
- pageParam: {
- pageNum: state.pageInfo.pageNum,
- pageSize: state.pageInfo.pageSize,
- },
- staticHeaderData: state.staticHeaderData,
- dynamicsHeaderDataMap: {
- answerDataTitle: state.answerDataTitle,
- },
- };
- // 3. 调用通用下载方法,并在完成后重置加载状态
- downloadExcel(studentTranscriptExcel, params).finally(() => {
- reportModuleRef.value?.SetExportLoading?.(false);
- });
- };
- //打开答题卡弹窗
- const OpenStudentPaper = (row: any) => {
- // 修复:移除多余的 prop 参数,添加 row 类型
- state.paperInfo = {
- examPaperId: analysisStore.filterObject?.subjectId || "", // 修复:增加可选链和默认值
- platformNumber: row.studentRegistrationCode, //学籍号平台号
- questionId: "", //题目id
- };
- state.paperTitle = `${getExamName.value}-${analysisStore.filterObject?.subjectName || ""}-${row.className}-${row.studentUserName}`; //学生姓名
- state.showStudentPaperDialog = true;
- };
- //更新弹窗状态
- const UpdateModelValue = (val: boolean) => {
- state.showStudentPaperDialog = val;
- };
- // 搜索事件
- const HandleSearch = () => {
- state.pageInfo.pageNum = 1;
- GetTableData();
- };
- // 表格内容滚动置顶
- const TableScrollTop = () => {
- const el = tableRef.value?.$el;
- if (!el) return;
- // 在下一帧执行,确保 DOM 已更新
- requestAnimationFrame(() => {
- // 尝试多种选择器,兼容不同版本
- const scrollWrap =
- el.querySelector(".el-scrollbar__wrap") ||
- el.querySelector(".el-table__body-wrapper") ||
- el.querySelector(".el-table__fixed-body-wrapper");
- if (scrollWrap) {
- scrollWrap.scrollTop = 0;
- scrollWrap.scrollLeft = 0;
- } else {
- // 若仍未找到,延迟再试一次(表格可能尚未完全渲染)
- setTimeout(() => {
- const retry =
- el.querySelector(".el-scrollbar__wrap") ||
- el.querySelector(".el-table__body-wrapper");
- if (retry) {
- retry.scrollTop = 0;
- retry.scrollLeft = 0;
- }
- }, 200);
- }
- });
- };
- // 设置表格高度
- const SetTableHeight = () => {
- const container = document.getElementsByClassName("report_content")?.[0];
- if (container) {
- state.tableHeight = container.clientHeight - 152;
- }
- };
- // 窗口大小改变事件
- const HandleResize = throttle(() => {
- SetTableHeight();
- }, 200);
- // 初始化
- const PageInit = () => {
- state.pageInfo.pageNum = 1;
- GetStudentTranscriptTitle();
- GetTableCount();
- GetTableData(true);
- };
- // 监听筛选条件
- watch(
- () => analysisStore.filterObject,
- () => {
- // 修复:移除不必要的 async
- PageInit();
- },
- { deep: true },
- );
- onMounted(() => {
- window.addEventListener("resize", HandleResize);
- PageInit();
- });
- onUnmounted(() => {
- // 移除监听 防止内存泄漏
- window.removeEventListener("resize", HandleResize);
- });
- </script>
- <style lang="scss" scoped>
- .input_with {
- margin-right: 10px;
- }
- .count_item {
- font-size: 16px;
- font-weight: 400;
- color: #333;
- line-height: 24px;
- margin-left: 10px;
- &.orange {
- color: #fb9f34;
- }
- }
- .checkbox_group {
- :deep(.el-checkbox) {
- margin-right: 10px;
- &:first-child {
- margin-right: 20px;
- }
- }
- }
- </style>
|