feat:图片压缩

This commit is contained in:
yangmf_0726 committed 2025-10-16 16:34:24 +08:00
1 parent 72e9ded8e2
commit 21fde5a6e4
6 files changed
+130 -59

No files matched your search

+29
View File
@@ -24,6 +24,7 @@
"benz-amr-recorder": "^1.1.5",
"bpmn-js-token-simulation": "^0.36.0",
"camunda-bpmn-moddle": "^7.0.1",
"compressorjs": "^1.2.1",
"cropperjs": "^1.6.1",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.10",
@@ -7275,6 +7276,12 @@
"integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
"dev": true
},
"node_modules/blueimp-canvas-to-blob": {
"version": "3.29.0",
"resolved": "https://registry.npmmirror.com/blueimp-canvas-to-blob/-/blueimp-canvas-to-blob-3.29.0.tgz",
"integrity": "sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg==",
"license": "MIT"
},
"node_modules/boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz",
@@ -7999,6 +8006,16 @@
"resolved": "https://registry.npmmirror.com/component-event/-/component-event-0.2.1.tgz",
"integrity": "sha512-wGA++isMqiDq1jPYeyv2as/Bt/u+3iLW0rEa+8NQ82jAv3TgqMiCM+B2SaBdn2DfLilLjjq736YcezihRYhfxw=="
},
"node_modules/compressorjs": {
"version": "1.2.1",
"resolved": "https://registry.npmmirror.com/compressorjs/-/compressorjs-1.2.1.tgz",
"integrity": "sha512-+geIjeRnPhQ+LLvvA7wxBQE5ddeLU7pJ3FsKFWirDw6veY3s9iLxAQEw7lXGHnhCJvBujEQWuNnGzZcvCvdkLQ==",
"license": "MIT",
"dependencies": {
"blueimp-canvas-to-blob": "^3.29.0",
"is-blob": "^2.1.0"
}
},
"node_modules/compute-scroll-into-view": {
"version": "1.0.20",
"resolved": "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz",
@@ -11499,6 +11516,18 @@
"node": ">=8"
}
},
"node_modules/is-blob": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/is-blob/-/is-blob-2.1.0.tgz",
"integrity": "sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw==",
"license": "MIT",
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-boolean-object": {
"version": "1.2.2",
"resolved": "https://registry.npmmirror.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
+1
View File
@@ -40,6 +40,7 @@
"benz-amr-recorder": "^1.1.5",
"bpmn-js-token-simulation": "^0.36.0",
"camunda-bpmn-moddle": "^7.0.1",
"compressorjs": "^1.2.1",
"cropperjs": "^1.6.1",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.10",
+24 -21
View File
@@ -1,19 +1,8 @@
<template>
<div class="upload-box">
<el-upload
:id="uuid"
:accept="fileType.join(',')"
:action="uploadUrl"
:before-upload="beforeUpload"
:class="['upload', drag ? 'no-border' : '']"
:disabled="disabled"
:drag="drag"
:http-request="httpRequest"
:multiple="false"
:on-error="uploadError"
:on-success="uploadSuccess"
:show-file-list="false"
>
<el-upload :id="uuid" :accept="fileType.join(',')" :action="uploadUrl" :before-upload="beforeUpload"
:class="['upload', drag ? 'no-border' : '']" :disabled="disabled" :drag="drag" :http-request="httpRequest"
:multiple="false" :on-error="uploadError" :on-success="uploadSuccess" :show-file-list="false">
<template v-if="modelValue">
<img :src="modelValue" class="upload-image" />
<div class="upload-handle" @click.stop>
@@ -52,7 +41,7 @@ import type { UploadProps } from 'element-plus'
import { generateUUID } from '@/utils'
import { propTypes } from '@/utils/propTypes'
import { createImageViewer } from '@/components/ImageViewer'
import { useUpload } from '@/components/UploadFile/src/useUpload'
import { compressImage, useUpload } from '@/components/UploadFile/src/useUpload'
defineOptions({ name: 'UploadImg' })
@@ -106,15 +95,27 @@ const editImg = () => {
dom && dom.dispatchEvent(new MouseEvent('click'))
}
const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
const beforeUpload: UploadProps['beforeUpload'] = async (rawFile) => {
const imgSize = rawFile.size / 1024 / 1024 < props.fileSize
const imgType = props.fileType
if (!imgType.includes(rawFile.type as FileTypes))
message.notifyWarning('上传图片不符合所需的格式!')
if (!imgSize) message.notifyWarning(`上传图片大小不能超过 ${props.fileSize}M!`)
return imgType.includes(rawFile.type as FileTypes) && imgSize
}
if (!imgType.includes(rawFile.type as FileTypes)) {
message.notifyWarning('上传图片不符合所需的格式!')
return false
}
if (!imgSize) {
message.notifyWarning(`上传图片大小不能超过 ${props.fileSize}M!`)
return false
}
// ===== 新增:压缩图片 =====
try {
const compressed = await compressImage(rawFile, 0.8)
return compressed
} catch {
return rawFile // 失败则用原图
}
}
// 图片上传成功提示
const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => {
message.success('上传成功')
@@ -129,6 +130,7 @@ const uploadError = () => {
<style lang="scss" scoped>
.is-error {
.upload {
:deep(.el-upload),
:deep(.el-upload-dragger) {
border: 1px dashed var(--el-color-danger) !important;
@@ -141,6 +143,7 @@ const uploadError = () => {
}
:deep(.disabled) {
.el-upload,
.el-upload-dragger {
cursor: not-allowed !important;
+24 -21
View File
@@ -1,21 +1,9 @@
<template>
<div class="upload-box">
<el-upload
v-model:file-list="fileList"
:accept="fileType.join(',')"
:action="uploadUrl"
:before-upload="beforeUpload"
:class="['upload', drag ? 'no-border' : '']"
:disabled="disabled"
:drag="drag"
:http-request="httpRequest"
:limit="limit"
:multiple="true"
:on-error="uploadError"
:on-exceed="handleExceed"
:on-success="uploadSuccess"
list-type="picture-card"
>
<el-upload v-model:file-list="fileList" :accept="fileType.join(',')" :action="uploadUrl"
:before-upload="beforeUpload" :class="['upload', drag ? 'no-border' : '']" :disabled="disabled" :drag="drag"
:http-request="httpRequest" :limit="limit" :multiple="true" :on-error="uploadError" :on-exceed="handleExceed"
:on-success="uploadSuccess" list-type="picture-card">
<div class="upload-empty">
<slot name="empty">
<Icon icon="ep:plus" />
@@ -47,7 +35,7 @@ import { ElNotification } from 'element-plus'
import { createImageViewer } from '@/components/ImageViewer'
import { propTypes } from '@/utils/propTypes'
import { useUpload } from '@/components/UploadFile/src/useUpload'
import { compressImage, useUpload } from '@/components/UploadFile/src/useUpload'
defineOptions({ name: 'UploadImgs' })
@@ -93,23 +81,36 @@ const uploadList = ref<UploadUserFile[]>([])
* @description 文件上传之前判断
* @param rawFile 上传的文件
* */
const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
const beforeUpload: UploadProps['beforeUpload'] = async (rawFile) => {
const imgSize = rawFile.size / 1024 / 1024 < props.fileSize
const imgType = props.fileType
if (!imgType.includes(rawFile.type as FileTypes))
if (!imgType.includes(rawFile.type as FileTypes)) {
ElNotification({
title: '温馨提示',
message: '上传图片不符合所需的格式!',
type: 'warning'
})
if (!imgSize)
return false
}
if (!imgSize) {
ElNotification({
title: '温馨提示',
message: `上传图片大小不能超过 ${props.fileSize}M!`,
type: 'warning'
})
return false
}
uploadNumber.value++
return imgType.includes(rawFile.type as FileTypes) && imgSize
// ===== 新增:压缩图片 =====
try {
const compressed = await compressImage(rawFile, 0.8)
return compressed
} catch {
return rawFile // 失败则用原图
}
}
// 图片上传成功
@@ -186,6 +187,7 @@ const handleExceed = () => {
<style lang="scss" scoped>
.is-error {
.upload {
:deep(.el-upload--picture-card),
:deep(.el-upload-dragger) {
border: 1px dashed var(--el-color-danger) !important;
@@ -198,6 +200,7 @@ const handleExceed = () => {
}
:deep(.disabled) {
.el-upload--picture-card,
.el-upload-dragger {
cursor: not-allowed;
@@ -2,6 +2,7 @@ import * as FileApi from '@/api/infra/file'
import CryptoJS from 'crypto-js'
import { UploadRawFile, UploadRequestOptions } from 'element-plus/es/components/upload/src/upload'
import axios from 'axios'
import Compressor from 'compressorjs'
/**
* 获得上传 URL
@@ -104,3 +105,29 @@ enum UPLOAD_TYPE {
// 客户端发送到后端上传
SERVER = 'server'
}
export const compressImage = (file: File, quality = 0.6): Promise<File> => {
return new Promise((resolve, reject) => {
new Compressor(file, {
quality,
maxWidth: 1920,
maxHeight: 1920,
mimeType: 'image/jpeg',
convertSize: 5 * 1024 * 1024,
checkOrientation: true,
success(result) {
const compressedFile = new File([result], file.name, {
type: result.type,
lastModified: Date.now()
})
console.log(`压缩: ${(file.size / 1024).toFixed(2)}KB → ${(compressedFile.size / 1024).toFixed(2)}KB`)
resolve(compressedFile)
},
error(err) {
console.error('压缩失败:', err)
reject(err)
}
})
})
}
+25 -17
View File
@@ -1,21 +1,9 @@
<template>
<Dialog v-model="dialogVisible" title="上传文件">
<el-upload
ref="uploadRef"
v-model:file-list="fileList"
:action="uploadUrl"
:auto-upload="false"
:data="data"
:disabled="formLoading"
:limit="1"
:on-change="handleFileChange"
:on-error="submitFormError"
:on-exceed="handleExceed"
:on-success="submitFormSuccess"
:http-request="httpRequest"
accept=".jpg, .png, .gif"
drag
>
<el-upload ref="uploadRef" v-model:file-list="fileList" :action="uploadUrl" :auto-upload="false"
:before-upload="beforeUpload" :data="data" :disabled="formLoading" :limit="1" :on-change="handleFileChange"
:on-error="submitFormError" :on-exceed="handleExceed" :on-success="submitFormSuccess" :http-request="httpRequest"
accept=".jpg, .png, .gif" drag>
<i class="el-icon-upload"></i>
<div class="el-upload__text"> 将文件拖到此处,或 <em>点击上传</em></div>
<template #tip>
@@ -31,7 +19,8 @@
</Dialog>
</template>
<script lang="ts" setup>
import { useUpload } from '@/components/UploadFile/src/useUpload'
import { useUpload, compressImage } from '@/components/UploadFile/src/useUpload'
import type { UploadProps, UploadRawFile } from 'element-plus'
defineOptions({ name: 'InfraFileForm' })
@@ -53,6 +42,25 @@ const open = async () => {
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 文件上传之前进行压缩 */
const beforeUpload: UploadProps['beforeUpload'] = async (file: UploadRawFile) => {
// 验证文件类型
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif']
if (!allowedTypes.includes(file.type)) {
message.error('仅允许上传 jpg、png、gif 格式文件!')
return false
}
// 压缩图片
try {
const compressed = await compressImage(file, 0.8)
return compressed
} catch (error) {
message.warning('图片压缩失败,将使用原图上传')
return file
}
}
/** 处理上传的文件发生变化 */
const handleFileChange = (file) => {
data.value.path = file.name