Sfoglia il codice sorgente

添加评价码的PDF导出

lm 1 mese fa
parent
commit
3ec15f05e9

File diff suppressed because it is too large
+ 664 - 0
package-lock.json


+ 4 - 1
package.json

@@ -17,12 +17,15 @@
     "@vue-office/excel": "^1.7.14",
     "axios": "^1.9.0",
     "echarts": "^6.0.0",
-    "exceljs": "^4.4.0",
     "element-plus": "^2.9.11",
+    "exceljs": "^4.4.0",
     "jsencrypt": "^3.5.4",
+    "jspdf": "^4.2.1",
+    "jspdf-autotable": "^5.0.8",
     "lodash": "^4.17.21",
     "particles.js": "^2.0.0",
     "pdfjs-dist": "^5.4.624",
+    "pdfmake": "^0.3.11",
     "pptx-preview": "^1.0.7",
     "qrcode": "^1.5.4",
     "vue": "^3.5.13",

BIN
public/font/SIMHEI.TTF


BIN
public/font/STXIHEI.TTF


+ 9 - 0
src/api/teachingRelationshipComponent.ts

@@ -51,4 +51,13 @@ export const clearTeachingRelationshipApi = (gradeId:any,classType:any) => {
         url: `${teacherPrefix}/api/v1/teacherRelation/removeTeachingRelation/${gradeId}/${classType}`,
         method: 'get',
     })
+}
+
+// 导入授课关系
+export const importTeachingRelationshipApi = (data:any,urlData:any) => {
+    return request({
+        url: `${teacherPrefix}/api/v1/teacherRelation/importTeachingRelation/${urlData.evaluationId}/${urlData.evaluationGradeId}/${urlData.classType}`,
+        method: 'post',
+        data,
+    },)
 }

+ 105 - 7
src/views/reviewManagement/stepComponent/reviewAccountComponent/GenerateReviewCode.vue

@@ -6,8 +6,8 @@
                     <FormSearchGroup :searchList="searchList" @searchChange="HandleSelectSearchChange"></FormSearchGroup>
                 </div>
                 <div class="content_right">
-                    <el-button class="button_refresh">导出PDF</el-button>
-                    <el-button class="button_refresh"  @click="HandleExportExcel">导出Excel</el-button>
+                    <el-button class="button_refresh"  @click="HandleExportPdf">导出 PDF</el-button>
+                    <el-button class="button_refresh"  @click="HandleExportExcel">导出 Excel</el-button>
                     <el-button :class="[store.state.evaluationManageStepData.isAllowDeleteOrEdit ?  'button_border' : 'is-disabled']" 
                         @click="store.state.evaluationManageStepData.isAllowDeleteOrEdit ?  GoToReviewAccountConfig() : ''">重新生成</el-button>
                     <el-button :class="[store.state.evaluationManageStepData.isAllowDeleteOrEdit ? 'button_background' : 'is-disabled']" 
@@ -43,13 +43,19 @@
 import { ref,watch } from 'vue';
 import { useStore } from 'vuex';
 import { useRoute,useRouter } from 'vue-router';
-
 import ExcelJS from 'exceljs';
 
-import FormSearchGroup from '@/baseComponents/FormSearchGroup.vue';
-import Table from '@/baseComponents/Table.vue';
 
+// 1. 引入 jsPDF 和 AutoTable 插件
+import { jsPDF } from 'jspdf';
+import autoTable from 'jspdf-autotable';
 
+// 字体文件 (确保该 WOFF/TTF 路径可以被正确 fetch 或 import)
+import STXIHEIFont from '/font/STXIHEI.TTF';
+
+
+import FormSearchGroup from '@/baseComponents/FormSearchGroup.vue';
+import Table from '@/baseComponents/Table.vue';
 import {makeQRCode,downloadQRCode } from '@/utils/makeQRCode'
 
 import { searchList } from './formSearchGroup';
@@ -333,7 +339,99 @@ const HandleExportExcel = async () => {
     URL.revokeObjectURL(url);
 };
 
