(企业级)二次封装ElementPlus图片上传组件Vue3

 下面是对饿了么PLUS组件文件上传的二次封装,继承了原有功能,拓展了一些新的功能,本组件限制了上传的文件为图片类型。

并且全面引入了TS。

<template>
	<div class="upload-box">
		<el-upload
			action="#"
			:id="uuid"
			:class="['upload', self_disabled ? 'disabled' : '', drag ? 'no-border' : '']"
			:multiple="false"
			:disabled="self_disabled"
			:show-file-list="false"
			:http-request="handleHttpUpload"
			:before-upload="beforeUpload"
			:on-success="uploadSuccess"
			:on-error="uploadError"
			:drag="drag"
			:accept="fileType.join(',')"
		>
			<template v-if="imageUrl">
				<img :src="imageUrl" class="upload-image" />
				<div class="upload-handle" @click.stop>
					<div class="handle-icon" @click="editImg" v-if="!self_disabled">
						<el-icon><Edit /></el-icon>
						<span>编辑</span>
					</div>
					<!-- <div class="handle-icon" @click="imgViewVisible = true">
						<el-icon><ZoomIn /></el-icon>
						<span>查看</span>
					</div> -->
					<div class="handle-icon" @click="deleteImg" v-if="!self_disabled">
						<el-icon><Delete /></el-icon>
						<span>删除</span>
					</div>
				</div>
			</template>
			<template v-else>
				<div class="upload-empty">
					<slot name="empty">
						<el-icon><Plus /></el-icon>
						<!-- <span>请上传图片</span> -->
					</slot>
				</div>
			</template>
		</el-upload>
		<div class="el-upload__tip">
			<slot name="tip"></slot>
		</div>
		<el-image-viewer v-if="imgViewVisible" @close="imgViewVisible = false" :url-list="[imageUrl]" />
	</div>
</template>

<script setup lang="ts" name="UploadImg">
import { ref, computed, inject } from "vue";
import { uploadImg } from "@/api/modules/upload";
import { generateUUID } from "@/utils/util";
import { ElNotification, formContextKey, formItemContextKey } from "element-plus";
import type { UploadProps, UploadRequestOptions } from "element-plus";

type FileTypes =
	| "image/apng"
	| "image/bmp"
	| "image/gif"
	| "image/jpeg"
	| "image/pjpeg"
	| "image/png"
	| "image/svg+xml"
	| "image/tiff"
	| "image/webp"
	| "image/x-icon";

interface UploadFileProps {
	imageUrl: string; // 图片地址 ==> 必传
	api?: (params: any) => Promise<any>; // 上传图片的 api 方法,一般项目上传都是同一个 api 方法,在组件里直接引入即可 ==> 非必传
	drag?: boolean; // 是否支持拖拽上传 ==> 非必传(默认为 true)
	disabled?: boolean; // 是否禁用上传组件 ==> 非必传(默认为 false)
	fileSize?: number; // 图片大小限制 ==> 非必传(默认为 5M)
	fileType?: FileTypes[]; // 图片类型限制 ==> 非必传(默认为 ["image/jpeg", "image/png", "image/gif"])
	height?: string; // 组件高度 ==> 非必传(默认为 150px)
	width?: string; // 组件宽度 ==> 非必传(默认为 150px)
	borderRadius?: string; // 组件边框圆角 ==> 非必传(默认为 8px)
	sizeHeight?:number;
	sizeWidth?:number;
}

// 接受父组件参数
const props = withDefaults(defineProps<UploadFileProps>(), {
	imageUrl: "",
	drag: true,
	disabled: false,
	fileSize: 5,
	fileType: () => ["image/jpeg", "image/png", "image/gif"],
	height: "150px",
	width: "150px",
	borderRadius: "8px",
	sizeHeight:0,
	sizeWidth:0
});

// 生成组件唯一id
const uuid = ref("id-" + generateUUID());

// 查看图片
const imgViewVisible = ref(false);
// 获取 el-form 组件上下文
const formContext = inject(formContextKey, void 0);
// 获取 el-form-item 组件上下文
const formItemContext = inject(formItemContextKey, void 0);
// 判断是否禁用上传和删除
const self_disabled = computed(() => {
	return props.disabled || formContext?.disabled;
});

/**
 * @description 图片上传
 * @param options 上传的文件
 * */
interface UploadEmits {
	(e: "update:imageUrl", value: string): void;
	(e: "check-validate"): void;
}
const emit = defineEmits<UploadEmits>();
const handleHttpUpload = async (options: UploadRequestOptions) => {
	let formData = new FormData();
	formData.append("file", options.file);
	try {
		const api = props.api ?? uploadImg;
		const { data } = await api(formData);
		emit("update:imageUrl", data.fileUrl);
		// 调用 el-form 内部的校验方法(可自动校验)
		formItemContext?.prop && formContext?.validateField([formItemContext.prop as string]);
		emit("check-validate");
	} catch (error) {
		options.onError(error as any);
	}
};

/**
 * @description 删除图片
 * */
const deleteImg = () => {
	emit("update:imageUrl", "");
};

/**
 * @description 编辑图片
 * */
