封装上传图片附件组件(超详细,cv即用)

197 阅读2分钟
<template>
    <div>
	<el-upload ref="uploadEl" :style="disabled ? { pointerEvents: 'none' } : ''"
			:class="isBtnUpload ? '' : 'uploader'" action="" :multiple="multiple" :limit="limit" :file-list="fileList"
			:list-type="listType" :accept="accept" :show-file-list="showFileList" :on-change="handleUploadChange"
			:on-remove="handleRemove" :before-remove="beforeRemove" :on-exceed="handleExceed"
			:http-request="function () { }" :disabled="disabled">
			<template v-if="!isBtnUpload">
				<div v-if="localValue && !showFileList" class="show-box">
					<img v-if="localValue" style="width: 146px; height: 146px; border-radius: 5px; vertical-align: top"
						class="el-upload-list__item-thumbnail" :src="localValue" />
					<i v-else class="el-icon-document" style="font-size: 68px" />
					<div v-if="!defaultConfig.disabled" class="thumb_action">
						<span>{{ defaultConfig.text ? '重新' + defaultConfig.text : '重新上传' }}</span>
						<span v-if="localValue && showDelBtn" class="del" @click.stop="onRemove">删除</span>
					</div>
				</div>
				<div v-if="!localValue || showFileList" class="slot-default">
					<i class="el-icon-plus" />
					<p class="co-text-gray" :style="defaultConfig.textStyles">
						<i v-show="defaultConfig.loading" class="el-icon-loading co-font-18"
							style="vertical-align: middle" />
						{{ defaultConfig.text || '点击上传' }}
					</p>
				</div>
			</template>
			<template v-else>
				<el-button size="small" type="primary">点击上传</el-button>
			</template>
			<!-- <div v-if="defaultConfig.tips" slot="tip" class="el-upload__tip">{{ defaultConfig.tips }}</div> -->
		</el-upload>

		<!-- 图片 且 显示删除按钮 -->
		<!-- <el-button v-if="localValue&&showDelBtn" type="warning" size="mini" :disabled="disabled" @click="onRemove">删除</el-button> -->
	</div>
</template>

<script>
// const IMAGE_TYPE = ["JPG", "PNG"]

export default {
	name: "ProUpload",
	props: {
		disabled: {
			type: Boolean,
			default: false
		},
		isBtnUpload: { // 是按钮上传吗
			type: Boolean,
			default: false
		},
		showDelBtn: { // 显示删除按钮吗
			type: Boolean,
			default: false
		},
		text: {
			type: Boolean,
			default: false
		},
		tip: {
			type: String,
			default: ""
		},
		multiple: {
			type: Boolean,
			default: false
		},
		limit: {
			type: Number,
			default: 1
		},
		listType: {
			type: String,
			default: ""
		},
		showFileList: {
			type: Boolean,
			default: false
		},
		maxSize: {
			// 单位为B,默认10MB
			type: Number,
			default: 10
		},
		accept: {
			type: String,
			default: ".jpg,.png,.jpeg"
		},
		noPreview: {
			type: Boolean,
			default: false
		},
		fileList: {
			type: Array,
			default: () => []
		},
		value: {
			// v-model 绑定的上传文件的文件流
			type: [File, String],
			default() {
				return null
			}
		}
	},
	data() {
		return {
			localValue: this.value || null,
			file: "",
			defaultConfig: {
				loading: false,
				accept: ".jpg,.png,.jpeg",
				limit: 1,
				multiple: false,
				tips: ""
			}
		}
	},
	computed: {
		upAccept() {
			return this.accept.toLocaleUpperCase()
		},
		formatAccept() {
			const up = this.accept.toLocaleUpperCase()
			const lower = this.accept.toLocaleLowerCase()
			return up + "," + lower
		}
	},
	watch: {
		value: {
			handler(val) {
				this.localValue = val
			},
			deep: true,
			immediate: true
		},
		localValue: {
			handler(val) {
				// 没有值 且 不显示文件列表 时 需要清除上传列表 否则不可再次上传
				if ((!val || !val.length) && !this.showFileList) {
					this.$refs.uploadEl?.clearFiles()
				}
			},
			deep: true,
			immediate: true
		}
	},
	mounted() {
		this.resetLocalValue()
	},
	methods: {
		resetLocalValue() {
			// this.localValue = null
			this.$refs.uploadEl?.clearFiles();
		},
		onRemove() {
			this.$confirm("确定删除吗?", "提示", {
				confirmButtonText: "确定",
				cancelButtonText: "取消",
				type: "warning"
			}).then(() => {
				this.localValue = null
				this.$emit("onRemoveCallback")
			}).catch(() => { });
		},
		// 预览
		handlePreview() {
		},
		handleExceed(files, fileList) {
			// 显示列表时才提示
			// console.log(this.showFileList, "超出限制", files, fileList)
			if (this.showFileList) this.$message.warning(`当前限制上传${this.limit}个文件,如需上传新的文件,请先删除上传过的文件`);
		},
		beforeRemove(file, fileList) {
			// console.log("确定删除???", file, fileList)
			return this.$confirm(`确定删除 ${file.name}?`);
		},
		// 删除
		handleRemove(file, fileList) {
			// console.log("删除", file, fileList)
			this.$emit("callback", { type: "remove", file: fileList })
		},
		// 下载
		handleDownload() {
			this.$emit("callback", { type: "download" })
		},
		handleUploadChange(file, fileList) {
			console.log("文件改变", file, fileList)
			// if (this.limit === 1 && fileList.length > 1) {
			// 	fileList.splice(0, 1);
			// }
			const name = file.name
			// const arr = name.split(".")
			// const fileType = arr[arr.length - 1]
			const isLimit = file.size / 1024 / 1024 > this.maxSize
			this.resetLocalValue()
			// console.log("文件大小超过???", isLimit)
			if (isLimit) {
				const uid = file.uid
				const idx = fileList.findIndex(item => item.uid === uid)
				fileList.splice(idx, 1) // 去除文件列表失败文件

				this.$message.error(`上传文件大小不能超过 ${this.maxSize}MB!`)
				return
			}

			const postfix = name.substring(name.lastIndexOf("."))
			// console.log("文件类型", name, postfix, this.accept)
			if (this.accept && !this.accept.includes(postfix)) {
				const uid = file.uid
				const idx = fileList.findIndex(item => item.uid === uid)
				fileList.splice(idx, 1) // 去除文件列表失败文件

				this.$message.error(`只能上传${this.accept}文件!`)
				return
			}

			// if (IMAGE_TYPE.includes(fileType.toLocaleUpperCase())) {
			// 	this.localValue = URL.createObjectURL(file.raw)
			// } else {
			// 	this.localValue = ""
			// }

			const imgType = ".jpg,.png,.jpeg"
			if (imgType.includes(postfix)) {
				// 只有图片进行赋值

				this.localValue = URL.createObjectURL(file.raw)
			} else {
				this.localValue = ""
			}
			// console.log("上传文件", imgType, postfix, imgType.includes(postfix))

			this.file = file
			this.$emit("callback", { type: "upload", file: file.raw })
		}
	}
}
</script>

<style lang="scss" scoped>
.hide_box {

	::v-deep .el-upload.el-upload--picture-card,
	::v-deep .el-upload.el-upload--picture {
		display: none;
	}
}

.show-box {
	position: relative;
	display: flex;
	justify-content: center;
	align-items: center;
	height: 148px;

	.thumb_action {
		position: absolute;
		top: 0;
		left: 0;
		width: 148px;
		height: 148px;
		z-index: 12;
		background-color: rgba(0, 0, 0, 0.5);
		color: #fff;
		opacity: 0;
		transition: opacity 0.15s;

		display: flex;
		flex-direction: column;
		justify-content: space-between;
		border-radius: 5px;

		.del {
			margin: 0 auto;

			&:hover {
				opacity: 0.8;
			}
		}
	}

	&:hover {
		.thumb_action {
			opacity: 1;
		}
	}
}

.slot-default {
	line-height: normal;
	width: 148px;
	height: 148px;
	text-align: center;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
}

.uploader {
	::v-deep .el-upload {
		border: 1px dashed #c0ccda;
		border-radius: 5px;
	}
}
</style>