diff --git a/package.json b/package.json index db2fa92..a555e52 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "dompurify": "^3.2.6", "echarts": "^5.6.0", "element-resize-detector": "^1.2.4", + "event-source-polyfill": "^1.0.31", "html2canvas": "^1.4.1", "jspdf": "^3.0.1", "lodash-es": "^4.17.21", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ac9f515..9071d24 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -41,6 +41,9 @@ importers: element-resize-detector: specifier: ^1.2.4 version: 1.2.4 + event-source-polyfill: + specifier: ^1.0.31 + version: 1.0.31 html2canvas: specifier: ^1.4.1 version: 1.4.1 @@ -2375,6 +2378,9 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + event-source-polyfill@1.0.31: + resolution: {integrity: sha512-4IJSItgS/41IxN5UVAVuAyczwZF7ZIEsM1XAoUzIHA6A+xzusEZUutdXz2Nr+MQPLxfTiCvqE79/C8HT8fKFvA==} + execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -8588,6 +8594,8 @@ snapshots: etag@1.8.1: {} + event-source-polyfill@1.0.31: {} + execa@5.1.1: dependencies: cross-spawn: 7.0.3 diff --git a/src/api/all/chat.ts b/src/api/all/chat.ts index 5290a61..345ee54 100644 --- a/src/api/all/chat.ts +++ b/src/api/all/chat.ts @@ -24,9 +24,8 @@ export const deleteHistoryItem = (id: string) => { }; - export const baseUrl = 'http://192.168.40.41:8001'; -const getHeaders = () => { +export const getHeaders = () => { const store = useEnterpriseStore(); return { Authorization: glsWithCatch('accessToken'), @@ -46,17 +45,6 @@ export const getAgentInfo = async () => { return data; }; -/** - * 指令输入 - */ -export const getInputAgent = async (params: {}) => { - const { data } = await axios.get(`${baseUrl}/api/agent/input`, { - params, - headers: { ...getHeaders(), Accept: 'text/event-stream' }, - }); - return data; -}; - /** * 生成会话id */ diff --git a/src/views/home/components/conversation-detail/index.vue b/src/views/home/components/conversation-detail/index.vue index b99c060..4ead331 100644 --- a/src/views/home/components/conversation-detail/index.vue +++ b/src/views/home/components/conversation-detail/index.vue @@ -4,17 +4,21 @@ import { BubbleList } from '@/components/xt-chat/xt-bubble'; import SenderInput from '../sender-input/index.vue'; import { Typography } from 'ant-design-vue'; import RightView from './rightView.vue'; -import type { Ref } from 'vue'; +import TextOverTips from '@/components/text-over-tips/index.vue'; import { useRoute } from 'vue-router'; import markdownit from 'markdown-it'; import { useClipboard } from '@vueuse/core'; import { genRandomId } from '@/utils/tools'; import { useChatStore } from '@/stores/modules/chat'; +import { getHeaders } from '@/api/all/chat'; +import { EventSourcePolyfill } from 'event-source-polyfill'; // 导入 event-source-polyfill import type { BubbleListProps } from '@/components/xt-chat/xt-bubble/types'; const QUESTION_ROLE = 'question'; const ANSWER_ROLE = 'text'; +const FILE_ROLE = 'file'; +const THOUGHT_ROLE = 'thought'; // 新增思考过程角色常量 export default { setup(props, { emit, expose }) { @@ -24,6 +28,7 @@ export default { const { copy } = useClipboard(); const senderRef = ref(null); + const eventSource = ref(null); const rightViewRef = ref(null); const bubbleListRef = ref(null); const searchValue = ref(''); @@ -80,78 +85,7 @@ export default { }); currentAnswerId.value = tempId; - setTimeout(() => { - const content = `# 测试数据表格 -## 用户信息表 - -| 表头1 | 表头2 | -|---|---| -| 内容1 | 内容2 | - -## 项目统计表 - -| 项目名称 | 负责人 | 进度 | 状态 | 预算(万元) | 完成度 | -|----------|--------|------|------|------------|--------| -| 电商平台重构 | 张三 | 75% | 进行中 | 50 | 🟡 | -| 移动端APP | 李四 | 90% | 测试中 | 30 | 🟢 | -| 数据分析系统 | 王五 | 45% | 开发中 | 80 | 🟡 | -| 客户管理系统 | 赵六 | 100% | 已完成 | 25 | 🟢 | -| 营销自动化 | 钱七 | 30% | 规划中 | 60 | 🔴 | - -## 销售数据 - -| 月份 | 销售额(万) | 订单数 | 客户数 | 增长率 | 备注 | -|------|------------|--------|--------|--------|------| -| 1月 | 120.5 | 156 | 89 | +12% | 春节促销 | -| 2月 | 98.3 | 134 | 76 | -18% | 淡季 | -| 3月 | 145.2 | 189 | 102 | +48% | 新品上市 | -| 4月 | 167.8 | 203 | 115 | +16% | 稳定增长 | -| 5月 | 189.6 | 234 | 128 | +13% | 持续增长 | - -> 以上数据仅供参考,实际数据请以系统为准。 - -**注意事项:** -- 所有数据均为测试数据 -- 表格支持Markdown格式渲染 -- 可以包含表情符号和特殊字符`; - - showRightView.value = true; - rightViewContent.value = ''; - nextTick(() => { - rightViewContent.value = content; - }); - - searchValue.value = ''; - conversationList.value.splice(tempIndex, 1, { - id: tempId, - loading: false, - role: ANSWER_ROLE, - content, - messageRender: (content) => ( - -
-
- ), - footer: ({ item }) => { - // 判断当前元素是否是最后一个非QUESTION_ROLE的元素 - const nonQuestionElements = conversationList.value.filter((item) => item.role !== QUESTION_ROLE); - const isLastAnswer = nonQuestionElements[nonQuestionElements.length - 1]?.id === item.id; - - return ( -
- onCopy(content)}> - - - {isLastAnswer && ( - onRefresh(tempId, tempIndex)}> - - - )} -
- ); - }, - }); - }, 1000); + initSse(); }; const handleCancel = () => { @@ -181,6 +115,39 @@ export default { margin: '0 auto', }, }, + [FILE_ROLE]: { + placement: 'start', + variant: 'borderless', + typing: { step: 2, interval: 100 }, + messageRender: (items) => { + return items.map((item) => ( +
+ +
+ + 创建时间:08-04 12:40 +
+
+ )); + }, + + style: { + width: '600px', + margin: '0 auto', + }, + }, + // 新增思考过程角色配置 + [THOUGHT_ROLE]: { + placement: 'start', + variant: 'borderless', + style: { + width: '600px', + margin: '0 auto', + }, + }, [QUESTION_ROLE]: { placement: 'end', shape: 'round', @@ -191,16 +158,129 @@ export default { }, }; - const initSse = () => { - console.log('initSse', { agentInfo: chatStore.agentInfo, searchValue: chatStore.searchValue }); + const onDownload = (content) => { + console.log('onDownload', content); }; + + const initSse = () => { + // 先清理可能存在的旧连接 + if (eventSource.value) { + eventSource.value.close(); + eventSource.value = null; + } + + // 构建SSE连接URL + const url = new URL('http://localhost:3000/agent/input'); + url.searchParams.append('content', searchValue.value || '测试'); + url.searchParams.append('session_id', conversationId.value as string); + url.searchParams.append('agent_id', chatStore.agentInfo?.agent_id || '67890'); + + try { + eventSource.value = new EventSourcePolyfill(url.toString(), { + headers: { ...getHeaders(), Accept: 'text/event-stream' }, + }); + + searchValue.value = ''; + + // 任务开始 + eventSource.value.addEventListener('start', function () { + conversationList.value.push({ + role: ANSWER_ROLE, + content: ( +
+ 智能思考 + +
+ ), + }); + }); + + eventSource.value.addEventListener('node_update', function (event) { + console.log('Node updated:', event.data); + const data = JSON.parse(event.data); + switch (data.status) { + case 'running': + conversationList.value.push({ + content: data.message, + role: ANSWER_ROLE, + }); + break; + case 'success': + conversationList.value.push({ + content: data.output, + role: ANSWER_ROLE, + }); + break; + } + }); + + eventSource.value.addEventListener('final_result', function (event) { + showRightView.value = true; + const data = JSON.parse(event.data); + const _files = data.output?.files; + rightViewContent.value = _files[0]?.content || ''; + + conversationList.value.push({ + id: currentAnswerId.value, + role: FILE_ROLE, + content: _files, + footer: ({ item }) => { + const nonQuestionElements = conversationList.value.filter((item) => item.role !== QUESTION_ROLE); + const isLastAnswer = nonQuestionElements[nonQuestionElements.length - 1]?.id === item.id; + + return ( +
+ onDownload(rightViewContent.value)}> + + + {isLastAnswer && ( + onRefresh(currentAnswerId.value, conversationList.value.length)} + > + + + )} +
+ ); + }, + }); + }); + + eventSource.value.addEventListener('end', function (event) { + generateLoading.value = false; + closeSse(); + }); + + // 处理错误事件 + eventSource.value.onerror = function (error) { + console.error('EventSource error:', error); + generateLoading.value = false; + message.error('连接服务器失败'); + closeSse(); + }; + } catch (error) { + console.error('Failed to initialize SSE:', error); + message.error('初始化连接失败'); + generateLoading.value = false; + } + }; + const closeSse = () => { - console.log('closeSse'); + if (eventSource.value) { + eventSource.value.close(); + eventSource.value = null; + } + // 确保加载状态被重置 + generateLoading.value = false; }; onMounted(() => { - initSse(); + searchValue.value = chatStore.searchValue; + chatStore.clearSearchValue(); + searchValue.value && initSse(); }); + onUnmounted(() => { closeSse(); }); diff --git a/src/views/home/components/conversation-detail/style.scss b/src/views/home/components/conversation-detail/style.scss index 2a1c36c..1915663 100644 --- a/src/views/home/components/conversation-detail/style.scss +++ b/src/views/home/components/conversation-detail/style.scss @@ -6,4 +6,15 @@ font-weight: 400; line-height: 22px; } + :deep(.xt-bubble) { + .file-card { + border-radius: 12px; + border: 1px solid var(--Border-2, #e6e6e8); + background: linear-gradient(90deg, #f6f4ff 0%, #fff 100%); + padding: 13px 16px; + display: flex; + justify-content: center; + align-items: center; + } + } } diff --git a/src/views/home/index.vue b/src/views/home/index.vue index 50c7f17..9b3b440 100644 --- a/src/views/home/index.vue +++ b/src/views/home/index.vue @@ -28,7 +28,7 @@ export default { onMounted(() => { getAgentData(); }); - + return () => (
{conversationId.value ? : }