155 lines
3.6 KiB
Vue
155 lines
3.6 KiB
Vue
<script setup lang="ts">
|
|
import { ref, watch, onMounted } from 'vue'
|
|
import { ElDialog, ElUpload, ElButton, ElMessage } from 'element-plus'
|
|
import type { UploadProps, UploadFile, UploadRawFile } from 'element-plus'
|
|
import { useUpload } from '#/components/upload/use-upload'
|
|
import { loadPdfLib,checkPdfJavaScript } from './pdf'
|
|
|
|
interface RowData {
|
|
id?: number
|
|
name?: string
|
|
[key: string]: any
|
|
}
|
|
|
|
const props = defineProps<{
|
|
visible: boolean
|
|
rowData: RowData | null
|
|
directory?: string
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
'update:visible': [value: boolean]
|
|
'success': [url: string, file: UploadFile]
|
|
}>()
|
|
|
|
const fileList = ref<UploadFile[]>([])
|
|
const uploading = ref(false)
|
|
|
|
const { uploadUrl, httpRequest } = useUpload(props.directory)
|
|
|
|
// 文件大小限制 10MB
|
|
const maxFileSize = 10
|
|
|
|
// 允许的文件类型
|
|
const acceptTypes = '.pdf'
|
|
|
|
// 监听弹窗关闭时清空文件列表
|
|
watch(() => props.visible, (val) => {
|
|
if (!val) {
|
|
fileList.value = []
|
|
uploading.value = false
|
|
}
|
|
})
|
|
// 上传前校验
|
|
const beforeUpload: UploadProps['beforeUpload'] = async (file: UploadRawFile) => {
|
|
const isLt20M = file.size / 1024 / 1024 < maxFileSize
|
|
if (!isLt20M) {
|
|
ElMessage.error(`文件大小不能超过 ${maxFileSize}MB!`)
|
|
return false
|
|
}
|
|
|
|
try {
|
|
await loadPdfLib()
|
|
const bytes = await file.arrayBuffer()
|
|
const hasJavaScript = await checkPdfJavaScript(bytes)
|
|
if (hasJavaScript) {
|
|
ElMessage.error('PDF 文件包含 JavaScript 代码,不允许上传')
|
|
return false
|
|
}
|
|
return true
|
|
} catch (error) {
|
|
console.error('PDF 检测失败:', error)
|
|
ElMessage.error('PDF 文件检测失败,请重试')
|
|
return false
|
|
}
|
|
}
|
|
|
|
// 自定义上传请求
|
|
const handleHttpRequest = (options: any) => {
|
|
uploading.value = true
|
|
return httpRequest(options.file)
|
|
}
|
|
|
|
// 上传成功
|
|
const handleSuccess: UploadProps['onSuccess'] = (response: any, file: UploadFile) => {
|
|
uploading.value = false
|
|
ElMessage.success('上传成功')
|
|
emit('success', response, file)
|
|
handleClose()
|
|
}
|
|
|
|
// 上传失败
|
|
const handleError: UploadProps['onError'] = () => {
|
|
uploading.value = false
|
|
ElMessage.error('上传失败,请重试')
|
|
}
|
|
|
|
// 关闭弹窗
|
|
const handleClose = () => {
|
|
emit('update:visible', false)
|
|
}
|
|
|
|
onMounted(async () => {
|
|
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<ElDialog
|
|
:model-value="visible"
|
|
title="附件上传"
|
|
width="500px"
|
|
@update:model-value="emit('update:visible', $event)"
|
|
@close="handleClose"
|
|
>
|
|
<div class="upload-container">
|
|
<ElUpload
|
|
v-model:file-list="fileList"
|
|
class="upload-demo"
|
|
drag
|
|
:action="uploadUrl"
|
|
:http-request="handleHttpRequest"
|
|
:before-upload="beforeUpload"
|
|
:on-success="handleSuccess"
|
|
:on-error="handleError"
|
|
:accept="acceptTypes"
|
|
:limit="1"
|
|
:disabled="uploading"
|
|
>
|
|
<div
|
|
class="flex min-h-[200px] flex-col items-center justify-center py-8"
|
|
>
|
|
<span
|
|
class="icon-[mdi--cloud-upload-outline] mb-4 text-6xl text-gray-400"
|
|
></span>
|
|
<div class="text-base text-gray-600">
|
|
点击或拖拽文件到此区域上传
|
|
</div>
|
|
<div class="mt-2 text-sm text-gray-400">
|
|
支持 {{acceptTypes}} 文件
|
|
</div>
|
|
</div>
|
|
</ElUpload>
|
|
</div>
|
|
|
|
<template #footer>
|
|
<ElButton @click="handleClose" :disabled="uploading">取消</ElButton>
|
|
</template>
|
|
</ElDialog>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.upload-container {
|
|
padding: 10px 0;
|
|
}
|
|
|
|
.upload-demo {
|
|
width: 100%;
|
|
}
|
|
|
|
:deep(.el-upload-dragger) {
|
|
width: 100%;
|
|
padding: 40px 20px;
|
|
}
|
|
</style>
|