();
+ const results: Array<{ name: string; path: string }> = [];
+ const exts = new Set(['.mp4', '.mov', '.mkv', '.avi', '.webm']);
+ const maxFolders = 120;
+
+ while (queue.length && visited.size < maxFolders) {
+ const path = queue.shift() || '';
+ if (visited.has(path)) {
+ continue;
+ }
+ visited.add(path);
+ try {
+ const resp = await fetch(`/api/gui/files/entries?path=${encodeURIComponent(path)}`, {
+ method: 'GET',
+ credentials: 'include',
+ headers: { Accept: 'application/json' }
+ });
+ const data = await resp.json().catch(() => null);
+ if (!data?.success) {
+ continue;
+ }
+ const items = Array.isArray(data?.data?.items) ? data.data.items : [];
+ for (const item of items) {
+ const rawPath =
+ item?.path ||
+ [path, item?.name].filter(Boolean).join('/').replace(/\\/g, '/').replace(/\/{2,}/g, '/');
+ const type = String(item?.type || '').toLowerCase();
+ if (type === 'directory' || type === 'folder') {
+ queue.push(rawPath);
+ continue;
+ }
+ const ext =
+ String(item?.extension || '').toLowerCase() ||
+ (rawPath.includes('.') ? `.${rawPath.split('.').pop()?.toLowerCase()}` : '');
+ if (exts.has(ext)) {
+ results.push({
+ name: item?.name || rawPath.split('/').pop() || rawPath,
+ path: rawPath
+ });
+ if (results.length >= 200) {
+ return results;
+ }
+ }
+ }
+ } catch (error) {
+ console.warn('遍历文件夹失败', path, error);
+ }
+ }
+ return results;
+ },
+
+ async loadWorkspaceVideos() {
+ this.videoLoading = true;
+ try {
+ const entries = await this.fetchAllVideoEntries('');
+ this.videoEntries = entries;
+ if (!entries.length) {
+ this.uiPushToast({
+ title: '未找到视频',
+ message: '工作区内没有可用的视频文件',
+ type: 'info'
+ });
+ }
+ } catch (error) {
+ console.error('加载视频列表失败', error);
+ this.uiPushToast({
+ title: '加载视频失败',
+ message: error?.message || '请稍后重试',
+ type: 'error'
+ });
+ } finally {
+ this.videoLoading = false;
+ }
+ },
+
handleImagesConfirmed(list) {
this.inputSetSelectedImages(Array.isArray(list) ? list : []);
this.inputSetImagePickerOpen(false);
@@ -2753,6 +2895,17 @@ const appOptions = {
handleRemoveImage(path) {
this.inputRemoveSelectedImage(path);
},
+ handleVideosConfirmed(list) {
+ const arr = Array.isArray(list) ? list.slice(0, 1) : [];
+ this.inputSetSelectedVideos(arr);
+ this.inputSetVideoPickerOpen(false);
+ if (arr.length) {
+ this.inputClearSelectedImages();
+ }
+ },
+ handleRemoveVideo(path) {
+ this.inputRemoveSelectedVideo(path);
+ },
handleQuickUpload() {
if (this.uploading || !this.isConnected) {
diff --git a/static/src/components/chat/ChatArea.vue b/static/src/components/chat/ChatArea.vue
index 256e1d4..704b3c8 100644
--- a/static/src/components/chat/ChatArea.vue
+++ b/static/src/components/chat/ChatArea.vue
@@ -12,6 +12,9 @@
{{ formatImageName(img) }}
+
+ {{ formatImageName(video) }}
+
diff --git a/static/src/components/input/InputComposer.vue b/static/src/components/input/InputComposer.vue
index abac9c8..3a1c4ec 100644
--- a/static/src/components/input/InputComposer.vue
+++ b/static/src/components/input/InputComposer.vue
@@ -18,6 +18,12 @@
+
+
+ {{ formatImageName(video) }}
+
+
+
+