fix(ai): 产物地址兼容字符串元素,创意助手与助手页链接可复制

video_result/photo_files 的元素可能直接是地址字符串,原实现按对象取 url,
字符串元素一律取空——视频有文件却不播放、复制链接永远为空。
This commit is contained in:
2026-10-25 15:55:49 +08:00
parent 1cbf996a41
commit 2c0460ddc1
3 changed files with 52 additions and 9 deletions
+14 -2
View File
@@ -28,11 +28,23 @@ export interface CreativeAssistantRequest {
user_files?: CreativeAssistantUploadFile[];
}
/**
* 工作流产出的一项文件。
*
* 正常是 { url, name, type } 这样的对象(后端已归一),但上游有时直接把
* 地址字符串塞进数组;端上按字符串兜一层,否则整条视频链路取不到链接
* (见禅道 bug #1:有视频不播放、复制链接为空)。
*/
export type CreativeFileItem = Partial<FileRecord> | string;
/** 文件列表:正常情况下是数组,退化时可能是单个对象或裸地址。 */
export type CreativeFileList = CreativeFileItem[] | CreativeFileItem;
export interface CreativeAssistantOutputs {
video_result?: Partial<FileRecord>[];
video_result?: CreativeFileList;
video_message?: string;
Script_result?: string;
photo_files?: Partial<FileRecord>[];
photo_files?: CreativeFileList;
photo_message?: string;
}
+22 -3
View File
@@ -197,7 +197,8 @@
import type { FileRecord } from '@/api/system/file/model';
import type {
CreativeAssistantOutputs,
CreativeAssistantResponse
CreativeAssistantResponse,
CreativeFileList
} from '@/api/ai/creativeAssistant/model';
import { copyText } from '@/utils/common';
import { resolveDisplayText, isFailedPayload, FAILED_TEXT } from '@/utils/ai-result';
@@ -227,9 +228,27 @@
return !!content.value.trim();
});
const videoFiles = computed(() => workflowOutputs.value?.video_result || []);
/**
* 文件列表兜底:上游可能给成单个对象或裸地址字符串,不是数组时当一项处理,
* 否则视频区直接空掉(见禅道 bug #1)。
*/
const asFileList = (
value?: CreativeFileList | null
): Partial<FileRecord>[] => {
const items = !value ? [] : Array.isArray(value) ? value : [value];
// 裸地址统一包成 { url },下游的 resolveFileUrl / getFileName 不必各自判类型。
return items.map((item) =>
typeof item === 'string' ? { url: item } : item
);
};
const videoFiles = computed(() =>
asFileList(workflowOutputs.value?.video_result)
);
const scriptMarkdown = computed(() => workflowOutputs.value?.Script_result || '');
const photoFiles = computed(() => workflowOutputs.value?.photo_files || []);
const photoFiles = computed(() =>
asFileList(workflowOutputs.value?.photo_files)
);
const statusMessage = computed(() => {
// 失败态优先:后端失败时 outputs 为空,若不先判失败,用户既看不到正文
// 也看不到原因,只有一个 toast 一闪而过(见 issue 10)。
+16 -4
View File
@@ -1493,8 +1493,13 @@ const getCreativeOutputs = (payload: any) => {
return payload || {};
};
const getCreativeFileUrl = (item: any) =>
String(
// 元素退化成裸地址字符串时也要取得到链接:上游的 video_result 有时直接给地址数组,
// 取不到 url 就只剩「当前文件缺少链接」(见禅道 bug #1)。
const getCreativeFileUrl = (item: any) => {
if (typeof item === "string") {
return item.trim();
}
return String(
item?.url ||
item?.remote_url ||
item?.downloadUrl ||
@@ -1502,6 +1507,7 @@ const getCreativeFileUrl = (item: any) =>
item?.path ||
""
).trim();
};
const getCreativeFileName = (item: any, fallback: string) =>
String(item?.name || item?.filename || fallback).trim();
@@ -1523,7 +1529,10 @@ const isCreativeImageFile = (item: any) => {
);
};
const formatFileList = (title: string, list: any[] = []) => {
const formatFileList = (title: string, list: any = []) => {
if (!Array.isArray(list)) {
list = list ? [list] : [];
}
if (!list.length) {
return "";
}
@@ -1540,7 +1549,10 @@ const formatFileList = (title: string, list: any[] = []) => {
.join("\n")}`;
};
const formatImageList = (title: string, list: any[] = []) => {
const formatImageList = (title: string, list: any = []) => {
if (!Array.isArray(list)) {
list = list ? [list] : [];
}
if (!list.length) {
return "";
}