321 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<form-item-wrapper :designer="designer" :field="field" :rules="rules" :design-state="designState"
:parent-widget="parentWidget" :parent-list="parentList" :index-of-parent-list="indexOfParentList"
:sub-form-row-index="subFormRowIndex" :sub-form-col-index="subFormColIndex" :sub-form-row-id="subFormRowId">
<!-- el-upload增加:name="field.options.name"会导致又拍云上传失败故删除之 -->
<el-upload ref="fieldEditor" :disabled="field.options.disabled"
:action="field.options.uploadURL" :headers="uploadHeaders" :data="uploadData"
:with-credentials="field.options.withCredentials"
:multiple="field.options.multipleSelect" :file-list="fileList" :show-file-list="field.options.showFileList"
list-type="picture-card" :class="{'hideUploadDiv': uploadBtnHidden}"
:limit="field.options.limit" :on-exceed="handlePictureExceed"
:before-upload="beforePictureUpload" :on-preview="handlePictureCardPreview"
:on-success="handlePictureUpload" :on-error="handleUploadError" >
<template #file="{ file }">
<el-image
ref="imageRef"
style="width: 100%; height: 100%"
:src="file.url"
:preview-src-list="previewList"
:initial-index="previewIndex"
fit="cover"
preview-teleported
/>
<!-- 上传成功状态 -->
<label class="el-upload-list__item-status-label">
<i class="el-icon--upload-success" style="color: #FFF"><svg-icon class="" icon-class="el-check" /></i>
</label>
<!-- 图片操作按钮 -->
<span class="el-upload-list__item-actions">
<!-- 预览 -->
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<svg-icon icon-class="el-zoom-in" />
</span>
<!-- 删除 -->
<span
class="el-upload-list__item-delete"
@click="handlePictureRemove(file)"
>
<svg-icon icon-class="el-delete" />
</span>
</span>
</template>
<template #tip>
<div class="el-upload__tip"
v-if="!!field.options.uploadTip">{{field.options.uploadTip}}</div>
</template>
<div class="uploader-icon"><svg-icon icon-class="el-plus" /></div>
</el-upload>
</form-item-wrapper>
</template>
<script>
import FormItemWrapper from './form-item-wrapper'
import emitter from '@/utils/emitter'
import i18n, {translate} from "@/utils/i18n";
import {deepClone} from "@/utils/util";
import fieldMixin from "@/components/form-designer/form-widget/field-widget/fieldMixin";
import SvgIcon from "@/components/svg-icon/index";
export default {
name: "picture-upload-widget",
componentName: 'FieldWidget', //必须固定为FieldWidget用于接收父级组件的broadcast事件
mixins: [emitter, fieldMixin, i18n],
props: {
field: Object,
parentWidget: Object,
parentList: Array,
indexOfParentList: Number,
designer: Object,
designState: {
type: Boolean,
default: false
},
subFormRowIndex: { /* 子表单组件行索引从0开始计数 */
type: Number,
default: -1
},
subFormColIndex: { /* 子表单组件列索引从0开始计数 */
type: Number,
default: -1
},
subFormRowId: { /* 子表单组件行Id唯一id且不可变 */
type: String,
default: ''
},
},
components: {
FormItemWrapper,
SvgIcon,
},
data() {
return {
oldFieldValue: null, //field组件change之前的值
fieldModel: [],
rules: [],
uploadHeaders: {},
uploadData: {
key: '', //七牛云上传文件名
//token: '', //七牛云上传token
//policy: '', //又拍云上传policy
//authorization: '', //又拍云上传签名
},
fileList: [], //上传文件列表
fileListBeforeRemove: [], //删除前的文件列表
uploadBtnHidden: false,
previewIndex: 1, // 初始预览图像索引
}
},
computed: {
previewList() {
return this.fileList.map(el => el.url);
}
},
beforeCreate() {
/* 这里不能访问方法和属性!! */
},
created() {
/* 注意子组件mounted在父组件created之后、父组件mounted之前触发故子组件mounted需要用到的prop
需要在父组件created中初始化 */
this.initFieldModel()
this.registerToRefList()
this.initEventHandler()
this.buildFieldRules()
this.handleOnCreated()
},
mounted() {
this.handleOnMounted()
},
beforeUnmount() {
this.unregisterFromRefList()
},
methods: {
handlePictureExceed() {
let uploadLimit = this.field.options.limit
this.$message.warning( this.i18nt('render.hint.uploadExceed').replace('${uploadLimit}', uploadLimit) )
},
beforePictureUpload(file) {
let fileTypeCheckResult = false
if (!!this.field.options && !!this.field.options.fileTypes) {
let uploadFileTypes = this.field.options.fileTypes
if (uploadFileTypes.length > 0) {
fileTypeCheckResult = uploadFileTypes.some( (ft) => {
return file.type === 'image/' + ft
})
}
}
if (!fileTypeCheckResult) {
this.$message.error(this.i18nt('render.hint.unsupportedFileType') + file.type)
return false;
}
let fileSizeCheckResult = false
let uploadFileMaxSize = 5 //5MB
if (!!this.field.options && !!this.field.options.fileMaxSize) {
uploadFileMaxSize = this.field.options.fileMaxSize
}
fileSizeCheckResult = file.size / 1024 / 1024 <= uploadFileMaxSize
if (!fileSizeCheckResult) {
this.$message.error(this.$('render.hint.fileSizeExceed') + uploadFileMaxSize + 'MB')
return false;
}
this.uploadData.key = file.name
return this.handleOnBeforeUpload(file)
},
handleOnBeforeUpload(file) {
if (!!this.field.options.onBeforeUpload) {
let bfFunc = new Function('file', this.field.options.onBeforeUpload)
let result = bfFunc.call(this, file)
if (typeof result === 'boolean') {
return result
} else {
return true
}
}
return true
},
updateFieldModelAndEmitDataChangeForUpload(fileList, customResult, defaultResult) {
let oldValue = deepClone(this.fieldModel)
if (!!customResult && !!customResult.name && !!customResult.url) {
this.fieldModel.push({
name: customResult.name,
url: customResult.url
})
} else if (!!defaultResult && !!defaultResult.name && !!defaultResult.url) {
this.fieldModel.push({
name: defaultResult.name,
url: defaultResult.url
})
} else {
this.fieldModel = deepClone(fileList)
}
this.syncUpdateFormModel(this.fieldModel)
this.emitFieldDataChange(this.fieldModel, oldValue)
},
handlePictureUpload(res, file, fileList) {
if (file.status === 'success') {
let customResult = null
if (!!this.field.options.onUploadSuccess) {
let customFn = new Function('result', 'file', 'fileList', this.field.options.onUploadSuccess)
customResult = customFn.call(this, res, file, fileList)
}
this.updateFieldModelAndEmitDataChangeForUpload(fileList, customResult, res)
this.fileList = deepClone(fileList)
this.uploadBtnHidden = fileList.length >= this.field.options.limit
}
},
updateFieldModelAndEmitDataChangeForRemove(file) {
let oldValue = deepClone(this.fieldModel)
let foundFileIdx = -1
this.fileListBeforeRemove.map((fi, idx) => { /* 跟element-ui不同element-plus删除文件时this.fileList数组对应元素已被删除 */
if ((fi.name === file.name) && ((fi.url === file.url) || (!!fi.uid && fi.uid === file.uid))) { /* 这个判断有问题?? */
foundFileIdx = idx
}
})
if (foundFileIdx > -1) {
this.fieldModel.splice(foundFileIdx, 1)
}
this.syncUpdateFormModel(this.fieldModel)
this.emitFieldDataChange(this.fieldModel, oldValue)
},
handleBeforeRemove(fileList) {
/* 保留删除之前的文件列表!! */
this.fileListBeforeRemove = deepClone(fileList)
},
handlePictureRemove(file) {
this.handleBeforeRemove(this.fileList) // 由于自定义了 #file slot需要手动调用 handleBeforeRemove并移除 @before-remove 和 @remove
this.fileList.splice(this.fileList.indexOf(file), 1) // 删除所点击的文件
this.updateFieldModelAndEmitDataChangeForRemove(file)
let fileList = deepClone(this.fileList); // 进行深拷贝,避免用户自定义函数对 fileList 进行修改时,影响组件内的数据
this.uploadBtnHidden = fileList.length >= this.field.options.limit
if (!!this.field.options.onFileRemove) {
let customFn = new Function('file', 'fileList', this.field.options.onFileRemove)
customFn.call(this, file, fileList)
}
},
handleUploadError(err, file, fileList) {
if (!!this.field.options.onUploadError) {
let customFn = new Function('error', 'file', 'fileList', this.field.options.onUploadError)
customFn.call(this, err, file, fileList)
} else {
this.$message({
message: this.i18nt('render.hint.uploadError') + err,
duration: 3000,
type: 'error',
})
}
},
handlePictureCardPreview({ url }) {
// 设置图片索引为当前点击的图片
this.previewIndex = this.previewList.indexOf(url)
// 模拟点击 <el-image> 组件下的 img 标签(点击事件被绑定在的每张 img 上)
this.$refs['imageRef'].$el.children[0].click()
}
}
}
</script>
<style lang="scss" scoped>
@import "../../../../styles/global.scss"; /* form-item-wrapper已引入还需要重复引入吗 */
.full-width-input {
width: 100% !important;
}
.hideUploadDiv {
:deep(div.el-upload--picture-card) { /* 隐藏最后的图片上传按钮 */
display: none;
}
:deep(div.el-upload--text) { /* 隐藏最后的文件上传按钮 */
display: none;
}
:deep(div.el-upload__tip) { /* 隐藏最后的文件上传按钮提示 */
display: none;
}
}
.uploader-icon {
height: 100%;
display: flex;
color: #8c939d;
font-size: 28px;
justify-content: center;
align-items: center;
}
</style>