feat: 拆分左侧栏、中间聊天区、右侧栏组件, Hooks 提取

This commit is contained in:
zdl
2025-11-24 15:23:22 +08:00
parent d483a230aa
commit f205cde7c1
6 changed files with 740 additions and 238 deletions

View File

@@ -0,0 +1,131 @@
// src/views/AgentChat/hooks/useFileUpload.ts
// 文件上传 Hook - 处理文件选择、预览、删除
import { useState, useRef } from 'react';
import type { ChangeEvent, RefObject } from 'react';
/**
* 上传文件数据结构
*/
export interface UploadedFile {
/** 文件名 */
name: string;
/** 文件大小(字节) */
size: number;
/** 文件 MIME 类型 */
type: string;
/** 文件预览 URL使用 URL.createObjectURL 创建) */
url: string;
}
/**
* useFileUpload Hook 返回值
*/
export interface UseFileUploadReturn {
/** 已上传文件列表 */
uploadedFiles: UploadedFile[];
/** 文件输入框引用(用于触发文件选择) */
fileInputRef: RefObject<HTMLInputElement>;
/** 处理文件选择事件 */
handleFileSelect: (event: ChangeEvent<HTMLInputElement>) => void;
/** 删除指定文件 */
removeFile: (index: number) => void;
/** 清空所有文件 */
clearFiles: () => void;
}
/**
* useFileUpload Hook
*
* 处理文件上传相关逻辑(选择、预览、删除)
*
* @returns UseFileUploadReturn
*
* @example
* ```tsx
* const { uploadedFiles, fileInputRef, handleFileSelect, removeFile } = useFileUpload();
*
* return (
* <>
* <input
* ref={fileInputRef}
* type="file"
* multiple
* accept="image/*,.pdf,.doc,.docx,.txt"
* onChange={handleFileSelect}
* style={{ display: 'none' }}
* />
* <Button onClick={() => fileInputRef.current?.click()}>上传文件</Button>
* {uploadedFiles.map((file, idx) => (
* <Tag key={idx}>
* {file.name}
* <TagCloseButton onClick={() => removeFile(idx)} />
* </Tag>
* ))}
* </>
* );
* ```
*/
export const useFileUpload = (): UseFileUploadReturn => {
const [uploadedFiles, setUploadedFiles] = useState<UploadedFile[]>([]);
const fileInputRef = useRef<HTMLInputElement>(null);
/**
* 处理文件选择事件
*/
const handleFileSelect = (event: ChangeEvent<HTMLInputElement>) => {
const files = Array.from(event.target.files || []);
const fileData: UploadedFile[] = files.map((file) => ({
name: file.name,
size: file.size,
type: file.type,
// 创建本地预览 URL实际上传时需要转换为 base64 或上传到服务器)
url: URL.createObjectURL(file),
}));
setUploadedFiles((prev) => [...prev, ...fileData]);
// 清空 input value允许重复选择同一文件
if (event.target) {
event.target.value = '';
}
};
/**
* 删除指定索引的文件
*/
const removeFile = (index: number) => {
setUploadedFiles((prev) => {
// 释放 URL.createObjectURL 创建的内存
const file = prev[index];
if (file?.url) {
URL.revokeObjectURL(file.url);
}
return prev.filter((_, i) => i !== index);
});
};
/**
* 清空所有文件
*/
const clearFiles = () => {
// 释放所有 URL 内存
uploadedFiles.forEach((file) => {
if (file.url) {
URL.revokeObjectURL(file.url);
}
});
setUploadedFiles([]);
};
return {
uploadedFiles,
fileInputRef,
handleFileSelect,
removeFile,
clearFiles,
};
};