<template>
|
<el-upload
|
ref="upload"
|
class="my-3 uploader" action="#"
|
v-model:file-list="list"
|
:accept="acceptType"
|
:list-type="listType"
|
:http-request="uploadRequest"
|
:multiple="true"
|
:limit="limitFileCount"
|
:on-preview="handlePreview"
|
:on-remove="handleRemove"
|
:before-remove="beforeRemove"
|
:on-exceed="handleExceed"
|
:disabled="disabled"
|
:class="{ hideUpload: hideUpload }"
|
>
|
<template #default>
|
<template v-if="listType=='picture-card'">
|
<el-icon v-if="!disabled"><Plus /></el-icon>
|
<el-text v-if="disabled&&list.length==0">暂未上传</el-text>
|
</template>
|
<template v-else>
|
<el-row v-if="!disabled">
|
<el-button type="primary">
|
<el-row align="middle">
|
<Icon icon="material-symbols:upload-rounded" width="20" height="20" style="color: #fff" />
|
<el-text class="text-white ml-1">{{ againUploadFlag ? '重新上传' : '立即上传' }}</el-text>
|
</el-row>
|
</el-button>
|
<el-text style="color: #666666" class="ml-4">
|
{{ tip }}
|
</el-text>
|
</el-row>
|
<el-row v-else>
|
<el-text class="ml-4" style="color: #a8abb2" v-if="disabled&&list.length==0">未上传文件</el-text>
|
</el-row>
|
</template>
|
</template>
|
<template #file="{ file, index }">
|
<template v-if="listType=='picture-card'">
|
<el-image
|
ref="previewImg"
|
:src="file.url?.includes('http') ? file.url : $qxueyou.qxyRes + file.url"
|
:initial-index="initialPreviewImgIndex"
|
:preview-src-list="filterPreviewImgList"
|
>
|
</el-image>
|
<span class="el-upload-list__item-actions">
|
<span @click="previewImage(index)">
|
<el-icon><zoom-in /></el-icon>
|
</span>
|
<span @click="deleteFileItem(index)">
|
<el-icon><delete /></el-icon>
|
</span>
|
</span>
|
</template>
|
<template v-else>
|
<div class="file-box cursor-p" :style="{ 'margin-top': disabled && index==0 ? '-30px' : '' }">
|
<Icon
|
v-if="!disabled"
|
@click.stop="deleteFileItem(index)"
|
class="cursor-p mr-3"
|
icon="eva:close-fill"
|
width="18"
|
height="18"
|
style="color: #666666"
|
/>
|
<FilePreview
|
:name="file.name"
|
:url="file.url"
|
:list="list"
|
:index="index"
|
></FilePreview>
|
</div>
|
</template>
|
</template>
|
</el-upload>
|
|
<ElImageViewer
|
v-if="showImageViewer"
|
:url-list="filterPreviewImgList"
|
:initial-index="initialPreviewImgIndex"
|
@close="showImageViewer=false"
|
>
|
</ElImageViewer>
|
|
<el-dialog v-model="pdfPreviewFlag" top="5vh">
|
<div style="height: 84vh;">
|
<PdfPreview v-if="previewUrl&&pdfPreviewFlag" :url="previewUrl"></PdfPreview>
|
</div>
|
</el-dialog>
|
|
</template>
|
|
<script>
|
const pdf = 'application/pdf'
|
const xls = 'application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
const doc = 'application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
const image = 'image/*'
|
const jpg = 'image/jpeg'
|
const png = 'image/png'
|
const zip = 'application/zip,application/x-zip-compressed'
|
import { genFileId, ElImageViewer } from 'element-plus'
|
import { getFileUrlName, getFileUrlType } from '@/utils/tool.js'
|
import FilePreview from '@/views/components/FilePreview.vue'
|
import PdfPreview from '@/views/components/PdfPreview.vue'
|
export default {
|
components: {
|
ElImageViewer,
|
PdfPreview,
|
FilePreview
|
},
|
data() {
|
return {
|
list: [],
|
previewUrl: '',
|
pdfPreviewFlag: false,
|
initialPreviewImgIndex: 0,
|
showImageViewer: false,
|
}
|
},
|
props: {
|
listType: {
|
type: String,
|
default: 'text'
|
},
|
accept: {
|
type: Array,
|
default: () => {
|
return ['pdf', 'xls', 'doc', 'image', 'jpg', 'png', 'zip']
|
}
|
},
|
limitFileCount: {
|
type: Number,
|
default: 1
|
},
|
modelValue: {
|
type: Array,
|
default: () => {
|
return []
|
}
|
},
|
disabled: {
|
type: Boolean,
|
default: false
|
}
|
},
|
watch: {
|
modelValue: {
|
handler: function() {
|
this.list = this.modelValue || []
|
this.list.forEach(ele => {
|
if (!ele.name) {
|
ele.name = getFileUrlName(ele.url)
|
}
|
})
|
},
|
immediate: true,
|
deep: true
|
},
|
list: {
|
handler: function(val) {
|
this.$emit('update:modelValue', val)
|
},
|
immediate: true,
|
deep: true
|
}
|
},
|
computed: {
|
acceptType() {
|
let obj = { pdf, xls, doc, image, jpg, png, zip }
|
return this.accept.map(ele => obj[ele]).join(',')
|
},
|
tip() {
|
let obj = {
|
pdf: 'PDF',
|
xls: 'EXCEL',
|
doc: 'WORD',
|
image: '图片',
|
jpg: 'JPEG/JPG',
|
png: 'PNG',
|
zip: 'ZIP'
|
}
|
let tip = this.accept.map(ele => obj[ele]).join('、')
|
return `支持${tip}类型文件`
|
},
|
againUploadFlag() {
|
return this.limitFileCount == 1 && this.list.length == 1
|
},
|
filterPreviewImgList() {
|
let list = this.list.map(ele => ele.url?.includes('http') ? ele.url : this.$qxueyou.qxyRes + ele.url )
|
return list
|
},
|
hideUpload() {
|
return this.list.length >= this.limitFileCount
|
}
|
},
|
methods: {
|
uploadRequest(UploadRequestOptions) {
|
const data = {
|
file: UploadRequestOptions.file,
|
directory: ''
|
}
|
this.$axios.post('/infra/file/exam/upload', data, {
|
headers: { 'Content-Type': "multipart/form-data" }
|
}).then(res => {
|
let index = this.list.findIndex(ele => ele.uid == data.file.uid)
|
if (res.data.code == 0) {
|
let index = this.list.findIndex(ele => ele.uid == data.file.uid)
|
if (index != -1) {
|
this.list[index].uploadStatus = 'success'
|
let url = res.data.data
|
this.list[index].url = url
|
}
|
this.$message.success('上传成功')
|
} else {
|
if (index != -1) {
|
this.list[index].uploadStatus = 'fail'
|
}
|
this.$message.error(res.data.msg || '上传失败')
|
}
|
})
|
},
|
deleteFileItem(index) {
|
this.list.splice(index, 1)
|
},
|
previewImage(index) {
|
this.initialPreviewImgIndex = index
|
this.$refs.previewImg?.showPreview()
|
},
|
replaceUpload() {},
|
handleRemove() {},
|
beforeRemove() {},
|
handlePreview() {},
|
handleExceed(file) {
|
if (this.againUploadFlag) {
|
this.$refs.upload?.clearFiles()
|
let newFile = file[0]
|
newFile.uid = genFileId()
|
this.$refs.upload?.handleStart(newFile)
|
this.$refs.upload?.submit()
|
} else {
|
this.$message.error(`最多支持上传 ${this.limitFileCount} 个文件`)
|
}
|
}
|
}
|
}
|
</script>
|
|
<style scoped>
|
.uploader .avatar {
|
width: 178px;
|
height: 178px;
|
display: block;
|
}
|
.hideUpload :deep(.el-upload--picture-card) {
|
display: none !important;
|
}
|
.file-box {
|
display: flex;
|
color: #007AFF;
|
align-content: center;
|
background-color: #ECF5FF;
|
padding: 8px;
|
font-size: 15px;
|
line-height: 16px;
|
}
|
.el-upload-list__item-actions {
|
position: absolute;
|
top: 0;
|
left: 0;
|
width: 100%;
|
height: 100%;
|
background-color: rgba(0, 0, 0, 0.5); /* 半透明黑色蒙版 */
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
opacity: 0; /* 默认透明 */
|
transition: opacity .3s; /* 添加过渡动画效果 */
|
}
|
|
/* 鼠标悬停时显示蒙版 */
|
.el-upload-list__item:hover .el-upload-list__item-actions {
|
opacity: 1;
|
}
|
</style>
|