feat(review-meeting): 添加文件预览对话框组件
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>pull/874/head
parent
6f931beed6
commit
422fd7fa2e
|
|
@ -0,0 +1,144 @@
|
||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogVisible"
|
||||||
|
:title="previewTitle"
|
||||||
|
width="80%"
|
||||||
|
:close-on-click-modal="true"
|
||||||
|
class="file-preview-dialog"
|
||||||
|
@close="handleClose"
|
||||||
|
>
|
||||||
|
<div class="preview-container" v-loading="loading" element-loading-text="加载预览中...">
|
||||||
|
<div v-if="error" class="preview-error">
|
||||||
|
<el-result
|
||||||
|
icon="error"
|
||||||
|
title="预览加载失败"
|
||||||
|
:sub-title="error"
|
||||||
|
>
|
||||||
|
<template #extra>
|
||||||
|
<el-button type="primary" @click="handleOpenInNewWindow">新窗口打开</el-button>
|
||||||
|
</template>
|
||||||
|
</el-result>
|
||||||
|
</div>
|
||||||
|
<iframe
|
||||||
|
v-else-if="previewUrl"
|
||||||
|
:src="previewUrl"
|
||||||
|
class="preview-iframe"
|
||||||
|
frameborder="0"
|
||||||
|
@load="handleIframeLoad"
|
||||||
|
/>
|
||||||
|
<el-empty v-else-if="!loading" description="暂无预览内容" />
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed, watch } from 'vue'
|
||||||
|
import { getAttachmentPreviewUrl, type GetAttachmentPreviewUrlParams } from '@/api/review/meeting'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
modelValue: boolean
|
||||||
|
meetingId: number
|
||||||
|
attachmentType: 'agenda' | 'minutes'
|
||||||
|
fileName?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
fileName: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:modelValue': [value: boolean]
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const dialogVisible = computed({
|
||||||
|
get: () => props.modelValue,
|
||||||
|
set: (val) => emit('update:modelValue', val)
|
||||||
|
})
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const error = ref('')
|
||||||
|
const previewUrl = ref('')
|
||||||
|
|
||||||
|
const previewTitle = computed(() => {
|
||||||
|
const typeName = props.attachmentType === 'agenda' ? '议程附件' : '会议纪要'
|
||||||
|
return props.fileName ? `${props.fileName} - ${typeName}` : typeName
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(() => props.modelValue, async (visible) => {
|
||||||
|
if (visible && props.meetingId) {
|
||||||
|
await loadPreviewUrl()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const loadPreviewUrl = async () => {
|
||||||
|
loading.value = true
|
||||||
|
error.value = ''
|
||||||
|
previewUrl.value = ''
|
||||||
|
|
||||||
|
try {
|
||||||
|
const params: GetAttachmentPreviewUrlParams = {
|
||||||
|
id: props.meetingId,
|
||||||
|
attachmentType: props.attachmentType
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = await getAttachmentPreviewUrl(params)
|
||||||
|
|
||||||
|
if (!url) {
|
||||||
|
error.value = '未找到预览文件'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
previewUrl.value = url
|
||||||
|
} catch (err: any) {
|
||||||
|
error.value = err.message || '获取预览地址失败'
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleIframeLoad = () => {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
previewUrl.value = ''
|
||||||
|
error.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOpenInNewWindow = () => {
|
||||||
|
if (previewUrl.value) {
|
||||||
|
window.open(previewUrl.value, '_blank')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.file-preview-dialog :deep(.el-dialog__body) {
|
||||||
|
padding: 0;
|
||||||
|
height: 70vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 70vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-iframe {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-error {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue