<template>
|
<div class="sc-upload" :class="{ 'sc-upload-round': round }" :style="style">
|
<div v-if="file && file.status != 'success'" class="sc-upload__uploading">
|
<div class="sc-upload__progress">
|
<el-progress :percentage="file.percentage" :text-inside="true" :stroke-width="16" />
|
</div>
|
<el-image class="image" :src="file.tempFile" fit="cover"></el-image>
|
</div>
|
<div v-if="file && file.status == 'success'" class="sc-upload__img">
|
<el-image class="image" :src="file.url" :preview-src-list="[file.url]" fit="cover" hide-on-click-modal
|
append-to-body :z-index="9999">
|
<template #placeholder>
|
<div class="sc-upload__img-slot">
|
Loading...
|
</div>
|
</template>
|
</el-image>
|
<div class="sc-upload__img-actions" v-if="!disabled">
|
<span class="del" @click="handleRemove()"><el-icon><el-icon-delete /></el-icon></span>
|
</div>
|
</div>
|
<el-upload v-if="!file" class="uploader" ref="uploader" :auto-upload="cropper ? false : autoUpload"
|
:disabled="disabled" :show-file-list="showFileList" :action="action" :name="name" :data="data"
|
:accept="accept" :limit="1" :http-request="request" :on-change="change" :before-upload="before"
|
:on-success="success" :on-error="error" :on-exceed="handleExceed">
|
<slot>
|
<div class="el-upload--picture-card">
|
<div class="file-empty">
|
<el-icon>
|
<component :is="icon" />
|
</el-icon>
|
<h4 v-if="title">{{ title }}</h4>
|
</div>
|
</div>
|
</slot>
|
</el-upload>
|
<span style="display:none!important"><el-input v-model="value"></el-input></span>
|
<el-dialog title="剪裁" draggable v-model="cropperDialogVisible" :width="580" @closed="cropperClosed"
|
destroy-on-close>
|
<sc-cropper :src="cropperFile.tempCropperFile" :compress="compress" :aspectRatio="aspectRatio"
|
ref="cropper"></sc-cropper>
|
<template #footer>
|
<el-button @click="cropperDialogVisible = false">取 消</el-button>
|
<el-button type="primary" @click="cropperSave">确 定</el-button>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script>
|
import { defineAsyncComponent } from 'vue'
|
import { genFileId } from 'element-plus'
|
const scCropper = defineAsyncComponent(() => import('@/components/scCropper'))
|
import config from "@/config/upload"
|
|
export default {
|
props: {
|
modelValue: { type: String, default: "" },
|
height: { type: Number, default: 148 },
|
width: { type: Number, default: 148 },
|
title: { type: String, default: "" },
|
icon: { type: String, default: "el-icon-plus" },
|
action: { type: String, default: "" },
|
apiObj: { type: Object, default: () => { } },
|
name: { type: String, default: config.filename },
|
data: { type: Object, default: () => { } },
|
accept: { type: String, default: "image/gif, image/jpeg, image/png" },
|
maxSize: { type: Number, default: config.maxSizeFile },
|
limit: { type: Number, default: 1 },
|
autoUpload: { type: Boolean, default: true },
|
showFileList: { type: Boolean, default: false },
|
disabled: { type: Boolean, default: false },
|
round: { type: Boolean, default: false },
|
onSuccess: { type: Function, default: () => { return true } },
|
host: { type: String, default: '' },
|
cropper: { type: Boolean, default: false },
|
compress: { type: Number, default: 1 },
|
aspectRatio: { type: Number, default: NaN }
|
},
|
components: {
|
scCropper
|
},
|
data() {
|
return {
|
value: "",
|
file: null,
|
style: {
|
width: this.width + "px",
|
height: this.height + "px"
|
},
|
cropperDialogVisible: false,
|
cropperFile: null,
|
hostNew: ''
|
}
|
},
|
watch: {
|
modelValue(val) {
|
this.value = val
|
this.newFile(val)
|
},
|
value(val) {
|
this.$emit('update:modelValue', val)
|
}
|
},
|
mounted() {
|
this.getHost().then(res => {
|
this.value = this.modelValue
|
this.newFile(this.modelValue)
|
})
|
},
|
methods: {
|
getHost() {
|
if (this.host) {
|
this.hostNew = this.host
|
return Promise.resolve()
|
}
|
return this.$API.setting.component.getImgHost.get().then(res => {
|
this.hostNew = res.data + '/visual/'
|
})
|
},
|
newFile(url) {
|
if (url) {
|
this.file = {
|
status: "success",
|
url: `${this.hostNew}${url}`
|
}
|
} else {
|
this.file = null
|
}
|
},
|
cropperSave() {
|
this.$refs.cropper.getCropFile(file => {
|
|
file.uid = this.cropperFile.uid
|
this.cropperFile.raw = file
|
|
this.file = this.cropperFile
|
this.file.tempFile = URL.createObjectURL(this.file.raw)
|
this.$refs.uploader.submit()
|
|
}, this.cropperFile.name, this.cropperFile.type)
|
this.cropperDialogVisible = false
|
},
|
cropperClosed() {
|
URL.revokeObjectURL(this.cropperFile.tempCropperFile)
|
delete this.cropperFile.tempCropperFile
|
},
|
handleRemove() {
|
this.clearFiles()
|
},
|
clearFiles() {
|
URL.revokeObjectURL(this.file.tempFile)
|
this.value = ""
|
this.file = null
|
this.$nextTick(() => {
|
this.$refs.uploader.clearFiles()
|
})
|
},
|
change(file, files) {
|
if (files.length > 1) {
|
files.splice(0, 1)
|
}
|
if (this.cropper && file.status == 'ready') {
|
const acceptIncludes = ["image/gif", "image/jpeg", "image/png"].includes(file.raw.type)
|
if (!acceptIncludes) {
|
this.$notify.warning({
|
title: '上传文件警告',
|
message: '选择的文件非图像类文件'
|
})
|
return false
|
}
|
this.cropperFile = file
|
this.cropperFile.tempCropperFile = URL.createObjectURL(file.raw)
|
this.cropperDialogVisible = true
|
return false
|
}
|
this.file = file
|
if (file.status == 'ready') {
|
file.tempFile = URL.createObjectURL(file.raw)
|
}
|
},
|
before(file) {
|
const acceptIncludes = this.accept.replace(/\s/g, "").split(",").includes(file.type)
|
if (!acceptIncludes) {
|
this.$notify.warning({
|
title: '上传文件警告',
|
message: '选择的文件非图像类文件'
|
})
|
this.clearFiles()
|
return false
|
}
|
const maxSize = file.size / 1024 / 1024 < this.maxSize;
|
if (!maxSize) {
|
this.$message.warning(`上传文件大小不能超过 ${this.maxSize}MB!`);
|
this.clearFiles()
|
return false
|
}
|
},
|
handleExceed(files) {
|
const file = files[0]
|
file.uid = genFileId()
|
this.$refs.uploader.handleStart(file)
|
},
|
success(res, file) {
|
//释放内存删除blob
|
URL.revokeObjectURL(file.tempFile)
|
try {
|
delete file.tempFile
|
} catch (error) {
|
|
}
|
var os = this.onSuccess(res, file)
|
if (os != undefined && os == false) {
|
this.$nextTick(() => {
|
this.file = null
|
this.value = ""
|
})
|
return false
|
}
|
var response = config.parseData(res)
|
file.url = response.src
|
this.value = file.url
|
},
|
error(err) {
|
this.$nextTick(() => {
|
this.clearFiles()
|
})
|
this.$notify.error({
|
title: '上传文件未成功',
|
message: err
|
})
|
},
|
request(param) {
|
var apiObj = config.apiObj;
|
if (this.apiObj) {
|
apiObj = this.apiObj;
|
}
|
const data = new FormData();
|
data.append(param.filename, param.file);
|
for (const key in param.data) {
|
data.append(key, param.data[key]);
|
}
|
apiObj.post(data, {
|
onUploadProgress: e => {
|
const complete = parseInt(((e.loaded / e.total) * 100) | 0, 10)
|
param.onProgress({ percent: complete })
|
}
|
}).then(res => {
|
var response = config.parseData(res);
|
if (response.code == config.successCode) {
|
param.onSuccess(res)
|
} else {
|
param.onError(response.msg || "未知错误")
|
}
|
}).catch(err => {
|
param.onError(err)
|
})
|
}
|
}
|
}
|
</script>
|
|
<style scoped>
|
.el-form-item.is-error .sc-upload .el-upload--picture-card {
|
border-color: var(--el-color-danger);
|
}
|
|
.sc-upload .el-upload--picture-card {
|
border-radius: 0;
|
}
|
|
.sc-upload .uploader,
|
.sc-upload:deep(.el-upload) {
|
width: 100%;
|
height: 100%;
|
}
|
|
.sc-upload__img {
|
width: 100%;
|
height: 100%;
|
position: relative;
|
}
|
|
.sc-upload__img .image {
|
width: 100%;
|
height: 100%;
|
}
|
|
.sc-upload__img-actions {
|
position: absolute;
|
top: 0;
|
right: 0;
|
display: none;
|
}
|
|
.sc-upload__img-actions span {
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
width: 25px;
|
height: 25px;
|
cursor: pointer;
|
color: #fff;
|
}
|
|
.sc-upload__img-actions span i {
|
font-size: 12px;
|
}
|
|
.sc-upload__img-actions .del {
|
background: #F56C6C;
|
}
|
|
.sc-upload__img:hover .sc-upload__img-actions {
|
display: block;
|
}
|
|
.sc-upload__img-slot {
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
width: 100%;
|
height: 100%;
|
font-size: 12px;
|
background-color: var(--el-fill-color-lighter);
|
}
|
|
.sc-upload__uploading {
|
width: 100%;
|
height: 100%;
|
position: relative;
|
}
|
|
.sc-upload__progress {
|
position: absolute;
|
width: 100%;
|
height: 100%;
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
background-color: var(--el-overlay-color-lighter);
|
z-index: 1;
|
padding: 10px;
|
}
|
|
.sc-upload__progress .el-progress {
|
width: 100%;
|
}
|
|
.sc-upload__uploading .image {
|
width: 100%;
|
height: 100%;
|
}
|
|
.sc-upload .file-empty {
|
width: 100%;
|
height: 100%;
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
flex-direction: column;
|
}
|
|
.sc-upload .file-empty i {
|
font-size: 28px;
|
}
|
|
.sc-upload .file-empty h4 {
|
font-size: 12px;
|
font-weight: normal;
|
color: #8c939d;
|
margin-top: 8px;
|
}
|
|
.sc-upload.sc-upload-round {
|
border-radius: 50%;
|
overflow: hidden;
|
}
|
|
.sc-upload.sc-upload-round .el-upload--picture-card {
|
border-radius: 50%;
|
}
|
|
.sc-upload.sc-upload-round .sc-upload__img-actions {
|
top: auto;
|
left: 0;
|
right: 0;
|
bottom: 0;
|
}
|
|
.sc-upload.sc-upload-round .sc-upload__img-actions span {
|
width: 100%;
|
}
|
</style>
|