const editImg = () => {
	const dom = document.querySelector(`#${uuid.value} .el-upload__input`);
	dom && dom.dispatchEvent(new MouseEvent("click"));
};

/**
 * @description 文件上传之前判断
 * @param rawFile 上传的文件
 * */
const beforeUpload: UploadProps["beforeUpload"] = async(rawFile) => {
	const imgSize = rawFile.size / 1024 / 1024 < props.fileSize;
	const imgType = props.fileType;
	console.log('sizeHeight',rawFile)
	if (!imgType.includes(rawFile.type as FileTypes))
		ElNotification({
			title: "温馨提示",
			message: "上传图片不符合所需的格式!",
			type: "warning",
		});
	if (!imgSize)
		ElNotification({
			title: "温馨提示",
			message: `上传图片大小不能超过 ${props.fileSize}M!`,
			type: "warning",
		});

		//判断尺寸大小
		let isSize = await checkSize(rawFile);
		if(!isSize){
			ElNotification({
			title: "温馨提示",
			message: `上传图片尺寸大小需为 ${props.sizeWidth} * ${props.sizeHeight}!`,
			type: "warning",
		});
		}
	return imgType.includes(rawFile.type as FileTypes) && imgSize && isSize;
};

const checkSize = (rawFile:any)=>{
	return new Promise<boolean>(function (resolve) {
		let width = props.sizeWidth;
		let height = props.sizeHeight;
		console.log(width,height)
		if(!width || !height) resolve(true)
		let _URL = window.URL || window.webkitURL;
		let img = new Image();
		img.onload = function () {
			let valid = img.width == width && img.height == height;
			console.log('value',img.width)
			valid ? resolve(true) : resolve(false);
		};
		img.src = _URL.createObjectURL(rawFile);
	})
}
// 图片上传成功提示
const uploadSuccess = () => {
	ElNotification({
		title: "温馨提示",
		message: "图片上传成功!",
		type: "success",
	});
};

// 图片上传错误提示
const uploadError = () => {
	ElNotification({
		title: "温馨提示",
		message: "图片上传失败,请您重新上传!",
		type: "error",
	});
};
</script>
<style scoped lang="scss">
.is-error {
	.upload {
		:deep(.el-upload),
		:deep(.el-upload-dragger) {
			border: 1px dashed var(--el-color-danger) !important;
			&:hover {
				border-color: var(--el-color-primary) !important;
			}
		}
	}
}
:deep(.disabled) {
	.el-upload,
	.el-upload-dragger {
		cursor: not-allowed !important;
		background: var(--el-disabled-bg-color);
		border: 1px dashed var(--el-border-color-darker) !important;
		&:hover {
			border: 1px dashed var(--el-border-color-darker) !important;
		}
	}
}
.upload-box {
	.no-border {
		:deep(.el-upload) {
			border: none !important;
		}
	}
	:deep(.upload) {
		.el-upload {
			position: relative;
			display: flex;
			align-items: center;
			justify-content: center;
			width: v-bind(width);
			height: v-bind(height);
			overflow: hidden;
			border: 1px dashed var(--el-border-color-darker);
			border-radius: v-bind(borderRadius);
			transition: var(--el-transition-duration-fast);
			&:hover {
				border-color: var(--el-color-primary);
				.upload-handle {
					opacity: 1;
				}
			}
			.el-upload-dragger {
				display: flex;
				align-items: center;
				justify-content: center;
				width: 100%;
				height: 100%;
				padding: 0;
				overflow: hidden;
				background-color: transparent;
				border: 1px dashed var(--el-border-color-darker);
				border-radius: v-bind(borderRadius);
				&:hover {
					border: 1px dashed var(--el-color-primary);
				}
			}
			.el-upload-dragger.is-dragover {
				background-color: var(--el-color-primary-light-9);
				border: 2px dashed var(--el-color-primary) !important;
			}
			.upload-image {
				width: 100%;
				height: 100%;
				object-fit: contain;
			}
			.upload-empty {
				position: relative;
				display: flex;
				flex-direction: column;
				align-items: center;
				justify-content: center;
				font-size: 12px;
				line-height: 30px;
				color: var(--el-color-info);
				.el-icon {
					font-size: 28px;
					color: var(--el-text-color-secondary);
				}
			}
			.upload-handle {
				position: absolute;
				top: 0;
				right: 0;
				box-sizing: border-box;
				display: flex;
				align-items: center;
				justify-content: center;
				width: 100%;
				height: 100%;
				cursor: pointer;
				background: rgb(0 0 0 / 60%);
				opacity: 0;
				transition: var(--el-transition-duration-fast);
				.handle-icon {
					display: flex;
					flex-direction: column;
					align-items: center;
					justify-content: center;
					padding: 0 6%;
					color: aliceblue;
					.el-icon {
						margin-bottom: 40%;
						font-size: 130%;
						line-height: 130%;
					}
					span {
						font-size: 85%;
						line-height: 85%;
					}
				}
			}
		}
	}
	.el-upload__tip {
		line-height: 18px;
		text-align: center;
	}
}
</style>

 对其进行测试。

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Web阿成

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值