From 77378a2791d4791a61c3cf66245865d20db92e9b Mon Sep 17 00:00:00 2001 From: weicw <594098497@qq.com> Date: Sat, 24 Oct 2026 17:37:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(ai):=20=E5=8A=A9=E6=89=8B=E9=A1=B5?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=86=99=E4=BD=9C/=E7=94=9F=E5=9B=BE?= =?UTF-8?q?=E7=AD=89=E8=AE=B0=E5=BD=95=E7=9A=84=E5=8E=86=E5=8F=B2=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E4=B8=8E=E6=8C=89=E7=B1=BB=E5=9E=8B=E7=AD=9B=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 三模式各自独立工作区:切模式不丢消息、草稿与当前记录 - 历史面板加类型徽标与筛选(全部/聊天/写作/图片/创意/活动策划),文案改「历史记录」 - 生成成功认领末帧 chatId 并刷新列表;聊天 __END__ 帧的 chatId 同样接受 - 生图回复支持多张 ![](url) 渲染 - 写作/图片记录支持「按原参数再来一次」(只回填、不自动发送) - 三个独立页(文稿/创意/活动策划)加历史入口,/ai?type= 预置筛选 - 后台 aiChatList 加类型列与筛选;历史列表提示仅展示最近 30 条 --- src/api/ai/aiChatList/model/index.ts | 5 + src/utils/ai-chat-type.ts | 42 ++ src/utils/ai-stream.ts | 26 +- src/views/ai/aiChatList/components/search.vue | 29 +- src/views/ai/aiChatList/index.vue | 29 + src/views/gxmu/activityOutlineGen/index.vue | 28 + src/views/gxmu/creativeAssistant/index.vue | 27 + src/views/gxmu/manuscriptGen/index.vue | 6 + src/views/oa/chatgpt/index.vue | 579 ++++++++++++++++-- 9 files changed, 726 insertions(+), 45 deletions(-) create mode 100644 src/utils/ai-chat-type.ts diff --git a/src/api/ai/aiChatList/model/index.ts b/src/api/ai/aiChatList/model/index.ts index 361fa08..653e4b9 100644 --- a/src/api/ai/aiChatList/model/index.ts +++ b/src/api/ai/aiChatList/model/index.ts @@ -28,6 +28,10 @@ export interface AiChatList { updateTime?: string; // title?: string; + // 记录类型:chat / writing / image / creative / activity_outline + chatType?: string; + // 生成记录入参快照(JSON 字符串,可能为 null) + params?: string; } /** @@ -43,5 +47,6 @@ export interface AiChatListParam extends PageParam { status?: number; deleted?: number; title?: string; + chatType?: string; keywords?: string; } diff --git a/src/utils/ai-chat-type.ts b/src/utils/ai-chat-type.ts new file mode 100644 index 0000000..02da8c7 --- /dev/null +++ b/src/utils/ai-chat-type.ts @@ -0,0 +1,42 @@ +/** + * AI 历史记录的类型(对应后端 `ai_chat_list.chat_type`)。 + * + * 契约见 `docs/ai-history-contract.md`:类型由后端在生成成功时判定,前端只负责展示与筛选。 + * 旧数据没有 `chat_type`,按聊天处理。 + */ + +export type AiChatType = + | 'chat' + | 'writing' + | 'image' + | 'creative' + | 'activity_outline'; + +/** 类型的中文名(徽标与后台表格共用) */ +export const AI_CHAT_TYPE_LABELS: Record = { + chat: '聊天', + writing: '写作', + image: '图片', + creative: '创意', + activity_outline: '活动策划' +}; + +/** 筛选下拉的可选项(不含「全部」,由使用方按需补充) */ +export const AI_CHAT_TYPE_OPTIONS: { label: string; value: AiChatType }[] = [ + { label: '聊天', value: 'chat' }, + { label: '写作', value: 'writing' }, + { label: '图片', value: 'image' }, + { label: '创意', value: 'creative' }, + { label: '活动策划', value: 'activity_outline' } +]; + +/** 把后端返回值收敛到已知类型,缺失或未知一律按聊天处理 */ +export function resolveAiChatType(value?: string | null): AiChatType { + const type = String(value || '').trim() as AiChatType; + return type in AI_CHAT_TYPE_LABELS ? type : 'chat'; +} + +/** 直接取中文名,用于模板与表格列 */ +export function getAiChatTypeLabel(value?: string | null): string { + return AI_CHAT_TYPE_LABELS[resolveAiChatType(value)]; +} diff --git a/src/utils/ai-stream.ts b/src/utils/ai-stream.ts index 49e9571..7edc60f 100644 --- a/src/utils/ai-stream.ts +++ b/src/utils/ai-stream.ts @@ -148,6 +148,28 @@ export function streamCreativeAssistant( }); } +/** + * 末帧 `result` 是普通接口的完整响应包(`{code, message, data}`)。 + * 生成成功后后端会在包里带上 `chatId`(见 `docs/ai-history-contract.md`), + * 既可能在包上也可能在 `data` 里;这里统一把它合并进返回的 data, + * 页面只读 `result.chatId` 即可。 + */ +function unwrapFinalData(finalResult: any) { + const data = finalResult?.data ?? null; + const chatId = Number(finalResult?.chatId); + if ( + data && + typeof data === 'object' && + !Array.isArray(data) && + Number.isFinite(chatId) && + chatId > 0 && + data.chatId == null + ) { + data.chatId = chatId; + } + return data; +} + /** * 创意助手(出图/出视频):跑完才有结果,中途一个字节都不推。 * @@ -181,7 +203,7 @@ export async function runCreativeAssistantStream( if (Number(finalResult.code) !== 0) { throw new Error(finalResult.message || '创意助手处理失败'); } - return finalResult.data ?? null; + return unwrapFinalData(finalResult); } /** @@ -224,5 +246,5 @@ export async function runManuscriptGenStream( if (Number(finalResult.code) !== 0) { throw new Error(finalResult.message || '稿件生成失败'); } - return finalResult.data ?? null; + return unwrapFinalData(finalResult); } diff --git a/src/views/ai/aiChatList/components/search.vue b/src/views/ai/aiChatList/components/search.vue index 82fea9d..f8cf842 100644 --- a/src/views/ai/aiChatList/components/search.vue +++ b/src/views/ai/aiChatList/components/search.vue @@ -1,6 +1,14 @@