Skip to content

Commit abf4414

Browse files
committed
feat(agent-ui): 添加智能体选择弹窗并优化默认标记样式
- 在AgentSingleView中添加智能体选择弹窗功能 - 重构默认智能体标记的样式和位置 - 仅管理员显示系统设置菜单项
1 parent 5f1f6be commit abf4414

File tree

3 files changed

+259
-25
lines changed

3 files changed

+259
-25
lines changed

web/src/components/UserInfoComponent.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
<component :is="themeStore.isDark ? Sun : Moon" size="16"/>
3030
<span class="menu-text">{{ themeStore.isDark ? '切换到浅色模式' : '切换到深色模式 (Beta)' }}</span>
3131
</a-menu-item>
32-
<a-menu-divider />
33-
<a-menu-item key="setting" @click="goToSetting">
32+
<a-menu-divider v-if="userStore.isAdmin"/>
33+
<a-menu-item v-if="userStore.isAdmin" key="setting" @click="goToSetting">
3434
<Settings size="16"/>
3535
<span class="menu-text">系统设置</span>
3636
</a-menu-item>

web/src/views/AgentSingleView.vue

Lines changed: 245 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,47 @@
11
<template>
22
<div class="agent-single-view">
3+
<!-- 智能体选择弹窗 -->
4+
<a-modal
5+
v-model:open="agentModalOpen"
6+
title="选择智能体"
7+
:width="800"
8+
:footer="null"
9+
:maskClosable="true"
10+
class="agent-modal"
11+
>
12+
<div class="agent-modal-content">
13+
<div class="agents-grid">
14+
<div
15+
v-for="agent in agents"
16+
:key="agent.id"
17+
class="agent-card"
18+
:class="{ 'selected': agent.id === agentId }"
19+
@click="selectAgentFromModal(agent.id)"
20+
>
21+
<div class="agent-card-header">
22+
<div class="agent-card-title">
23+
<span class="agent-card-name">{{ agent.name || 'Unknown' }}</span>
24+
</div>
25+
<StarFilled v-if="agent.id === defaultAgentId" class="default-icon" />
26+
<StarOutlined v-else @click.prevent="setAsDefaultAgent(agent.id)" class="default-icon" />
27+
</div>
28+
29+
<div class="agent-card-description">
30+
{{ agent.description || '' }}
31+
</div>
32+
</div>
33+
</div>
34+
</div>
35+
</a-modal>
36+
337
<!-- 智能体聊天界面 -->
438
<AgentChatComponent ref="chatComponentRef" :agent-id="agentId" :single-mode="true">
39+
<template #header-left>
40+
<div type="button" class="agent-nav-btn" @click="openAgentModal">
41+
<span class="text">{{ currentAgentName || '选择智能体' }}</span>
42+
<ChevronDown size="16" class="switch-icon" />
43+
</div>
44+
</template>
545
<template #header-right>
646
<div type="button" class="agent-nav-btn" @click="handleShareChat">
747
<Share2 size="18" class="nav-btn-icon" />
@@ -14,19 +54,66 @@
1454
</template>
1555

