跟新
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
<span class="flex-1 text-gray-700 truncate transition-colors duration-200 group-hover:text-blue-600">
|
<span class="flex-1 text-gray-700 truncate transition-colors duration-200 group-hover:text-blue-600">
|
||||||
{{ getFileName(file) }}
|
{{ getFileName(file) }}
|
||||||
</span>
|
</span>
|
||||||
|
<el-icon class="mr-2 text-gray-400 transition-colors duration-200 group-hover:text-blue-500" @click.stop="handlePreview(file)"><View /></el-icon>
|
||||||
<el-icon class="text-gray-400 transition-colors duration-200 group-hover:text-blue-500"><Download /></el-icon>
|
<el-icon class="text-gray-400 transition-colors duration-200 group-hover:text-blue-500"><Download /></el-icon>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -69,6 +70,24 @@
|
|||||||
<span class="file-name" style="flex: 1; font-size: 14px; color: #606266; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
|
<span class="file-name" style="flex: 1; font-size: 14px; color: #606266; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
|
||||||
{{ getFileName(file) }}
|
{{ getFileName(file) }}
|
||||||
</span>
|
</span>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
size="small"
|
||||||
|
@click="handlePreview(file)"
|
||||||
|
style="margin-left: 4px;"
|
||||||
|
>
|
||||||
|
<el-icon><View /></el-icon>
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
size="small"
|
||||||
|
@click="handleDownload(file)"
|
||||||
|
style="margin-left: 4px;"
|
||||||
|
>
|
||||||
|
<el-icon><Download /></el-icon>
|
||||||
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
type="danger"
|
type="danger"
|
||||||
link
|
link
|
||||||
@@ -78,15 +97,6 @@
|
|||||||
>
|
>
|
||||||
<el-icon><Delete /></el-icon>
|
<el-icon><Delete /></el-icon>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
link
|
|
||||||
size="small"
|
|
||||||
@click="handlePreview(file)"
|
|
||||||
style="margin-left: 4px;"
|
|
||||||
>
|
|
||||||
<el-icon><Download /></el-icon>
|
|
||||||
</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -124,15 +134,48 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 图片预览:使用 teleport + 最高 z-index 确保全屏 -->
|
||||||
|
<teleport to="body">
|
||||||
|
<div
|
||||||
|
v-if="imagePreviewVisible"
|
||||||
|
class="image-preview-overlay"
|
||||||
|
@click="imagePreviewVisible = false"
|
||||||
|
>
|
||||||
|
<img :src="imagePreviewUrl" class="image-preview-img" />
|
||||||
|
<button class="image-preview-close" @click.stop="imagePreviewVisible = false">✕</button>
|
||||||
|
</div>
|
||||||
|
</teleport>
|
||||||
|
|
||||||
|
<!-- PDF预览:使用 teleport -->
|
||||||
|
<teleport to="body">
|
||||||
|
<el-dialog
|
||||||
|
v-model="pdfPreviewVisible"
|
||||||
|
title="文件预览"
|
||||||
|
:top="'5vh'"
|
||||||
|
width="90%"
|
||||||
|
:center-dialog="true"
|
||||||
|
:z-index="9999"
|
||||||
|
:close-on-click-modal="true"
|
||||||
|
append-to-body
|
||||||
|
class="pdf-preview-teleport"
|
||||||
|
>
|
||||||
|
<iframe
|
||||||
|
:src="pdfPreviewUrl"
|
||||||
|
style="width: 100%; height: 80vh; border: none;"
|
||||||
|
/>
|
||||||
|
</el-dialog>
|
||||||
|
</teleport>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts" name="upload-file">
|
<script setup lang="ts" name="upload-file">
|
||||||
import { useMessage } from '/@/hooks/message';
|
import { useMessage } from '/@/hooks/message';
|
||||||
import { Session } from '/@/utils/storage';
|
import { Session } from '/@/utils/storage';
|
||||||
import other from '/@/utils/other';
|
import other from '/@/utils/other';
|
||||||
|
import request from '/@/utils/request';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { ref, computed, watch } from 'vue';
|
import { ref, computed, watch } from 'vue';
|
||||||
import { Document, Download, Upload, Delete } from '@element-plus/icons-vue';
|
import { Document, Download, Upload, Delete, View } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
// 定义基础URL
|
// 定义基础URL
|
||||||
const baseUrl = import.meta.env.VITE_API_URL || '';
|
const baseUrl = import.meta.env.VITE_API_URL || '';
|
||||||
@@ -207,6 +250,11 @@ const props = defineProps({
|
|||||||
type: String,
|
type: String,
|
||||||
default: '/admin/sys-file/upload',
|
default: '/admin/sys-file/upload',
|
||||||
},
|
},
|
||||||
|
// 自定义下载/预览URL,如果不传则根据uploadFileUrl自动推导
|
||||||
|
downloadFileUrl: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'default',
|
default: 'default',
|
||||||
@@ -240,6 +288,14 @@ const uploadList = ref<UploadFileItem[]>([]);
|
|||||||
const fileUpload = ref();
|
const fileUpload = ref();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
// 图片预览状态
|
||||||
|
const imagePreviewVisible = ref(false);
|
||||||
|
const imagePreviewUrl = ref('');
|
||||||
|
|
||||||
|
// PDF预览状态
|
||||||
|
const pdfPreviewVisible = ref(false);
|
||||||
|
const pdfPreviewUrl = ref('');
|
||||||
|
|
||||||
// 请求头处理
|
// 请求头处理
|
||||||
const headers = computed(() => {
|
const headers = computed(() => {
|
||||||
return {
|
return {
|
||||||
@@ -248,6 +304,22 @@ const headers = computed(() => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 推导下载URL:优先使用传入的downloadFileUrl,否则根据uploadFileUrl自动推导
|
||||||
|
const downloadBaseUrl = computed(() => {
|
||||||
|
if (props.downloadFileUrl) {
|
||||||
|
return props.downloadFileUrl;
|
||||||
|
}
|
||||||
|
// 根据uploadFileUrl自动推导下载URL
|
||||||
|
if (props.uploadFileUrl.includes('/purchase/purchasingfiles/')) {
|
||||||
|
return '/purchase/purchasingfiles/downloadById';
|
||||||
|
}
|
||||||
|
if (props.uploadFileUrl.includes('/admin/sys-file/')) {
|
||||||
|
return '/admin/sys-file/download';
|
||||||
|
}
|
||||||
|
// 默认返回空字符串,使用URL直接下载
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
// 请求参数处理
|
// 请求参数处理
|
||||||
const formData = computed(() => {
|
const formData = computed(() => {
|
||||||
return Object.assign(props.data, { dir: props.dir });
|
return Object.assign(props.data, { dir: props.dir });
|
||||||
@@ -376,11 +448,12 @@ const handleRemove = (file: { name?: string; id?: string; url?: string }) => {
|
|||||||
emit('change', listToString(fileList.value), fileList.value);
|
emit('change', listToString(fileList.value), fileList.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePreview = (file: any) => {
|
const handleDownload = (file: any) => {
|
||||||
// 优先使用文件ID下载(采购附件使用 downloadById 接口)
|
// 优先使用文件ID下载(如果有自定义下载URL)
|
||||||
if (file.id) {
|
if (file.id && downloadBaseUrl.value) {
|
||||||
// 判断是否是采购附件(通过 fileType 判断)
|
const downloadUrl = downloadBaseUrl.value.includes('?')
|
||||||
const downloadUrl = `/purchase/purchasingfiles/downloadById?fileId=${encodeURIComponent(file.id)}`;
|
? `${downloadBaseUrl.value}&fileId=${encodeURIComponent(file.id)}`
|
||||||
|
: `${downloadBaseUrl.value}?fileId=${encodeURIComponent(file.id)}`;
|
||||||
other.downBlobFile(downloadUrl, {}, file.name || file.fileTitle || '文件');
|
other.downBlobFile(downloadUrl, {}, file.name || file.fileTitle || '文件');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -392,6 +465,91 @@ const handlePreview = (file: any) => {
|
|||||||
useMessage().warning('无法获取文件下载信息');
|
useMessage().warning('无法获取文件下载信息');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handlePreview = async (file: any) => {
|
||||||
|
// 获取文件URL
|
||||||
|
let fileUrl = '';
|
||||||
|
if (file.id && downloadBaseUrl.value) {
|
||||||
|
// 使用配置的下载URL
|
||||||
|
fileUrl = downloadBaseUrl.value.includes('?')
|
||||||
|
? `${downloadBaseUrl.value}&fileId=${encodeURIComponent(file.id)}`
|
||||||
|
: `${downloadBaseUrl.value}?fileId=${encodeURIComponent(file.id)}`;
|
||||||
|
} else if (file.id) {
|
||||||
|
// 兼容旧的ID方式
|
||||||
|
fileUrl = `/purchase/purchasingfiles/downloadById?fileId=${encodeURIComponent(file.id)}`;
|
||||||
|
} else if (file.url) {
|
||||||
|
fileUrl = file.url;
|
||||||
|
} else {
|
||||||
|
useMessage().warning('无法获取文件信息');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取文件名:优先使用 name,其次从 url 提取
|
||||||
|
let fileName = file.name || file.fileTitle || '';
|
||||||
|
if (!fileName && file.url) {
|
||||||
|
// 从 URL 中提取文件名
|
||||||
|
try {
|
||||||
|
const urlObj = new URL(file.url, window.location.origin);
|
||||||
|
const nameFromUrl = urlObj.searchParams.get('originalFileName') || urlObj.searchParams.get('fileName');
|
||||||
|
if (nameFromUrl) {
|
||||||
|
fileName = nameFromUrl;
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
if (!fileName) {
|
||||||
|
fileName = '文件';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取文件扩展名
|
||||||
|
const ext = fileName.split('.').pop()?.toLowerCase() || '';
|
||||||
|
|
||||||
|
// 判断是否是图片
|
||||||
|
const imageExts = ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp'];
|
||||||
|
// 判断是否是PDF
|
||||||
|
const isPdf = ext === 'pdf';
|
||||||
|
|
||||||
|
// 检查是否是图片
|
||||||
|
const isImage = imageExts.includes(ext);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (isImage) {
|
||||||
|
// 图片预览:使用 el-image-viewer(teleported 到 body,全屏显示)
|
||||||
|
const response = await request({
|
||||||
|
url: fileUrl,
|
||||||
|
method: 'get',
|
||||||
|
responseType: 'blob',
|
||||||
|
}) as unknown as Response;
|
||||||
|
if (!response || response.type === 'error') {
|
||||||
|
useMessage().error('获取文件失败');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const blob = response as unknown as Blob;
|
||||||
|
imagePreviewUrl.value = window.URL.createObjectURL(blob);
|
||||||
|
imagePreviewVisible.value = true;
|
||||||
|
} else if (isPdf) {
|
||||||
|
// PDF预览:使用 el-dialog(append-to-body,全屏显示)
|
||||||
|
const response = await request({
|
||||||
|
url: fileUrl,
|
||||||
|
method: 'get',
|
||||||
|
responseType: 'blob',
|
||||||
|
}) as unknown as Response;
|
||||||
|
if (!response || response.type === 'error') {
|
||||||
|
useMessage().error('获取文件失败');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const blob = response as unknown as Blob;
|
||||||
|
pdfPreviewUrl.value = window.URL.createObjectURL(blob);
|
||||||
|
pdfPreviewVisible.value = true;
|
||||||
|
} else {
|
||||||
|
// 其他类型文件,提示无法预览,只能下载
|
||||||
|
useMessage().warning('该文件类型暂不支持预览,请下载查看');
|
||||||
|
handleDownload(file);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('预览失败:', error);
|
||||||
|
useMessage().error('预览失败');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 添加 handleExceed 函数
|
// 添加 handleExceed 函数
|
||||||
const handleExceed = () => {
|
const handleExceed = () => {
|
||||||
useMessage().warning(`${t('excel.uploadLimit')} ${props.limit} ${t('excel.files')}`);
|
useMessage().warning(`${t('excel.uploadLimit')} ${props.limit} ${t('excel.files')}`);
|
||||||
@@ -481,3 +639,62 @@ defineExpose({
|
|||||||
submit,
|
submit,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.w-full {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.upload-file {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.mb20 {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- 全局样式:图片预览遮罩(teleport 到 body,不受 scoped 限制) -->
|
||||||
|
<style>
|
||||||
|
.image-preview-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background: rgba(0, 0, 0, 0.9);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 99999;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview-img {
|
||||||
|
max-width: 95%;
|
||||||
|
max-height: 95%;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview-close {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: 30px;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-preview-close:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* PDF 预览对话框样式 */
|
||||||
|
.pdf-preview-teleport .el-dialog__body {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user