From ca32b6d102c75dda9c33a6f64d3fee1c79b30583 Mon Sep 17 00:00:00 2001 From: shixiaohe Date: Sat, 18 Jan 2025 09:10:58 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E5=8E=8B=E7=BC=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/UploadFile/src/UploadImg.vue | 121 ++++++++++++++++---- 1 file changed, 100 insertions(+), 21 deletions(-) diff --git a/src/components/UploadFile/src/UploadImg.vue b/src/components/UploadFile/src/UploadImg.vue index ac0c162d6..7bfe90232 100644 --- a/src/components/UploadFile/src/UploadImg.vue +++ b/src/components/UploadFile/src/UploadImg.vue @@ -71,21 +71,74 @@ type FileTypes = // 接受父组件参数 const props = defineProps({ modelValue: propTypes.string.def(''), - drag: propTypes.bool.def(true), // 是否支持拖拽上传 ==> 非必传(默认为 true) - disabled: propTypes.bool.def(false), // 是否禁用上传组件 ==> 非必传(默认为 false) - fileSize: propTypes.number.def(5), // 图片大小限制 ==> 非必传(默认为 5M) - fileType: propTypes.array.def(['image/jpeg', 'image/png', 'image/gif']), // 图片类型限制 ==> 非必传(默认为 ["image/jpeg", "image/png", "image/gif"]) - height: propTypes.string.def('150px'), // 组件高度 ==> 非必传(默认为 150px) - width: propTypes.string.def('150px'), // 组件宽度 ==> 非必传(默认为 150px) - borderradius: propTypes.string.def('8px'), // 组件边框圆角 ==> 非必传(默认为 8px) - showDelete: propTypes.bool.def(true), // 是否显示删除按钮 - showBtnText: propTypes.bool.def(true) // 是否显示按钮文字 + drag: propTypes.bool.def(true), + disabled: propTypes.bool.def(false), + fileSize: propTypes.number.def(5), + fileType: propTypes.array.def(['image/jpeg', 'image/png', 'image/gif']), + height: propTypes.string.def('150px'), + width: propTypes.string.def('150px'), + borderradius: propTypes.string.def('8px'), + showDelete: propTypes.bool.def(true), + showBtnText: propTypes.bool.def(true), + // 新增压缩相关属性 + enableCompress: propTypes.bool.def(false), // 是否启用压缩 + compressSize: propTypes.number.def(200), // 压缩后的大小限制,单位KB + compressQuality: propTypes.number.def(0.8) // 压缩质量,0-1之间 }) -const { t } = useI18n() // 国际化 -const message = useMessage() // 消息弹窗 -// 生成组件唯一id + +const { t } = useI18n() +const message = useMessage() const uuid = ref('id-' + generateUUID()) -// 查看图片 + +// 压缩图片方法 +const compressImage = (file: File): Promise => { + return new Promise((resolve, reject) => { + const reader = new FileReader() + reader.readAsDataURL(file) + reader.onload = (e) => { + const img = new Image() + img.src = e.target?.result as string + img.onload = () => { + const canvas = document.createElement('canvas') + const ctx = canvas.getContext('2d') + if (!ctx) { + reject(new Error('Failed to get canvas context')) + return + } + + // 计算压缩后的尺寸 + let width = img.width + let height = img.height + const maxSize = Math.max(width, height) + if (maxSize > 1920) { + const ratio = 1920 / maxSize + width *= ratio + height *= ratio + } + + canvas.width = width + canvas.height = height + ctx.drawImage(img, 0, 0, width, height) + + // 转换为Blob + canvas.toBlob( + (blob) => { + if (blob) { + resolve(blob) + } else { + reject(new Error('Failed to compress image')) + } + }, + file.type, + props.compressQuality + ) + } + img.onerror = () => reject(new Error('Failed to load image')) + } + reader.onerror = () => reject(new Error('Failed to read file')) + }) +} + const imagePreview = (imgUrl: string) => { createImageViewer({ zIndex: 9999999, @@ -106,26 +159,52 @@ const editImg = () => { dom && dom.dispatchEvent(new MouseEvent('click')) } -const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => { - const imgSize = rawFile.size / 1024 / 1024 < props.fileSize - const imgType = props.fileType - if (!imgType.includes(rawFile.type as FileTypes)) +// 修改上传前的处理方法 +const beforeUpload: UploadProps['beforeUpload'] = async (rawFile) => { + // 检查文件类型 + if (!props.fileType.includes(rawFile.type as FileTypes)) { message.notifyWarning('上传图片不符合所需的格式!') - if (!imgSize) message.notifyWarning(`上传图片大小不能超过 ${props.fileSize}M!`) - return imgType.includes(rawFile.type as FileTypes) && imgSize + return false + } + + // 检查原始文件大小 + const originalSize = rawFile.size / 1024 / 1024 + if (originalSize > props.fileSize) { + message.notifyWarning(`上传图片大小不能超过 ${props.fileSize}M!`) + return false + } + + // 如果启用压缩且文件大小超过压缩限制 + if (props.enableCompress && rawFile.size > props.compressSize * 1024) { + try { + const compressedBlob = await compressImage(rawFile) + // 如果压缩后仍然超过限制 + if (compressedBlob.size > props.compressSize * 1024) { + message.notifyWarning(`压缩后图片仍然超过 ${props.compressSize}KB,请选择更小的图片!`) + return false + } + // 创建新的文件对象 + const newFile = new File([compressedBlob], rawFile.name, { type: rawFile.type }) + return newFile + } catch (error) { + message.notifyError('图片压缩失败,请重试!') + return false + } + } + + return true } -// 图片上传成功提示 const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => { message.success('上传成功') emit('update:modelValue', res.data) } -// 图片上传错误提示 const uploadError = () => { message.notifyError('图片上传失败,请您重新上传!') } +