|
|
@@ -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>
|