فهرست منبع

[Mod 0000] AI助手文件预览支持多种格式

wangkeyi 4 هفته پیش
والد
کامیت
f64b39f5b9

+ 12 - 4
src/views/ventAI/manageAssistent/components/AiAssistantModal.vue

@@ -146,6 +146,7 @@
   import FilePreviewPanel from './chatModal/FilePreviewPanel.vue';
   import TodoListBar from './chatModal/TodoListBar.vue';
   import type { AttachedFile, Message, Task } from './chatModal/types';
+  import { isTextFile } from './chatModal/utils';
 
   interface Props {
     visible: boolean;
@@ -702,10 +703,17 @@
       };
 
       const reader = new FileReader();
-      reader.onload = (e) => {
-        newFile.preview = e.target?.result as string;
-      };
-      reader.readAsDataURL(file);
+      if (isTextFile(file.name)) {
+        reader.onload = (e) => {
+          newFile.content = e.target?.result as string;
+        };
+        reader.readAsText(file);
+      } else {
+        reader.onload = (e) => {
+          newFile.preview = e.target?.result as string;
+        };
+        reader.readAsDataURL(file);
+      }
 
       pendingFile.value = newFile;
       message.success(`文件 "${file.name}" 已添加,点击发送按钮提交`);

+ 96 - 8
src/views/ventAI/manageAssistent/components/chatModal/FilePreviewPanel.vue

@@ -12,17 +12,40 @@
       </div>
       <div v-else-if="isPdfFile(file.name)" class="pdf-preview">
         <iframe v-if="file.preview" :src="file.preview" frameborder="0"></iframe>
-        <div v-else class="pdf-no-preview">无法加载PDF预览</div>
+        <div v-else class="no-preview-text">无法加载PDF预览</div>
       </div>
-      <div v-else class="text-preview">
+      <div v-else-if="isVideoFile(file.name)" class="video-preview">
+        <video v-if="file.preview" controls>
+          <source :src="file.preview" />
+          您的浏览器不支持视频预览
+        </video>
+        <div v-else class="no-preview-text">无法加载视频预览</div>
+      </div>
+      <div v-else-if="isAudioFile(file.name)" class="audio-preview">
+        <div class="audio-info">
+          <div class="audio-icon"></div>
+          <span class="audio-name">{{ file.name }}</span>
+        </div>
+        <audio v-if="file.preview" controls>
+          <source :src="file.preview" />
+          您的浏览器不支持音频预览
+        </audio>
+        <div v-else class="no-preview-text">无法加载音频预览</div>
+      </div>
+      <div v-else-if="isTextFile(file.name)" class="text-preview">
         <pre>{{ file.content || '文件内容加载中...' }}</pre>
       </div>
+      <div v-else class="no-preview">
+        <div class="no-preview-icon"></div>
+        <div class="no-preview-text">不支持预览此文件格式</div>
+        <div class="no-preview-hint">{{ getFileExtension(file.name) }} 格式文件暂不支持在线预览</div>
+      </div>
     </div>
   </div>
 </template>
 
 <script setup lang="ts">
-  import { isImageFile, isPdfFile } from './utils';
+  import { isImageFile, isPdfFile, isVideoFile, isAudioFile, isTextFile, getFileExtension } from './utils';
   import type { AttachedFile } from './types';
 
   defineProps<{
@@ -117,14 +140,51 @@
         height: 100%;
         border: none;
       }
+    }
+
+    .video-preview {
+      width: 100%;
+      height: 100%;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      video {
+        width: 100%;
+        max-height: 100%;
+        border-radius: 4px;
+      }
+    }
+
+    .audio-preview {
+      width: 100%;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      gap: 16px;
+      padding: 20px 0;
 
-      .pdf-no-preview {
+      .audio-info {
         display: flex;
         align-items: center;
-        justify-content: center;
-        height: 100%;
-        color: #8b949e;
-        font-size: 14px;
+        gap: 10px;
+
+        .audio-icon {
+          width: 40px;
+          height: 40px;
+          background-image: var(--img-chat-attach-icon);
+          background-repeat: no-repeat;
+          background-size: 100% 100%;
+        }
+
+        .audio-name {
+          font-size: 14px;
+          color: #e0e6ed;
+        }
+      }
+
+      audio {
+        width: 100%;
       }
     }
 
@@ -137,5 +197,33 @@
         color: #e0e6ed;
       }
     }
+
+    .no-preview {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: center;
+      height: 100%;
+      gap: 12px;
+
+      .no-preview-icon {
+        width: 64px;
+        height: 64px;
+        background-image: var(--img-chat-attach-icon);
+        background-repeat: no-repeat;
+        background-size: 100% 100%;
+        opacity: 0.4;
+      }
+    }
+
+    .no-preview-text {
+      color: #8b949e;
+      font-size: 14px;
+    }
+
+    .no-preview-hint {
+      color: #6e7681;
+      font-size: 12px;
+    }
   }
 </style>

+ 15 - 2
src/views/ventAI/manageAssistent/components/chatModal/utils.ts

@@ -107,7 +107,20 @@ export const formatFileSize = (bytes: number): string => {
   return (bytes / (1024 * 1024)).toFixed(2) + ' MB';
 };
 
-export const isImageFile = (filename: string): boolean =>
-  /\.(jpg|jpeg|png|gif|bmp|webp)$/i.test(filename);
+export const isImageFile = (filename: string): boolean => /\.(jpg|jpeg|png|gif|bmp|webp|svg|ico|tiff|tif)$/i.test(filename);
 
 export const isPdfFile = (filename: string): boolean => /\.pdf$/i.test(filename);
+
+export const isVideoFile = (filename: string): boolean => /\.(mp4|webm|ogg|mov|avi|mkv|flv|wmv|m4v)$/i.test(filename);
+
+export const isAudioFile = (filename: string): boolean => /\.(mp3|wav|ogg|aac|flac|wma|m4a|opus)$/i.test(filename);
+
+export const isTextFile = (filename: string): boolean =>
+  /\.(txt|md|json|csv|xml|yaml|yml|log|ini|toml|cfg|conf|env|gitignore|dockerignore|editorconfig|prettierrc|eslintrc|babelrc|properties|sh|bat|ps1|cmd|bash|zsh|fish|sql|graphql|proto|js|ts|jsx|tsx|vue|css|scss|sass|less|html|htm|svelte|py|java|kt|kts|c|cpp|h|hpp|cc|cxx|cs|go|rs|rb|php|swift|m|mm|r|R|lua|pl|pm|hs|ex|exs|erl|clj|scala|dart|zig|nim|v|sol|tf|hcl|gradle|cmake|makefile|mk)$/i.test(
+    filename
+  );
+
+export const getFileExtension = (filename: string): string => {
+  const match = filename.match(/\.([^.]+)$/);
+  return match ? `.${match[1]}`.toUpperCase() : '';
+};