mirror of
https://github.com/continew-org/continew-admin-ui.git
synced 2025-11-08 08:57:12 +08:00
165 lines
4.0 KiB
Vue
165 lines
4.0 KiB
Vue
<template>
|
||
<a-modal
|
||
v-model:visible="visible"
|
||
:width="width >= 1350 ? 1350 : '100%'"
|
||
:on-before-close="onClose"
|
||
:footer="false"
|
||
esc-to-close="esc-to-close"
|
||
@close="onClose"
|
||
>
|
||
<template #title>
|
||
{{ modalTitle }}
|
||
<div class="toolbar">
|
||
<a-tooltip position="tl" content="在新标签页打开">
|
||
<icon-launch style="cursor: pointer" @click="onOpen" />
|
||
</a-tooltip>
|
||
</div>
|
||
</template>
|
||
<a-spin :loading="loading" class="w-full mt--10">
|
||
<a-card class="preview-content">
|
||
<VueOfficePdf
|
||
v-if="filePreview.fileInfo?.fileType === 'pdf'"
|
||
:src="filePreview.fileInfo?.data"
|
||
class="h-full"
|
||
@rendered="renderedHandler"
|
||
@error="errorHandler"
|
||
/>
|
||
<VueOfficeDocx
|
||
v-else-if="WordTypes.includes(filePreview.fileInfo?.fileType || '')"
|
||
:src="filePreview.fileInfo?.data"
|
||
class="h-full"
|
||
@rendered="renderedHandler"
|
||
@error="errorHandler"
|
||
/>
|
||
<VueOfficeExcel
|
||
v-else-if="ExcelTypes.includes(filePreview.fileInfo?.fileType || '')"
|
||
:src="filePreview.fileInfo?.data"
|
||
style="height: 80vh; width: 100%"
|
||
:options="filePreview.excelConfig"
|
||
@rendered="renderedHandler"
|
||
@error="errorHandler"
|
||
/>
|
||
</a-card>
|
||
</a-spin>
|
||
</a-modal>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import VueOfficePdf from '@vue-office/pdf'
|
||
import VueOfficeDocx from '@vue-office/docx'
|
||
import '@vue-office/docx/lib/index.css'
|
||
import VueOfficeExcel from '@vue-office/excel'
|
||
import '@vue-office/excel/lib/index.css'
|
||
import { Message } from '@arco-design/web-vue'
|
||
import { useWindowSize } from '@vueuse/core'
|
||
import type { FilePreview } from '@/components/FilePreview/type'
|
||
import { ExcelTypes, WordTypes } from '@/constant/file'
|
||
|
||
const visible = ref<boolean>(false)
|
||
const loading = ref<boolean>(false)
|
||
const { width } = useWindowSize()
|
||
|
||
// 用于关闭弹框时销毁,释放内存
|
||
const blobUrl = ref<string>('')
|
||
|
||
// 文件预览对象
|
||
const filePreview = reactive<FilePreview>({
|
||
fileInfo: {},
|
||
excelConfig: {}
|
||
})
|
||
// 弹框标题
|
||
const modalTitle = computed(() => {
|
||
const { fileName, fileType } = filePreview.fileInfo || {}
|
||
return fileName && fileType ? `${fileName}.${fileType}` : '文件预览'
|
||
})
|
||
|
||
// 预览
|
||
const onPreview = (previewInfo: FilePreview) => {
|
||
filePreview.fileInfo = previewInfo.fileInfo
|
||
visible.value = true
|
||
loading.value = true
|
||
}
|
||
|
||
const renderedHandler = () => {
|
||
loading.value = false
|
||
}
|
||
const errorHandler = () => {
|
||
loading.value = false
|
||
Message.error('文件加载失败')
|
||
}
|
||
|
||
// 新标签页打开
|
||
const onOpen = () => {
|
||
const data = filePreview.fileInfo?.data
|
||
|
||
if (!data) {
|
||
console.error('没有数据提供')
|
||
return
|
||
}
|
||
|
||
let url: string | null = null
|
||
|
||
if (typeof data === 'string') {
|
||
// 如果是字符串,假设它是一个 URL,直接使用
|
||
url = data
|
||
} else if (data instanceof Blob || data instanceof ArrayBuffer) {
|
||
// 如果之前创建了 Blob URL,先释放
|
||
if (blobUrl.value) {
|
||
URL.revokeObjectURL(blobUrl.value)
|
||
}
|
||
|
||
// 如果是 Blob 或 ArrayBuffer,则将其转换为 Blob
|
||
const blob = data instanceof Blob ? data : new Blob([data])
|
||
url = URL.createObjectURL(blob)
|
||
blobUrl.value = url
|
||
} else {
|
||
console.error('不支持的类型')
|
||
return
|
||
}
|
||
|
||
// 打开生成的 URL
|
||
window.open(url)
|
||
}
|
||
// 关闭弹框
|
||
const onClose = () => {
|
||
Object.assign(filePreview, {
|
||
fileInfo: {},
|
||
excelConfig: {}
|
||
})
|
||
loading.value = false
|
||
visible.value = false
|
||
|
||
// 关闭时释放 Blob URL
|
||
if (blobUrl.value) {
|
||
URL.revokeObjectURL(blobUrl.value)
|
||
blobUrl.value = ''
|
||
}
|
||
}
|
||
|
||
onUnmounted(() => {
|
||
if (blobUrl.value) {
|
||
URL.revokeObjectURL(blobUrl.value)
|
||
}
|
||
})
|
||
|
||
defineExpose({ onPreview })
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.h-full {
|
||
height: 100vh;
|
||
}
|
||
|
||
.card {
|
||
background: white !important;
|
||
padding-bottom: 5px;
|
||
}
|
||
|
||
.toolbar {
|
||
position: absolute;
|
||
float: right;
|
||
right: 50px;
|
||
top: 10px;
|
||
}
|
||
</style>
|