feat: 消息ui优化

This commit is contained in:
Gsh
2026-01-31 23:38:39 +08:00
parent 6af3fb44f4
commit 3b6887dc2e
18 changed files with 2640 additions and 1938 deletions

View File

@@ -0,0 +1,127 @@
import { ElMessage } from 'element-plus';
export interface CompressionLevel {
maxWidth: number;
maxHeight: number;
quality: number;
}
export const DEFAULT_COMPRESSION_LEVELS: CompressionLevel[] = [
{ maxWidth: 800, maxHeight: 800, quality: 0.6 },
{ maxWidth: 600, maxHeight: 600, quality: 0.5 },
{ maxWidth: 400, maxHeight: 400, quality: 0.4 },
];
/**
* 压缩图片
* @param file - 要压缩的图片文件
* @param maxWidth - 最大宽度
* @param maxHeight - 最大高度
* @param quality - 压缩质量 (0-1)
* @returns 压缩后的 Blob
*/
export function compressImage(
file: File,
maxWidth = 1024,
maxHeight = 1024,
quality = 0.8,
): Promise<Blob> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
let width = img.width;
let height = img.height;
if (width > maxWidth || height > maxHeight) {
const ratio = Math.min(maxWidth / width, maxHeight / height);
width = width * ratio;
height = height * ratio;
}
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d')!;
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob(
(blob) => {
if (blob) {
resolve(blob);
}
else {
reject(new Error('压缩失败'));
}
},
file.type,
quality,
);
};
img.onerror = reject;
img.src = e.target?.result as string;
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
/**
* 将 Blob 转换为 base64
* @param blob - Blob 对象
* @returns base64 字符串
*/
export function blobToBase64(blob: Blob): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result as string);
};
reader.onerror = reject;
reader.readAsDataURL(blob);
});
}
/**
* 尝试多级压缩直到满足大小限制
* @param file - 图片文件
* @param currentTotalLength - 当前已使用的总长度
* @param maxTotalLength - 最大允许总长度
* @returns 压缩结果或 null如果无法满足限制
*/
export async function tryCompressToLimit(
file: File,
currentTotalLength: number,
maxTotalLength: number,
compressionLevels = DEFAULT_COMPRESSION_LEVELS,
): Promise<{ blob: Blob; base64: string; estimatedLength: number } | null> {
for (const level of compressionLevels) {
const compressedBlob = await compressImage(
file,
level.maxWidth,
level.maxHeight,
level.quality,
);
const base64 = await blobToBase64(compressedBlob);
const estimatedLength = Math.floor(base64.length * 0.5);
if (currentTotalLength + estimatedLength <= maxTotalLength) {
return { blob: compressedBlob, base64, estimatedLength };
}
}
return null;
}
/**
* Composable: 使用图片压缩
*/
export function useImageCompression() {
return {
compressImage,
blobToBase64,
tryCompressToLimit,
DEFAULT_COMPRESSION_LEVELS,
};
}