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" />
1454</template >
1555
1656<script setup>
17- import { computed , ref } from ' vue' ;
57+ import { computed , ref , onMounted } from ' vue' ;
1858import { 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' ;
2162import AgentChatComponent from ' @/components/AgentChatComponent.vue' ;
2263import UserInfoComponent from ' @/components/UserInfoComponent.vue' ;
2364import { ChatExporter } from ' @/utils/chatExporter' ;
2465import { handleChatError } from ' @/utils/errorHandler' ;
66+ import { useAgentStore } from ' @/stores/agent' ;
67+ import { storeToRefs } from ' pinia' ;
2568
2669const route = useRoute ();
70+ const router = useRouter ();
71+ const agentStore = useAgentStore ();
72+
2773const agentId = computed (() => route .params .agent_id );
2874const 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+
30117const 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+
0 commit comments