Skip to content

Commit 94e9492

Browse files
ikun-Lgstringl1l1l1l
authored andcommitted
feat: complete basic agent ui and interface integration (#1)
* feat: 完善agent基础页面以及接口对接 * feat: 完善agent ui * docs: 修改文案 * fix: 修复请求头 * feat: 添加错误信息显示和重试功能 * feat: 优化消息滚动和错误处理逻辑 * feat: 完善agentui * feat: 完善agentui * fix(AgentDrawer): 修正 AI 思考状态更新逻辑 - 在 sendMessage 函数开始时立即设置 isAiThinking 状态 - 注释掉 message_start 事件中重复设置 isAiThinking 的代码 - 保持 AI 状态更新逻辑的一致性与及时性 * style(ui-vue3): 优化聊天及消息区域滚动条样式 - 为聊天区域滚动条添加细窄样式,适配Webkit与Firefox浏览器 - 优化消息内容区域滚动条视觉效果,统一颜色与圆角设计 - 调整代码块滚动条样式,提升整体页面一致性和美观度 - 采用hover效果增强用户交互体验 * refactor(ai-chat): 拆分AgentDrawer为多个子组件提升可维护性 - 抽离消息列表为MessageList组件,简化主组件代码 - 抽离消息项为MessageItem组件,负责单条消息展示逻辑 - 抽离输入区域为ChatInput组件,处理消息输入及相关事件 - 抽离会话历史为SessionHistoryModal组件,管理历史对话界面 - 抽离建议问题卡片为SuggestionCard组件,统一样式和交互 - 调整AgentDrawer组件,改用子组件替代原有内联结构 - 移除原有部分变量,使用子组件ref及prop进行状态管理 - 优化滚动到底部逻辑,委托给MessageList组件处理 - 通过事件和prop实现主组件与子组件间通信和功能调用
1 parent fa4af2e commit 94e9492

15 files changed

Lines changed: 2797 additions & 823 deletions

ui-vue3/package.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@
2424
"@antv/g2": "^5.1.12",
2525
"@iconify/json": "^2.2.157",
2626
"@iconify/vue": "^4.1.1",
27+
"@types/highlight.js": "^9.12.4",
2728
"@types/lodash": "^4.14.202",
2829
"@types/lodash-es": "^4.17.12",
2930
"@types/nprogress": "^0.2.3",
3031
"ant-design-vue": "4.x",
3132
"dayjs": "^1.11.13",
3233
"gsap": "^3.12.7",
34+
"highlight.js": "^11.11.1",
3335
"js-cookie": "^3.0.5",
3436
"js-yaml": "^4.1.0",
3537
"less": "^4.2.0",
@@ -51,6 +53,7 @@
5153
"@rushstack/eslint-patch": "^1.3.3",
5254
"@tsconfig/node18": "^18.2.2",
5355
"@types/jsdom": "^21.1.6",
56+
"@types/markdown-it": "^14.1.2",
5457
"@types/mockjs": "^1.0.10",
5558
"@types/node": "^20.10.6",
5659
"@vitejs/plugin-vue": "^4.5.1",
@@ -59,15 +62,19 @@
5962
"@vue/eslint-config-typescript": "^12.0.0",
6063
"@vue/test-utils": "^2.4.3",
6164
"@vue/tsconfig": "^0.4.0",
65+
"autoprefixer": "^10.4.21",
6266
"cypress": "^13.6.1",
6367
"eslint": "^8.49.0",
6468
"eslint-plugin-cypress": "^2.15.1",
6569
"eslint-plugin-vue": "^9.17.0",
6670
"husky": "^9.0.6",
6771
"jsdom": "^23.0.1",
72+
"markdown-it": "^14.1.0",
6873
"npm-run-all2": "^6.1.1",
74+
"postcss": "^8.5.6",
6975
"prettier": "^3.0.3",
7076
"start-server-and-test": "^2.0.3",
77+
"tailwindcss": "3",
7178
"typescript": "~5.2.0",
7279
"vite": "^5.0.5",
7380
"vitest": "^1.0.1",

ui-vue3/postcss.config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default {
2+
plugins: {
3+
tailwindcss: {},
4+
autoprefixer: {},
5+
},
6+
}

ui-vue3/src/App.vue

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@
1919
import { RouterView, useRouter } from 'vue-router'
2020
import enUS from 'ant-design-vue/es/locale/en_US'
2121
import zhCN from 'ant-design-vue/es/locale/zh_CN'
22-
import { provide, reactive, watch } from 'vue'
22+
import { provide, reactive, ref, watch } from 'vue'
2323
import dayjs from 'dayjs'
24-
import { QuestionCircleOutlined } from '@ant-design/icons-vue'
24+
import { QuestionCircleOutlined, SlackOutlined } from '@ant-design/icons-vue'
2525
import { PROVIDE_INJECT_KEY } from '@/base/enums/ProvideInject'
2626
import { PRIMARY_COLOR } from '@/base/constants'
2727
import { i18n, localeConfig } from '@/base/i18n'
2828
import devTool from '@/utils/DevToolUtil'
2929
import { getAuthState } from '@/utils/AuthUtil'
30+
import AgentDrawer from '@/components/AgentDrawer.vue'
3031
3132
dayjs.locale('en')
3233
@@ -49,6 +50,12 @@ function globalQuestion() {
4950
const localeGlobal = reactive(i18n.global.locale)
5051
5152
const router = useRouter()
53+
54+
const agentDrawerOpen = ref<boolean>(false)
55+
56+
const openAgentDrawer = () => {
57+
agentDrawerOpen.value = true
58+
}
5259
</script>
5360

5461
<template>
@@ -62,15 +69,30 @@ const router = useRouter()
6269
>
6370
<RouterView />
6471

72+
<a-float-button
73+
type="primary"
74+
:style="{
75+
right: '100px'
76+
}"
77+
@click="openAgentDrawer"
78+
>
79+
<template #icon>
80+
<SlackOutlined />
81+
</template>
82+
</a-float-button>
83+
6584
<a-float-button type="primary" class="__global_float_button_question" @click="globalQuestion">
6685
<template #icon>
6786
<QuestionCircleOutlined />
6887
</template>
6988
</a-float-button>
89+
90+
<AgentDrawer v-model:agentDrawerOpen="agentDrawerOpen" />
7091
</a-config-provider>
7192
</template>
7293

7394
<style lang="less">
95+
7496
.__global_float_button_question {
7597
right: 24px;
7698
}

ui-vue3/src/api/service/ai.ts

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import axios from 'axios'
19+
20+
const BASE_URL = '/api/v1'
21+
22+
// 定义接口类型
23+
export interface Session {
24+
session_id: string
25+
created_at: string
26+
updated_at: string
27+
message_count: number
28+
status: string
29+
}
30+
31+
export interface ChatMessage {
32+
id: string
33+
content: string
34+
role: 'user' | 'assistant'
35+
timestamp: number
36+
type?: 'normal' | 'error' | 'partial_error'
37+
}
38+
39+
export interface ChatResponse {
40+
data: {
41+
session_id: string
42+
messages: ChatMessage[]
43+
}
44+
}
45+
46+
// AI 服务接口
47+
export const aiService = {
48+
// 创建新会话
49+
async createSession(): Promise<string> {
50+
const response = await axios.post(`${BASE_URL}/ai/sessions`)
51+
return response.data.data.session_id
52+
},
53+
54+
// 获取会话列表
55+
async getSessions(): Promise<Session[]> {
56+
const response = await axios.get(`${BASE_URL}/ai/sessions`)
57+
return response.data.data.sessions || []
58+
},
59+
60+
// 获取特定会话信息
61+
async getSessionInfo(sessionId: string): Promise<ChatResponse> {
62+
const response = await axios.get(`${BASE_URL}/ai/sessions/${sessionId}`)
63+
return response.data
64+
},
65+
66+
// 删除会话
67+
async deleteSession(sessionId: string): Promise<void> {
68+
await axios.delete(`${BASE_URL}/ai/sessions/${sessionId}`)
69+
},
70+
71+
// 发送聊天消息(流式响应)
72+
async sendChatMessage(message: string, sessionId?: string): Promise<ReadableStream> {
73+
const headers: Record<string, string> = {
74+
'Content-Type': 'application/json'
75+
}
76+
77+
if (sessionId) {
78+
headers['X-Session-ID'] = sessionId
79+
}
80+
81+
const response = await fetch(`${BASE_URL}/ai/chat/stream`, {
82+
method: 'POST',
83+
headers,
84+
body: JSON.stringify({
85+
message,
86+
sessionID: sessionId
87+
}),
88+
mode: 'cors', // 允许跨域
89+
credentials: 'include' // 允许携带 cookie
90+
})
91+
92+
if (!response.ok) {
93+
throw new Error(`HTTP error! status: ${response.status}`)
94+
}
95+
96+
return response.body!
97+
}
98+
}
99+
100+
export default aiService

0 commit comments

Comments
 (0)