You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
372 lines
9.4 KiB
372 lines
9.4 KiB
<!--
|
|
* @FileDescription: index
|
|
* @Author: kahu
|
|
* @Date: 2022/12/14
|
|
* @LastEditors: kahu
|
|
* @LastEditTime: 2022/12/14
|
|
-->
|
|
<template>
|
|
<div class="content">
|
|
<el-upload
|
|
v-loading="isUploading"
|
|
:disabled="componentError"
|
|
class="upload-demo"
|
|
drag
|
|
:headers="headers"
|
|
:file-list="viewFileList"
|
|
:name="name"
|
|
:show-file-list="showFileList"
|
|
:list-type="showFileListType"
|
|
:multiple="multiple"
|
|
:action="uploadUrl"
|
|
:limit="limit"
|
|
:on-success="handleUploadSuccess"
|
|
:before-upload="handleBeforeUpload"
|
|
:on-error="handleUploadError"
|
|
:on-exceed="handleExceed"
|
|
:before-remove="handleBeforeRemove"
|
|
:on-remove="handleRemove"
|
|
:on-preview="handlePreviewOpen"
|
|
>
|
|
<i class="el-icon-upload"></i>
|
|
<div
|
|
class="el-upload__text"
|
|
v-if="!componentError"
|
|
>将文件拖到此处,或<em>点击上传</em></div>
|
|
<div
|
|
class="error-text"
|
|
v-else
|
|
> 组件配置错误,请查看控制台
|
|
</div>
|
|
<div
|
|
class="el-upload__tip"
|
|
slot="tip"
|
|
>
|
|
文件大小不超过{{ limitSize }}m,文件类型为{{ types.toString() }}
|
|
</div>
|
|
</el-upload>
|
|
|
|
<!-- 预览 -->
|
|
<el-dialog
|
|
title="预览"
|
|
:visible.sync="previewObj.show"
|
|
width="60%"
|
|
:before-close="handlePreviewClose"
|
|
>
|
|
<div class="preview-content">
|
|
<template v-if="previewObj.file && previewObj.file.type.includes('image')">
|
|
<el-image class="preview-item" :src="previewObj.file.url" />
|
|
</template>
|
|
<template v-if="previewObj.file && previewObj.file.type.includes('video')">
|
|
<video class="preview-item" controls :src="previewObj.file.url" />
|
|
</template>
|
|
</div>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import mime from 'mime'
|
|
let fullLoading = null
|
|
const baseURL = process.env.VUE_APP_DOMAIN_PREFIX
|
|
export default {
|
|
name: "Upload",
|
|
props: {
|
|
headers:{
|
|
type:Object,
|
|
default:()=>({})
|
|
},
|
|
/** 上传时候表单的KEY */
|
|
name: {
|
|
type: String,
|
|
default: () => "file"
|
|
},
|
|
/** 限制上传数量 */
|
|
limit: {
|
|
type: Number,
|
|
default: () => 5
|
|
},
|
|
/** 限制的那张大小 单位M */
|
|
limitSize: {
|
|
type: Number,
|
|
default: () => 5
|
|
},
|
|
/** 是否多选 */
|
|
multiple: {
|
|
type: Boolean,
|
|
default: () => true
|
|
},
|
|
/** 是否展示文件列表 */
|
|
showFileList: {
|
|
type: Boolean,
|
|
default: () => true
|
|
},
|
|
/** 文件展示方式 text/picture/picture-card */
|
|
showFileListType:{
|
|
type:String,
|
|
default:()=>'text'
|
|
},
|
|
/** 允许上传的文件尾缀 string[] */
|
|
types: {
|
|
type: Array,
|
|
default: () => (['jpg', 'png', 'gif'])
|
|
},
|
|
/** 默认的文件列表 string[] */
|
|
defaultFileList: {
|
|
type: Array,
|
|
default: () => ([])
|
|
},
|
|
/** 上传成功后端返回的字段名称 */
|
|
responseFileName: {
|
|
type: String,
|
|
default: () => 'url'
|
|
},
|
|
/** 是否需要全屏loading */
|
|
needFullScreenLoading:{
|
|
type:Boolean,
|
|
default:()=>true
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
uploadUrl: `${ baseURL }/file/upload`,
|
|
// 真实文件列表
|
|
fileList: [],
|
|
// 默认展示的list,解决多上传只回调success一次的问题
|
|
viewFileList:[],
|
|
// 组件是否部署错误
|
|
componentError: false,
|
|
// 是否正在上传
|
|
isUploading:false,
|
|
// 预览对象
|
|
previewObj:{
|
|
show:false,
|
|
file:null
|
|
}
|
|
}
|
|
},
|
|
watch: {
|
|
defaultFileList: {
|
|
handler() {
|
|
// 判断类型
|
|
const flag = Object.prototype.toString.call(this.defaultFileList) === '[object Array]'
|
|
&& this.defaultFileList.length > 0 &&
|
|
Object.prototype.toString.call(this.defaultFileList[0]) !== '[object String]'
|
|
if (flag) {
|
|
this.componentError = true
|
|
throw new Error('defaultFileList格式错误,应为string[]格式')
|
|
}else{
|
|
this.componentError = false
|
|
}
|
|
this.viewFileList = this.defaultFileList.map(defaultFilePath => ({name: defaultFilePath, url: defaultFilePath}))
|
|
this.viewFileList.forEach(item=>{
|
|
this.fileList.push(item)
|
|
})
|
|
},
|
|
deep: true,
|
|
immediate: true
|
|
},
|
|
fileList:{
|
|
handler(){
|
|
this.handleNotifyFather()
|
|
},
|
|
deep:true,
|
|
immediate:false
|
|
}
|
|
},
|
|
methods: {
|
|
/**
|
|
* 检查type是否符合types的mime
|
|
* @param type 文件后缀
|
|
* @param types 可用文件后缀集合
|
|
*/
|
|
handleCheckFileMime(type, types) {
|
|
const typeMimes = types.map(item => mime.getType(item))
|
|
return typeMimes.includes(type)
|
|
},
|
|
|
|
handleCheckFileSize(fileSize, limitSize) {
|
|
const limitByteSize = limitSize * 1024 * 1024
|
|
return limitByteSize > fileSize
|
|
},
|
|
|
|
/**
|
|
* 上传之前的钩子
|
|
* @param file
|
|
* @return {undefined}
|
|
*/
|
|
handleBeforeUpload(file) {
|
|
// 检查mime
|
|
const fileType = file.type || mime.getType(file.name.slice(file.name.lastIndexOf('.') + 1))
|
|
const checkFileMime = this.handleCheckFileMime(fileType, this.types)
|
|
const checkFileSize = this.handleCheckFileSize(file.size, this.limitSize);
|
|
!checkFileSize ? file.isJumpRemove = true : undefined
|
|
!checkFileSize ? this.$notify.warning(`文件大小不得超出${ this.limitSize }m`) : undefined
|
|
!checkFileMime ? file.isJumpRemove = true : undefined
|
|
!checkFileMime ? this.$notify.warning(`文件类型不在合法列表 ${ this.types }`) : undefined
|
|
if(checkFileSize && checkFileMime){
|
|
// 开启loading
|
|
this.isUploading = true
|
|
if(this.needFullScreenLoading){
|
|
fullLoading = this.$loading({
|
|
background:`rgba(255,255,255,0.5)`,
|
|
text:'上传中',
|
|
fullscreen:true
|
|
})
|
|
}
|
|
}
|
|
return checkFileSize && checkFileMime
|
|
},
|
|
|
|
/**
|
|
* 上传成功钩子
|
|
* @param response
|
|
* @param file
|
|
* @param fileList
|
|
*/
|
|
handleUploadSuccess(response, file, fileList) {
|
|
this.isUploading = false
|
|
if(this.needFullScreenLoading){
|
|
fullLoading?.close()
|
|
}
|
|
const successObj = {
|
|
url: response.data[this.responseFileName],
|
|
name: file.name
|
|
}
|
|
file.url = response.data[this.responseFileName]
|
|
this.fileList.push(successObj)
|
|
},
|
|
|
|
/**
|
|
* 上传失败的钩子
|
|
* @param err
|
|
* @param file
|
|
* @param fileList
|
|
*/
|
|
handleUploadError(err, file, fileList) {
|
|
},
|
|
|
|
/**
|
|
* 超出数量的钩子
|
|
* @param files
|
|
* @param fileList
|
|
*/
|
|
handleExceed(files, fileList) {
|
|
this.$notify.warning(`文件总数大于可上传数量 ${ this.limit }`)
|
|
},
|
|
|
|
/**
|
|
* 文件即将移除的钩子
|
|
* @param file
|
|
* @param fileList
|
|
*/
|
|
async handleBeforeRemove(file, fileList) {
|
|
// 如果是超出文件大小调用,放行
|
|
if (file?.raw?.isJumpRemove) {
|
|
return true
|
|
}
|
|
return await this.$confirm('此操作将会删除已上传的文件, 是否继续?', '提示', {
|
|
confirmButtonText: this.$t('common.sure'),
|
|
cancelButtonText: this.$t('common.cancel'),
|
|
type: 'warning'
|
|
})
|
|
},
|
|
|
|
/**
|
|
* 移除文件的钩子
|
|
*/
|
|
handleRemove(file, fileList) {
|
|
if (file?.raw?.isJumpRemove) {
|
|
return
|
|
}
|
|
this.fileList.splice(this.fileList.findIndex(fileItem => file?.response?.data[this.responseFileName] === fileItem.url || file.url === fileItem.url), 1)
|
|
},
|
|
|
|
/**
|
|
* 通知父组件
|
|
*/
|
|
handleNotifyFather(){
|
|
this.$emit('change',this.fileList)
|
|
},
|
|
|
|
/**
|
|
* 预览
|
|
* 图片视频直接预览,其他下载
|
|
* @param file
|
|
*/
|
|
handlePreviewOpen(file){
|
|
if(!file.type){
|
|
file.type = mime.getType(file?.url?.slice(file?.url?.lastIndexOf('.')+1)) || mime.getType(file?.name?.slice(file?.name.lastIndexOf('.')+1)) || undefined
|
|
}
|
|
if(file.type.includes('image') || file.type.includes('video')){
|
|
this.previewObj.file = file
|
|
this.previewObj.show = true
|
|
}else{
|
|
this.$confirm('需要下载才能预览此文件, 是否继续?', '提示', {
|
|
confirmButtonText: this.$t('common.sure'),
|
|
cancelButtonText: this.$t('common.cancel'),
|
|
type: 'warning'
|
|
}).then(() => {
|
|
let htmlAnchorElement = document.createElement('a');
|
|
htmlAnchorElement.download = file?.url.slice(file?.url.lastIndexOf('/')+1)
|
|
htmlAnchorElement.target='_bank'
|
|
htmlAnchorElement.href = file?.url
|
|
htmlAnchorElement.click()
|
|
htmlAnchorElement = null
|
|
}).catch(() => {
|
|
});
|
|
}
|
|
},
|
|
|
|
handlePreviewClose(){
|
|
this.previewObj.file = null
|
|
this.previewObj.show = false
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style
|
|
lang="scss"
|
|
scoped
|
|
>
|
|
::v-deep .el-upload {
|
|
width: 100% !important;
|
|
|
|
.el-upload-dragger {
|
|
width: 100% !important;
|
|
}
|
|
}
|
|
|
|
.error-text {
|
|
font-size: 18px;
|
|
font-weight: bolder;
|
|
color: red;
|
|
animation: error-animation 2.5s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes error-animation {
|
|
0%, 100% {
|
|
font-size: 18px;
|
|
color: red;
|
|
}
|
|
25%, 75% {
|
|
font-size: 16px;
|
|
color: #b9b1b1;
|
|
}
|
|
50% {
|
|
font-size: 18px;
|
|
color: #500000;
|
|
}
|
|
}
|
|
|
|
.preview-content{
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
.preview-item{
|
|
min-width: 800px;
|
|
}
|
|
}
|
|
|
|
</style>
|