142 lines
4.1 KiB
Vue
142 lines
4.1 KiB
Vue
<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 约 50px,padding 约 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>
|
||
|
||
|