From 7656250d9a78768220e2745abed8da3442a547dd Mon Sep 17 00:00:00 2001 From: zhangyahui Date: Wed, 11 Feb 2026 16:32:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AF=B9=E6=8E=A5AI=E5=AF=B9=E8=AF=9D?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/agent.ts | 79 ++++++ src/stores/userInfo.ts | 14 +- src/views/home/agent/components/Agent.vue | 199 +++++++++++--- src/views/home/agent/components/Item.vue | 65 +++-- src/views/home/agent/components/List.vue | 25 +- src/views/home/agent/example.vue | 317 ++++++++++++++++++++++ src/views/home/components/Input.vue | 33 ++- 7 files changed, 637 insertions(+), 95 deletions(-) create mode 100644 src/api/agent.ts create mode 100644 src/views/home/agent/example.vue diff --git a/src/api/agent.ts b/src/api/agent.ts new file mode 100644 index 0000000..535248d --- /dev/null +++ b/src/api/agent.ts @@ -0,0 +1,79 @@ +import request from '@/utils/request' + +// 对话 +export interface AgentParamsType { + message: string // 消息 + threadId: string // 对话ID + checkpointId?: string // 检查点ID + imageUrlList?: string[] // 图片URL列表 + configParams: Record // 其他配置参数 + token: string +} +export const fetchAgentReply = (data: AgentParamsType): Promise => { + return request({ + url: '/api/ai-design/chat', + method: 'post', + data, + meta: { responseAll: true } + }) +} + +// 流式对话 +export const fetchAgentReplyStream = async ( + data: AgentParamsType, + onMessage: (chunk: string) => void, + onEnd: () => void +) => { + try { + const params = new URLSearchParams({ + message: data.message, + threadId: data.threadId, + token: data.token, + configParams: JSON.stringify(data.configParams) + }) + + const response = await fetch(`/api/ai-design/chat?${params.toString()}`, { + method: 'GET' + }) + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`) + } + + const reader = response.body?.getReader() + if (!reader) { + throw new Error('Response body is not readable') + } + + const decoder = new TextDecoder() + let buffer = '' + + while (true) { + const { done, value } = await reader.read() + if (done) break + + buffer += decoder.decode(value, { stream: true }) + const lines = buffer.split('\n') + buffer = lines.pop() || '' // 保留不完整的行 + + for (const line of lines) { + console.log('line---', line) + + const trimmedLine = line.trim() + if (trimmedLine.startsWith('data: ')) { + const chunk = trimmedLine.slice(6) + if (chunk === '[DONE]') { + onEnd() + return + } else { + onMessage(chunk) + } + } + } + } + onEnd() + } catch (error) { + console.error('Stream error:', error) + onEnd() + } +} diff --git a/src/stores/userInfo.ts b/src/stores/userInfo.ts index 3ce85a0..096377e 100644 --- a/src/stores/userInfo.ts +++ b/src/stores/userInfo.ts @@ -5,13 +5,13 @@ import { removeLocal, setLocal } from '@/utils/local' import MyEvent from '@/utils/myEvent' export const useUserInfoStore = defineStore('userInfo', () => { const state = ref({ - userInfo: {}, - token: '', - generateParams: { - stylist: '', - sex: '', - stylistImage: '' - } + userInfo: {}, + token: '', + generateParams: { + stylist: '', + sex: '', + stylistImage: '' + } }) // getters diff --git a/src/views/home/agent/components/Agent.vue b/src/views/home/agent/components/Agent.vue index b0b076c..0667d73 100644 --- a/src/views/home/agent/components/Agent.vue +++ b/src/views/home/agent/components/Agent.vue @@ -8,17 +8,21 @@
- +
@@ -127,6 +236,8 @@ } .agent-body { padding: 3.2rem; + overflow: hidden; + row-gap: 2.4rem; .assist-input-wrapper { width: 100%; height: 14.4rem; diff --git a/src/views/home/agent/components/Item.vue b/src/views/home/agent/components/Item.vue index 9348607..a1e221d 100644 --- a/src/views/home/agent/components/Item.vue +++ b/src/views/home/agent/components/Item.vue @@ -4,8 +4,13 @@
-
-
{{ content.text }}
+
+
+
+
diff --git a/src/views/home/agent/components/List.vue b/src/views/home/agent/components/List.vue index 83ca229..ca579e4 100644 --- a/src/views/home/agent/components/List.vue +++ b/src/views/home/agent/components/List.vue @@ -1,20 +1,41 @@ diff --git a/src/views/home/agent/example.vue b/src/views/home/agent/example.vue new file mode 100644 index 0000000..78bc134 --- /dev/null +++ b/src/views/home/agent/example.vue @@ -0,0 +1,317 @@ + + + diff --git a/src/views/home/components/Input.vue b/src/views/home/components/Input.vue index 4d37ea8..652e783 100644 --- a/src/views/home/components/Input.vue +++ b/src/views/home/components/Input.vue @@ -22,6 +22,7 @@ :placeholder="$t('Input.placeholder')" @input="handleEditorInput" @paste="handleEditorPaste" + @keypress="handleKeyPress" >
{ - emits('send', inputValue.value) - // 发送后清空输入框 - if(editorRef.value){ - editorRef.value.innerHTML = '' - } - inputValue.value = '' - } + const handleKeyPress = (e) => { + // 检测回车 + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault() + handleSendAgent() + } + } + + const handleSendAgent = () => { + if (!inputValue.value.trim()) return + emits('send', { text: inputValue.value.trim(), images: uploadedImages.value }) + // 发送后清空输入框 + if (editorRef.value) { + editorRef.value.innerHTML = '' + } + inputValue.value = '' + } // 监听 inputValue 外部变化 watch(inputValue, () => { nextTick(() => { @@ -314,7 +324,6 @@ onMounted(() => { autoResizeEditor() }) - const typeValue = ref('') const areaValue = ref('') @@ -598,7 +607,7 @@ .agent { padding: 1.2rem; box-shadow: none; - border-radius: 1.5rem; + border-radius: 1.5rem; border: 0.1rem solid #0000001a; .scroll-content { padding: 0; @@ -611,7 +620,7 @@ min-height: initial; max-height: initial; padding: 0; - height: 100%; + height: 100%; } } .operate {