Files
Yi.Admin/Yi.Ai.Vue3/src/composables/chat/useFilePaste.ts
2026-01-31 23:38:39 +08:00

145 lines
3.6 KiB
TypeScript

import { ElMessage } from 'element-plus';
import { useImageCompression, type CompressionLevel } from './useImageCompression';
import type { FileItem } from '@/stores/modules/files';
export interface UseFilePasteOptions {
/** 最大文件大小 (字节) */
maxFileSize?: number;
/** 最大总内容长度 */
maxTotalContentLength?: number;
/** 压缩级别配置 */
compressionLevels?: CompressionLevel[];
/** 获取当前文件列表总长度 */
getCurrentTotalLength: () => number;
/** 添加文件到列表 */
addFiles: (files: FileItem[]) => void;
/** 是否只接受图片 (默认true) */
imagesOnly?: boolean;
}
/**
* 从剪贴板数据项中提取文件
*/
function extractFilesFromItems(items: DataTransferItemList): File[] {
const files: File[] = [];
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.kind === 'file') {
const file = item.getAsFile();
if (file) {
files.push(file);
}
}
}
return files;
}
/**
* Composable: 处理粘贴事件中的文件
*/
export function useFilePaste(options: UseFilePasteOptions) {
const {
maxFileSize = 3 * 1024 * 1024,
maxTotalContentLength = 150000,
compressionLevels,
getCurrentTotalLength,
addFiles,
imagesOnly = true,
} = options;
const { tryCompressToLimit } = useImageCompression();
/**
* 处理单个粘贴的文件
*/
async function processPastedFile(
file: File,
currentTotalLength: number,
): Promise<FileItem | null> {
// 验证文件大小
if (file.size > maxFileSize) {
ElMessage.error(`文件 ${file.name} 超过 3MB 限制,已跳过`);
return null;
}
const isImage = file.type.startsWith('image/');
if (isImage) {
try {
const result = await tryCompressToLimit(
file,
currentTotalLength,
maxTotalContentLength,
compressionLevels,
);
if (!result) {
ElMessage.error(`${file.name} 图片内容过大,请压缩后上传`);
return null;
}
return {
uid: crypto.randomUUID(),
name: file.name,
fileSize: file.size,
file,
maxWidth: '200px',
showDelIcon: true,
imgPreview: true,
imgVariant: 'square',
url: result.base64,
isUploaded: true,
base64: result.base64,
fileType: 'image',
};
}
catch (error) {
console.error('处理图片失败:', error);
ElMessage.error(`${file.name} 处理失败`);
return null;
}
}
else if (!imagesOnly) {
// 如果不是仅图片模式,可以在这里处理其他类型文件
ElMessage.warning(`${file.name} 不支持粘贴,请使用上传按钮`);
return null;
}
return null;
}
/**
* 处理粘贴事件
*/
async function handlePaste(event: ClipboardEvent) {
const items = event.clipboardData?.items;
if (!items) return;
const files = extractFilesFromItems(items);
if (files.length === 0) return;
event.preventDefault();
let totalContentLength = getCurrentTotalLength();
const newFiles: FileItem[] = [];
for (const file of files) {
const processedFile = await processPastedFile(file, totalContentLength);
if (processedFile) {
newFiles.push(processedFile);
totalContentLength += Math.floor((processedFile.base64?.length || 0) * 0.5);
}
}
if (newFiles.length > 0) {
addFiles(newFiles);
ElMessage.success(`已添加 ${newFiles.length} 个文件`);
}
}
return {
handlePaste,
processPastedFile,
};
}