<template>
|
<div class="zt-upload">
|
<!-- 文件上传 -->
|
<el-upload
|
ref="upload"
|
class="upload-input"
|
:class="{hide: uploadDisabled || !isShowUpload}"
|
:accept="accept"
|
:action="uploadAction"
|
:before-upload="handleBeforeUpload"
|
:data="data"
|
:disabled="uploading"
|
:drag="false"
|
:limit="limit"
|
list-type="text"
|
:multiple="multiple"
|
:name="name"
|
:on-change="handleChange"
|
:on-error="handleUploadError"
|
:on-exceed="handleExceed"
|
:on-success="handleUploadSuccess"
|
:on-progress="onUploadProgress"
|
:show-file-list="false"
|
:file-list="fileList"
|
v-if="!image && !crop"
|
|
>
|
<el-button class="upload-btn" size="mini" type="primary" icon="el-icon-upload"
|
v-show="!uploadDisabled && isShowUpload">{{ buttonText }}
|
</el-button>
|
<div slot="tip" class="el-upload__tip" v-show="!uploadDisabled && isShowUpload && tip">{{ tip }}</div>
|
</el-upload>
|
|
<div v-show="progressFlag">
|
<el-progress :text-inside="true" :stroke-width="30" :percentage="progressPercent" :format="format"></el-progress>
|
|
</div>
|
|
<ul class="el-upload-list el-upload-list--text" v-if="!image && !crop && showFileList">
|
<li tabindex="0" class="el-upload-list__item is-success" v-for="(file, index) in uploadList" :key="file.id">
|
<a class="el-upload-list__item-name" :href="file.url" target="_blank"><i
|
class="el-icon-document"></i>{{ file.name }}</a>
|
<label class="el-upload-list__item-status-label">
|
<i class="el-icon-upload-success el-icon-circle-check"></i>
|
</label>
|
<i class="el-icon-close" @click.stop="handleRemove(index)" v-show="!uploadDisabled"></i>
|
</li>
|
</ul>
|
|
<!-- 图片列表 -->
|
<ele-gallery
|
:lazy="lazy"
|
:remove-fn="handleRemove"
|
:size="size"
|
:sliceSingle="true"
|
:source="galleryValues"
|
:thumbSuffix="thumbSuffix"
|
:title="title"
|
v-if="image && !crop"
|
>
|
<template slot="uploader">
|
<!-- 上传组件 -->
|
<el-upload
|
ref="upload"
|
:accept="accept"
|
:action="uploadAction"
|
:before-upload="handleBeforeUpload"
|
:data="data"
|
:disabled="uploading"
|
:drag="Boolean(drag)"
|
:limit="limit"
|
:list-type="drag ? 'picture' : 'picture-card'"
|
:multiple="multiple"
|
:on-change="handleChange"
|
:on-error="handleUploadError"
|
:on-exceed="handleExceed"
|
:on-success="handleUploadSuccess"
|
:show-file-list="false"
|
:file-list="fileList"
|
v-show="!uploadDisabled && isShowUpload"
|
>
|
<div v-loading="uploading">
|
<!-- drag显示 -->
|
<template v-if="drag">
|
<i class="el-icon-upload"></i>
|
<div class="el-upload__text">
|
将文件拖到此处,或
|
<em>点击上传</em>
|
</div>
|
</template>
|
|
<!-- 非drag -->
|
<template v-else>
|
<div :style="{ width: size + 'px', height: size + 'px', lineHeight: size + 'px' }">
|
<i class="el-icon-plus"></i>
|
</div>
|
</template>
|
</div>
|
|
<!-- 公共 -->
|
<div
|
class="el-upload__tip"
|
slot="tip"
|
v-if="showTip"
|
>
|
请上传
|
<b style="color: #F56C6C">{{ fileType.length ? fileType.join('/') : '图片' }}</b>
|
格式文件
|
<template v-if="fileSize">
|
,且大小不超过
|
<b style="color: #F56C6C">{{ fileSize }}MB</b>
|
</template>
|
</div>
|
</el-upload>
|
</template>
|
</ele-gallery>
|
|
<!-- 图片裁剪 -->
|
<div v-if="crop">
|
<div
|
:style="{ width: size + 'px', height: size + 'px', lineHeight: size + 'px' }"
|
@click="isShowCrop = true"
|
class="el-upload el-upload--picture-card"
|
style="margin-bottom: 20px;"
|
v-show="!uploadDisabled && isShowUpload"
|
>
|
<i class="el-icon-plus avatar-uploader-icon"></i>
|
</div>
|
|
<cropper
|
ref="cropper"
|
:field="name"
|
:width="cropWidth"
|
:height="cropHeight"
|
:noCircle="cropHeight !== cropWidth"
|
:params="data"
|
:url="uploadAction"
|
@crop-success="handleCropSuccess"
|
@crop-upload-fail="handleCropUploadError"
|
@crop-upload-success="handleCropUploadSuccess"
|
@src-file-set="handleSetFileSet"
|
class="zt-upload-image--cropper"
|
img-format="png"
|
v-if="isShowCrop"
|
v-model="isShowCrop"
|
></cropper>
|
|
<ele-gallery
|
:lazy="lazy"
|
:remove-fn="handleRemove"
|
:size="size"
|
:sliceSingle="true"
|
:source="galleryValues"
|
:thumbSuffix="thumbSuffix"
|
:title="title"
|
:disabled="uploadDisabled"
|
v-show="isShowGrllery"
|
/>
|
</div>
|
</div>
|
</template>
|
|
<script>
|
import Cookies from 'js-cookie'
|
import Cropper from 'vue-image-crop-upload'
|
import EleGallery from '../../vue-ele-gallery'
|
|
export default {
|
name: 'ZtUploader',
|
props: {
|
// 值
|
value: {
|
type: [Object, Array],
|
default() {
|
return this.multiple ? [] : null
|
}
|
},
|
action: {
|
type: String,
|
default: '/sys/oss/upload'
|
},
|
// 是否为图片
|
image: {
|
type: Boolean,
|
default: false
|
},
|
// 是否剪裁
|
crop: {
|
type: Boolean,
|
default: false
|
},
|
// 裁剪高度
|
cropHeight: {
|
type: Number
|
},
|
// 裁剪宽度
|
cropWidth: {
|
type: Number
|
},
|
// 是否启用拖拽上传
|
drag: {
|
type: Boolean,
|
default: false
|
},
|
// 是否支持多选文件
|
multiple: {
|
type: Boolean,
|
default: false
|
},
|
// 图片显示大小
|
size: {
|
type: Number,
|
default: 100
|
},
|
// 大小限制(MB)
|
fileSize: {
|
type: Number
|
},
|
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
fileType: {
|
type: Array,
|
default: () => []
|
},
|
// 缩略图后缀, 例如七牛云缩略图样式 (?imageView2/1/w/20/h/20)
|
thumbSuffix: {
|
type: String,
|
default: ''
|
},
|
// 是否显示提示
|
isShowTip: {
|
type: Boolean,
|
default: true
|
},
|
// 弹窗标题
|
title: String,
|
// 图片懒加载
|
lazy: {
|
type: Boolean,
|
default: false
|
},
|
// 文件个数显示
|
limit: {
|
type: Number,
|
default: 10
|
},
|
// 上传时附带的额外参数
|
data: Object,
|
// 上传的文件字段名
|
name: {
|
type: String,
|
default: 'file'
|
},
|
// 接受上传的文件类型(thumbnail-mode 模式下此参数无效)
|
accept: String,
|
buttonText: {
|
type: String,
|
default: '上传'
|
},
|
tip: String,
|
showFileList: {
|
type: Boolean,
|
default: true
|
},
|
disabled: {
|
type: Boolean,
|
default: false
|
}
|
},
|
components: {
|
Cropper,
|
EleGallery
|
},
|
inject: {
|
elForm: {
|
default: ''
|
}
|
},
|
data() {
|
return {
|
progressPercent: 0,
|
uploadAction: `${window.SITE_CONFIG['apiURL']}${this.action}?token=${Cookies.get('token')}`,
|
cropData: {},
|
isShowCrop: false,
|
uploading: false,
|
fileList: this.getUploadList(this.value),
|
uploadList: this.getUploadList(this.value),
|
progressFlag:false
|
}
|
},
|
computed: {
|
// 是否显示提示
|
showTip() {
|
return this.isShowTip && (this.fileType.length || this.fileSize)
|
},
|
galleryValues() {
|
let urls = []
|
if (this.value) {
|
if (this.multiple) {
|
this.value.forEach(v => urls.push({
|
src: v.url,
|
title: v.name
|
}))
|
} else {
|
urls.push({
|
src: this.value.url,
|
title: this.value.name
|
})
|
}
|
}
|
return urls
|
},
|
isShowUpload() { // 是否显示上传按钮
|
if (this.multiple) {
|
return this.uploadList.length < this.limit
|
} else {
|
return this.uploadList.length === 0
|
}
|
},
|
uploadDisabled() {
|
return this.disabled || (this.elForm || {}).disabled
|
},
|
successFiles() {
|
return this.uploadList.filter((file) => file.status === 'success')
|
},
|
isShowGrllery() {
|
return this.galleryValues.length > 0
|
}
|
},
|
watch: {
|
value(val, oldval) {
|
if (this.uploadList !== val) {
|
this.uploadList = this.getUploadList(val)
|
this.fileList = this.getUploadList(val)
|
}
|
},
|
isShowCrop(value) {
|
if (value === false) {
|
this.cropData = {}
|
}
|
}
|
},
|
methods: {
|
format(percentage) {
|
return percentage === 100 ? '后台正在处理' : `${percentage}%`;
|
},
|
// 获取已上传的文件列表
|
getUploadList(val) {
|
if (val) {
|
console.log(val, 'getUploadList val')
|
if (!this.multiple) { // 单选
|
return [val]
|
} else {
|
console.log([...val], 'getUploadList [...val]')
|
return [...val]
|
}
|
} else {
|
return []
|
}
|
},
|
onUploadProgress(event, file, fileList) {
|
this.progressFlag = true
|
let per = Number(
|
((event.loaded / event.total) * 100).toFixed(2)
|
)
|
this.progressPercent = per > 100 ? 100 : per
|
},
|
handleSetFileSet(fileName, fileType, fileSize) {
|
const uid = this.cropData.uid || new Date().getTime()
|
this.cropData = {
|
name: fileName,
|
percentage: 0,
|
size: fileSize,
|
type: fileType,
|
status: 'ready',
|
uid: uid
|
}
|
},
|
handleCropSuccess(b64Data) {
|
this.cropData.url = b64Data
|
},
|
handleCropUploadError(status) {
|
this.$message.error('上传失败, 请重试')
|
this.$emit('error', status)
|
},
|
handleCropUploadSuccess(response) {
|
this.cropData.status = 'success'
|
this.cropData.percentage = 100
|
this.cropData.response = response
|
const file = Object.assign({}, this.cropData)
|
this.handleUploadSuccess(response, file)
|
},
|
// 上传前校检格式和大小
|
handleBeforeUpload(file) {
|
let isAccept = true
|
if (this.fileType.length) { // 文件类型(后缀)一致
|
let fileExtension = ''
|
if (file.name.lastIndexOf('.') > -1) {
|
fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1)
|
}
|
isAccept = this.fileType.some((type) => {
|
if (file.type.indexOf(type) > -1) return true
|
if (fileExtension && fileExtension.indexOf(type) > -1) return true
|
return false
|
})
|
}
|
if (!isAccept) {
|
this.$message.error(`文件格式不正确, 请上传${this.fileType.join('/')}格式文件!`)
|
return false
|
}
|
if (this.fileSize) {
|
const isLt = file.size / 1024 / 1024 < this.fileSize
|
if (!isLt) {
|
this.$message.error(`上传大小不能超过 ${this.fileSize} MB!`)
|
return false
|
}
|
}
|
this.uploading = true
|
return true
|
},
|
handleChange() {
|
this.uploading = false
|
/*const config = {
|
onUploadProgress: (progressEvent) => {
|
this.progressPercent = Number(
|
((progressEvent.loaded / progressEvent.total) * 90).toFixed(2)
|
)
|
}
|
}
|
//this.progressFlag = true;
|
let form = new FormData();
|
form.append("file", params.file);
|
this.$http.post( this.uploadAction,form,config ).then((res) => {
|
this.progressPercent = 100;
|
})*/
|
},
|
// 文件个数超出
|
handleExceed() {
|
this.$message.error(`最多上传${this.limit}个`)
|
},
|
// 上传失败
|
handleUploadError(err) {
|
this.uploading = false
|
this.$message.error('上传失败, 请重试')
|
this.$emit('error', err)
|
},
|
// 上传成功回调
|
handleUploadSuccess(response, file) {
|
console.log(response, file,'response, file')
|
if (response.code === 0) {
|
this.progressPercent = 100
|
this.uploading = false
|
// this.$message.success('上传成功')
|
if (this.multiple) {
|
this.uploadList.push(response.data)
|
this.$emit('input', this.uploadList)
|
} else {
|
this.$emit('input', response.data)
|
}
|
this.progressFlag = false
|
this.progressPercent = 0
|
}
|
},
|
async handleRemove(index) {
|
let file = this.uploadList[index]
|
if (file.status === 1) { // 证书文件,直接从列表删除,不保存数据库
|
this.handRemoveAndSetValue(index)
|
} else {
|
if (await this.$tip.confirm(this.$t('prompt.info', {'handle': this.$t('delete')}))) {
|
let res = await this.$http.delete('/sys/oss', {'data': [file.id]})
|
if (res.success) {
|
await this.$tip.success()
|
this.handRemoveAndSetValue(index)
|
}
|
}
|
}
|
},
|
handRemoveAndSetValue(index) {
|
if (this.multiple) {
|
this.uploadList.splice(index, 1)
|
this.$refs.upload.uploadFiles.splice(index, 1)
|
this.$emit('input', this.uploadList || [])
|
} else {
|
this.$emit('input', null)
|
}
|
}
|
},
|
mounted() {
|
// 插入到body中, 避免弹出层被遮盖
|
if (this.crop && this.$refs.cropper) {
|
document.body.appendChild(this.$refs.cropper.$el)
|
}
|
}
|
}
|
</script>
|
|
<style>
|
.zt-upload {
|
line-height: 1;
|
}
|
|
.zt .upload-input .upload-btn {
|
padding: 8px 12px;
|
}
|
|
.zt .upload-input.hide {
|
height: 0px;
|
}
|
|
.zt-upload .el-loading-spinner {
|
line-height: 1;
|
}
|
|
.zt-upload .el-icon-plus {
|
vertical-align: middle;
|
}
|
|
.zt-upload .el-upload--picture-card {
|
width: auto;
|
height: auto;
|
background: none;
|
line-height: inherit;
|
}
|
|
/* 裁剪 */
|
.vue-image-crop-upload.zt-upload-image--cropper {
|
z-index: 99;
|
}
|
|
.zt-upload-image--cropper .vicp-drop-area {
|
background-color: #fbfdff !important;
|
}
|
|
.zt-upload-image--cropper .vicp-icon1-arrow {
|
border-bottom-color: #909399 !important;
|
}
|
|
.zt-upload-image--cropper .vicp-icon1-body {
|
background-color: #909399 !important;
|
}
|
|
.zt-upload-image--cropper .vicp-icon1-bottom {
|
border-color: #909399 !important;
|
}
|
</style>
|