Commit b32db88e by yuzhenWang

Merge branch 'wyz' into 'test'

修改预约附件可预览文件

See merge request !142
parents a0e22287 c53ccfbd
......@@ -2,21 +2,14 @@
<div class="uploadContainer">
<CardOne title="材料信息">
<template #headerRight>
<div style="margin-top: 20px;">
<el-button
type="primary"
:loading="downloading"
@click="handleBatchDownloadSelected"
>
<div style="margin-top: 20px">
<el-button type="primary" :loading="downloading" @click="handleBatchDownloadSelected">
{{ downloading ? '正在打包中...' : '下载材料包' }}
</el-button>
<div v-if="downloading" style="margin-top: 10px;">
<el-progress
:percentage="progressPercentage"
:format="progressFormat"
/>
<div style="font-size: 12px; color: #666; margin-top: 5px;">
<div v-if="downloading" style="margin-top: 10px">
<el-progress :percentage="progressPercentage" :format="progressFormat" />
<div style="font-size: 12px; color: #666; margin-top: 5px">
已处理文件:{{ currentCount }} / {{ totalCount }}
</div>
</div>
......@@ -120,7 +113,7 @@
</div>
</el-upload>
</div>
<div class="tip">(支持Word,Excel,PDF,图片格式)</div>
<div class="tip">(支持PDF,图片格式)</div>
</div>
<div class="dialogRight">
<div
......@@ -136,14 +129,55 @@
class="uploaded-file-item"
>
<div class="fileName">{{ file.originalName }}</div>
<el-icon color="red" size="18" @click="removeUploadedFile(file, index)"
<div>
<el-button type="primary" size="small" link @click="previewFile(file)">
查看
</el-button>
<el-button type="danger" size="small" link @click="removeUploadedFile(file, index)">
删除
</el-button>
</div>
<!-- <el-icon color="red" size="18" @click="removeUploadedFile(file, index)"
><Delete
/></el-icon>
/></el-icon> -->
</div>
</el-scrollbar>
</div>
</div>
</CommonDialog>
<!-- 文件预览弹窗(页面内查看,不打开新窗口) -->
<el-dialog
v-model="previewDialogVisible"
:title="previewFileName"
width="70%"
:close-on-click-modal="false"
destroy-on-close
@close="previewUrl = ''"
>
<div class="preview-container">
<!-- 图片预览 -->
<div v-if="previewFileType === 'image'" class="preview-image-wrapper">
<img :src="previewUrl" class="preview-image" alt="预览图片" />
</div>
<!-- PDF 预览(使用 iframe 内嵌) -->
<iframe
v-else-if="previewFileType === 'pdf'"
:src="previewUrl"
class="preview-pdf"
frameborder="0"
></iframe>
<!-- 不支持预览的文件类型 -->
<div v-else-if="previewFileType === 'unsupported'" class="preview-unsupported">
<el-icon :size="48" color="#909399"><Document /></el-icon>
<p>暂不支持预览此类型文件</p>
<el-button type="primary" @click="previewDialogVisible = false"> 关闭 </el-button>
</div>
</div>
</el-dialog>
<el-dialog v-model="imageViewerVisible" title="图片预览" width="60%">
<div style="text-align: center">
<el-image :src="imageUrl" fit="contain" />
......@@ -153,9 +187,9 @@
</template>
<script setup name="FileUpload">
import { ref } from 'vue';
import { ElMessage } from 'element-plus';
import { downloadFilesAsZip } from '@/utils/zipDownload'; // 引入刚才封装的工具
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { downloadFilesAsZip } from '@/utils/zipDownload' // 引入刚才封装的工具
import CommonDialog from '@/components/commonDialog'
import CardOne from '@/components/formCard/cardOne'
import { getToken } from '@/utils/auth'
......@@ -188,6 +222,40 @@ const limit = ref(10)
const fileSize = ref(10)
const headers = ref({ Authorization: 'Bearer ' + getToken() })
const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + '/oss/api/oss/upload') // 上传的服务器地址
// ==================== 文件预览弹窗 ====================
const previewDialogVisible = ref(false)
const previewUrl = ref('')
const previewFileName = ref('')
const previewFileType = ref('') // 'image', 'pdf', 'unsupported'
// 预览文件(页面内弹窗,不打开新窗口)
function previewFile(file) {
const url = file.url || file.fileUrl
if (!url) {
ElMessage.warning('文件地址不存在')
return
}
const ext = (file.originalName || '').split('.').pop().toLowerCase()
previewUrl.value = url
previewFileName.value = file.originalName || '文件'
if (['jpg', 'jpeg', 'png', 'webp', 'gif', 'bmp', 'svg'].includes(ext)) {
previewFileType.value = 'image'
previewDialogVisible.value = true
} else if (ext === 'pdf') {
previewFileType.value = 'pdf'
previewDialogVisible.value = true
} else {
// 不支持预览的文件类型,弹窗显示提示
previewFileType.value = 'unsupported'
previewDialogVisible.value = true
}
console.log('====================================')
console.log('previewUrl.value', previewUrl.value)
console.log('previewFileName.value', previewFileName.value)
console.log('previewFileType.value', previewFileType.value)
console.log('====================================')
}
// 图片查看相关状态
const imageViewerVisible = ref(false)
const imageUrl = ref('')
......@@ -201,21 +269,25 @@ const data = reactive({
// 下载材料包
// 状态管理
const downloading = ref(false);
const currentCount = ref(0);
const totalCount = ref(0);
const downloading = ref(false)
const currentCount = ref(0)
const totalCount = ref(0)
const viewFile = file => {
console.log('====================================')
console.log('file', file)
console.log('====================================')
}
const progressPercentage = computed(() => {
if (totalCount.value === 0) return 0;
return Math.floor((currentCount.value / totalCount.value) * 100);
});
if (totalCount.value === 0) return 0
return Math.floor((currentCount.value / totalCount.value) * 100)
})
const progressFormat = (percentage) => `${currentCount.value}/${totalCount.value}`;
const progressFormat = percentage => `${currentCount.value}/${totalCount.value}`
// 2. 核心处理方法
const handleBatchDownloadSelected = async () => {
let apiMaterialDtoList = []
if (!props.idsObj.appointmentBizId) {
if (!props.idsObj.appointmentBizId) {
apiMaterialDtoList = fileTableList.value.map(item => {
return {
dataPerson: item.dataPerson, //资料人(字典)
......@@ -228,7 +300,7 @@ if (!props.idsObj.appointmentBizId) {
}))
}
})
}else{
} else {
apiMaterialDtoList = fileTableList.value.map(item => {
return {
dataPerson: item.dataPerson, //资料人(字典)
......@@ -238,40 +310,40 @@ if (!props.idsObj.appointmentBizId) {
fileUrlList: item.fileUrlList
}
})
}
}
if (!apiMaterialDtoList || apiMaterialDtoList.length === 0) {
ElMessage.warning('没有要下载的材料');
return;
ElMessage.warning('没有要下载的材料')
return
}
// --- 步骤 1: 数据清洗与扁平化 ---
const flatFileList = [];
let hasFiles = false;
apiMaterialDtoList.forEach((item,index) => {
const flatFileList = []
let hasFiles = false
apiMaterialDtoList.forEach((item, index) => {
// 安全检查:确保 fileUrlList 存在且是数组
const urls = item.fileUrlList;
const urls = item.fileUrlList
if (!urls || !Array.isArray(urls) || urls.length === 0) {
return; // 跳过没有文件的行
return // 跳过没有文件的行
}
hasFiles = true;
hasFiles = true
// 生成安全的业务前缀
// 规则:[人员类型]_[资料类型]_[业务ID]
// 例如:POLICYHOLDER_FRONT_2216
const safePerson = (item.dataPersonName || 'UNKNOWN').replace(/[\/\\:*?"<>|]/g, '_');
const safeType = (item.dataTypeName || 'FILE').replace(/[\/\\:*?"<>|]/g, '_');
const safePerson = (item.dataPersonName || 'UNKNOWN').replace(/[\/\\:*?"<>|]/g, '_')
const safeType = (item.dataTypeName || 'FILE').replace(/[\/\\:*?"<>|]/g, '_')
const filePrefix = `${safePerson}_${safeType}`;
const filePrefix = `${safePerson}_${safeType}`
urls.forEach((fileItem, fIndex) => {
// 兼容 fileUrlList 可能是字符串数组 或 对象数组
let fileUrl = fileItem.fileUrl;
let originalFileName = fileItem.fileName;
let fileUrl = fileItem.fileUrl
let originalFileName = fileItem.fileName
// --- 关键:构建最终文件名 ---
const finalFileName = `${filePrefix}_${originalFileName}`;
const finalFileName = `${filePrefix}_${originalFileName}`
if (fileUrl) {
flatFileList.push({
......@@ -282,41 +354,36 @@ if (!props.idsObj.appointmentBizId) {
type: item.dataType,
note: item.precautions
}
});
})
}
});
});
})
})
if (!hasFiles) {
ElMessage.warning('选中的项中没有包含任何附件');
return;
ElMessage.warning('选中的项中没有包含任何附件')
return
}
// --- 步骤 2: 执行下载 ---
totalCount.value = flatFileList.length;
currentCount.value = 0;
downloading.value = true;
totalCount.value = flatFileList.length
currentCount.value = 0
downloading.value = true
try {
const zipName = `预约附件包_${new Date().toISOString().slice(0, 10)}.zip`;
const zipName = `预约附件包_${new Date().toISOString().slice(0, 10)}.zip`
await downloadFilesAsZip(flatFileList, zipName, (current, total) => {
currentCount.value = current;
});
currentCount.value = current
})
ElMessage.success(`成功打包 ${flatFileList.length} 个文件`);
ElMessage.success(`成功打包 ${flatFileList.length} 个文件`)
} catch (error) {
ElMessage.error('下载过程中出现异常,请查看控制台');
console.error(error);
ElMessage.error('下载过程中出现异常,请查看控制台')
console.error(error)
} finally {
downloading.value = false;
downloading.value = false
}
};
}
const { queryParams, form } = toRefs(data)
// 新增:用于存储已上传成功的文件列表
......@@ -444,7 +511,7 @@ const handleView = row => {
const downloadFile = () => {
let apiMaterialDtoList = []
let params = {
projectBizId:userStore.projectInfo.projectBizId || '',
projectBizId: userStore.projectInfo.projectBizId || '',
objectName: '预约附件材料包', //对象名(包名)
objectBizId: '' //对象业务ID
}
......@@ -522,7 +589,7 @@ function handleExceed() {
// 文件上传成功回调
const uploadSuccess = (res, file, fileList) => {
console.log('上传成功', res, file)
proxy.$modal.closeLoading();
proxy.$modal.closeLoading()
if (res.code === 200) {
// 构造前端使用的文件对象(保留原始 file 信息 + 后端返回的 url 等)
const uploadedFile = {
......@@ -614,6 +681,32 @@ defineExpose({
})
</script>
<style lang="scss" scoped>
.preview-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 400px;
}
.preview-image-wrapper {
text-align: center;
}
.preview-image {
max-width: 100%;
max-height: 70vh;
object-fit: contain;
}
.preview-pdf {
width: 100%;
height: 70vh;
}
.preview-unsupported {
text-align: center;
padding: 40px;
}
.preview-unsupported p {
margin: 16px 0;
color: #909399;
}
.uploadContainer {
padding-left: 10px;
padding-top: 10px;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment