fix(ai): 产物地址兼容字符串元素,创意助手与助手页链接可复制
video_result/photo_files 的元素可能直接是地址字符串,原实现按对象取 url, 字符串元素一律取空——视频有文件却不播放、复制链接永远为空。
This commit is contained in:
@@ -28,11 +28,23 @@ export interface CreativeAssistantRequest {
|
|||||||
user_files?: CreativeAssistantUploadFile[];
|
user_files?: CreativeAssistantUploadFile[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 工作流产出的一项文件。
|
||||||
|
*
|
||||||
|
* 正常是 { url, name, type } 这样的对象(后端已归一),但上游有时直接把
|
||||||
|
* 地址字符串塞进数组;端上按字符串兜一层,否则整条视频链路取不到链接
|
||||||
|
* (见禅道 bug #1:有视频不播放、复制链接为空)。
|
||||||
|
*/
|
||||||
|
export type CreativeFileItem = Partial<FileRecord> | string;
|
||||||
|
|
||||||
|
/** 文件列表:正常情况下是数组,退化时可能是单个对象或裸地址。 */
|
||||||
|
export type CreativeFileList = CreativeFileItem[] | CreativeFileItem;
|
||||||
|
|
||||||
export interface CreativeAssistantOutputs {
|
export interface CreativeAssistantOutputs {
|
||||||
video_result?: Partial<FileRecord>[];
|
video_result?: CreativeFileList;
|
||||||
video_message?: string;
|
video_message?: string;
|
||||||
Script_result?: string;
|
Script_result?: string;
|
||||||
photo_files?: Partial<FileRecord>[];
|
photo_files?: CreativeFileList;
|
||||||
photo_message?: string;
|
photo_message?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -197,7 +197,8 @@
|
|||||||
import type { FileRecord } from '@/api/system/file/model';
|
import type { FileRecord } from '@/api/system/file/model';
|
||||||
import type {
|
import type {
|
||||||
CreativeAssistantOutputs,
|
CreativeAssistantOutputs,
|
||||||
CreativeAssistantResponse
|
CreativeAssistantResponse,
|
||||||
|
CreativeFileList
|
||||||
} from '@/api/ai/creativeAssistant/model';
|
} from '@/api/ai/creativeAssistant/model';
|
||||||
import { copyText } from '@/utils/common';
|
import { copyText } from '@/utils/common';
|
||||||
import { resolveDisplayText, isFailedPayload, FAILED_TEXT } from '@/utils/ai-result';
|
import { resolveDisplayText, isFailedPayload, FAILED_TEXT } from '@/utils/ai-result';
|
||||||
@@ -227,9 +228,27 @@
|
|||||||
return !!content.value.trim();
|
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 scriptMarkdown = computed(() => workflowOutputs.value?.Script_result || '');
|
||||||
const photoFiles = computed(() => workflowOutputs.value?.photo_files || []);
|
const photoFiles = computed(() =>
|
||||||
|
asFileList(workflowOutputs.value?.photo_files)
|
||||||
|
);
|
||||||
const statusMessage = computed(() => {
|
const statusMessage = computed(() => {
|
||||||
// 失败态优先:后端失败时 outputs 为空,若不先判失败,用户既看不到正文
|
// 失败态优先:后端失败时 outputs 为空,若不先判失败,用户既看不到正文
|
||||||
// 也看不到原因,只有一个 toast 一闪而过(见 issue 10)。
|
// 也看不到原因,只有一个 toast 一闪而过(见 issue 10)。
|
||||||
|
|||||||
@@ -1493,8 +1493,13 @@ const getCreativeOutputs = (payload: any) => {
|
|||||||
return payload || {};
|
return payload || {};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCreativeFileUrl = (item: any) =>
|
// 元素退化成裸地址字符串时也要取得到链接:上游的 video_result 有时直接给地址数组,
|
||||||
String(
|
// 取不到 url 就只剩「当前文件缺少链接」(见禅道 bug #1)。
|
||||||
|
const getCreativeFileUrl = (item: any) => {
|
||||||
|
if (typeof item === "string") {
|
||||||
|
return item.trim();
|
||||||
|
}
|
||||||
|
return String(
|
||||||
item?.url ||
|
item?.url ||
|
||||||
item?.remote_url ||
|
item?.remote_url ||
|
||||||
item?.downloadUrl ||
|
item?.downloadUrl ||
|
||||||
@@ -1502,6 +1507,7 @@ const getCreativeFileUrl = (item: any) =>
|
|||||||
item?.path ||
|
item?.path ||
|
||||||
""
|
""
|
||||||
).trim();
|
).trim();
|
||||||
|
};
|
||||||
|
|
||||||
const getCreativeFileName = (item: any, fallback: string) =>
|
const getCreativeFileName = (item: any, fallback: string) =>
|
||||||
String(item?.name || item?.filename || fallback).trim();
|
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) {
|
if (!list.length) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -1540,7 +1549,10 @@ const formatFileList = (title: string, list: any[] = []) => {
|
|||||||
.join("\n")}`;
|
.join("\n")}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatImageList = (title: string, list: any[] = []) => {
|
const formatImageList = (title: string, list: any = []) => {
|
||||||
|
if (!Array.isArray(list)) {
|
||||||
|
list = list ? [list] : [];
|
||||||
|
}
|
||||||
if (!list.length) {
|
if (!list.length) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user