<template>
|
<div class="submission-files">
|
<h4>提交资料</h4>
|
|
<div v-if="files && files.length > 0" class="files-grid">
|
<div
|
v-for="file in files"
|
:key="file.id"
|
class="file-item"
|
:class="getFileTypeClass(file)"
|
>
|
<!-- 图片预览 -->
|
<div v-if="isImage(file)" class="image-preview">
|
<el-image
|
:src="file.url"
|
:alt="file.name"
|
fit="cover"
|
:preview-src-list="imageUrls"
|
class="preview-image"
|
/>
|
<div class="file-info">
|
<span class="file-name">{{ file.name }}</span>
|
<span class="file-size">{{ formatFileSize(file.fileSize) }}</span>
|
</div>
|
</div>
|
|
<!-- 视频预览 -->
|
<div v-else-if="isVideo(file)" class="video-preview">
|
<video
|
:src="file.url"
|
controls
|
preload="metadata"
|
class="preview-video"
|
>
|
您的浏览器不支持视频播放
|
</video>
|
<div class="file-info">
|
<span class="file-name">{{ file.name }}</span>
|
<span class="file-size">{{ formatFileSize(file.fileSize) }}</span>
|
</div>
|
</div>
|
|
<!-- 文档预览 -->
|
<div v-else class="document-preview">
|
<div class="document-icon">
|
<el-icon :size="40">
|
<Document v-if="isDocument(file)" />
|
<Files v-else />
|
</el-icon>
|
</div>
|
<div class="file-info">
|
<span class="file-name">{{ file.name }}</span>
|
<span class="file-size">{{ formatFileSize(file.fileSize) }}</span>
|
<el-button
|
type="primary"
|
size="small"
|
@click="downloadFile(file)"
|
class="download-btn"
|
>
|
下载
|
</el-button>
|
</div>
|
</div>
|
</div>
|
</div>
|
|
<div v-else class="no-files">
|
<el-empty description="暂无提交资料" :image-size="80" />
|
</div>
|
</div>
|
</template>
|
|
<script setup>
|
import { computed } from 'vue'
|
import { ElImage, ElButton, ElIcon, ElEmpty } from 'element-plus'
|
import { Document, Files } from '@element-plus/icons-vue'
|
|
const props = defineProps({
|
files: {
|
type: Array,
|
default: () => []
|
}
|
})
|
|
// 图片文件URL列表(用于预览)
|
const imageUrls = computed(() => {
|
return props.files
|
.filter(file => isImage(file))
|
.map(file => file.url)
|
})
|
|
// 判断是否为图片
|
const isImage = (file) => {
|
const imageExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp']
|
return imageExts.includes(file.fileExt?.toLowerCase())
|
}
|
|
// 判断是否为视频
|
const isVideo = (file) => {
|
const videoExts = ['mp4', 'avi', 'mov', 'wmv', 'flv', 'webm']
|
return videoExts.includes(file.fileExt?.toLowerCase())
|
}
|
|
// 判断是否为文档
|
const isDocument = (file) => {
|
const docExts = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt']
|
return docExts.includes(file.fileExt?.toLowerCase())
|
}
|
|
// 获取文件类型样式类
|
const getFileTypeClass = (file) => {
|
if (isImage(file)) return 'file-image'
|
if (isVideo(file)) return 'file-video'
|
return 'file-document'
|
}
|
|
// 格式化文件大小
|
const formatFileSize = (bytes) => {
|
if (!bytes) return '未知大小'
|
|
const sizes = ['B', 'KB', 'MB', 'GB']
|
const i = Math.floor(Math.log(bytes) / Math.log(1024))
|
return Math.round(bytes / Math.pow(1024, i) * 100) / 100 + ' ' + sizes[i]
|
}
|
|
// 下载文件
|
const downloadFile = (file) => {
|
const link = document.createElement('a')
|
link.href = file.url
|
link.download = file.name
|
link.target = '_blank'
|
document.body.appendChild(link)
|
link.click()
|
document.body.removeChild(link)
|
}
|
</script>
|
|
<style scoped>
|
.submission-files {
|
margin-top: 20px;
|
}
|
|
.submission-files h4 {
|
margin: 0 0 16px 0;
|
color: #303133;
|
font-size: 16px;
|
font-weight: 600;
|
}
|
|
.files-grid {
|
display: grid;
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
gap: 16px;
|
}
|
|
.file-item {
|
border: 1px solid #EBEEF5;
|
border-radius: 8px;
|
overflow: hidden;
|
transition: all 0.3s;
|
}
|
|
.file-item:hover {
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
}
|
|
.image-preview,
|
.video-preview,
|
.document-preview {
|
height: 100%;
|
display: flex;
|
flex-direction: column;
|
}
|
|
.preview-image,
|
.preview-video {
|
width: 100%;
|
height: 120px;
|
object-fit: cover;
|
}
|
|
.document-preview {
|
padding: 20px;
|
text-align: center;
|
min-height: 160px;
|
justify-content: center;
|
}
|
|
.document-icon {
|
color: #909399;
|
margin-bottom: 12px;
|
}
|
|
.file-info {
|
padding: 12px;
|
background: #FAFAFA;
|
flex-grow: 1;
|
display: flex;
|
flex-direction: column;
|
gap: 4px;
|
}
|
|
.file-name {
|
font-weight: 500;
|
color: #303133;
|
font-size: 14px;
|
word-break: break-all;
|
line-height: 1.4;
|
}
|
|
.file-size {
|
color: #909399;
|
font-size: 12px;
|
}
|
|
.download-btn {
|
margin-top: 8px;
|
align-self: center;
|
}
|
|
.no-files {
|
text-align: center;
|
padding: 40px 20px;
|
color: #909399;
|
}
|
|
.file-image {
|
border-color: #67C23A;
|
}
|
|
.file-video {
|
border-color: #409EFF;
|
}
|
|
.file-document {
|
border-color: #E6A23C;
|
}
|
</style>
|