新增PDF批量预生成
This commit is contained in:
@@ -34,7 +34,7 @@
|
||||
<el-table-column prop="valid_rows" label="可导入" width="100" />
|
||||
<el-table-column prop="failed_rows" label="失败" width="100" />
|
||||
<el-table-column prop="created_at" label="上传时间" width="190" />
|
||||
<el-table-column label="操作" width="380">
|
||||
<el-table-column label="操作" width="470">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" @click="downloadSource(row)">下载原文件</el-button>
|
||||
<el-button
|
||||
@@ -46,27 +46,65 @@
|
||||
>
|
||||
确认导入
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 'imported'"
|
||||
size="small"
|
||||
:loading="preGeneratingId === row.id"
|
||||
@click="startPregeneration(row)"
|
||||
>
|
||||
预生成PDF
|
||||
</el-button>
|
||||
<el-button v-if="row.error_report_path" size="small" @click="downloadErrorReport(row)">错误报告</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteBatch(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="pregenerationVisible" title="批量预生成PDF" width="560px" @closed="stopPregenerationPolling">
|
||||
<div v-if="pregenerationJob" class="pregeneration-panel">
|
||||
<el-progress :percentage="pregenerationJob.percent" />
|
||||
<dl class="pregeneration-stats">
|
||||
<div><dt>总数</dt><dd>{{ pregenerationJob.total }}</dd></div>
|
||||
<div><dt>已完成</dt><dd>{{ pregenerationJob.completed }}</dd></div>
|
||||
<div><dt>新生成</dt><dd>{{ pregenerationJob.generated }}</dd></div>
|
||||
<div><dt>已缓存</dt><dd>{{ pregenerationJob.cached }}</dd></div>
|
||||
<div><dt>跳过</dt><dd>{{ pregenerationJob.skipped }}</dd></div>
|
||||
<div><dt>失败</dt><dd>{{ pregenerationJob.failed }}</dd></div>
|
||||
</dl>
|
||||
<p class="progress-note">当前任务按系统设置的最大并发数执行:{{ pregenerationJob.concurrency_limit }}</p>
|
||||
<el-alert
|
||||
v-if="pregenerationDone"
|
||||
:type="pregenerationJob.failed ? 'warning' : 'success'"
|
||||
:closable="false"
|
||||
:title="pregenerationJob.failed ? '预生成已完成,但有部分失败。' : '预生成已完成。'"
|
||||
/>
|
||||
<ul v-if="pregenerationJob.errors.length" class="error-list">
|
||||
<li v-for="item in pregenerationJob.errors" :key="item">{{ item }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { UploadFilled } from "@element-plus/icons-vue";
|
||||
import { ElMessage, ElMessageBox, type UploadFile } from "element-plus";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { computed, onMounted, onUnmounted, ref } from "vue";
|
||||
|
||||
import { http, type ImportBatch } from "../api";
|
||||
import { downloadFile } from "../download";
|
||||
import { http, type ImportBatch, type PdfPregenerationJob } from "../api";
|
||||
import { apiErrorMessage, downloadFile } from "../download";
|
||||
|
||||
const batches = ref<ImportBatch[]>([]);
|
||||
const selectedFile = ref<File | null>(null);
|
||||
const uploading = ref(false);
|
||||
const confirmingId = ref<number | null>(null);
|
||||
const preGeneratingId = ref<number | null>(null);
|
||||
const pregenerationVisible = ref(false);
|
||||
const pregenerationJob = ref<PdfPregenerationJob | null>(null);
|
||||
let pregenerationTimer: number | null = null;
|
||||
|
||||
const pregenerationDone = computed(() => ["completed", "completed_with_errors"].includes(pregenerationJob.value?.status || ""));
|
||||
|
||||
function statusText(status: string) {
|
||||
const labels: Record<string, string> = {
|
||||
@@ -134,7 +172,42 @@ async function deleteBatch(row: ImportBatch) {
|
||||
await loadBatches();
|
||||
}
|
||||
|
||||
async function startPregeneration(row: ImportBatch) {
|
||||
preGeneratingId.value = row.id;
|
||||
try {
|
||||
const { data } = await http.post<PdfPregenerationJob>("/admin/certificates/pdf-pregeneration-jobs", {
|
||||
import_batch_id: row.id,
|
||||
});
|
||||
pregenerationJob.value = data;
|
||||
pregenerationVisible.value = true;
|
||||
startPregenerationPolling(data.id);
|
||||
} catch (error: any) {
|
||||
ElMessage.error(await apiErrorMessage(error, "启动PDF预生成失败"));
|
||||
} finally {
|
||||
preGeneratingId.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
function startPregenerationPolling(jobId: string) {
|
||||
stopPregenerationPolling();
|
||||
pregenerationTimer = window.setInterval(async () => {
|
||||
const { data } = await http.get<PdfPregenerationJob>(`/admin/certificates/pdf-pregeneration-jobs/${jobId}`);
|
||||
pregenerationJob.value = data;
|
||||
if (["completed", "completed_with_errors"].includes(data.status)) {
|
||||
stopPregenerationPolling();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function stopPregenerationPolling() {
|
||||
if (pregenerationTimer) {
|
||||
clearInterval(pregenerationTimer);
|
||||
pregenerationTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadBatches);
|
||||
onUnmounted(stopPregenerationPolling);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -163,4 +236,45 @@ onMounted(loadBatches);
|
||||
margin-top: 20px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.pregeneration-panel {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.pregeneration-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.pregeneration-stats div {
|
||||
border: 1px solid #e2edf0;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pregeneration-stats dt {
|
||||
color: #64748b;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.pregeneration-stats dd {
|
||||
margin: 4px 0 0;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.progress-note {
|
||||
margin: 0;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.error-list {
|
||||
margin: 0;
|
||||
color: #b91c1c;
|
||||
line-height: 1.8;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user