增加图片自动压缩
parent
ca32b6d102
commit
d102e0a297
|
|
@ -71,25 +71,44 @@ type FileTypes =
|
||||||
// 接受父组件参数
|
// 接受父组件参数
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: propTypes.string.def(''),
|
modelValue: propTypes.string.def(''),
|
||||||
drag: propTypes.bool.def(true),
|
drag: propTypes.bool.def(true), // 是否支持拖拽上传 ==> 非必传(默认为 true)
|
||||||
disabled: propTypes.bool.def(false),
|
disabled: propTypes.bool.def(false), // 是否禁用上传组件 ==> 非必传(默认为 false)
|
||||||
fileSize: propTypes.number.def(5),
|
fileSize: propTypes.number.def(5), // 图片大小限制 ==> 非必传(默认为 5M)
|
||||||
fileType: propTypes.array.def(['image/jpeg', 'image/png', 'image/gif']),
|
fileType: propTypes.array.def(['image/jpeg', 'image/png', 'image/gif']), // 图片类型限制 ==> 非必传(默认为 ["image/jpeg", "image/png", "image/gif"])
|
||||||
height: propTypes.string.def('150px'),
|
height: propTypes.string.def('150px'), // 组件高度 ==> 非必传(默认为 150px)
|
||||||
width: propTypes.string.def('150px'),
|
width: propTypes.string.def('150px'), // 组件宽度 ==> 非必传(默认为 150px)
|
||||||
borderradius: propTypes.string.def('8px'),
|
borderradius: propTypes.string.def('8px'), // 组件边框圆角 ==> 非必传(默认为 8px)
|
||||||
showDelete: propTypes.bool.def(true),
|
showDelete: propTypes.bool.def(true), // 是否显示删除按钮
|
||||||
showBtnText: propTypes.bool.def(true),
|
showBtnText: propTypes.bool.def(true), // 是否显示按钮文字
|
||||||
// 新增压缩相关属性
|
// 新增压缩相关属性
|
||||||
enableCompress: propTypes.bool.def(false), // 是否启用压缩
|
enableCompress: propTypes.bool.def(false), // 是否启用压缩
|
||||||
compressSize: propTypes.number.def(200), // 压缩后的大小限制,单位KB
|
compressSize: propTypes.number.def(300), // 压缩后的大小限制,单位KB
|
||||||
compressQuality: propTypes.number.def(0.8) // 压缩质量,0-1之间
|
compressQuality: propTypes.number.def(0.8) // 压缩质量,0-1之间
|
||||||
})
|
})
|
||||||
|
const { t } = useI18n() // 国际化
|
||||||
const { t } = useI18n()
|
const message = useMessage() // 消息弹窗
|
||||||
const message = useMessage()
|
// 生成组件唯一id
|
||||||
const uuid = ref('id-' + generateUUID())
|
const uuid = ref('id-' + generateUUID())
|
||||||
|
// 查看图片
|
||||||
|
const imagePreview = (imgUrl: string) => {
|
||||||
|
createImageViewer({
|
||||||
|
zIndex: 9999999,
|
||||||
|
urlList: [imgUrl]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
|
const deleteImg = () => {
|
||||||
|
emit('update:modelValue', '')
|
||||||
|
}
|
||||||
|
|
||||||
|
const { uploadUrl, httpRequest } = useUpload()
|
||||||
|
|
||||||
|
const editImg = () => {
|
||||||
|
const dom = document.querySelector(`#${uuid.value} .el-upload__input`)
|
||||||
|
dom && dom.dispatchEvent(new MouseEvent('click'))
|
||||||
|
}
|
||||||
// 压缩图片方法
|
// 压缩图片方法
|
||||||
const compressImage = (file: File): Promise<Blob> => {
|
const compressImage = (file: File): Promise<Blob> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
@ -138,27 +157,6 @@ const compressImage = (file: File): Promise<Blob> => {
|
||||||
reader.onerror = () => reject(new Error('Failed to read file'))
|
reader.onerror = () => reject(new Error('Failed to read file'))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const imagePreview = (imgUrl: string) => {
|
|
||||||
createImageViewer({
|
|
||||||
zIndex: 9999999,
|
|
||||||
urlList: [imgUrl]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue'])
|
|
||||||
|
|
||||||
const deleteImg = () => {
|
|
||||||
emit('update:modelValue', '')
|
|
||||||
}
|
|
||||||
|
|
||||||
const { uploadUrl, httpRequest } = useUpload()
|
|
||||||
|
|
||||||
const editImg = () => {
|
|
||||||
const dom = document.querySelector(`#${uuid.value} .el-upload__input`)
|
|
||||||
dom && dom.dispatchEvent(new MouseEvent('click'))
|
|
||||||
}
|
|
||||||
|
|
||||||
// 修改上传前的处理方法
|
// 修改上传前的处理方法
|
||||||
const beforeUpload: UploadProps['beforeUpload'] = async (rawFile) => {
|
const beforeUpload: UploadProps['beforeUpload'] = async (rawFile) => {
|
||||||
// 检查文件类型
|
// 检查文件类型
|
||||||
|
|
@ -180,7 +178,7 @@ const beforeUpload: UploadProps['beforeUpload'] = async (rawFile) => {
|
||||||
const compressedBlob = await compressImage(rawFile)
|
const compressedBlob = await compressImage(rawFile)
|
||||||
// 如果压缩后仍然超过限制
|
// 如果压缩后仍然超过限制
|
||||||
if (compressedBlob.size > props.compressSize * 1024) {
|
if (compressedBlob.size > props.compressSize * 1024) {
|
||||||
message.notifyWarning(`压缩后图片仍然超过 ${props.compressSize}KB,请选择更小的图片!`)
|
message.notifyWarning(`压缩后图片${compressedBlob.size/1024}KB仍然超过 ${props.compressSize}KB,请选择更小的图片!`)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// 创建新的文件对象
|
// 创建新的文件对象
|
||||||
|
|
@ -195,16 +193,17 @@ const beforeUpload: UploadProps['beforeUpload'] = async (rawFile) => {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 图片上传成功提示
|
||||||
const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => {
|
const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => {
|
||||||
message.success('上传成功')
|
message.success('上传成功')
|
||||||
emit('update:modelValue', res.data)
|
emit('update:modelValue', res.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 图片上传错误提示
|
||||||
const uploadError = () => {
|
const uploadError = () => {
|
||||||
message.notifyError('图片上传失败,请您重新上传!')
|
message.notifyError('图片上传失败,请您重新上传!')
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.is-error {
|
.is-error {
|
||||||
.upload {
|
.upload {
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,11 @@ const props = defineProps({
|
||||||
fileType: propTypes.array.def(['image/jpeg', 'image/png', 'image/gif']), // 图片类型限制 ==> 非必传(默认为 ["image/jpeg", "image/png", "image/gif"])
|
fileType: propTypes.array.def(['image/jpeg', 'image/png', 'image/gif']), // 图片类型限制 ==> 非必传(默认为 ["image/jpeg", "image/png", "image/gif"])
|
||||||
height: propTypes.string.def('150px'), // 组件高度 ==> 非必传(默认为 150px)
|
height: propTypes.string.def('150px'), // 组件高度 ==> 非必传(默认为 150px)
|
||||||
width: 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()
|
const { uploadUrl, httpRequest } = useUpload()
|
||||||
|
|
@ -89,27 +93,110 @@ const { uploadUrl, httpRequest } = useUpload()
|
||||||
const fileList = ref<UploadUserFile[]>([])
|
const fileList = ref<UploadUserFile[]>([])
|
||||||
const uploadNumber = ref<number>(0)
|
const uploadNumber = ref<number>(0)
|
||||||
const uploadList = ref<UploadUserFile[]>([])
|
const uploadList = ref<UploadUserFile[]>([])
|
||||||
|
// 压缩图片方法
|
||||||
|
const compressImage = (file: File): Promise<Blob> => {
|
||||||
|
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 文件上传之前判断
|
* @description 文件上传之前判断
|
||||||
* @param rawFile 上传的文件
|
* @param rawFile 上传的文件
|
||||||
* */
|
* */
|
||||||
const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
|
// 修改文件上传前的处理方法
|
||||||
const imgSize = rawFile.size / 1024 / 1024 < props.fileSize
|
const beforeUpload: UploadProps['beforeUpload'] = async (rawFile) => {
|
||||||
const imgType = props.fileType
|
// 检查文件类型
|
||||||
if (!imgType.includes(rawFile.type as FileTypes))
|
if (!props.fileType.includes(rawFile.type as FileTypes)) {
|
||||||
ElNotification({
|
ElNotification({
|
||||||
title: '温馨提示',
|
title: '温馨提示',
|
||||||
message: '上传图片不符合所需的格式!',
|
message: '上传图片不符合所需的格式!',
|
||||||
type: 'warning'
|
type: 'warning'
|
||||||
})
|
})
|
||||||
if (!imgSize)
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查原始文件大小
|
||||||
|
const originalSize = rawFile.size / 1024 / 1024
|
||||||
|
if (originalSize > props.fileSize) {
|
||||||
ElNotification({
|
ElNotification({
|
||||||
title: '温馨提示',
|
title: '温馨提示',
|
||||||
message: `上传图片大小不能超过 ${props.fileSize}M!`,
|
message: `上传图片大小不能超过 ${props.fileSize}M!`,
|
||||||
type: 'warning'
|
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++
|
uploadNumber.value++
|
||||||
return imgType.includes(rawFile.type as FileTypes) && imgSize
|
return newFile
|
||||||
|
} catch (error) {
|
||||||
|
ElNotification({
|
||||||
|
title: '温馨提示',
|
||||||
|
message: '图片压缩失败,请重试!',
|
||||||
|
type: 'error'
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadNumber.value++
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 图片上传成功
|
// 图片上传成功
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue