|
|
@@ -1,6 +1,6 @@
|
|
|
<script setup>
|
|
|
import { ref, computed } from 'vue'
|
|
|
-import { pdfToOfd, ofdToPdf } from '@/api/sysApplication/signature' // 根据实际路径调整
|
|
|
+import { checkOfdToPdf, checkPdfFontRisk, pdfToOfd, ofdToPdf } from '@/api/sysApplication/signature'
|
|
|
|
|
|
// ---------- 状态 ----------
|
|
|
const conversionMode = ref('ofd2pdf') // 'ofd2pdf' | 'pdf2ofd'
|
|
|
@@ -9,6 +9,11 @@ const isConverting = ref(false)
|
|
|
const message = ref('')
|
|
|
const messageType = ref('') // '' | 'error' | 'success'
|
|
|
const uploadRef = ref(null)
|
|
|
+const convertedBlob = ref(null)
|
|
|
+const convertedFilename = ref('')
|
|
|
+const isCheckingRisk = ref(false)
|
|
|
+const riskCheckResult = ref('')
|
|
|
+const maxFileSize = 50 * 1024 * 1024
|
|
|
|
|
|
// ---------- 计算属性 ----------
|
|
|
const acceptedExtension = computed(() =>
|
|
|
@@ -37,6 +42,11 @@ const resetMessage = () => {
|
|
|
}
|
|
|
|
|
|
const validateFile = (file) => {
|
|
|
+ if (file.size > maxFileSize) {
|
|
|
+ message.value = '文件大小不能超过 50MB'
|
|
|
+ messageType.value = 'error'
|
|
|
+ return false
|
|
|
+ }
|
|
|
const ext = file.name.split('.').pop().toLowerCase()
|
|
|
const expectedExt = conversionMode.value === 'ofd2pdf' ? 'ofd' : 'pdf'
|
|
|
if (ext !== expectedExt) {
|
|
|
@@ -49,16 +59,56 @@ const validateFile = (file) => {
|
|
|
|
|
|
const handleFileChange = (uploadFile) => {
|
|
|
const file = uploadFile.raw
|
|
|
+ convertedBlob.value = null
|
|
|
+ convertedFilename.value = ''
|
|
|
+ riskCheckResult.value = ''
|
|
|
if (!validateFile(file)) {
|
|
|
uploadRef.value?.clearFiles()
|
|
|
return
|
|
|
}
|
|
|
currentFile.value = file
|
|
|
resetMessage()
|
|
|
+ checkUploadedFileRisk(file)
|
|
|
+}
|
|
|
+
|
|
|
+const checkUploadedFileRisk = async(file) => {
|
|
|
+ isCheckingRisk.value = true
|
|
|
+ const formData = new FormData()
|
|
|
+ formData.append('file', file)
|
|
|
+
|
|
|
+ try {
|
|
|
+ const checkRisk = conversionMode.value === 'pdf2ofd' ? checkPdfFontRisk : checkOfdToPdf
|
|
|
+ const result = await checkRisk(formData)
|
|
|
+ if (currentFile.value === file) {
|
|
|
+ const riskResult = result?.data || result
|
|
|
+ if (conversionMode.value === 'pdf2ofd') {
|
|
|
+ riskCheckResult.value = riskResult?.safe
|
|
|
+ ? '未发现 PDF 字体风险'
|
|
|
+ : riskResult?.riskMessage || '检测到 PDF 字体风险'
|
|
|
+ } else {
|
|
|
+ riskCheckResult.value = riskResult?.convertible
|
|
|
+ ? '该 OFD 文件可以转换为 PDF'
|
|
|
+ : riskResult?.issues?.join('\n') || '该 OFD 文件无法转换为 PDF'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ if (currentFile.value === file) {
|
|
|
+ riskCheckResult.value = error.message || '转换风险检查失败'
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ if (currentFile.value === file) {
|
|
|
+ isCheckingRisk.value = false
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
const clearFile = () => {
|
|
|
currentFile.value = null
|
|
|
+ convertedBlob.value = null
|
|
|
+ convertedFilename.value = ''
|
|
|
+ riskCheckResult.value = ''
|
|
|
+ isCheckingRisk.value = false
|
|
|
+ resetMessage()
|
|
|
uploadRef.value?.clearFiles()
|
|
|
}
|
|
|
|
|
|
@@ -70,16 +120,16 @@ const formatFileSize = (bytes) => {
|
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]
|
|
|
}
|
|
|
|
|
|
-// 触发浏览器下载
|
|
|
-const downloadBlob = (blob, filename) => {
|
|
|
- const url = window.URL.createObjectURL(blob)
|
|
|
+const downloadConvertedFile = () => {
|
|
|
+ if (!convertedBlob.value) return
|
|
|
+ const url = window.URL.createObjectURL(convertedBlob.value)
|
|
|
const link = document.createElement('a')
|
|
|
link.href = url
|
|
|
- link.download = filename
|
|
|
+ link.download = convertedFilename.value
|
|
|
document.body.appendChild(link)
|
|
|
link.click()
|
|
|
document.body.removeChild(link)
|
|
|
- window.URL.revokeObjectURL(url)
|
|
|
+ window.setTimeout(() => window.URL.revokeObjectURL(url), 60000)
|
|
|
}
|
|
|
|
|
|
// 从 Content-Disposition 响应头解析文件名
|
|
|
@@ -116,6 +166,8 @@ const startConversion = async() => {
|
|
|
if (!currentFile.value || isConverting.value) return
|
|
|
|
|
|
isConverting.value = true
|
|
|
+ convertedBlob.value = null
|
|
|
+ convertedFilename.value = ''
|
|
|
resetMessage()
|
|
|
|
|
|
try {
|
|
|
@@ -125,6 +177,8 @@ const startConversion = async() => {
|
|
|
const api = conversionMode.value === 'ofd2pdf' ? ofdToPdf : pdfToOfd
|
|
|
const response = await api(formData)
|
|
|
|
|
|
+ const risk = response?.headers?.['x-convert-risk']
|
|
|
+
|
|
|
// 兼容处理:封装可能返回完整 response,也可能直接返回 blob
|
|
|
let blob = null
|
|
|
let headers = {}
|
|
|
@@ -160,11 +214,17 @@ const startConversion = async() => {
|
|
|
const defaultFilename = `converted.${targetExt}`
|
|
|
const filename = getFilenameFromHeaders(headers, defaultFilename)
|
|
|
|
|
|
- // 触发下载
|
|
|
- downloadBlob(blob, filename)
|
|
|
-
|
|
|
- message.value = `转换成功!已开始下载 ${filename}`
|
|
|
- messageType.value = 'success'
|
|
|
+ if (risk) {
|
|
|
+ message.value = decodeURIComponent(risk)
|
|
|
+ messageType.value = 'warning'
|
|
|
+ convertedBlob.value = blob
|
|
|
+ convertedFilename.value = filename
|
|
|
+ } else {
|
|
|
+ convertedBlob.value = blob
|
|
|
+ convertedFilename.value = filename
|
|
|
+ message.value = `转换成功:${filename},点击下方按钮下载`
|
|
|
+ messageType.value = 'success'
|
|
|
+ }
|
|
|
} catch (error) {
|
|
|
console.error('转换失败:', error)
|
|
|
|
|
|
@@ -207,6 +267,7 @@ const startConversion = async() => {
|
|
|
|
|
|
<el-row justify="center">
|
|
|
<el-space
|
|
|
+ class="file-convert-space"
|
|
|
fill
|
|
|
>
|
|
|
<el-radio-group
|
|
|
@@ -228,12 +289,14 @@ const startConversion = async() => {
|
|
|
:accept="acceptedExtension"
|
|
|
:disabled="isConverting"
|
|
|
:on-change="handleFileChange"
|
|
|
+
|
|
|
>
|
|
|
<el-icon><UploadFilled /></el-icon>
|
|
|
<div class="el-upload__text">
|
|
|
点击或拖拽 <em>{{ acceptedExtensionText }}</em> 文件到此处
|
|
|
</div>
|
|
|
<template #tip>
|
|
|
+ <div class="el-upload__tip">大小限制:50MB</div>
|
|
|
<div class="el-upload__tip">仅支持单个 {{ acceptedExtensionText }} 文件</div>
|
|
|
</template>
|
|
|
</el-upload>
|
|
|
@@ -248,8 +311,7 @@ const startConversion = async() => {
|
|
|
</el-descriptions>
|
|
|
<el-button
|
|
|
v-if="currentFile"
|
|
|
- type="danger"
|
|
|
- plain
|
|
|
+ type="primary"
|
|
|
:disabled="isConverting"
|
|
|
@click="clearFile"
|
|
|
>
|
|
|
@@ -258,13 +320,36 @@ const startConversion = async() => {
|
|
|
|
|
|
<el-button
|
|
|
type="primary"
|
|
|
- :disabled="!currentFile || isConverting"
|
|
|
+ :disabled="!currentFile || isConverting || isCheckingRisk || (riskCheckResult && riskCheckResult.includes('无法转换'))"
|
|
|
:loading="isConverting"
|
|
|
@click="startConversion"
|
|
|
>
|
|
|
{{ isConverting ? '转换中…' : `转换为 ${targetFormat}` }}
|
|
|
</el-button>
|
|
|
|
|
|
+ <el-alert
|
|
|
+ v-if="currentFile && isCheckingRisk"
|
|
|
+ title="正在检查转换风险…"
|
|
|
+ type="info"
|
|
|
+ show-icon
|
|
|
+ :closable="false"
|
|
|
+ />
|
|
|
+ <el-card
|
|
|
+ v-if="currentFile && riskCheckResult"
|
|
|
+ shadow="never"
|
|
|
+ >
|
|
|
+ <template #header>转换风险检查结果</template>
|
|
|
+ <pre class="risk-result">{{ riskCheckResult }}</pre>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-button
|
|
|
+ v-if="convertedBlob"
|
|
|
+ type="primary"
|
|
|
+ @click="downloadConvertedFile"
|
|
|
+ >
|
|
|
+ 下载转换文件
|
|
|
+ </el-button>
|
|
|
+
|
|
|
<el-alert
|
|
|
v-if="message"
|
|
|
:title="message"
|
|
|
@@ -276,3 +361,15 @@ const startConversion = async() => {
|
|
|
</el-row>
|
|
|
</el-card>
|
|
|
</template>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.file-convert-space {
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.risk-result {
|
|
|
+ margin: 0;
|
|
|
+ white-space: pre-wrap;
|
|
|
+ word-break: break-word;
|
|
|
+}
|
|
|
+</style>
|