Преглед изворни кода

新功能:错题分析打印、报告下载样式修改、成绩单打印增加选择项

liurongli пре 3 недеља
родитељ
комит
93cebc4415

+ 88 - 25
src/components/ErrorsTablePdf.vue

@@ -107,7 +107,7 @@
               :key="key"
               label=""
               :prop="key"
-              :width="[28, 50, 30, 360][index]"
+              :width="[28, 50, 30, 348][index]"
             >
               <template #default="{ row: org }">
                 <span v-if="key == 'registrationCodeList'">{{ org[key] }}</span>
@@ -182,8 +182,12 @@ const printPageHeight = 1285; // 打印页固定高度(含上下内边距)
 const lineHeight = 23; // 单元格单行行高(与样式 .cell line-height:23px 一致)
 const rowBorder = 1; // 行下边框
 // 学号名单列每行容纳的条目数:仅用于首轮估算行数(行数 = ceil(条目数 / 每行条目数)),
-// 首轮渲染后会按每行真实渲染高度重新分页,因此该值无需精确
-let codeItemsPerLine = 3.0;
+// 该列宽 348px 内,3 字姓名实测约 8 个/行;首轮按 7.0 保守估算(低于真实容量,
+// 防止估算偏密导致页面内容溢出),首轮渲染后由 correctPages 按实测行高反推更准的密度。
+// 注意:旧值 3.0 按每行仅 3 个条目估算行数,行内拆分时一次只切下 3 个/行的片段,
+// 与真实渲染(约 8 个/行)相差 2.6 倍,页面可排内容被严重低估、过早换页,
+// 是各页出现 87~763px 不规则底部留白的根因之一
+let codeItemsPerLine = 7.0;
 const headerHeight = 24; // 表头高度(23px 行高 + 1px 边框;td padding 已在样式清零,实测 24px)
 const miniHeaderHeight = 30; // 非首页页头固定高度(第几页总页数 + 打印时间)
 
@@ -200,7 +204,8 @@ const LoadData = () => {
 };
 
 // 学号名单列文本行数估算(仅首轮用):浏览器按顿号等边界分词换行,
