|
|
|
|
@ -0,0 +1,164 @@
|
|
|
|
|
<template>
|
|
|
|
|
<div v-loading="loading" class="oss-file-preview">
|
|
|
|
|
<div v-if="imageFiles.length" class="oss-file-preview__images">
|
|
|
|
|
<div v-for="(file, index) in imageFiles" :key="file.ossId || index" class="oss-file-preview__item">
|
|
|
|
|
<el-image
|
|
|
|
|
:src="file.url"
|
|
|
|
|
fit="cover"
|
|
|
|
|
:style="{ width: thumbSize, height: thumbSize }"
|
|
|
|
|
:preview-src-list="imagePreviewList"
|
|
|
|
|
:initial-index="index"
|
|
|
|
|
preview-teleported
|
|
|
|
|
>
|
|
|
|
|
<template #error>
|
|
|
|
|
<div class="oss-file-preview__error">
|
|
|
|
|
<el-icon><picture-filled /></el-icon>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</el-image>
|
|
|
|
|
<span v-if="showFileName" class="oss-file-preview__name" :title="getDisplayName(file)">{{ getDisplayName(file) }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<ul v-if="otherFiles.length && !imageOnly" class="oss-file-preview__files">
|
|
|
|
|
<li v-for="file in otherFiles" :key="file.ossId" class="oss-file-preview__file-item">
|
|
|
|
|
<el-icon><document /></el-icon>
|
|
|
|
|
<el-link type="primary" :underline="false" @click="handleDownload(file)">{{ getDisplayName(file) }}</el-link>
|
|
|
|
|
</li>
|
|
|
|
|
</ul>
|
|
|
|
|
|
|
|
|
|
<span v-if="!loading && !fileList.length" class="oss-file-preview__empty">{{ emptyText }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { listByIds } from '@/api/system/oss';
|
|
|
|
|
import type { OssVO } from '@/api/system/oss/types';
|
|
|
|
|
import { isImageOssFile, parseOssIds } from '@/utils/ossPreview';
|
|
|
|
|
import { propTypes } from '@/utils/propTypes';
|
|
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
/** OSS 文件 ID,支持逗号分隔字符串、单个 ID 或 ID 数组 */
|
|
|
|
|
ossIds: {
|
|
|
|
|
type: [String, Number, Array],
|
|
|
|
|
default: ''
|
|
|
|
|
},
|
|
|
|
|
/** 缩略图尺寸 */
|
|
|
|
|
thumbSize: propTypes.string.def('80px'),
|
|
|
|
|
/** 是否仅展示图片 */
|
|
|
|
|
imageOnly: propTypes.bool.def(false),
|
|
|
|
|
/** 是否显示文件名 */
|
|
|
|
|
showFileName: propTypes.bool.def(false),
|
|
|
|
|
/** 无附件时的提示文案 */
|
|
|
|
|
emptyText: propTypes.string.def('暂无附件')
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
|
|
|
|
|
|
|
const loading = ref(false);
|
|
|
|
|
const fileList = ref<OssVO[]>([]);
|
|
|
|
|
|
|
|
|
|
const imageFiles = computed(() => fileList.value.filter((file) => isImageOssFile(file)));
|
|
|
|
|
const otherFiles = computed(() => fileList.value.filter((file) => !isImageOssFile(file)));
|
|
|
|
|
const imagePreviewList = computed(() => imageFiles.value.map((file) => file.url));
|
|
|
|
|
|
|
|
|
|
const getDisplayName = (file: OssVO) => file.originalName || file.fileName || '未命名文件';
|
|
|
|
|
|
|
|
|
|
const loadFiles = async (value?: string | number | Array<string | number>) => {
|
|
|
|
|
const ids = parseOssIds(value);
|
|
|
|
|
if (!ids.length) {
|
|
|
|
|
fileList.value = [];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
loading.value = true;
|
|
|
|
|
try {
|
|
|
|
|
const res = await listByIds(ids.join(','));
|
|
|
|
|
fileList.value = res.data || [];
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('加载附件预览失败:', error);
|
|
|
|
|
fileList.value = [];
|
|
|
|
|
} finally {
|
|
|
|
|
loading.value = false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleDownload = (file: OssVO) => {
|
|
|
|
|
if (file.ossId) {
|
|
|
|
|
proxy?.$download.oss(file.ossId);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
watch(
|
|
|
|
|
() => props.ossIds,
|
|
|
|
|
(val) => {
|
|
|
|
|
loadFiles(val);
|
|
|
|
|
},
|
|
|
|
|
{ immediate: true }
|
|
|
|
|
);
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
.oss-file-preview {
|
|
|
|
|
min-height: 32px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.oss-file-preview__images {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.oss-file-preview__item {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 4px;
|
|
|
|
|
max-width: 120px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.oss-file-preview__name {
|
|
|
|
|
width: 100%;
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
color: #606266;
|
|
|
|
|
text-align: center;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.oss-file-preview__error {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
color: #909399;
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
background: #f5f7fa;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.oss-file-preview__files {
|
|
|
|
|
margin: 8px 0 0;
|
|
|
|
|
padding: 0;
|
|
|
|
|
list-style: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.oss-file-preview__file-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
line-height: 28px;
|
|
|
|
|
color: #606266;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.oss-file-preview__empty {
|
|
|
|
|
color: #909399;
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.el-image) {
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
border: 1px solid #ebeef5;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
</style>
|