Files
school-developer/src/components/tools/preview-file.vue
guochunsi 9e3e775b0f ren
2026-01-07 18:33:03 +08:00

142 lines
4.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<!-- 组件不占据任何布局空间所有预览组件都是 teleported -->
<div style="display: none;">
<!-- 图片直接使用 el-image-viewer 全屏预览 -->
<el-image-viewer
v-if="!showIframe && imageSrc && imagePreviewVisible"
:url-list="[imageSrc]"
:teleported="true"
hide-on-click-modal
@close="imagePreviewVisible = false"
/>
<!-- PDF dialog 中显示 -->
<el-dialog
v-if="showIframe"
v-model="pdfDialogVisible"
:title="dialogTitle || '文件预览'"
append-to-body
width="90%"
class="pdf-preview-dialog"
>
<iframe ref="authIframeRef" :style="{ width: '100%', height: pdfIframeHeight }" />
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, nextTick, computed, onUnmounted } from 'vue';
import { ElImageViewer } from 'element-plus';
import { Session } from "/@/utils/storage";
// 定义 props
const props = defineProps<{
authSrc: string;
imgWidth?: string;
imgHeight?: string;
dialogTitle?: string;
}>();
// 定义响应式数据
const showIframe = ref(false);
const imageSrc = ref<string>('');
const imagePreviewVisible = ref(false);
const pdfDialogVisible = ref(false);
const authIframeRef = ref<HTMLIFrameElement | null>(null);
const windowHeight = ref(window.innerHeight);
// 计算 PDF iframe 的合适高度(优先使用外部传入的 imgHeight否则根据窗口高度动态计算
const pdfIframeHeight = computed(() => {
// 如果外部传入了 imgHeight优先使用
if (props.imgHeight) {
return props.imgHeight;
}
// 否则根据窗口高度动态计算dialog header 约 50pxpadding 约 40px留一些余量
return `${windowHeight.value - 120}px`;
});
// 监听窗口大小变化
const handleResize = () => {
windowHeight.value = window.innerHeight;
};
onMounted(() => {
window.addEventListener('resize', handleResize);
getImgSrcByToken();
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
});
// 携带token请求img的src
const getImgSrcByToken = (src?: string) => {
if (props.authSrc.indexOf(".pdf") >= 0) {
showIframe.value = true;
pdfDialogVisible.value = true;
nextTick(() => {
const imgSrc = src || props.authSrc;
const tenantId = Session.getTenant();
const iframe = authIframeRef.value;
if (!iframe) return;
const request = new XMLHttpRequest();
request.responseType = 'blob';
request.open('get', imgSrc, true);
request.setRequestHeader('Authorization', "Bearer " + Session.getToken());
request.setRequestHeader('TENANT-ID', tenantId);
request.onreadystatechange = () => {
if (request.readyState == XMLHttpRequest.DONE && request.status == 200) {
const binaryData: BlobPart[] = [];
binaryData.push(request.response);
iframe.src = window.URL.createObjectURL(new Blob(binaryData, { type: 'application/pdf' }));
iframe.onload = () => {
URL.revokeObjectURL(iframe.src);
};
}
};
request.send(null);
});
} else {
// 图片处理逻辑:加载后直接打开预览
showIframe.value = false;
pdfDialogVisible.value = false;
const imgSrc = src || props.authSrc;
const tenantId = Session.getTenant();
const request = new XMLHttpRequest();
request.responseType = 'blob';
request.open('get', imgSrc, true);
request.setRequestHeader('Authorization', "Bearer " + Session.getToken());
request.setRequestHeader('TENANT-ID', tenantId);
request.onreadystatechange = () => {
if (request.readyState == XMLHttpRequest.DONE && request.status == 200) {
imageSrc.value = URL.createObjectURL(request.response);
imagePreviewVisible.value = true;
}
};
request.send(null);
}
};
// 刷新图片
const refreshImg = (src?: string) => {
getImgSrcByToken(src);
};
// 暴露方法供外部调用
defineExpose({
refreshImg
});
</script>
<style scoped>
.pdf-preview-dialog :deep(.el-dialog__body) {
padding: 20px !important;
overflow-y: hidden !important;
}
</style>