|
|
|
|
@ -1,25 +1,25 @@ |
|
|
|
|
<template> |
|
|
|
|
<el-upload |
|
|
|
|
ref="upload" |
|
|
|
|
:before-upload="beforeUpload" |
|
|
|
|
:class="{disabled: hideUploader}" |
|
|
|
|
:file-list="tempData" |
|
|
|
|
:http-request="httpRequest" |
|
|
|
|
:limit="limit" |
|
|
|
|
:multiple="multiple" |
|
|
|
|
:on-error="error" |
|
|
|
|
:on-exceed="exceed" |
|
|
|
|
:on-success="success" |
|
|
|
|
action="" |
|
|
|
|
list-type="picture-card" |
|
|
|
|
ref="upload" |
|
|
|
|
:before-upload="beforeUpload" |
|
|
|
|
:class="{disabled: hideUploader}" |
|
|
|
|
:file-list="tempData" |
|
|
|
|
:http-request="httpRequest" |
|
|
|
|
:limit="limit" |
|
|
|
|
:multiple="multiple" |
|
|
|
|
:on-error="error" |
|
|
|
|
:on-exceed="exceed" |
|
|
|
|
:on-success="success" |
|
|
|
|
action="" |
|
|
|
|
list-type="picture-card" |
|
|
|
|
> |
|
|
|
|
<i class="el-icon-plus"/> |
|
|
|
|
|
|
|
|
|
<template v-slot:file="{file}"> |
|
|
|
|
<span |
|
|
|
|
class="el-upload-list__item-actions" |
|
|
|
|
@mouseenter="e => handleBlockMouseEnter(e,file)" |
|
|
|
|
@mouseleave="handleBlockMouseLeave" |
|
|
|
|
class="el-upload-list__item-actions" |
|
|
|
|
@mouseenter="e => handleBlockMouseEnter(e,file)" |
|
|
|
|
@mouseleave="handleBlockMouseLeave" |
|
|
|
|
> |
|
|
|
|
<span class="el-upload-list__item-preview" @click="() => preview(file)"> |
|
|
|
|
<i class="el-icon-zoom-in"/> |
|
|
|
|
@ -49,193 +49,193 @@ |
|
|
|
|
</template> |
|
|
|
|
|
|
|
|
|
<script> |
|
|
|
|
/* |
|
|
|
|
* 直传七牛云 |
|
|
|
|
* 传入fileList自动拼接七牛云外链前缀,并增加downloadUrl属性(图片类型的文件url与downloadUrl相同) |
|
|
|
|
* 非图片类型的文件使用kkFileView预览 |
|
|
|
|
* success、remove事件中的file.url均不带七牛云外链前缀 |
|
|
|
|
* */ |
|
|
|
|
import axios from 'axios' |
|
|
|
|
import {attachmentPrefix} from '@/config' |
|
|
|
|
import {debounce, isEmpty} from '@/utils' |
|
|
|
|
import {elError} from "@/utils/message" |
|
|
|
|
import {isImage, isDoc, isPdf, isPpt, isRar, isXls, isTxt, isZip} from "@/utils/validate" |
|
|
|
|
import {preview, deleteUpload, download, upload, autoCompleteUrl} from "@/utils/file" |
|
|
|
|
|
|
|
|
|
const typeMapper = [ |
|
|
|
|
{test: isImage}, {test: isDoc, type: 'doc'}, {test: isPdf, type: 'pdf'}, {test: isPpt, type: 'ppt'}, |
|
|
|
|
{test: isRar, type: 'rar'}, {test: isXls, type: 'xls'}, {test: isTxt, type: 'txt'}, {test: isZip, type: 'zip'}, |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
function parseMaxSize(maxSize) { |
|
|
|
|
if (typeof maxSize === 'number') { |
|
|
|
|
return maxSize |
|
|
|
|
/* |
|
|
|
|
* 直传七牛云 |
|
|
|
|
* 传入fileList自动拼接七牛云外链前缀,并增加downloadUrl属性(图片类型的文件url与downloadUrl相同) |
|
|
|
|
* 非图片类型的文件使用kkFileView预览 |
|
|
|
|
* success、remove事件中的file.url均不带七牛云外链前缀 |
|
|
|
|
* */ |
|
|
|
|
import axios from 'axios' |
|
|
|
|
import {attachmentPrefix} from '@/config' |
|
|
|
|
import {debounce, isEmpty} from '@/utils' |
|
|
|
|
import {elError} from "@/utils/message" |
|
|
|
|
import {isImage, isDoc, isPdf, isPpt, isRar, isXls, isTxt, isZip} from "@/utils/validate" |
|
|
|
|
import {preview, deleteUpload, download, upload, autoCompleteUrl} from "@/utils/file" |
|
|
|
|
|
|
|
|
|
const typeMapper = [ |
|
|
|
|
{test: isImage}, {test: isDoc, type: 'doc'}, {test: isPdf, type: 'pdf'}, {test: isPpt, type: 'ppt'}, |
|
|
|
|
{test: isRar, type: 'rar'}, {test: isXls, type: 'xls'}, {test: isTxt, type: 'txt'}, {test: isZip, type: 'zip'}, |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
function parseMaxSize(maxSize) { |
|
|
|
|
if (typeof maxSize === 'number') { |
|
|
|
|
return maxSize |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const map = {KB: 1024, MB: 1024 * 1024}, |
|
|
|
|
upper = maxSize.toUpperCase(), |
|
|
|
|
num = upper.replace(/[^0-9]/ig, ""), |
|
|
|
|
unit = upper.replace(num, "") |
|
|
|
|
|
|
|
|
|
return parseInt(num) * (map[unit] || 1) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function getCoverImage(url, fileType) { |
|
|
|
|
const mapper = typeMapper.find(({test}) => test(fileType)) |
|
|
|
|
|
|
|
|
|
return mapper && mapper.type ? `${process.env.BASE_URL}static/img/fileType/${mapper.type}.png` : url |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export default { |
|
|
|
|
name: 'UploadFile', |
|
|
|
|
|
|
|
|
|
props: { |
|
|
|
|
disabled: Boolean, |
|
|
|
|
multiple: {type: Boolean, default: true}, |
|
|
|
|
fileList: {type: Array, default: () => []}, |
|
|
|
|
limit: {type: Number, default: 10}, |
|
|
|
|
maxSize: {type: [Number, String], default: '10MB'} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
data() { |
|
|
|
|
return { |
|
|
|
|
tempData: this.fileList, |
|
|
|
|
data: this.fileList, |
|
|
|
|
tooltipContent: null |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
const map = {KB: 1024, MB: 1024 * 1024}, |
|
|
|
|
upper = maxSize.toUpperCase(), |
|
|
|
|
num = upper.replace(/[^0-9]/ig, ""), |
|
|
|
|
unit = upper.replace(num, "") |
|
|
|
|
computed: { |
|
|
|
|
count() { |
|
|
|
|
return this.data.length |
|
|
|
|
}, |
|
|
|
|
hideUploader() { |
|
|
|
|
return this.disabled || this.count >= this.limit |
|
|
|
|
}, |
|
|
|
|
previewUrlList() { |
|
|
|
|
return this.data ? this.data.map(i => i.downloadUrl) : [] |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
watch: { |
|
|
|
|
fileList: { |
|
|
|
|
immediate: true, |
|
|
|
|
handler(fileList) { |
|
|
|
|
if (isEmpty(fileList)) this.data = [] |
|
|
|
|
else { |
|
|
|
|
this.data = [...this.fileList.map(file => { |
|
|
|
|
const downloadUrl = autoCompleteUrl(file.url) |
|
|
|
|
return {...file, url: getCoverImage(downloadUrl, file.name), downloadUrl} |
|
|
|
|
})] |
|
|
|
|
} |
|
|
|
|
this.tempData = JSON.parse(JSON.stringify(this.data)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
methods: { |
|
|
|
|
//模仿el-table的tooltip |
|
|
|
|
handleBlockMouseEnter(event, file) { |
|
|
|
|
const tooltip = this.$refs.tooltip |
|
|
|
|
this.tooltipContent = file.name |
|
|
|
|
tooltip.referenceElm = event.target |
|
|
|
|
tooltip.$refs.popper && (tooltip.$refs.popper.style.display = 'none') |
|
|
|
|
tooltip.doDestroy() |
|
|
|
|
tooltip.setExpectedState(true) |
|
|
|
|
this.activateTooltip(tooltip) |
|
|
|
|
}, |
|
|
|
|
handleBlockMouseLeave() { |
|
|
|
|
const tooltip = this.$refs.tooltip |
|
|
|
|
if (tooltip) { |
|
|
|
|
tooltip.setExpectedState(false) |
|
|
|
|
tooltip.handleClosePopper() |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
return parseInt(num) * (map[unit] || 1) |
|
|
|
|
} |
|
|
|
|
//附件移除时,仅当非本次上传时触发remove事件 |
|
|
|
|
remove(file) { |
|
|
|
|
//删除上传文件时隐藏tooltip |
|
|
|
|
this.handleBlockMouseLeave() |
|
|
|
|
|
|
|
|
|
function getCoverImage(url, fileType) { |
|
|
|
|
const mapper = typeMapper.find(({test}) => test(fileType)) |
|
|
|
|
//区分是否为本次新上传,以及是否上传成功 |
|
|
|
|
//若不是新上传的 |
|
|
|
|
if (!file.raw) this.$emit('remove', {url: file.downloadUrl.replace(attachmentPrefix, '')}) |
|
|
|
|
//判断是否上传成功(有可能是正在上传) |
|
|
|
|
else if (file.response && !file.response.err) deleteUpload(file.raw.key) |
|
|
|
|
|
|
|
|
|
return mapper && mapper.type ? `${process.env.BASE_URL}static/img/fileType/${mapper.type}.png` : url |
|
|
|
|
} |
|
|
|
|
this.$refs.upload.handleRemove(file) |
|
|
|
|
const index = this.data.findIndex(i => i.downloadUrl === file.downloadUrl) |
|
|
|
|
if (index > -1) this.data.splice(index, 1) |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
export default { |
|
|
|
|
name: 'UploadFile', |
|
|
|
|
//预览 |
|
|
|
|
preview(file) { |
|
|
|
|
if (!isImage(file.name)) return preview(file.downloadUrl) |
|
|
|
|
|
|
|
|
|
props: { |
|
|
|
|
disabled: Boolean, |
|
|
|
|
multiple: {type: Boolean, default: true}, |
|
|
|
|
fileList: {type: Array, default: () => []}, |
|
|
|
|
limit: {type: Number, default: 10}, |
|
|
|
|
maxSize: {type: Number | String, default: '10MB'} |
|
|
|
|
const index = this.data.findIndex(i => i.downloadUrl === file.downloadUrl) |
|
|
|
|
this.$image({index, urlList: this.previewUrlList}) |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
data() { |
|
|
|
|
return { |
|
|
|
|
tempData: this.fileList, |
|
|
|
|
data: this.fileList, |
|
|
|
|
tooltipContent: null |
|
|
|
|
} |
|
|
|
|
//下载 |
|
|
|
|
download(file) { |
|
|
|
|
download(file.downloadUrl, file.name) |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
computed: { |
|
|
|
|
count() { |
|
|
|
|
return this.data.length |
|
|
|
|
}, |
|
|
|
|
hideUploader() { |
|
|
|
|
return this.disabled || this.count >= this.limit |
|
|
|
|
}, |
|
|
|
|
previewUrlList() { |
|
|
|
|
return this.data ? this.data.map(i => i.downloadUrl) : [] |
|
|
|
|
} |
|
|
|
|
//数量超限 |
|
|
|
|
exceed() { |
|
|
|
|
elError(`最多只能上传${this.limit}个文件`) |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
watch: { |
|
|
|
|
fileList: { |
|
|
|
|
immediate: true, |
|
|
|
|
handler(fileList) { |
|
|
|
|
if (isEmpty(fileList)) this.data = [] |
|
|
|
|
else { |
|
|
|
|
this.data = [...this.fileList.map(file => { |
|
|
|
|
const downloadUrl = autoCompleteUrl(file.url) |
|
|
|
|
return {...file, url: getCoverImage(downloadUrl, file.name), downloadUrl} |
|
|
|
|
})] |
|
|
|
|
} |
|
|
|
|
this.tempData = JSON.parse(JSON.stringify(this.data)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
//新附件上传成功,触发success事件,记录文件key |
|
|
|
|
success(res, file) { |
|
|
|
|
file.url = getCoverImage(file.url, file.name) |
|
|
|
|
file.raw.key = res.key |
|
|
|
|
file.downloadUrl = autoCompleteUrl(res.key) |
|
|
|
|
this.data.push(file) |
|
|
|
|
this.$emit('success', file, res) |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
methods: { |
|
|
|
|
//模仿el-table的tooltip |
|
|
|
|
handleBlockMouseEnter(event, file) { |
|
|
|
|
const tooltip = this.$refs.tooltip |
|
|
|
|
this.tooltipContent = file.name |
|
|
|
|
tooltip.referenceElm = event.target |
|
|
|
|
tooltip.$refs.popper && (tooltip.$refs.popper.style.display = 'none') |
|
|
|
|
tooltip.doDestroy() |
|
|
|
|
tooltip.setExpectedState(true) |
|
|
|
|
this.activateTooltip(tooltip) |
|
|
|
|
}, |
|
|
|
|
handleBlockMouseLeave() { |
|
|
|
|
const tooltip = this.$refs.tooltip |
|
|
|
|
if (tooltip) { |
|
|
|
|
tooltip.setExpectedState(false) |
|
|
|
|
tooltip.handleClosePopper() |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
//附件移除时,仅当非本次上传时触发remove事件 |
|
|
|
|
remove(file) { |
|
|
|
|
//删除上传文件时隐藏tooltip |
|
|
|
|
this.handleBlockMouseLeave() |
|
|
|
|
|
|
|
|
|
//区分是否为本次新上传,以及是否上传成功 |
|
|
|
|
//若不是新上传的 |
|
|
|
|
if (!file.raw) this.$emit('remove', {url: file.downloadUrl.replace(attachmentPrefix, '')}) |
|
|
|
|
//判断是否上传成功(有可能是正在上传) |
|
|
|
|
else if (file.response && !file.response.err) deleteUpload(file.raw.key) |
|
|
|
|
|
|
|
|
|
this.$refs.upload.handleRemove(file) |
|
|
|
|
const index = this.data.findIndex(i => i.downloadUrl === file.downloadUrl) |
|
|
|
|
if (index > -1) this.data.splice(index, 1) |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
//预览 |
|
|
|
|
preview(file) { |
|
|
|
|
if (!isImage(file.name)) return preview(file.downloadUrl) |
|
|
|
|
|
|
|
|
|
const index = this.data.findIndex(i => i.downloadUrl === file.downloadUrl) |
|
|
|
|
this.$image({index, urlList: this.previewUrlList}) |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
//下载 |
|
|
|
|
download(file) { |
|
|
|
|
download(file.downloadUrl, file.name) |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
//数量超限 |
|
|
|
|
exceed() { |
|
|
|
|
elError(`最多只能上传${this.limit}个文件`) |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
//新附件上传成功,触发success事件,记录文件key |
|
|
|
|
success(res, file) { |
|
|
|
|
file.url = getCoverImage(file.url, file.name) |
|
|
|
|
file.raw.key = res.key |
|
|
|
|
file.downloadUrl = autoCompleteUrl(res.key) |
|
|
|
|
this.data.push(file) |
|
|
|
|
this.$emit('success', file, res) |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
//上传失败时移除文件 |
|
|
|
|
error(err, file) { |
|
|
|
|
elError('上传文件失败,' + err) |
|
|
|
|
file.response.err = err |
|
|
|
|
this.remove(file) |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
//上传前过滤文件 |
|
|
|
|
beforeUpload(file) { |
|
|
|
|
if (!typeMapper.some(({test}) => test(file.name))) { |
|
|
|
|
elError('只支持图片、word、pdf、ppt、rar、excel、txt、zip类型的文件') |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
const maxSize = parseMaxSize(this.maxSize) |
|
|
|
|
if (file.size > maxSize) { |
|
|
|
|
elError(`${file.name}的大小超出${this.$options.filters.numberFormatter(maxSize)}`) |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
httpRequest({file, onProgress}) { |
|
|
|
|
const source = axios.CancelToken.source() |
|
|
|
|
const promise = upload(file, file.name, { |
|
|
|
|
onUploadProgress(e) { |
|
|
|
|
if (e.total > 0) { |
|
|
|
|
e.percent = (Number)((e.loaded / e.total * 100).toFixed(2)) |
|
|
|
|
} |
|
|
|
|
onProgress(e) |
|
|
|
|
}, |
|
|
|
|
cancelToken: source.token |
|
|
|
|
}) |
|
|
|
|
//由于ele原有的上传方法是原生ajax,所以取消上传时会调用一次abort方法 |
|
|
|
|
promise.abort = source.cancel |
|
|
|
|
return promise |
|
|
|
|
//上传失败时移除文件 |
|
|
|
|
error(err, file) { |
|
|
|
|
elError('上传文件失败,' + err) |
|
|
|
|
file.response.err = err |
|
|
|
|
this.remove(file) |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
//上传前过滤文件 |
|
|
|
|
beforeUpload(file) { |
|
|
|
|
if (!typeMapper.some(({test}) => test(file.name))) { |
|
|
|
|
elError('只支持图片、word、pdf、ppt、rar、excel、txt、zip类型的文件') |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
const maxSize = parseMaxSize(this.maxSize) |
|
|
|
|
if (file.size > maxSize) { |
|
|
|
|
elError(`${file.name}的大小超出${this.$options.filters.numberFormatter(maxSize)}`) |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
created() { |
|
|
|
|
this.activateTooltip = debounce(tooltip => tooltip.handleShowPopper(), 200) |
|
|
|
|
httpRequest({file, onProgress}) { |
|
|
|
|
const source = axios.CancelToken.source() |
|
|
|
|
const promise = upload(file, file.name, { |
|
|
|
|
onUploadProgress(e) { |
|
|
|
|
if (e.total > 0) { |
|
|
|
|
e.percent = (Number)((e.loaded / e.total * 100).toFixed(2)) |
|
|
|
|
} |
|
|
|
|
onProgress(e) |
|
|
|
|
}, |
|
|
|
|
cancelToken: source.token |
|
|
|
|
}) |
|
|
|
|
//由于ele原有的上传方法是原生ajax,所以取消上传时会调用一次abort方法 |
|
|
|
|
promise.abort = source.cancel |
|
|
|
|
return promise |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
created() { |
|
|
|
|
this.activateTooltip = debounce(tooltip => tooltip.handleShowPopper(), 200) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
</script> |
|
|
|
|
|
|
|
|
|
<style lang="scss" src="./style.scss"></style> |
|
|
|
|
|