From 4596cd9fa5baa4211c727fe33be2a23b5fe5dd8a Mon Sep 17 00:00:00 2001 From: gjd Date: Thu, 12 Jun 2025 18:26:10 +0800 Subject: [PATCH] =?UTF-8?q?feat(ai):=20=E6=B7=BB=E5=8A=A0=20AI=20=E8=81=8A?= =?UTF-8?q?=E5=A4=A9=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 AI 聊天对话和消息相关 API - 实现聊天界面,包括对话列表、消息列表、发送消息等功能 - 添加音乐生成功能的初始框架 --- apps/web-antd/public/static/copy.svg | 1 + apps/web-antd/public/static/delete.svg | 1 + apps/web-antd/public/static/gpt.svg | 1 + .../src/api/ai/chat/conversation/index.ts | 8 +- .../web-antd/src/api/ai/chat/message/index.ts | 39 +- apps/web-antd/src/api/ai/mindmap/index.ts | 29 +- apps/web-antd/src/api/ai/write/index.ts | 29 +- .../src/components/MarkdownView/index.vue | 209 +++++ .../conversation/ConversationList.vue | 546 ++++++++++++ .../conversation/ConversationUpdateForm.vue | 82 ++ .../components/message/MessageKnowledge.vue | 103 +++ .../index/components/message/MessageList.vue | 296 +++++++ .../components/message/MessageListEmpty.vue | 81 ++ .../components/message/MessageLoading.vue | 15 + .../message/MessageNewConversation.vue | 48 + .../components/role/RoleCategoryList.vue | 54 ++ .../chat/index/components/role/RoleList.vue | 183 ++++ .../index/components/role/RoleRepository.vue | 287 ++++++ apps/web-antd/src/views/ai/chat/index/data.ts | 79 ++ .../src/views/ai/chat/index/index.vue | 831 +++++++++++++++++- .../src/views/ai/music/index/index.vue | 38 +- .../src/views/ai/music/index/mode/desc.vue | 70 ++ .../src/views/ai/music/index/mode/index.vue | 43 + .../src/views/ai/music/index/mode/lyric.vue | 103 +++ .../src/views/ai/music/index/title/index.vue | 27 + 25 files changed, 3109 insertions(+), 94 deletions(-) create mode 100644 apps/web-antd/public/static/copy.svg create mode 100644 apps/web-antd/public/static/delete.svg create mode 100644 apps/web-antd/public/static/gpt.svg create mode 100644 apps/web-antd/src/components/MarkdownView/index.vue create mode 100644 apps/web-antd/src/views/ai/chat/index/components/conversation/ConversationList.vue create mode 100644 apps/web-antd/src/views/ai/chat/index/components/conversation/ConversationUpdateForm.vue create mode 100644 apps/web-antd/src/views/ai/chat/index/components/message/MessageKnowledge.vue create mode 100644 apps/web-antd/src/views/ai/chat/index/components/message/MessageList.vue create mode 100644 apps/web-antd/src/views/ai/chat/index/components/message/MessageListEmpty.vue create mode 100644 apps/web-antd/src/views/ai/chat/index/components/message/MessageLoading.vue create mode 100644 apps/web-antd/src/views/ai/chat/index/components/message/MessageNewConversation.vue create mode 100644 apps/web-antd/src/views/ai/chat/index/components/role/RoleCategoryList.vue create mode 100644 apps/web-antd/src/views/ai/chat/index/components/role/RoleList.vue create mode 100644 apps/web-antd/src/views/ai/chat/index/components/role/RoleRepository.vue create mode 100644 apps/web-antd/src/views/ai/chat/index/data.ts create mode 100644 apps/web-antd/src/views/ai/music/index/mode/desc.vue create mode 100644 apps/web-antd/src/views/ai/music/index/mode/index.vue create mode 100644 apps/web-antd/src/views/ai/music/index/mode/lyric.vue create mode 100644 apps/web-antd/src/views/ai/music/index/title/index.vue diff --git a/apps/web-antd/public/static/copy.svg b/apps/web-antd/public/static/copy.svg new file mode 100644 index 000000000..f51f8d81c --- /dev/null +++ b/apps/web-antd/public/static/copy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web-antd/public/static/delete.svg b/apps/web-antd/public/static/delete.svg new file mode 100644 index 000000000..d2ee18eda --- /dev/null +++ b/apps/web-antd/public/static/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web-antd/public/static/gpt.svg b/apps/web-antd/public/static/gpt.svg new file mode 100644 index 000000000..2625ec012 --- /dev/null +++ b/apps/web-antd/public/static/gpt.svg @@ -0,0 +1 @@ + diff --git a/apps/web-antd/src/api/ai/chat/conversation/index.ts b/apps/web-antd/src/api/ai/chat/conversation/index.ts index c15f80893..f937f4f30 100644 --- a/apps/web-antd/src/api/ai/chat/conversation/index.ts +++ b/apps/web-antd/src/api/ai/chat/conversation/index.ts @@ -26,9 +26,9 @@ export namespace AiChatConversationApi { // 获得【我的】聊天对话 export function getChatConversationMy(id: number) { - return requestClient.get< - PageResult - >(`/ai/chat/conversation/get-my?id=${id}`); + return requestClient.get( + `/ai/chat/conversation/get-my?id=${id}`, + ); } // 新增【我的】聊天对话 @@ -46,7 +46,7 @@ export function updateChatConversationMy( } // 删除【我的】聊天对话 -export function deleteChatConversationMy(id: string) { +export function deleteChatConversationMy(id: number) { return requestClient.delete(`/ai/chat/conversation/delete-my?id=${id}`); } diff --git a/apps/web-antd/src/api/ai/chat/message/index.ts b/apps/web-antd/src/api/ai/chat/message/index.ts index 2232c7f1d..ce5fac0c7 100644 --- a/apps/web-antd/src/api/ai/chat/message/index.ts +++ b/apps/web-antd/src/api/ai/chat/message/index.ts @@ -1,10 +1,12 @@ import type { PageResult } from '@vben/request'; +import { useAppConfig } from '@vben/hooks'; import { fetchEventSource } from '@vben/request'; import { useAccessStore } from '@vben/stores'; import { requestClient } from '#/api/request'; +const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD); const accessStore = useAccessStore(); export namespace AiChatMessageApi { export interface ChatMessageVO { @@ -50,30 +52,27 @@ export function sendChatMessageStream( onClose: any, ) { const token = accessStore.accessToken; - return fetchEventSource( - `${import.meta.env.VITE_BASE_URL}/ai/chat/message/send-stream`, - { - method: 'post', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${token}`, - }, - openWhenHidden: true, - body: JSON.stringify({ - conversationId, - content, - useContext: enableContext, - }), - onmessage: onMessage, - onerror: onError, - onclose: onClose, - signal: ctrl.signal, + return fetchEventSource(`${apiURL}/ai/chat/message/send-stream`, { + method: 'post', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, }, - ); + openWhenHidden: true, + body: JSON.stringify({ + conversationId, + content, + useContext: enableContext, + }), + onmessage: onMessage, + onerror: onError, + onclose: onClose, + signal: ctrl.signal, + }); } // 删除消息 -export function deleteChatMessage(id: string) { +export function deleteChatMessage(id: number) { return requestClient.delete(`/ai/chat/message/delete?id=${id}`); } diff --git a/apps/web-antd/src/api/ai/mindmap/index.ts b/apps/web-antd/src/api/ai/mindmap/index.ts index 496199ed0..5d32ce91d 100644 --- a/apps/web-antd/src/api/ai/mindmap/index.ts +++ b/apps/web-antd/src/api/ai/mindmap/index.ts @@ -1,8 +1,10 @@ +import { useAppConfig } from '@vben/hooks'; import { fetchEventSource } from '@vben/request'; import { useAccessStore } from '@vben/stores'; import { requestClient } from '#/api/request'; +const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD); const accessStore = useAccessStore(); export namespace AiMindmapApi { // AI 思维导图 VO @@ -36,22 +38,19 @@ export function generateMindMap({ onMessage?: (res: any) => void; }) { const token = accessStore.accessToken; - return fetchEventSource( - `${import.meta.env.VITE_BASE_URL}/ai/mind-map/generate-stream`, - { - method: 'post', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${token}`, - }, - openWhenHidden: true, - body: JSON.stringify(data), - onmessage: onMessage, - onerror: onError, - onclose: onClose, - signal: ctrl.signal, + return fetchEventSource(`${apiURL}/ai/mind-map/generate-stream`, { + method: 'post', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, }, - ); + openWhenHidden: true, + body: JSON.stringify(data), + onmessage: onMessage, + onerror: onError, + onclose: onClose, + signal: ctrl.signal, + }); } // 查询思维导图分页 diff --git a/apps/web-antd/src/api/ai/write/index.ts b/apps/web-antd/src/api/ai/write/index.ts index 7a4a8b2a2..1f98ca3d9 100644 --- a/apps/web-antd/src/api/ai/write/index.ts +++ b/apps/web-antd/src/api/ai/write/index.ts @@ -2,11 +2,13 @@ import type { PageParam, PageResult } from '@vben/request'; import type { AiWriteTypeEnum } from '#/utils/constants'; +import { useAppConfig } from '@vben/hooks'; import { fetchEventSource } from '@vben/request'; import { useAccessStore } from '@vben/stores'; import { requestClient } from '#/api/request'; +const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD); const accessStore = useAccessStore(); export namespace AiWriteApi { export interface WriteVO { @@ -64,22 +66,19 @@ export function writeStream({ onMessage?: (res: any) => void; }) { const token = accessStore.accessToken; - return fetchEventSource( - `${import.meta.env.VITE_BASE_URL}/ai/write/generate-stream`, - { - method: 'post', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${token}`, - }, - openWhenHidden: true, - body: JSON.stringify(data), - onmessage: onMessage, - onerror: onError, - onclose: onClose, - signal: ctrl.signal, + return fetchEventSource(`${apiURL}/ai/write/generate-stream`, { + method: 'post', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, }, - ); + openWhenHidden: true, + body: JSON.stringify(data), + onmessage: onMessage, + onerror: onError, + onclose: onClose, + signal: ctrl.signal, + }); } // 获取写作列表 diff --git a/apps/web-antd/src/components/MarkdownView/index.vue b/apps/web-antd/src/components/MarkdownView/index.vue new file mode 100644 index 000000000..90f1aa10b --- /dev/null +++ b/apps/web-antd/src/components/MarkdownView/index.vue @@ -0,0 +1,209 @@ + + + + + diff --git a/apps/web-antd/src/views/ai/chat/index/components/conversation/ConversationList.vue b/apps/web-antd/src/views/ai/chat/index/components/conversation/ConversationList.vue new file mode 100644 index 000000000..459d4c86b --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/components/conversation/ConversationList.vue @@ -0,0 +1,546 @@ + + + + + diff --git a/apps/web-antd/src/views/ai/chat/index/components/conversation/ConversationUpdateForm.vue b/apps/web-antd/src/views/ai/chat/index/components/conversation/ConversationUpdateForm.vue new file mode 100644 index 000000000..6a2140d22 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/components/conversation/ConversationUpdateForm.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-antd/src/views/ai/chat/index/components/message/MessageKnowledge.vue b/apps/web-antd/src/views/ai/chat/index/components/message/MessageKnowledge.vue new file mode 100644 index 000000000..943bdbd4d --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/components/message/MessageKnowledge.vue @@ -0,0 +1,103 @@ + + + diff --git a/apps/web-antd/src/views/ai/chat/index/components/message/MessageList.vue b/apps/web-antd/src/views/ai/chat/index/components/message/MessageList.vue new file mode 100644 index 000000000..97c973a1f --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/components/message/MessageList.vue @@ -0,0 +1,296 @@ + + + diff --git a/apps/web-antd/src/views/ai/chat/index/components/message/MessageListEmpty.vue b/apps/web-antd/src/views/ai/chat/index/components/message/MessageListEmpty.vue new file mode 100644 index 000000000..d65023cf6 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/components/message/MessageListEmpty.vue @@ -0,0 +1,81 @@ + + + + diff --git a/apps/web-antd/src/views/ai/chat/index/components/message/MessageLoading.vue b/apps/web-antd/src/views/ai/chat/index/components/message/MessageLoading.vue new file mode 100644 index 000000000..4d9231dc3 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/components/message/MessageLoading.vue @@ -0,0 +1,15 @@ + + + + + diff --git a/apps/web-antd/src/views/ai/chat/index/components/message/MessageNewConversation.vue b/apps/web-antd/src/views/ai/chat/index/components/message/MessageNewConversation.vue new file mode 100644 index 000000000..ed57819b2 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/components/message/MessageNewConversation.vue @@ -0,0 +1,48 @@ + + + + diff --git a/apps/web-antd/src/views/ai/chat/index/components/role/RoleCategoryList.vue b/apps/web-antd/src/views/ai/chat/index/components/role/RoleCategoryList.vue new file mode 100644 index 000000000..29288c110 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/components/role/RoleCategoryList.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/apps/web-antd/src/views/ai/chat/index/components/role/RoleList.vue b/apps/web-antd/src/views/ai/chat/index/components/role/RoleList.vue new file mode 100644 index 000000000..e4a9fa737 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/components/role/RoleList.vue @@ -0,0 +1,183 @@ + + + + diff --git a/apps/web-antd/src/views/ai/chat/index/components/role/RoleRepository.vue b/apps/web-antd/src/views/ai/chat/index/components/role/RoleRepository.vue new file mode 100644 index 000000000..8edc82966 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/components/role/RoleRepository.vue @@ -0,0 +1,287 @@ + + + + + diff --git a/apps/web-antd/src/views/ai/chat/index/data.ts b/apps/web-antd/src/views/ai/chat/index/data.ts new file mode 100644 index 000000000..c59d9610e --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/data.ts @@ -0,0 +1,79 @@ +import type { VbenFormSchema } from '#/adapter/form'; + +import { getModelSimpleList } from '#/api/ai/model/model'; +import { AiModelTypeEnum } from '#/utils'; + +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'systemMessage', + label: '角色设定', + component: 'Textarea', + componentProps: { + rows: 4, + placeholder: '请输入角色设定', + }, + }, + { + component: 'ApiSelect', + fieldName: 'modelId', + label: '模型', + componentProps: { + api: () => getModelSimpleList(AiModelTypeEnum.CHAT), + labelField: 'name', + valueField: 'id', + allowClear: true, + placeholder: '请选择模型', + }, + rules: 'required', + }, + { + fieldName: 'temperature', + label: '温度参数', + component: 'InputNumber', + componentProps: { + controlsPosition: 'right', + placeholder: '请输入温度参数', + class: 'w-full', + precision: 2, + min: 0, + max: 2, + }, + rules: 'required', + }, + { + fieldName: 'maxTokens', + label: '回复数 Token 数', + component: 'InputNumber', + componentProps: { + controlsPosition: 'right', + placeholder: '请输入回复数 Token 数', + class: 'w-full', + min: 0, + max: 8192, + }, + rules: 'required', + }, + { + fieldName: 'maxContexts', + label: '上下文数量', + component: 'InputNumber', + componentProps: { + controlsPosition: 'right', + placeholder: '请输入上下文数量', + class: 'w-full', + min: 0, + max: 20, + }, + rules: 'required', + }, + ]; +} diff --git a/apps/web-antd/src/views/ai/chat/index/index.vue b/apps/web-antd/src/views/ai/chat/index/index.vue index 02c2fdcd8..6208fe464 100644 --- a/apps/web-antd/src/views/ai/chat/index/index.vue +++ b/apps/web-antd/src/views/ai/chat/index/index.vue @@ -1,28 +1,817 @@ + + diff --git a/apps/web-antd/src/views/ai/music/index/index.vue b/apps/web-antd/src/views/ai/music/index/index.vue index bdb74630f..0f275a361 100644 --- a/apps/web-antd/src/views/ai/music/index/index.vue +++ b/apps/web-antd/src/views/ai/music/index/index.vue @@ -1,28 +1,28 @@ diff --git a/apps/web-antd/src/views/ai/music/index/mode/desc.vue b/apps/web-antd/src/views/ai/music/index/mode/desc.vue new file mode 100644 index 000000000..1173a7baa --- /dev/null +++ b/apps/web-antd/src/views/ai/music/index/mode/desc.vue @@ -0,0 +1,70 @@ + + + diff --git a/apps/web-antd/src/views/ai/music/index/mode/index.vue b/apps/web-antd/src/views/ai/music/index/mode/index.vue new file mode 100644 index 000000000..0dc52ef4a --- /dev/null +++ b/apps/web-antd/src/views/ai/music/index/mode/index.vue @@ -0,0 +1,43 @@ + + + diff --git a/apps/web-antd/src/views/ai/music/index/mode/lyric.vue b/apps/web-antd/src/views/ai/music/index/mode/lyric.vue new file mode 100644 index 000000000..1d8a04b75 --- /dev/null +++ b/apps/web-antd/src/views/ai/music/index/mode/lyric.vue @@ -0,0 +1,103 @@ + + + diff --git a/apps/web-antd/src/views/ai/music/index/title/index.vue b/apps/web-antd/src/views/ai/music/index/title/index.vue new file mode 100644 index 000000000..481260be9 --- /dev/null +++ b/apps/web-antd/src/views/ai/music/index/title/index.vue @@ -0,0 +1,27 @@ + + +