新增PDF生成并发设置

This commit is contained in:
2026-06-23 12:54:14 +08:00
parent 529e175d2c
commit 1e9404cc7d
27 changed files with 541 additions and 24 deletions

View File

@@ -97,7 +97,14 @@
<div class="card-actions">
<el-button @click="preview(item)">预览</el-button>
<el-button type="primary" :disabled="!item.can_download_pdf" @click="downloadPdf(item)">下载 PDF</el-button>
<el-button
type="primary"
:disabled="!item.can_download_pdf"
:loading="downloadingCertificateNo === item.certificate_no"
@click="downloadPdf(item)"
>
下载 PDF
</el-button>
</div>
</article>
</section>
@@ -111,6 +118,10 @@
<div v-if="previewPdfUrl" class="pdf-frame">
<embed :src="previewPdfUrl" type="application/pdf" />
</div>
<div v-else-if="previewLoading" class="pdf-loading">
<el-icon class="is-loading"><Loading /></el-icon>
<p>PDF正在排队或生成请稍候...</p>
</div>
<div v-else-if="current" class="preview-card">
<p class="preview-kicker">Training Completion Certificate</p>
<h2>{{ current.certificate_name }}</h2>
@@ -126,16 +137,26 @@
</div>
<template #footer>
<el-button @click="closePreview">关闭</el-button>
<el-button v-if="current?.can_download_pdf" type="primary" @click="downloadPdf(current)">下载 PDF</el-button>
<el-button
v-if="current?.can_download_pdf"
type="primary"
:loading="downloadingCertificateNo === current.certificate_no"
@click="downloadPdf(current)"
>
下载 PDF
</el-button>
</template>
</el-dialog>
</main>
</template>
<script setup lang="ts">
import { Loading } from "@element-plus/icons-vue";
import { ElLoading, ElMessage } from "element-plus";
import { onMounted, onUnmounted, reactive, ref, watch } from "vue";
import { http, type Captcha, type PublicCertificate } from "../api";
import { apiErrorMessage, saveBlob } from "../download";
interface SearchResponse {
items: PublicCertificate[];
@@ -159,6 +180,8 @@ const results = ref<PublicCertificate[]>([]);
const previewVisible = ref(false);
const current = ref<PublicCertificate | null>(null);
const previewPdfUrl = ref("");
const previewLoading = ref(false);
const downloadingCertificateNo = ref("");
const smsId = ref("");
const sendingSms = ref(false);
const smsCooldown = ref(0);
@@ -285,12 +308,20 @@ async function submitQuery() {
async function preview(item: PublicCertificate) {
current.value = item;
clearPreviewUrl();
if (item.can_download_pdf && item.download_url) {
const url = item.download_url.replace(/^\/api/, "");
const { data } = await http.get(url, { responseType: "blob" });
previewPdfUrl.value = URL.createObjectURL(data);
}
previewVisible.value = true;
if (item.can_download_pdf && item.download_url) {
previewLoading.value = true;
try {
const url = item.download_url.replace(/^\/api/, "");
const { data } = await http.get(url, { responseType: "blob" });
previewPdfUrl.value = URL.createObjectURL(data);
} catch (error: any) {
message.value = await apiErrorMessage(error, "PDF生成失败请稍后再试");
messageType.value = "error";
} finally {
previewLoading.value = false;
}
}
}
function clearPreviewUrl() {
@@ -304,8 +335,24 @@ function closePreview() {
previewVisible.value = false;
}
function downloadPdf(item: PublicCertificate) {
if (item.download_url) window.location.href = item.download_url;
async function downloadPdf(item: PublicCertificate) {
if (!item.download_url) return;
downloadingCertificateNo.value = item.certificate_no;
const loading = ElLoading.service({
lock: true,
text: "PDF正在排队或生成请稍候...",
background: "rgba(255, 255, 255, 0.72)",
});
try {
const url = item.download_url.replace(/^\/api/, "");
const { data } = await http.get(url, { responseType: "blob" });
saveBlob(data, `${item.certificate_no}.pdf`);
} catch (error: any) {
ElMessage.error(await apiErrorMessage(error, "PDF生成失败请稍后再试"));
} finally {
loading.close();
downloadingCertificateNo.value = "";
}
}
onMounted(loadCaptcha);
@@ -520,6 +567,20 @@ h1 {
height: 100%;
}
.pdf-loading {
min-height: 240px;
display: grid;
place-items: center;
align-content: center;
gap: 12px;
color: #64748b;
}
.pdf-loading .el-icon {
color: #16817e;
font-size: 28px;
}
.preview-kicker {
margin: 0 0 10px;
color: #16817e;