-// 首轮按每行 3.0 个条目估算,渲染后以实测行高重新分页,估算不准会被实测修正
+// 首轮按每行 7.0 个条目估算(实测列宽约容纳 8 个 3 字姓名,7.0 留安全余量),
+// 渲染后以实测行高重新分页,估算不准会被实测修正
 const getCodeLines = (code: string): number => {
   const items = String(code).split("、").filter(Boolean).length;
   return Math.max(1, Math.ceil(items / codeItemsPerLine));
@@ -231,11 +236,13 @@ const getRowHeight = (row: TableItem): number => {
 const TablePagesData = () => {
   // 数据更新后清空旧行高缓存,重新采集
   measuredRowHeights.clear();
+  // 每行条目数密度按本次数据重新学习,避免沿用上次数据的学习结果
+  codeItemsPerLine = 7.0;
   const data = props.tableData as TableItem[];
   // 第 1 页页头实测高度(标题可换行,高度动态获取)
   const titleBlockHeight = pageTitle.value?.offsetHeight || 30;
   // 打印页内容区高度:总高 - 上下内边距(校正过程中按需缩小)
-  const baseContentHeight = printPageHeight - 20;
+  const baseContentHeight = printPageHeight - 30;
   let contentBudget = baseContentHeight;
 
   const buildPages = (contentHeight: number): TableItem[][] => {
@@ -452,11 +459,13 @@ const TablePagesData = () => {
   tablePageData.value = pages;
   totalPageNum.value = pages.length;
 
-  // 后续轮次:渲染后按实际渲染情况修正分页,直到收敛(最多 8 轮):
+  // 后续轮次:渲染后按实际渲染情况修正分页,直到收敛(最多 12 轮):
   // 1) 采集每行真实渲染高度(key = 学号名单列文本),用实测高度重新分页,
   //    保证每页排满——估算按"每行条目数"换算行数,与实际换行存在偏差
   //    (名单长短不同、条目宽度不同),实测是最准确的校正依据;
-  // 2) 仍有页面溢出时,按精确溢出像素安全缩减预算(每轮最多 40px、下限 60%),
+  // 2) 按实测行高反推每行条目数密度(codeItemsPerLine),使行内拆分的
+  //    片段大小贴合真实换行,减少因估算偏疏/偏密导致的反复重排;
+  // 3) 仍有页面溢出时,按精确溢出像素安全缩减预算(每轮最多 40px、下限 60%),
   //    防止出现"每页只有一行 + 大面积留白"的坍缩
   const correctPages = (attempt: number, lastCacheKey: string) => {
     // 等 el-table 完成内部布局后再测量
@@ -481,6 +490,26 @@ const TablePagesData = () => {
           });
         });
 
+        // 1.5) 用实测行高反推"每行条目数"密度,供后续轮次的行内拆分/行数估算使用:
+        // 小片段(行数 < 4)因取整误差大且方向偏小,会拉低密度,故只统计足够高的行;
+        // 取观测上界 × 0.9(行尾不满的行密度偏低,上界才接近真实容量,0.9 留余量),
+        // 上限 7.0 保证对未知数据保守(3 字姓名实测约 8 个/行,7.0 低于真实容量,
+        // 估算行数只会偏多、不会偏少,杜绝页面内容溢出),长姓名等密集数据
+        // 的观测密度较低,会自动学到更小的值
+        let maxPerLine = 0;
+        measuredRowHeights.forEach((h, text) => {
+          const items = String(text).split("、").filter(Boolean).length;
+          if (items > 1) {
+            const lines = Math.max(1, Math.round(h / lineHeight));
+            if (lines >= 4) {
+              maxPerLine = Math.max(maxPerLine, items / lines);
+            }
+          }
+        });
+        if (maxPerLine > 0) {
+          codeItemsPerLine = Math.max(1.5, Math.min(7.0, maxPerLine * 0.9));
+        }
+
         // 2) 检查每页表格内容是否超出内容预算
         let maxOverflow = 0;
         els.forEach((el, i) => {
@@ -503,10 +532,10 @@ const TablePagesData = () => {
 
         // 3) 有溢出:按精确溢出像素缩减预算重新分页(每轮最多减 40px,
         //    避免一次大幅缩减导致各页出现大面积底部留白;下限 60% 防坍缩)
-        if (maxOverflow > 1 && attempt < 8) {
+        if (maxOverflow > 1 && attempt < 12) {
           const shrink = Math.min(maxOverflow, 40);
           contentBudget -= Math.ceil(shrink);
-          const floor = Math.max(100, (printPageHeight - 20) * 0.6);
+          const floor = Math.max(100, (printPageHeight - 30) * 0.6);
           if (contentBudget < floor) contentBudget = floor;
           console.log(
             "[CORRECT]",
@@ -525,7 +554,7 @@ const TablePagesData = () => {
 
         // 4) 无溢出但实测行高有更新(首次采集或拆分片段变化):用真实行高重新分页,
         //    把每页排满;同一文本实测高度稳定,重排一次即收敛
-        if (cacheKey !== lastCacheKey && attempt < 8) {
+        if (cacheKey !== lastCacheKey && attempt < 12) {
           const p = buildPages(contentBudget);
           tablePageData.value = p;
           totalPageNum.value = p.length;
@@ -582,17 +611,17 @@ const DownloadPdf = async () => {
           // "white-space": "normal",
           // position: "relative",
           // visibility: "visible",
-          'transform': 'scale(2)',
-          'transform-origin': 'top left',
-          'background-color': '#ffffff',
+          transform: "scale(2)",
+          "transform-origin": "top left",
+          "background-color": "#ffffff",
           // 关键:背景图尺寸适配原元素
-          'background-size': `${htmlElement.scrollWidth}px ${htmlElement.scrollHeight}px`, // 优先铺满且完整显示
-          'background-position': 'top left',
-          'background-repeat': 'no-repeat', // 防止背景图重复导致视觉不完整
-          'overflow': 'visible',
-          'white-space': 'normal',
-          'position': 'relative',
-          'visibility': 'visible'
+          "background-size": `${htmlElement.scrollWidth}px ${htmlElement.scrollHeight}px`, // 优先铺满且完整显示
+          "background-position": "top left",
+          "background-repeat": "no-repeat", // 防止背景图重复导致视觉不完整
+          overflow: "visible",
+          "white-space": "normal",
+          position: "relative",
+          visibility: "visible",
         },
         bgcolor: "#ffffff",
       });
@@ -601,12 +630,23 @@ const DownloadPdf = async () => {
       const imgWidth = imgProps.width;
       const imgHeight = imgProps.height;
 
+      // 打印安全边距(单位 pt,1pt ≈ 0.353mm)。
+      // 打印机存在不可打印边距(常见 4.5~6.3mm),若图片铺满整页,
+      // 表格左右边框距页面边缘仅约 3.5mm,打印时会被打印机物理裁掉
+      // (中间列竖线在页面内部不受影响)。故四周留出安全边距:
+      // 14pt 时左右边框距页面边缘约 6.6mm、底部约 6.9mm、顶部约 13.7mm,
+      // 均超过打印机极限边距 6.3mm,可放心打印,同时留白最小。
+      // 若个别打印机边距更大,打印时选择"适应页面/缩小以适合可打印区域"兜底。
+      // const margin = 14; // 14pt ≈ 4.9mm
+      // const availWidth = pdfWidth - margin * 2;
+      // const availHeight = pdfHeight - margin * 2;
+      // const ratio = Math.min(availWidth / imgWidth, availHeight / imgHeight);
       const ratio = Math.min(pdfWidth / imgWidth, pdfHeight / imgHeight);
       const adjustWidth = imgWidth * ratio;
       const adjustHeight = imgHeight * ratio;
 
       const xPosition = (pdfWidth - adjustWidth) / 2;
-      const yPosition = (pdfHeight - adjustHeight) / 2;
+      const yPosition = 0;//(pdfHeight - adjustHeight) / 2;
 
       if (pageIndex > 0) {
         pdf.addPage();
@@ -751,7 +791,7 @@ defineExpose({
   .area_page {
     width: 908px;
     margin-top: 20px;
-    padding: 10px !important;
+    padding: 8px 15px 0 !important;
     background-color: #fff;
     box-sizing: border-box;
     overflow: hidden;
@@ -765,6 +805,8 @@ defineExpose({
       line-height: 30px;
       display: flex;
       gap: 5px;
+      padding: 0 5px;
+      box-sizing: border-box;
 
       .page_num {
         font-size: 12px;
@@ -800,11 +842,32 @@ defineExpose({
       width: 100%;
       box-sizing: border-box;
       border-collapse: collapse;
-
+      
       :deep(.el-table) {
+        // --el-table-border-color: #e9e9e9;
+        // --el-table-header-border-color: #e9e9e9;
+        // border-left: 1px solid #e9e9e9;
+        // border-right: 1px solid #e9e9e9;
+        // border-top: 1px solid #e9e9e9;
+        // border-radius: 6px;
+        // box-sizing: border-box;
+
+        /* 重置表格边框渲染规则,避免伪元素干扰 */
+        --el-table-border-color: #e9e9e9 !important;
+        --el-table-header-border-color: #e9e9e9;
+        border-collapse: collapse !important;
+        border: 1px solid #e9e9e9 !important;
         border-radius: 6px;
-        border-left: 0px;
-        border-right: 0px;
+        box-sizing: border-box;
+
+        // element-plus 的左右/顶部边框用伪元素绘制,dom-to-image 截图不渲染伪元素,
+        // 故禁用伪元素边框,由上面真实边框替代,保证 PDF/打印中左右边框可见
+        &::before,
+        &::after,
+        .el-table__inner-wrapper::before,
+        .el-table__inner-wrapper::after {
+          display: none;
+        }
         thead {
           height: 23px !important;
           font-size: 12px;

+ 3 - 1
src/components/ReportModule.vue

@@ -25,7 +25,7 @@
             <img
               v-if="!state.printLoading"
               src="@/assets/icon/print_icon.webp"
-            />打印PDF
+            />{{ printText }}
           </el-button>
         </template>
         <template v-if="showExportBtn">
@@ -96,6 +96,7 @@ interface TitleListType {
   titleList?: string[];
   showTitle?: boolean;
   showPrintBtn?: boolean;
+  printText?: string;
   showExportBtn?: boolean;
   tableOrChart?: "table" | "chart" | "qita";
   showTablePage?: boolean;
@@ -111,6 +112,7 @@ const props = withDefaults(defineProps<TitleListType>(), {
   titleList: () => [], //标题
   showTitle: true, //是否显示标题
   showPrintBtn: true, //是否显示打印按钮
+  printText: "打印PDF", //打印按钮文本
   showExportBtn: true, //是否显示导出按钮
   tableOrChart: "table", //表格、图表、其他
   showTablePage: true, //是否显示分页

+ 25 - 6
src/components/ScorePdf.vue

@@ -72,6 +72,8 @@ interface TableCount {
 interface State {
   keyWord: string;
   selectClassList: string[];
+  sortType: number;
+  filterType: number;
   tableData: any[];
   pageTotal: number;
   staticHeaderData: TableColumn[];
@@ -87,6 +89,8 @@ const examName = computed(() => {
 const state = reactive<State>({
   keyWord: '',
   selectClassList: [],
+  sortType: 1,
+  filterType: 1,
   tableData: [],
   pageTotal: 0,
   staticHeaderData: [],
@@ -129,6 +133,8 @@ const GetTableData = async () => {
       topicControl: 0,
       queryStr: state.keyWord,
       classIdCodes: state.selectClassList,
+      sortType: state.sortType,
+      filterType: state.filterType
     });
     if (res.code === 200) {
       const tableData = res?.data || [];
@@ -181,9 +187,11 @@ const tablePageData = (list: any[]) => {
   state.pageTotal = pageList.length;
 }
 // 初始化
-const PageInit = async (keyWord: string, selectClassList: string[]) => {
+const PageInit = async (keyWord: string, selectClassList: string[], sortType: number, filterType: number) => {
   state.keyWord = keyWord;
   state.selectClassList = selectClassList;
+  state.sortType = sortType;
+  state.filterType = filterType;
   await Promise.all([GetStudentTranscriptTitle(), GetTableData()]);
 };
 
@@ -218,7 +226,7 @@ const ExportPdf = async () => {
     await new Promise((resolve) => setTimeout(resolve, 150));
 
     const dataUrl = await domtoimage.toJpeg(htmlElement, {
-      quality: 0.9,
+      quality: 0.8,
       width: htmlElement.scrollWidth * 2,
       height: htmlElement.scrollHeight * 2,
       canvasWidth: htmlElement.scrollWidth * 2,
@@ -311,8 +319,8 @@ const ExportPdf = async () => {
 };
 
 // 加载数据并导出PDF(供父组件一步调用)
-const LoadAndExportPdf = async (keyWord: string, selectClassList: string[]) => {
-  await PageInit(keyWord, selectClassList);
+const LoadAndExportPdf = async (keyWord: string, selectClassList: string[], sortType: number, filterType: number) => {
+  await PageInit(keyWord, selectClassList, sortType, filterType); 
   await nextTick();
   await ExportPdf();
 };
@@ -396,9 +404,20 @@ defineExpose({
       width: 100%;
 
       :deep(.el-table) {
+        --el-table-border-color: #e9e9e9;
+        --el-table-header-border-color: #e9e9e9;
+        border: 1px solid #e9e9e9;
         border-radius: 6px;
-        // border-left: 0px;
-        // border-right: 0px;
+        box-sizing: border-box;
+
+        // element-plus 的左右/顶部边框用伪元素绘制,dom-to-image 截图不渲染伪元素,
+        // 故禁用伪元素边框,由上面真实边框替代,保证 PDF/打印中左右边框可见
+        &::before,
+        &::after,
+        .el-table__inner-wrapper::before,
+        .el-table__inner-wrapper::after {
+          display: none;
+        }
 
         .el-table__header {
           height: 20px !important;

+ 8 - 0
src/styles/common.scss

@@ -2279,6 +2279,10 @@ body {
       // align-items: center;
       &.score_class_popup {
         width: 100%;
+        .item_option{
+          display: flex;
+          width: 100%;
+        }
       }
 
       .el-checkbox-group {
@@ -6013,6 +6017,10 @@ body {
     margin-top: 10px;
     width: 100%;
     flex: 1;
+    // 改为纵向 flex:让子页面(如报表下载页)根节点可 flex:1 撑满剩余高度;
+    // 其余分析页根节点不设 flex-grow,高度仍按内容自适应,布局不受影响
+    display: flex;
+    flex-direction: column;
     overflow: auto;
   }
 }

+ 7 - 6
src/views/analysis/errorAnalysis.vue

@@ -5,6 +5,7 @@
     :titleList="['错题分析表']"
     :showDescribe="false"
     tableOrChart="table"
+    printText="打印错题"
     :showTablePage="false"
     @ExportExcel="ExportExcel"
     @PrintPdf="PrintPdf"
@@ -127,28 +128,28 @@
       </el-table>
     </template>
   </ReportModule>
-  <ErrorsPdf
+  <!-- <ErrorsPdf
     ref="errPdfReport"
     :tableData="state.tableData"
     :className="analysisStore.filterObject.classGroupName"
     :subjectName="analysisStore.filterObject.subjectName || ''"
     :examName="getExamName"
     @PdfLoadEnd="PdfLoadEnd"
-  />
-  <!-- <ErrorsTablePdf
+  /> -->
+  <ErrorsTablePdf
     ref="errPdfReport"
     :tableData="state.tableData"
     :className="analysisStore.filterObject.classGroupName"
     :subjectName="analysisStore.filterObject.subjectName || ''"
     :examName="getExamName"
     @PdfLoadEnd="PdfLoadEnd"
-  /> -->
+  />
 </template>
 <script lang="ts" setup>
 import ReportModule from "@/components/ReportModule.vue";
 import { useAnalysisStore } from "@/store/analysis";
-import ErrorsPdf from "@/components/ErrorsPdf.vue";
-// import ErrorsTablePdf from "@/components/ErrorsTablePdf.vue";
+// import ErrorsPdf from "@/components/ErrorsPdf.vue";
+import ErrorsTablePdf from "@/components/ErrorsTablePdf.vue";
 import { onMounted, reactive, computed, ref, watch } from "vue";
 import { errorQuestionAnalysis, exportErrorQuestion } from "@/api/analysis";
 import { loadingSvgIcon } from "@/utils/common";

+ 23 - 10
src/views/analysis/reportDownload.vue

@@ -6,6 +6,7 @@
     :showExportBtn="false"
     :showTablePage="false"
     tableOrChart="qita"
+    style="display: flex;flex-direction: column;flex: 1;"
   >
     <template #title_left>
       <span>重点页面表格批量下载中心</span>
@@ -39,7 +40,7 @@
           v-for="module in excelModuleList"
           :key="module.menuInfo"
         >
-          <div class="item_content_title">
+          <!-- <div class="item_content_title">
             <div class="content_title_left">
               <img src="@/assets/icon/download_icon.webp" />
               <span>{{ module.menuValue }}</span>
@@ -47,14 +48,14 @@
             <div class="content_title_right" @click="singleExport(module)">
               下载
             </div>
-          </div>
+          </div> -->
           <div class="item_content_list">
-            <el-checkbox
+            <!-- <el-checkbox
               :indeterminate="module.isIndeterminate"
               v-model="module.checkAll"
               @change="(val: boolean) => handleCheckAllChange(val, module)"
               >全选</el-checkbox
-            >
+            > -->
             <el-checkbox-group
               v-model="module.excelModuleList"
               @change="
@@ -355,7 +356,7 @@ function levelDistributionModule(): ExcelModule {
 function classContrastModule(): ExcelModule {
   let excelsList: ExcelsItem[] = [];
   let obj: ExcelsItem = {
-    label: "班级平均分+各率分析表",
+    label: "班级分析表",
     value: "classContrast0",
   };
   if (filterObject.value.subjectGroupType === 0) {
@@ -707,13 +708,25 @@ onBeforeUnmount(() => {
   }
 }
 
+// ReportModule 中包裹本页内容的 .module_qita:占满标题栏以下剩余高度,
+// 并改为纵向 flex,让 module_content 能继续撑满剩余内容高度
+// (双类名提升优先级,确保覆盖 ReportModule 自身的 .module_qita 样式)
+:deep(.module_qita.module_qita) {
+  flex: 1;
+  min-height: 0;
+  display: flex;
+  flex-direction: column;
+}
+
 .module_content {
+  flex: 1; // 撑满 .module_qita 剩余高度
   justify-content: flex-start !important;
+  align-content: flex-start; // 卡片行从顶部排布,多余高度留在容器底部
   padding-bottom: 20px;
   display: flex;
   flex-wrap: wrap;
   gap: 20px;
-  min-height: 400px;
+  // min-height: 400px;
   width: calc(100% - 40px);
   margin: auto;
 
@@ -721,7 +734,7 @@ onBeforeUnmount(() => {
     width: calc((100% - 80px) / 5);
     padding: 16px;
     box-sizing: border-box;
-    background: linear-gradient(170deg, #ebf2ff 10%, #ffffff 50%, #ffffff 100%);
+    // background: linear-gradient(170deg, #ebf2ff 10%, #ffffff 50%, #ffffff 100%);
     border-radius: 10px;
     border: 1px solid #e1e2e7;
 
@@ -765,7 +778,7 @@ onBeforeUnmount(() => {
     }
 
     .item_content_list {
-      margin-top: 12px;
+      // margin-top: 12px;
       display: flex;
       flex-direction: column;
 
@@ -774,11 +787,11 @@ onBeforeUnmount(() => {
         flex-direction: column;
 
         .el-checkbox {
-          margin-top: 12px;
+          // margin-top: 12px;
           margin-right: 0;
           white-space: normal;
           display: flex;
-          align-items: flex-start;
+          // align-items: flex-start;
 
           /* 关键:让复选框和文本顶部对齐 */
           .el-checkbox__label {

+ 26 - 7
src/views/analysis/score.vue

@@ -5,6 +5,7 @@
     :showTitle="false"
     :showDescribe="false"
     :showPrintBtn="true"
+    printText="打印成绩单"
     tableOrChart="table"
     :currentPage="state.pageInfo.pageNum"
     :pageSize="state.pageInfo.pageSize"
@@ -150,12 +151,14 @@
     >
       <div class="dialog_center padding_20 page_search">
         <div class="select_check_content score_class_popup">
-          <el-checkbox
-            :indeterminate="isIndeterminate"
-            v-model="checkAll"
-            @change="HandleCheckAllChange"
-            >全部</el-checkbox
-          >
+          <div class="item_option">
+            <el-checkbox
+              :indeterminate="isIndeterminate"
+              v-model="checkAll"
+              @change="HandleCheckAllChange"
+              >全部</el-checkbox
+            >
+          </div>
           <el-checkbox-group
             v-model="selectClassList"
             @change="HandleCheckedClassChange"
@@ -168,6 +171,18 @@
             >
           </el-checkbox-group>
         </div>
+        <div class="item_option">
+          <el-radio-group v-model="sortType">
+            <el-radio :value="1">按排名排序</el-radio>
+            <el-radio :value="2">按考号排序</el-radio>
+          </el-radio-group>
+        </div>
+        <div class="item_option">
+          <el-radio-group v-model="filterType">
+            <el-radio :value="1">不过滤缺考学生</el-radio>
+            <el-radio :value="2">过滤缺考学生</el-radio>
+          </el-radio-group>
+        </div>
       </div>
       <div slot="footer" class="el-dialog__footer dialog-footer">
         <el-button size="small" @click="showClassPop = false">取 消</el-button>
@@ -289,6 +304,8 @@ const showClassPop = ref<boolean>(false); //选择班级弹框
 const isIndeterminate = ref<boolean>(false); //是否班级全选半选
 const checkAll = ref<boolean>(false); //班级全选
 const selectClassList = ref<string[]>([]); //选中的班级
+const sortType = ref<number>(1); //排序类型
+const filterType = ref<number>(1); //是否打印缺考学生
 const buttonLoading = ref<boolean>(false); //按钮loading
 const classList = computed(() => {//班级列表
   const classNameList = analysisStore.filtersData.find((item: any) => item.type === "className")?.list || [];
@@ -423,6 +440,8 @@ const PrintPdf = () => {
   selectClassList.value = [];//清空选择的数据
   isIndeterminate.value = false;//默认不半选
   buttonLoading.value = false;
+  sortType.value = 1;
+  filterType.value = 1;
   if(analysisStore?.filterObject?.classLevel===0){//年级
     selectClassList.value = classList.value.map((item: any) => item.classIdCode);
     checkAll.value = true;//默认全选
@@ -479,7 +498,7 @@ const ConfirmEnter = () => {
   setTimeout(() => {
     showClassPop.value = false;//先关闭弹框,不等PDF生成完成
   }, 500);
-  scorePdfRef.value?.LoadAndExportPdf(state.keyWord, selectClassList.value)//加载并导出PDF
+  scorePdfRef.value?.LoadAndExportPdf(state.keyWord, selectClassList.value, sortType.value, filterType.value)//加载并导出PDF
     .then(() => {
       ElMessage.success("成绩单已在新页面打开,请去下载或打印");
     })