diff --git a/src/components/UploadFile/src/UploadImg.vue b/src/components/UploadFile/src/UploadImg.vue index ac0c162d66674f66d3b1b3dd60fb11ba62d04a6e..5f8c3e5e64957097fd0ffbb0b2c538c46630db87 100644 --- a/src/components/UploadFile/src/UploadImg.vue +++ b/src/components/UploadFile/src/UploadImg.vue @@ -79,7 +79,11 @@ const props = defineProps({ width: propTypes.string.def('150px'), // 组件宽度 ==> 非必传(默认为 150px) borderradius: propTypes.string.def('8px'), // 组件边框圆角 ==> 非必传(默认为 8px) showDelete: propTypes.bool.def(true), // 是否显示删除按钮 - showBtnText: propTypes.bool.def(true) // 是否显示按钮文字 + showBtnText: propTypes.bool.def(true), // 是否显示按钮文字 + // 新增压缩相关属性 + enableCompress: propTypes.bool.def(false), // 是否启用压缩 + compressSize: propTypes.number.def(300), // 压缩后的大小限制,单位KB + compressQuality: propTypes.number.def(0.8) // 压缩质量,0-1之间 }) const { t } = useI18n() // 国际化 const message = useMessage() // 消息弹窗 @@ -105,14 +109,88 @@ const editImg = () => { const dom = document.querySelector(`#${uuid.value} .el-upload__input`) dom && dom.dispatchEvent(new MouseEvent('click')) } +// 压缩图片方法 +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) -const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => { - const imgSize = rawFile.size / 1024 / 1024 < props.fileSize - const imgType = props.fileType - if (!imgType.includes(rawFile.type as FileTypes)) + // 转换为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 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(`压缩后图片${compressedBlob.size/1024}KB仍然超过 ${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 } // 图片上传成功提示 diff --git a/src/components/UploadFile/src/UploadImgs.vue b/src/components/UploadFile/src/UploadImgs.vue index 59857a98036ffd28def8c6ba30307b32906d490f..38e06e2120801e295e93c220c58129535fb91dac 100644 --- a/src/components/UploadFile/src/UploadImgs.vue +++ b/src/components/UploadFile/src/UploadImgs.vue @@ -81,7 +81,11 @@ const props = defineProps({ 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) + borderradius: propTypes.string.def('8px'), // 组件边框圆角 ==> 非必传(默认为 8px) + // 新增压缩相关属性 + enableCompress: propTypes.bool.def(false), // 是否启用压缩 + compressSize: propTypes.number.def(300), // 压缩后的大小限制,单位KB + compressQuality: propTypes.number.def(0.8) // 压缩质量,0-1之间 }) const { uploadUrl, httpRequest } = useUpload() @@ -89,27 +93,110 @@ const { uploadUrl, httpRequest } = useUpload() const fileList = ref([]) const uploadNumber = ref(0) const uploadList = ref([]) +// 压缩图片方法 +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')) + }) +} /** * @description 文件上传之前判断 * @param rawFile 上传的文件 * */ -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)) { ElNotification({ title: '温馨提示', message: '上传图片不符合所需的格式!', type: 'warning' }) - if (!imgSize) + return false + } + + // 检查原始文件大小 + const originalSize = rawFile.size / 1024 / 1024 + if (originalSize > props.fileSize) { ElNotification({ title: '温馨提示', message: `上传图片大小不能超过 ${props.fileSize}M!`, type: 'warning' }) + return false + } + + // 如果启用压缩且文件大小超过压缩限制 + if (props.enableCompress && rawFile.size > props.compressSize * 1024) { + try { + const compressedBlob = await compressImage(rawFile) + // 如果压缩后仍然超过限制 + if (compressedBlob.size > props.compressSize * 1024) { + ElNotification({ + title: '温馨提示', + message: `压缩后图片仍然超过 ${props.compressSize}KB,请选择更小的图片!`, + type: 'warning' + }) + return false + } + // 创建新的文件对象 + const newFile = new File([compressedBlob], rawFile.name, { type: rawFile.type }) + uploadNumber.value++ + return newFile + } catch (error) { + ElNotification({ + title: '温馨提示', + message: '图片压缩失败,请重试!', + type: 'error' + }) + return false + } + } + uploadNumber.value++ - return imgType.includes(rawFile.type as FileTypes) && imgSize + return true } // 图片上传成功