1656
<script setup>
17-
import { computed, ref } from 'vue';
57+
import { computed, ref, onMounted } from 'vue';
1858
import { message } from 'ant-design-vue';
19-
import { useRoute } from 'vue-router';
20-
import { Share2 } from 'lucide-vue-next';
59+
import { useRoute, useRouter } from 'vue-router';
60+
import { Share2, ChevronDown } from 'lucide-vue-next';
61+
import { StarFilled, StarOutlined } from '@ant-design/icons-vue';
2162
import AgentChatComponent from '@/components/AgentChatComponent.vue';
2263
import UserInfoComponent from '@/components/UserInfoComponent.vue';
2364
import { ChatExporter } from '@/utils/chatExporter';
2465
import { handleChatError } from '@/utils/errorHandler';
66+
import { useAgentStore } from '@/stores/agent';
67+
import { storeToRefs } from 'pinia';
2568
2669
const route = useRoute();
70+
const router = useRouter();
71+
const agentStore = useAgentStore();
72+
2773
const agentId = computed(() => route.params.agent_id);
2874
const chatComponentRef = ref(null);
2975
76+
// 智能体选择弹窗状态
77+
const agentModalOpen = ref(false);
78+
79+
// 从 store 获取智能体数据
80+
const { agents, defaultAgentId } = storeToRefs(agentStore);
81+
82+
// 当前智能体名称
83+
const currentAgentName = computed(() => {
84+
if (!agentId.value || !agents.value?.length) return '智能体加载中……';
85+
const agent = agents.value.find(a => a.id === agentId.value);
86+
return agent ? agent.name : '未知智能体';
87+
});
88+
89+
// 打开智能体选择弹窗
90+
const openAgentModal = () => {
91+
agentModalOpen.value = true;
92+
};
93+
94+
// 从弹窗中选择智能体 - 切换路由
95+
const selectAgentFromModal = (newAgentId) => {
96+
if (newAgentId === agentId.value) {
97+
// 如果选择的是当前智能体,只需要关闭弹窗
98+
agentModalOpen.value = false;
99+
return;
100+
}
101+
102+
// 跳转到新的智能体页面
103+
router.push(`/agent/${newAgentId}`);
104+
agentModalOpen.value = false;
105+
};
106+
107+
// 设置默认智能体
108+
const setAsDefaultAgent = async (agentIdToSet) => {
109+
try {
110+
await agentStore.setDefaultAgent(agentIdToSet);
111+
message.success('已设置为默认智能体');
112+
} catch (error) {
113+
handleChatError(error, 'save');
114+
}
115+
};
116+
30117
const handleShareChat = async () => {
31118
try {
32119
const exportData = chatComponentRef.value?.getExportPayload?.();
@@ -54,6 +141,17 @@ const handleShareChat = async () => {
54141
handleChatError(error, 'export');
55142
}
56143
};
144+
145+
// 初始化时确保智能体 store 已加载
146+
onMounted(async () => {
147+
if (!agentStore.isInitialized) {
148+
try {
149+
await agentStore.initialize();
150+
} catch (error) {
151+
console.error('初始化智能体 store 失败:', error);
152+
}
153+
}
154+
});
57155
</script>
58156
59157
<style lang="less" scoped>
@@ -73,6 +171,115 @@ const handleShareChat = async () => {
73171
z-index: 10;
74172
}
75173
174+
// 智能体选择弹窗样式
175+
.agent-modal {
176+
:deep(.ant-modal-content) {
177+
border-radius: 8px;
178+
overflow: hidden;
179+
}
180+
181+
:deep(.ant-modal-header) {
182+
background: var(--gray-0);
183+
border-bottom: 1px solid var(--gray-200);
184+
padding: 16px 20px;
185+
186+
.ant-modal-title {
187+
font-weight: 600;
188+
color: var(--gray-900);
189+
}
190+
}
191+
192+
:deep(.ant-modal-body) {
193+
padding: 20px;
194+
background: var(--gray-0);
195+
}
196+
197+
.agent-modal-content {
198+
.agents-grid {
199+
display: grid;
200+
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
201+
gap: 12px;
202+
max-height: 500px;
203+
overflow-y: auto;
204+
}
205+
206+
.agent-card {
207+
border: 1px solid var(--gray-200);
208+
border-radius: 8px;
209+
padding: 16px;
210+
cursor: pointer;
211+
transition: all 0.2s ease;
212+
background: var(--gray-0);
213+
214+
&:hover {
215+
border-color: var(--main-color);
216+
}
217+
218+
.agent-card-header {
219+
display: flex;
220+
justify-content: space-between;
221+
align-items: flex-start;
222+
margin-bottom: 12px;
223+
224+
.agent-card-title {
225+
flex: 1;
226+
227+
.agent-card-name {
228+
font-size: 16px;
229+
font-weight: 600;
230+
color: var(--gray-900);
231+
line-height: 1.4;
232+
}
233+
}
234+
235+
.default-icon {
236+
color: var(--color-warning-500);
237+
font-size: 16px;
238+
flex-shrink: 0;
239+
margin-left: 8px;
240+
cursor: pointer;
241+
242+
&:hover {
243+
color: var(--color-warning-600);
244+
}
245+
}
246+
}
247+
248+
.agent-card-description {
249+
font-size: 14px;
250+
color: var(--gray-700);
251+
line-height: 1.5;
252+
word-break: break-word;
253+
white-space: pre-wrap;
254+
}
255+
256+
&.selected {
257+
border-color: var(--main-color);
258+
background: var(--main-20);
259+
260+
.agent-card-header .agent-card-title .agent-card-name {
261+
color: var(--main-color);
262+
}
263+
264+
.agent-card-description {
265+
color: var(--gray-900);
266+
}
267+
}
268+
}
269+
}
270+
}
271+
272+
// 响应式适配智能体弹窗
273+
@media (max-width: 768px) {
274+
.agent-modal {
275+
.agent-modal-content {
276+
.agents-grid {
277+
grid-template-columns: 1fr;
278+
}
279+
}
280+
}
281+
}
282+
76283
// 侧边栏样式
77284
.sidebar {
78285
// position: absolute;
@@ -129,3 +336,38 @@ const handleShareChat = async () => {
129336
}
130337
</style>
131338
339+
<style lang="less">
340+
.agent-nav-btn {
341+
display: flex;
342+
gap: 10px;
343+
padding: 6px 14px;
344+
justify-content: center;
345+
align-items: center;
346+
border-radius: 12px;
347+
color: var(--gray-900);
348+
cursor: pointer;
349+
width: auto;
350+
font-size: 15px;
351+
transition: background-color 0.3s;
352+
border: none;
353+
background: transparent;
354+
355+
&:hover {
356+
background-color: var(--gray-50);
357+
}
358+
359+
.nav-btn-icon {
360+
height: 24px;
361+
}
362+
363+
.switch-icon {
364+
color: var(--gray-500);
365+
transition: all 0.2s ease;
366+
}
367+
368+
&:hover .switch-icon {
369+
color: var(--main-500);
370+
}
371+
}
372+
</style>
373+

web/src/views/AgentView.vue

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222
<div class="agent-card-header">
2323
<div class="agent-card-title">
2424
<span class="agent-card-name">{{ agent.name || 'Unknown' }}</span>
25-
<StarFilled v-if="agent.id === defaultAgentId" class="default-icon" />
26-
<StarOutlined v-else @click.prevent="setAsDefaultAgent(agent.id)" class="default-icon" />
2725
</div>
26+
<StarFilled v-if="agent.id === defaultAgentId" class="default-icon" />
27+
<StarOutlined v-else @click.prevent="setAsDefaultAgent(agent.id)" class="default-icon" />
2828
</div>
2929

3030
<div class="agent-card-description">
@@ -428,12 +428,6 @@ const handlePreview = () => {
428428
white-space: pre-wrap;
429429
}
430430
}
431-
432-
.default-icon {
433-
color: var(--color-warning-500);
434-
font-size: 14px;
435-
margin-left: 4px;
436-
}
437431
}
438432
// 工具选择器样式(与项目风格一致)
439433
.tools-selector {
@@ -754,11 +748,6 @@ const handlePreview = () => {
754748
color: var(--gray-900);
755749
font-weight: 500;
756750
}
757-
758-
.default-icon {
759-
color: var(--color-warning-500);
760-
font-size: 14px;
761-
}
762751
}
763752
}
764753
@@ -814,9 +803,6 @@ const handlePreview = () => {
814803
margin-bottom: 12px;
815804
816805
.agent-card-title {
817-
display: flex;
818-
align-items: center;
819-
gap: 8px;
820806
flex: 1;
821807
822808
.agent-card-name {
@@ -825,11 +811,17 @@ const handlePreview = () => {
825811
color: var(--gray-900);
826812
line-height: 1.4;
827813
}
814+
}
828815
829-
.default-icon {
830-
color: var(--color-warning-500);
831-
font-size: 16px;
832-
flex-shrink: 0;
816+
.default-icon {
817+
color: var(--color-warning-500);
818+
font-size: 16px;
819+
flex-shrink: 0;
820+
margin-left: 8px;
821+
cursor: pointer;
822+
823+
&:hover {
824+
color: var(--color-warning-600);
833825
}
834826
}
835827
}

0 commit comments

Comments
 (0)