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

错题分析修改合并单元格逻辑

liurongli пре 2 недеља
родитељ
комит
f3392413e1
1 измењених фајлова са 41 додато и 17 уклоњено
  1. 41 17
      src/components/ErrorsTablePdf.vue

+ 41 - 17
src/components/ErrorsTablePdf.vue

@@ -37,7 +37,7 @@
           :data="pageData"
           border
           stripe
-          row-key="questionId"
+          :row-key="(row) => row._rowKey"
           :show-header="pIndex === 0"
           :span-method="SpanMethod"
           :row-class-name="ErrorTableRowClassName"
@@ -158,6 +158,7 @@ import domtoimage from "dom-to-image";
 // 定义数据类型
 interface TableItem {
   rowKey: number | string;
+  questionId?: string | number; // 行唯一标识(模板 row-key 使用;拆分片段克隆自同一行,questionId 相同)
   questionName?: string;
   questionType?: string;
   answerValue?: string;
@@ -174,6 +175,12 @@ interface TableItem {
   hiddenStu?: boolean;
   rowspan?: number;
   colspan?: number;
+  // ---- 以下为本组件内部字段,请勿由上游数据传入 ----
+  _rowKey?: string; // 分页行唯一 key(拆分片段也生成新 key,避免 row-key 重复)
+  _fromSplit?: boolean; // 拆分片段标记:并入所在题块,不独立成合并块
+  _anchorRowspan?: number; // 合并锚点行跨行数
+  _anchorColspan?: number; // 合并锚点行跨列数(恒为 1)
+  _anchorBlockIndex?: number; // 合并块全局序号(斑马纹按块交替底色)
 }
 
 // 定义 Props
@@ -320,9 +327,11 @@ const TablePagesData = () => {
       }
     };
 
+    // 行唯一 key 自增序号:buildPages 每次重建时重置,保证同一页内 _rowKey 全局唯一
+    let rowSeq = 0;
     // 放入页面的行一律克隆,避免改动原数据
     const cloneRows = (rows: TableItem[]): TableItem[] =>
-      rows.map((r) => ({ ...r }));
+      rows.map((r) => ({ ...r, _rowKey: `${r.questionId}_${rowSeq++}` }));
 
     // 行内拆分:整行放不下时,把学号名单内容按条目边界切分为
     // [当前页部分, 续排部分];前部分填满当前页剩余空白,剩余部分在下一页继续。
@@ -353,15 +362,15 @@ const TablePagesData = () => {
       }
       const rowA = {
         ...row,
+        _rowKey: `${row.questionId}_a_${rowSeq++}`,
         registrationCodeList: String(code).slice(0, cut),
-        rowspan: 1,
-        colspan: 1,
+        _fromSplit: true, // 拆分片段:并入所在题块,不开启新合并块
       };
       const rowB = {
         ...row,
-        registrationCodeList: String(code).slice(cut),
-        rowspan: 1,
-        colspan: 1,
+        _rowKey: `${row.questionId}_b_${rowSeq++}`,
+        registrationCodeList: String(code).slice(cut).replace(/、+$/, ""), // 去除末尾残留顿号
+        _fromSplit: true,
       };
       // rowA 行数 = ceil(fitItems/codeItemsPerLine) <= fitLines,其高度必 <= availableHeight
       return [rowA, rowB];
@@ -479,20 +488,31 @@ const TablePagesData = () => {
 
     // 统一修正每页合并单元格:
     // 每页第一行必须作为合并锚点,保证前 8 列(小题名称~正确答案)在每一页都可见;
-    // 页内遇到新的组起点(rowKey==0)或拆分片段(rowspan)时重新开启一个锚点,
+    // 页内遇到新的组起点(rowKey==0)时闭合当前锚点块、开启新锚点;
+    // 拆分片段(_fromSplit)并入所在题块、不开启新锚点,避免同一页出现
+    // 相邻合并块导致前 8 列题信息重复显示;
     // 锚点 rowspan 覆盖到下一个锚点前的所有行,其余行被合并隐藏
+    let blockIndex = 0; // 合并块全局序号(跨页递增),供斑马纹按题块交替底色
     pages.forEach((page) => {
       if (!page.length) return;
       let anchorIdx = 0;
       for (let i = 1; i < page.length; i++) {
-        if (Number(page[i].rowKey) === 0 || page[i].rowspan) {
-          page[anchorIdx].rowspan = i - anchorIdx;
-          page[anchorIdx].colspan = 1;
+        // 拆分片段:并入当前块,不开启新锚点
+        if (page[i]._fromSplit) continue;
+        if (Number(page[i].rowKey) === 0) {
+          page[anchorIdx]._anchorRowspan = i - anchorIdx;
+          page[anchorIdx]._anchorColspan = 1;
+          page.slice(anchorIdx, i).forEach(
+            (r) => (r._anchorBlockIndex = blockIndex),
+          );
+          blockIndex++;
           anchorIdx = i;
         }
       }
-      page[anchorIdx].rowspan = page.length - anchorIdx;
-      page[anchorIdx].colspan = 1;
+      page[anchorIdx]._anchorRowspan = page.length - anchorIdx;
+      page[anchorIdx]._anchorColspan = 1;
+      page.slice(anchorIdx).forEach((r) => (r._anchorBlockIndex = blockIndex));
+      blockIndex++;
     });
 
     return pages;
@@ -827,11 +847,13 @@ const SpanMethod = ({ row, column }: any) => {
     "studentNum",
     "registrationCodeList",
   ];
+  // 合并信息读取组件内部字段(_anchorRowspan/_anchorColspan),
+  // 与上游数据可能自带的 rowspan/colspan 字段解耦
   if (props.indexOf(column.property) == -1) {
-    if (row.rowspan) {
+    if (row._anchorRowspan) {
       return {
-        rowspan: row.rowspan,
-        colspan: row.colspan,
+        rowspan: row._anchorRowspan,
+        colspan: row._anchorColspan || 1,
       };
     } else {
       return {
@@ -844,7 +866,9 @@ const SpanMethod = ({ row, column }: any) => {
 
 // 定义行样式
 const ErrorTableRowClassName = ({ row }: any) => {
-  if (row.rowspanIndex % 2 === 0) {
+  // 按合并块序号交替底色:同一题块内(含拆分片段、被合并隐藏行)保持同色
+  const idx = row._anchorBlockIndex ?? 0;
+  if (idx % 2 === 0) {
     return "row_color_FFFFFF";
   } else {
     return "row_color_FAFAFA";