-
+// 导出 pdf
+const HandleExportPdf = async () => {
+  let finalTableColumn = generateCodeTableColumns.value.slice(1).filter(column => !column.hidden)
+  // 表格内容
+  let finalTableData = tableData.value
+  // 1. 表头数据 (注意:在 header 后面,我们要手动加一列用来放二维码,避免 URL 文本和二维码重合)
+  const headers = [...finalTableColumn.map(column => column.label || column.name), '快捷扫码'];
+
+  // 2. 格式化表格数据
+  const body = finalTableData.map(row => {
+    return [
+      row.evaluationGradeName,
+      row.evaluationClassName,
+      row.genderName,
+      row.evaluationCode,
+      row.requestUrl,
+      '' // 留空给最后一列绘制二维码图片
+    ];
+  });
+
+  // 3. 创建 PDF 实例
+  const doc = new jsPDF('p', 'mm', 'a4');
+
+  try {
+    // 4. 加载中文字体以防乱码
+    const fontBase64 = await convertFontToBase64(STXIHEIFont);
+    // 注册字体 (文件名,字体名,样式)
+    doc.addFileToVFS('STXIHEI.ttf', fontBase64);
+    doc.addFont('STXIHEI.ttf', 'STXIHEI', 'normal');
+    doc.setFont('STXIHEI'); // 应用字体
+  } catch (error) {
+    console.error('字体加载失败,中文可能会显示乱码', error);
+  }
+
+  // 5. 使用 autoTable 渲染表格
+  autoTable(doc, {
+    head: [headers],
+    body: body,
+    styles: {
+      font: 'STXIHEI', // 单元格使用中文字体
+      fontStyle: 'normal',
+      halign: 'center',
+      valign: 'middle'
+    },
+    // 手动调整列宽,给二维码列留出足够位置
+    columnStyles: {
+      0: { cellWidth: 20 }, // 年级
+      1: { cellWidth: 20 }, // 班级
+      2: { cellWidth: 15 }, // 性别
+      3: { cellWidth: 25 }, // 评价码
+      4: { cellWidth: 70 }, // 网址
+      5: { cellWidth: 35 }  // 二维码
+    },
+    // 核心:在单元格渲染完毕后,利用钩子函数将二维码图片画进最后一列
+    didDrawCell: (data) => {
+      // 判断是否是最后一列且为数据行(非表头)
+      if (data.column.index === 5 && data.cell.section === 'body') {
+        const rowIndex = data.row.index;
+        const qrCodeDataUrl = finalTableData[rowIndex]?.qrCode;
+        if (qrCodeDataUrl) {
+          // 在单元格内部计算二维码居中的坐标和大小
+          const padding = 2;
+          const size = Math.min(data.cell.width, data.cell.height) - padding * 2;
+          const x = data.cell.x + (data.cell.width - size) / 2;
+          const y = data.cell.y + (data.cell.height - size) / 2;
+          
+          // 绘制二维码图片
+          doc.addImage(qrCodeDataUrl, 'PNG', x, y, size, size);
+        }
+      }
+    },
+    // 适当增加行高,保证二维码不会太小
+    rowPageBreak: 'avoid',
+    bodyStyles: { minCellHeight: 32 } 
+  });
+
+  // 6. 保存 PDF 文件
+  doc.save('评价码列表.pdf');
+}
+// 辅助函数:将字体文件转为 Base64 以便 jsPDF 加载
+const convertFontToBase64 = async (url: string): Promise<string> => {
+  const response = await fetch(url);
+  const blob = await response.blob();
+  return new Promise((resolve) => {
+    const reader = new FileReader();
+    reader.onloadend = () => {
+      const base64String = reader.result as string;
+      // 截取掉 data:application/font-woff;base64, 等前缀
+      resolve(base64String.split(',')[1]);
+    };
+    reader.readAsDataURL(blob);
+  });
+};
 
 // 根据是否按照性别生成评价码 控制 表格项和搜索项的显隐
 watch(()=>store.state.evaluationManageStepData.isGenerateAccountByGender,(newVal)=>{
@@ -376,4 +474,4 @@ onMounted(async ()=>{
         height: 24px;
     }
 }
-</style>
+</style>

+ 2 - 2
vite.config.ts

@@ -31,13 +31,13 @@ export default defineConfig({
       '/teaching':{
         // target:'https://dev.k12100.net/admin/adminApi',
         // target:'https://index.k12100.net/teaching/', //教师端 测试环境
-        target:'http://192.168.1.44:47001/',  //教师端 本地
+        target:'http://192.168.1.58:47001/',  //教师端 本地
         changeOrigin:true,
         rewrite:path => path.replace(/^\/teaching/, '')
       },
       '/student':{
         // target:'https://index.k12100.net/student/',    //学生端 测试环境
-        target:'http://192.168.1.44:47003/',    //学生端 本地
+        target:'http://192.168.1.58:47003/',    //学生端 本地
         changeOrigin:true,
         rewrite:path => path.replace(/^\/student/, '')
       },

Some files were not shown because too many files changed in this diff