1
0

useChatFiles.ts 3.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import { ref } from 'vue';
  2. import dayjs from 'dayjs';
  3. import { message } from 'ant-design-vue';
  4. import type { AttachedFile } from '/@/views/ventAI/manageAssistent/types/index';
  5. import { isAudioFile, isCsvFile, isDxfFile, isExcelFile, isTextFile, isVideoFile, isWordFile } from '/@/views/ventAI/manageAssistent/lib/index';
  6. import type ChatInputArea from '/@/views/ventAI/manageAssistent/components/chat/ChatInputArea.vue';
  7. /**
  8. * 输入区附件:文件选择 / 拖拽 / 粘贴三条入口统一走 addFile —— 校验 → 按类型读取(文本/二进制/预览)
  9. * → 落到待发送状态。三条入口的差别只在「文件从哪来」,校验与分流必须共用一份,否则会各自漂移。
  10. */
  11. export const useChatFiles = (getChatInput: () => InstanceType<typeof ChatInputArea> | undefined) => {
  12. const pendingFile = ref<AttachedFile | null>(null);
  13. const activeFileReader = ref<FileReader | null>(null);
  14. const triggerFileUpload = () => {
  15. getChatInput()?.fileInputRef?.click();
  16. };
  17. /**
  18. * 校验并读入一个文件作为待发送附件。
  19. * 待发附件是单值(pendingFile),多选/多文件场景由调用方取第一个后再进来。
  20. */
  21. const addFile = (file: File): void => {
  22. // 剪贴板里取到的文件可能没有文件名(部分浏览器的截图项),补一个仅用于展示与分流的名字
  23. const name = file.name || `粘贴附件_${dayjs().format('HHmmss')}`;
  24. if (isVideoFile(name) || isAudioFile(name)) {
  25. message.warning('不支持上传音频和视频文件');
  26. return;
  27. }
  28. try {
  29. const newFile: AttachedFile = {
  30. id: `file-${Date.now()}`,
  31. name,
  32. size: file.size,
  33. type: file.type,
  34. uploadTime: dayjs().format('YYYY-MM-DD HH:mm'),
  35. originalFile: file,
  36. };
  37. // 换附件时中断上一次仍在读取的 FileReader
  38. if (activeFileReader.value) {
  39. activeFileReader.value.abort();
  40. activeFileReader.value = null;
  41. }
  42. const reader = new FileReader();
  43. activeFileReader.value = reader;
  44. // 读取方式只判断一次:onload 与 readAs* 两处各写一遍条件容易走偏。
  45. // .dxf 必须在此显式分流到文本:否则会落到 dataURL 分支,附件里只有 preview、拿不到 content,
  46. // DXF 预览面板只能显示空白。
  47. const readMode = isTextFile(name) || isCsvFile(name) || isDxfFile(name) ? 'text' : isWordFile(name) || isExcelFile(name) ? 'binary' : 'dataurl';
  48. reader.onload = (e) => {
  49. activeFileReader.value = null;
  50. if (readMode === 'text') {
  51. newFile.content = e.target?.result as string;
  52. } else if (readMode === 'binary') {
  53. newFile.arrayBuffer = e.target?.result as ArrayBuffer;
  54. } else {
  55. newFile.preview = e.target?.result as string;
  56. }
  57. };
  58. if (readMode === 'text') {
  59. reader.readAsText(file);
  60. } else if (readMode === 'binary') {
  61. reader.readAsArrayBuffer(file);
  62. } else {
  63. reader.readAsDataURL(file);
  64. }
  65. pendingFile.value = newFile;
  66. message.success(`文件 "${name}" 已添加,点击发送按钮提交`);
  67. } catch (error) {
  68. console.error('文件处理失败:', error);
  69. message.error('文件处理失败,请重试');
  70. }
  71. };
  72. const handleFileUpload = (event: Event) => {
  73. const target = event.target as HTMLInputElement;
  74. const file = target.files?.[0];
  75. // 无论是否取到文件都复位,否则再次选择同一个文件不会再触发 change
  76. target.value = '';
  77. if (!file) return;
  78. addFile(file);
  79. };
  80. const removePendingFile = () => {
  81. pendingFile.value = null;
  82. };
  83. return { pendingFile, activeFileReader, triggerFileUpload, handleFileUpload, addFile, removePendingFile };
  84. };