refactor(agent): 重构智能体相关逻辑至Pinia store

将智能体管理、配置、线程和消息等逻辑从组件中抽离,统一管理至Pinia store
优化代码结构,提升可维护性和复用性
修复多处直接调用API的问题,统一通过store管理状态
This commit is contained in:
Wenjie Zhang 2025-08-25 01:46:31 +08:00
parent b7c54d80c0
commit 3bca00a864
9 changed files with 712 additions and 384 deletions

View File

@ -1,5 +1,13 @@
<script setup> <script setup>
import { themeConfig } from '@/assets/theme' import { themeConfig } from '@/assets/theme'
import { useAgentStore } from '@/stores/agent'
import { onMounted } from 'vue'
const agentStore = useAgentStore();
onMounted(async () => {
await agentStore.initialize();
})
</script> </script>
<template> <template>
<a-config-provider :theme="themeConfig"> <a-config-provider :theme="themeConfig">

View File

@ -7,7 +7,7 @@
:is-initial-render="state.isInitialRender" :is-initial-render="state.isInitialRender"
:single-mode="props.singleMode" :single-mode="props.singleMode"
:agents="agents" :agents="agents"
:selected-agent-id="props.agentId" :selected-agent-id="agentStore.selectedAgentId"
@create-chat="createNewChat" @create-chat="createNewChat"
@select-chat="selectChat" @select-chat="selectChat"
@delete-chat="deleteChat" @delete-chat="deleteChat"
@ -34,8 +34,8 @@
</div> </div>
</div> </div>
<div class="header__center" @mouseenter="showRenameButton = true" @mouseleave="showRenameButton = false"> <div class="header__center" @mouseenter="showRenameButton = true" @mouseleave="showRenameButton = false">
<div @click="console.log(currentChat)" class="center-title"> <div @click="console.log(agentStore.currentThread)" class="center-title">
{{ currentChat?.title }} {{ agentStore.currentThread?.title }}
</div> </div>
<div class="rename-button" v-if="currentChatId" :class="{ 'visible': showRenameButton }" @click="handleRenameChat"> <div class="rename-button" v-if="currentChatId" :class="{ 'visible': showRenameButton }" @click="handleRenameChat">
<EditOutlined style="font-size: 14px; color: var(--gray-600);"/> <EditOutlined style="font-size: 14px; color: var(--gray-600);"/>
@ -54,7 +54,7 @@
</div> </div>
</div> </div>
<div v-if="isLoading" class="chat-loading"> <div v-if="isLoadingThreads || isLoadingMessages" class="chat-loading">
<LoadingOutlined /> <LoadingOutlined />
<span>正在加载历史记录...</span> <span>正在加载历史记录...</span>
</div> </div>
@ -147,19 +147,12 @@ import MessageInputComponent from '@/components/MessageInputComponent.vue'
import AgentMessageComponent from '@/components/AgentMessageComponent.vue' import AgentMessageComponent from '@/components/AgentMessageComponent.vue'
import ChatSidebarComponent from '@/components/ChatSidebarComponent.vue' import ChatSidebarComponent from '@/components/ChatSidebarComponent.vue'
import RefsComponent from '@/components/RefsComponent.vue' import RefsComponent from '@/components/RefsComponent.vue'
import { agentApi, threadApi } from '@/apis/agent'
import { PanelLeftOpen, MessageSquarePlus } from 'lucide-vue-next'; import { PanelLeftOpen, MessageSquarePlus } from 'lucide-vue-next';
import { useAgentStore } from '@/stores/agent';
import { storeToRefs } from 'pinia';
// propsagentId // propsagentId
const props = defineProps({ const props = defineProps({
agentId: {
type: String,
default: null
},
config: {
type: Object,
default: () => ({})
},
state: { state: {
type: Object, type: Object,
default: () => ({}) default: () => ({})
@ -172,6 +165,19 @@ const props = defineProps({
const emit = defineEmits(['open-config', 'open-agent-modal']); const emit = defineEmits(['open-config', 'open-agent-modal']);
// ==================== Store ====================
const agentStore = useAgentStore();
const {
agents,
currentAgentThreads,
currentThread,
currentThreadMessages,
isLoadingThreads,
isLoadingMessages,
selectedAgent,
configSchema
} = storeToRefs(agentStore);
// ==================== ==================== // ==================== ====================
// UI // UI
@ -272,15 +278,17 @@ const getLastMessage = (conv) => {
const messagesContainer = ref(null); const messagesContainer = ref(null);
// //
const agents = ref({}); //
const userInput = ref(''); // const userInput = ref(''); //
const currentChatId = ref(null); // ID
const chatsList = ref([]); //
const isLoading = ref(false); //
const convs = ref([]); const convs = ref([]);
const currentAgent = computed(() => agents.value[props.agentId]); const currentAgent = computed(() => {
const currentChat = computed(() => chatsList.value.find(chat => chat.id === currentChatId.value)); if (!agentStore.selectedAgentId || !agents.value) return null;
return agents.value[agentStore.selectedAgentId];
});
// 使 agentStore 线
const currentChatId = computed(() => agentStore.currentThreadId);
const chatsList = computed(() => currentAgentThreads.value || []);
const onGoingConv = reactive({ const onGoingConv = reactive({
msgChunks: {}, msgChunks: {},
@ -308,7 +316,7 @@ const expandedToolCalls = ref(new Set()); // 展开的工具调用集合
// //
const createNewChat = async () => { const createNewChat = async () => {
// AgentID // AgentID
if (!props.agentId) { if (!agentStore.selectedAgentId) {
console.warn("未指定AgentID无法创建对话"); console.warn("未指定AgentID无法创建对话");
return; return;
} }
@ -319,24 +327,20 @@ const createNewChat = async () => {
return; return;
} }
if (currentChatId.value &&convs.value.length === 0) { if (currentChatId.value && convs.value.length === 0) {
return; return;
} }
try { try {
// API // 使 agentStore
state.creatingNewChat = true; state.creatingNewChat = true;
const response = await threadApi.createThread(props.agentId, '新的对话'); const thread = await agentStore.createThread(agentStore.selectedAgentId, '新的对话');
if (!response || !response.id) { if (!thread || !thread.id) {
throw new Error('创建对话失败'); throw new Error('创建对话失败');
} }
// // 线
currentChatId.value = response.id;
resetThread(); resetThread();
//
loadChatsList();
} catch (error) { } catch (error) {
console.error('创建对话失败:', error); console.error('创建对话失败:', error);
message.error('创建对话失败'); message.error('创建对话失败');
@ -348,37 +352,33 @@ const createNewChat = async () => {
// //
const selectChat = async (chatId) => { const selectChat = async (chatId) => {
// AgentID // AgentID
if (!props.agentId) { if (!agentStore.selectedAgentId) {
console.warn("未指定AgentID无法选择对话"); console.warn("未指定AgentID无法选择对话");
return; return;
} }
console.log("选择对话:", chatId); console.log("选择对话:", chatId);
// // 使 agentStore 线
currentChatId.value = chatId; agentStore.selectThread(chatId);
await getAgentHistory(); await getAgentHistory();
}; };
// //
const deleteChat = async (chatId) => { const deleteChat = async (chatId) => {
if (!props.agentId) { if (!agentStore.selectedAgentId) {
console.warn("未指定AgentID无法删除对话"); console.warn("未指定AgentID无法删除对话");
return; return;
} }
try { try {
// API // 使 agentStore
await threadApi.deleteThread(chatId); await agentStore.deleteThread(chatId);
// //
if (chatId === currentChatId.value) { if (chatId === currentChatId.value) {
resetThread(); resetThread();
currentChatId.value = null;
} }
//
loadChatsList();
} catch (error) { } catch (error) {
console.error('删除对话失败:', error); console.error('删除对话失败:', error);
message.error('删除对话失败'); message.error('删除对话失败');
@ -395,7 +395,7 @@ const renameChat = async (data) => {
} }
// AgentID // AgentID
if (!props.agentId) { if (!agentStore.selectedAgentId) {
console.warn("未指定AgentID无法重命名对话"); console.warn("未指定AgentID无法重命名对话");
return; return;
} }
@ -406,11 +406,8 @@ const renameChat = async (data) => {
} }
try { try {
// API // 使 agentStore
await threadApi.updateThread(chatId, title); await agentStore.updateThread(chatId, title);
//
loadChatsList();
} catch (error) { } catch (error) {
console.error('重命名对话失败:', error); console.error('重命名对话失败:', error);
message.error('重命名对话失败'); message.error('重命名对话失败');
@ -419,12 +416,12 @@ const renameChat = async (data) => {
// //
const handleRenameChat = () => { const handleRenameChat = () => {
if (!currentChatId.value || !currentChat.value) { if (!currentChatId.value || !currentThread.value) {
message.warning('请先选择对话'); message.warning('请先选择对话');
return; return;
} }
let newTitle = currentChat.value.title; let newTitle = currentThread.value.title;
Modal.confirm({ Modal.confirm({
title: '重命名对话', title: '重命名对话',
@ -473,7 +470,7 @@ const shareChat = () => {
const link = document.createElement('a'); const link = document.createElement('a');
// //
const chatTitle = currentChat.value?.title || '新对话'; const chatTitle = currentThread.value?.title || '新对话';
const timestamp = new Date().toLocaleString('zh-CN').replace(/[:/\s]/g, '-'); const timestamp = new Date().toLocaleString('zh-CN').replace(/[:/\s]/g, '-');
const filename = `${chatTitle}-${timestamp}.html`; const filename = `${chatTitle}-${timestamp}.html`;
@ -497,7 +494,7 @@ const shareChat = () => {
// HTML // HTML
const generateChatHTML = () => { const generateChatHTML = () => {
const chatTitle = currentChat.value?.title || '新对话'; const chatTitle = agentStore.currentThread.value?.title || '新对话';
const agentName = currentAgent.value?.name || '智能助手'; const agentName = currentAgent.value?.name || '智能助手';
const agentDescription = currentAgent.value?.description || ''; const agentDescription = currentAgent.value?.description || '';
const exportTime = new Date().toLocaleString('zh-CN'); const exportTime = new Date().toLocaleString('zh-CN');
@ -939,7 +936,7 @@ const sendMessageToServer = async (text) => {
const requestData = { const requestData = {
query: text.trim(), query: text.trim(),
config: { config: {
...props.config, ...agentStore.agentConfig,
thread_id: currentChatId.value thread_id: currentChatId.value
}, },
meta: { meta: {
@ -950,7 +947,7 @@ const sendMessageToServer = async (text) => {
try { try {
state.waitingServerResponse = true; state.waitingServerResponse = true;
const response = await agentApi.sendAgentMessage(currentAgent.value.id, requestData); const response = await agentStore.sendMessage(currentAgent.value.id, requestData);
if (!response.ok) { if (!response.ok) {
throw new Error('请求失败'); throw new Error('请求失败');
} }
@ -1049,52 +1046,37 @@ const processResponseChunk = async (data) => {
// //
const initAll = async () => { const initAll = async () => {
try { try {
isLoading.value = true; // 使 agentStore
// // agent store
if (!agentStore.agents || Object.keys(agentStore.agents).length === 0) {
await agentStore.initialize();
}
//
setTimeout(async () => { setTimeout(async () => {
await fetchAgents(); // await loadChatsList();
await loadChatsList(); //
isLoading.value = false;
}, 100); }, 100);
} catch (error) { } catch (error) {
console.error("组件挂载出错:", error); console.error("组件挂载出错:", error);
message.error(`加载数据失败: ${error}`); message.error(`加载数据失败: ${error}`);
isLoading.value = false;
} }
} }
//
const fetchAgents = async () => {
try {
const data = await agentApi.getAgents();
//
agents.value = data.agents.reduce((acc, agent) => {
acc[agent.id] = agent;
return acc;
}, {});
console.log("agents", agents.value);
} catch (error) {
console.error('获取智能体错误:', error);
}
};
// //
const loadChatsList = async () => { const loadChatsList = async () => {
try { try {
if (!props.agentId) { if (!agentStore.selectedAgentId) {
console.warn("未指定AgentID无法加载状态"); console.warn("未指定AgentID无法加载状态");
return; return;
} }
// // 使 agentStore
const threads = await threadApi.getThreads(props.agentId); await agentStore.fetchThreads(agentStore.selectedAgentId);
if (threads && Array.isArray(threads) && threads.length > 0) { if (currentAgentThreads.value && currentAgentThreads.value.length > 0) {
// //
chatsList.value = threads; await agentStore.selectThread(currentAgentThreads.value[0].id);
currentChatId.value = threads[0].id; // await getAgentHistory();
await getAgentHistory()
} else { } else {
// //
await createNewChat(); await createNewChat();
@ -1106,21 +1088,22 @@ const loadChatsList = async () => {
// //
const getAgentHistory = async () => { const getAgentHistory = async () => {
if (!props.agentId || !currentChatId.value) { if (!agentStore.selectedAgentId || !currentChatId.value) {
console.warn('未选择智能体或对话ID'); console.warn('未选择智能体或对话ID');
return; return;
} }
try { try {
console.debug(`正在获取智能体[${props.agentId}]的历史记录对话ID: ${currentChatId.value}`); console.debug(`正在获取智能体[${agentStore.selectedAgentId}]的历史记录对话ID: ${currentChatId.value}`);
const response = await agentApi.getAgentHistory(props.agentId, currentChatId.value);
console.debug('智能体历史记录:', response);
//
if (response && Array.isArray(response.history)) { // 使 agentStore 线
await agentStore.fetchThreadMessages(currentChatId.value);
// store
if (currentThreadMessages.value && Array.isArray(currentThreadMessages.value)) {
// //
onGoingConv.msgChunks = {}; onGoingConv.msgChunks = {};
convs.value = convertServerHistoryToMessages(response.history); convs.value = convertServerHistoryToMessages(currentThreadMessages.value);
// //
shouldAutoScroll.value = true; shouldAutoScroll.value = true;
@ -1202,7 +1185,7 @@ onMounted(async () => {
// agentId // agentId
onMounted(() => { onMounted(() => {
watch(() => props.agentId, async (newAgentId, oldAgentId) => { watch(() => agentStore.selectedAgentId, async (newAgentId, oldAgentId) => {
try { try {
console.debug("智能体ID变化", oldAgentId, "->", newAgentId); console.debug("智能体ID变化", oldAgentId, "->", newAgentId);

View File

@ -18,7 +18,7 @@
<!-- 侧边栏内容 --> <!-- 侧边栏内容 -->
<div class="sidebar-content"> <div class="sidebar-content">
<div class="agent-info"> <div class="agent-info" v-if="selectedAgent">
<div class="agent-basic-info"> <div class="agent-basic-info">
<h4>{{ selectedAgent.name || '未选择智能体' }}</h4> <h4>{{ selectedAgent.name || '未选择智能体' }}</h4>
<p class="agent-description">{{ selectedAgent.description }}</p> <p class="agent-description">{{ selectedAgent.description }}</p>
@ -70,7 +70,8 @@
<!-- 系统提示词 --> <!-- 系统提示词 -->
<a-textarea <a-textarea
v-else-if="key === 'system_prompt'" v-else-if="key === 'system_prompt'"
v-model:value="agentConfig[key]" :value="agentConfig[key]"
@update:value="(val) => agentStore.updateAgentConfig({ [key]: val })"
:rows="3" :rows="3"
:placeholder="getPlaceholder(key, value)" :placeholder="getPlaceholder(key, value)"
class="system-prompt-input" class="system-prompt-input"
@ -116,13 +117,15 @@
<!-- 布尔类型 --> <!-- 布尔类型 -->
<a-switch <a-switch
v-else-if="typeof agentConfig[key] === 'boolean'" v-else-if="typeof agentConfig[key] === 'boolean'"
v-model:checked="agentConfig[key]" :checked="agentConfig[key]"
@update:checked="(val) => agentStore.updateAgentConfig({ [key]: val })"
/> />
<!-- 单选 --> <!-- 单选 -->
<a-select <a-select
v-else-if="value?.options && (value?.type === 'str' || value?.type === 'select')" v-else-if="value?.options && (value?.type === 'str' || value?.type === 'select')"
v-model:value="agentConfig[key]" :value="agentConfig[key]"
@update:value="(val) => agentStore.updateAgentConfig({ [key]: val })"
class="config-select" class="config-select"
> >
<a-select-option v-for="option in value.options" :key="option" :value="option"> <a-select-option v-for="option in value.options" :key="option" :value="option">
@ -168,7 +171,8 @@
<!-- 数字 --> <!-- 数字 -->
<a-input-number <a-input-number
v-else-if="value?.type === 'number'" v-else-if="value?.type === 'number'"
v-model:value="agentConfig[key]" :value="agentConfig[key]"
@update:value="(val) => agentStore.updateAgentConfig({ [key]: val })"
:placeholder="getPlaceholder(key, value)" :placeholder="getPlaceholder(key, value)"
class="config-input-number" class="config-input-number"
/> />
@ -176,7 +180,8 @@
<!-- 滑块 --> <!-- 滑块 -->
<a-slider <a-slider
v-else-if="value?.type === 'slider'" v-else-if="value?.type === 'slider'"
v-model:value="agentConfig[key]" :value="agentConfig[key]"
@update:value="(val) => agentStore.updateAgentConfig({ [key]: val })"
:min="value.min" :min="value.min"
:max="value.max" :max="value.max"
:step="value.step" :step="value.step"
@ -186,7 +191,8 @@
<!-- 其他类型 --> <!-- 其他类型 -->
<a-input <a-input
v-else v-else
v-model:value="agentConfig[key]" :value="agentConfig[key]"
@update:value="(val) => agentStore.updateAgentConfig({ [key]: val })"
:placeholder="getPlaceholder(key, value)" :placeholder="getPlaceholder(key, value)"
class="config-input" class="config-input"
/> />
@ -279,45 +285,41 @@ import {
} from '@ant-design/icons-vue'; } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import ModelSelectorComponent from '@/components/ModelSelectorComponent.vue'; import ModelSelectorComponent from '@/components/ModelSelectorComponent.vue';
import { agentApi } from '@/apis/agent'; import { useAgentStore } from '@/stores/agent';
import { storeToRefs } from 'pinia';
// Props // Props
const props = defineProps({ const props = defineProps({
isOpen: { isOpen: {
type: Boolean, type: Boolean,
default: false default: false
},
selectedAgent: {
type: Object,
default: () => ({})
},
selectedAgentId: {
type: String,
default: null
},
agentConfig: {
type: Object,
default: () => ({})
} }
}); });
// Emits // Emits
const emit = defineEmits([ const emit = defineEmits([
'close', 'close'
'update:agentConfig',
'config-saved',
'config-reset'
]); ]);
// Store
const agentStore = useAgentStore();
const {
availableTools,
selectedAgent,
selectedAgentId,
agentConfig
} = storeToRefs(agentStore);
// console.log(availableTools.value)
// //
const debugMode = ref(false); const debugMode = ref(false);
const toolsModalOpen = ref(false); const toolsModalOpen = ref(false);
const availableTools = ref([]);
const selectedTools = ref([]); const selectedTools = ref([]);
const toolsSearchText = ref(''); const toolsSearchText = ref('');
// //
const configSchema = computed(() => props.selectedAgent.config_schema || {}); const configSchema = computed(() => selectedAgent.value?.config_schema || {});
const configurableItems = computed(() => { const configurableItems = computed(() => {
const items = configSchema.value.configurable_items || {}; const items = configSchema.value.configurable_items || {};
@ -333,15 +335,16 @@ const configurableItems = computed(() => {
}); });
const isEmptyConfig = computed(() => { const isEmptyConfig = computed(() => {
return !props.selectedAgentId || Object.keys(configurableItems.value).length === 0; return !selectedAgentId.value || Object.keys(configurableItems.value).length === 0;
}); });
const filteredTools = computed(() => { const filteredTools = computed(() => {
const toolsList = availableTools.value ? Object.values(availableTools.value) : [];
if (!toolsSearchText.value) { if (!toolsSearchText.value) {
return availableTools.value; return toolsList;
} }
const searchLower = toolsSearchText.value.toLowerCase(); const searchLower = toolsSearchText.value.toLowerCase();
return availableTools.value.filter(tool => return toolsList.filter(tool =>
tool.name.toLowerCase().includes(searchLower) || tool.name.toLowerCase().includes(searchLower) ||
tool.description.toLowerCase().includes(searchLower) tool.description.toLowerCase().includes(searchLower)
); );
@ -357,6 +360,7 @@ const toggleDebugMode = () => {
}; };
const getConfigLabel = (key, value) => { const getConfigLabel = (key, value) => {
// console.log(configurableItems)
if (value.description && value.name !== key) { if (value.description && value.name !== key) {
return `${value.name}${key}`; return `${value.name}${key}`;
} }
@ -368,33 +372,32 @@ const getPlaceholder = (key, value) => {
}; };
const handleModelChange = (data) => { const handleModelChange = (data) => {
const newConfig = { ...props.agentConfig }; agentStore.updateAgentConfig({
newConfig.model = `${data.provider}/${data.name}`; model: `${data.provider}/${data.name}`
emit('update:agentConfig', newConfig); });
}; };
// //
const ensureArray = (key) => { const ensureArray = (key) => {
const config = { ...props.agentConfig }; const config = agentConfig.value || {};
if (!config[key] || !Array.isArray(config[key])) { if (!config[key] || !Array.isArray(config[key])) {
config[key] = []; return [];
} }
return config; return config[key];
}; };
const isOptionSelected = (key, option) => { const isOptionSelected = (key, option) => {
const config = ensureArray(key); const currentOptions = ensureArray(key);
return config[key].includes(option); return currentOptions.includes(option);
}; };
const getSelectedCount = (key) => { const getSelectedCount = (key) => {
const config = ensureArray(key); const currentOptions = ensureArray(key);
return config[key].length; return currentOptions.length;
}; };
const toggleOption = (key, option) => { const toggleOption = (key, option) => {
const config = ensureArray(key); const currentOptions = [...ensureArray(key)];
const currentOptions = [...config[key]];
const index = currentOptions.indexOf(option); const index = currentOptions.indexOf(option);
if (index > -1) { if (index > -1) {
@ -403,26 +406,27 @@ const toggleOption = (key, option) => {
currentOptions.push(option); currentOptions.push(option);
} }
config[key] = currentOptions; agentStore.updateAgentConfig({
emit('update:agentConfig', config); [key]: currentOptions
});
}; };
const clearSelection = (key) => { const clearSelection = (key) => {
const config = { ...props.agentConfig }; agentStore.updateAgentConfig({
config[key] = []; [key]: []
emit('update:agentConfig', config); });
}; };
// //
const getToolNameById = (toolId) => { const getToolNameById = (toolId) => {
const tool = availableTools.value.find(t => t.id === toolId); const toolsList = availableTools.value ? Object.values(availableTools.value) : [];
const tool = toolsList.find(t => t.id === toolId);
return tool ? tool.name : toolId; return tool ? tool.name : toolId;
}; };
const loadAvailableTools = async () => { const loadAvailableTools = async () => {
try { try {
const response = await agentApi.getTools(); await agentStore.fetchTools();
availableTools.value = Object.values(response.tools || {});
} catch (error) { } catch (error) {
console.error('加载工具列表失败:', error); console.error('加载工具列表失败:', error);
} }
@ -430,10 +434,10 @@ const loadAvailableTools = async () => {
const openToolsModal = async () => { const openToolsModal = async () => {
try { try {
if (availableTools.value.length === 0) { if (!availableTools.value || Object.keys(availableTools.value).length === 0) {
await loadAvailableTools(); await loadAvailableTools();
} }
selectedTools.value = [...(props.agentConfig.tools || [])]; selectedTools.value = [...(agentConfig.value?.tools || [])];
toolsModalOpen.value = true; toolsModalOpen.value = true;
} catch (error) { } catch (error) {
console.error('打开工具选择弹窗失败:', error); console.error('打开工具选择弹窗失败:', error);
@ -451,18 +455,20 @@ const toggleToolSelection = (toolId) => {
}; };
const removeSelectedTool = (toolId) => { const removeSelectedTool = (toolId) => {
const config = { ...props.agentConfig }; const currentTools = [...(agentConfig.value?.tools || [])];
const index = config.tools.indexOf(toolId); const index = currentTools.indexOf(toolId);
if (index > -1) { if (index > -1) {
config.tools.splice(index, 1); currentTools.splice(index, 1);
emit('update:agentConfig', config); agentStore.updateAgentConfig({
tools: currentTools
});
} }
}; };
const confirmToolsSelection = () => { const confirmToolsSelection = () => {
const config = { ...props.agentConfig }; agentStore.updateAgentConfig({
config.tools = [...selectedTools.value]; tools: [...selectedTools.value]
emit('update:agentConfig', config); });
toolsModalOpen.value = false; toolsModalOpen.value = false;
toolsSearchText.value = ''; toolsSearchText.value = '';
}; };
@ -475,15 +481,14 @@ const cancelToolsSelection = () => {
// //
const saveConfig = async () => { const saveConfig = async () => {
if (!props.selectedAgentId) { if (!selectedAgentId.value) {
message.error('没有选择智能体'); message.error('没有选择智能体');
return; return;
} }
try { try {
await agentApi.saveAgentConfig(props.selectedAgentId, props.agentConfig); await agentStore.saveAgentConfig();
message.success('配置已保存到服务器'); message.success('配置已保存到服务器');
emit('config-saved');
} catch (error) { } catch (error) {
console.error('保存配置到服务器出错:', error); console.error('保存配置到服务器出错:', error);
message.error('保存配置到服务器失败'); message.error('保存配置到服务器失败');
@ -491,14 +496,13 @@ const saveConfig = async () => {
}; };
const resetConfig = async () => { const resetConfig = async () => {
if (!props.selectedAgentId) { if (!selectedAgentId.value) {
message.error('没有选择智能体'); message.error('没有选择智能体');
return; return;
} }
try { try {
await agentApi.saveAgentConfig(props.selectedAgentId, {}); agentStore.resetAgentConfig();
emit('config-reset');
message.info('配置已重置'); message.info('配置已重置');
} catch (error) { } catch (error) {
console.error('重置配置出错:', error); console.error('重置配置出错:', error);
@ -508,7 +512,7 @@ const resetConfig = async () => {
// //
watch(() => props.isOpen, (newVal) => { watch(() => props.isOpen, (newVal) => {
if (newVal && availableTools.value.length === 0) { if (newVal && (!availableTools.value || Object.keys(availableTools.value).length === 0)) {
loadAvailableTools(); loadAvailableTools();
} }
}); });

View File

@ -35,10 +35,10 @@
<span v-if="!toolCall.tool_call_result"> <span v-if="!toolCall.tool_call_result">
<span><Loader size="16" class="tool-loader rotate" /></span> &nbsp; <span><Loader size="16" class="tool-loader rotate" /></span> &nbsp;
<span>正在调用工具: </span> <span>正在调用工具: </span>
<span class="tool-name">{{ toolCall.name || toolCall.function.name }}</span> <span class="tool-name">{{ getToolNameByToolCall(toolCall) }}</span>
</span> </span>
<span v-else> <span v-else>
<span><CircleCheckBig size="16" class="tool-loader" /></span> &nbsp; 工具 <span class="tool-name">{{ toolCall.name || toolCall.function.name }}</span> 执行完成 <span><CircleCheckBig size="16" class="tool-loader" /></span> &nbsp; 工具 <span class="tool-name">{{ getToolNameByToolCall(toolCall) }}</span> 执行完成
</span> </span>
</div> </div>
<div class="tool-content" v-show="expandedToolCalls.has(toolCall.id)"> <div class="tool-content" v-show="expandedToolCalls.has(toolCall.id)">
@ -85,6 +85,8 @@ import { CaretRightOutlined, ThunderboltOutlined, LoadingOutlined } from '@ant-d
import RefsComponent from '@/components/RefsComponent.vue' import RefsComponent from '@/components/RefsComponent.vue'
import { Loader, CircleCheckBig } from 'lucide-vue-next'; import { Loader, CircleCheckBig } from 'lucide-vue-next';
import { ToolResultRenderer } from '@/components/ToolCallingResult' import { ToolResultRenderer } from '@/components/ToolCallingResult'
import { useAgentStore } from '@/stores/agent'
import { storeToRefs } from 'pinia'
import { MdPreview } from 'md-editor-v3' import { MdPreview } from 'md-editor-v3'
@ -138,6 +140,18 @@ const isEmptyAndLoading = computed(() => {
return isEmpty && isLoading; return isEmpty && isLoading;
}); });
// store
const agentStore = useAgentStore();
const { availableTools } = storeToRefs(agentStore);
//
const getToolNameByToolCall = (toolCall) => {
const toolId = toolCall.name || toolCall.function.name;
const toolsList = availableTools.value ? Object.values(availableTools.value) : [];
const tool = toolsList.find(t => t.id === toolId);
return tool ? tool.name : toolId;
};
const parsedMessage = computed(() => { const parsedMessage = computed(() => {
const message = props.message; const message = props.message;
if (message.content) { if (message.content) {

View File

@ -2,7 +2,7 @@ import { createRouter, createWebHistory } from 'vue-router'
import AppLayout from '@/layouts/AppLayout.vue'; import AppLayout from '@/layouts/AppLayout.vue';
import BlankLayout from '@/layouts/BlankLayout.vue'; import BlankLayout from '@/layouts/BlankLayout.vue';
import { useUserStore } from '@/stores/user'; import { useUserStore } from '@/stores/user';
import { agentApi } from '@/apis/agent'; import { useAgentStore } from '@/stores/agent';
const router = createRouter({ const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL), history: createWebHistory(import.meta.env.BASE_URL),
@ -121,21 +121,24 @@ router.beforeEach(async (to, from, next) => {
if (requiresAdmin && !isAdmin) { if (requiresAdmin && !isAdmin) {
// 如果是普通用户,跳转到默认智能体页面 // 如果是普通用户,跳转到默认智能体页面
try { try {
// 先尝试获取默认智能体 const agentStore = useAgentStore();
const data = await agentApi.getDefaultAgent(); // 等待 store 初始化完成
if (data && data.default_agent_id) { if (!agentStore.isInitialized) {
// 如果存在默认智能体,直接跳转 await agentStore.initialize();
next(`/agent/${data.default_agent_id}`);
return;
} }
// 如果没有默认智能体,则获取第一个可用智能体 const defaultAgent = agentStore.defaultAgent;
const agentData = await agentApi.getAgents(); if (defaultAgent && defaultAgent.id) {
if (agentData && agentData.agents && agentData.agents.length > 0) { next(`/agent/${defaultAgent.id}`);
const firstAgentId = agentData.agents[0].name;
next(`/agent/${firstAgentId}`);
} else { } else {
next('/'); // 如果没有默认智能体,可以考虑跳转到第一个可用的智能体,或者一个特定的页面
const agentIds = Object.keys(agentStore.agents);
if (agentIds.length > 0) {
next(`/agent/${agentIds[0]}`);
} else {
// 没有可用的智能体,跳转到首页
next('/');
}
} }
} catch (error) { } catch (error) {
console.error('获取智能体信息失败:', error); console.error('获取智能体信息失败:', error);

445
web/src/stores/agent.js Normal file
View File

@ -0,0 +1,445 @@
import { defineStore } from 'pinia';
import { agentApi, threadApi } from '@/apis/agent';
export const useAgentStore = defineStore('agent', {
state: () => ({
// 智能体相关状态
agents: {}, // 以ID为键的智能体对象
selectedAgentId: null, // 当前选中的智能体ID
defaultAgentId: null, // 默认智能体ID
// 智能体配置相关状态
agentConfig: {}, // 当前智能体的配置
originalAgentConfig: {}, // 原始配置,用于重置
// 工具相关状态
availableTools: [], // 所有可用工具列表
// 线程相关状态
threads: {}, // 以智能体ID为键的线程列表
currentThreadId: null, // 当前选中的线程ID
threadMessages: {}, // 以线程ID为键的消息列表
// 加载状态
isLoadingAgents: false,
isLoadingConfig: false,
isLoadingTools: false,
isLoadingThreads: false,
isLoadingMessages: false,
// 错误状态
error: null,
// 初始化状态
isInitialized: false
}),
getters: {
// 获取当前选中的智能体
selectedAgent() {
return this.selectedAgentId ? this.agents[this.selectedAgentId] : null;
},
// 获取默认智能体,默认取第一个
defaultAgent() {
return this.defaultAgentId ? this.agents[this.defaultAgentId] : this.agents[Object.keys(this.agents)[0]];
},
// 获取智能体列表(数组形式)
agentsList() {
return Object.values(this.agents);
},
// 判断当前智能体是否为默认智能体
isDefaultAgent() {
return this.selectedAgentId === this.defaultAgentId;
},
// 获取当前智能体的配置schema
configSchema() {
const agent = this.selectedAgentId ? this.agents[this.selectedAgentId] : null;
return agent?.config_schema || {};
},
// 获取可配置项处理x_oap_ui_config
configurableItems() {
const schema = this.configSchema || {};
if (!schema || !schema.configurable_items) return {};
const items = { ...schema.configurable_items };
// 处理x_oap_ui_config将其提升到上一层
Object.keys(items).forEach(key => {
const item = items[key];
if (item && item.x_oap_ui_config) {
items[key] = {
...item,
...item.x_oap_ui_config
};
delete items[key].x_oap_ui_config;
}
});
return items;
},
// 检查配置是否有变更
hasConfigChanges() {
return JSON.stringify(this.agentConfig) !== JSON.stringify(this.originalAgentConfig);
},
// 获取当前智能体的线程列表
currentAgentThreads() {
return this.selectedAgentId ? (this.threads[this.selectedAgentId] || []) : [];
},
// 获取当前线程
currentThread() {
if (!this.currentThreadId || !this.selectedAgentId) return null;
const agentThreads = this.threads[this.selectedAgentId] || [];
return agentThreads.find(thread => thread.id === this.currentThreadId);
},
// 获取当前线程的消息
currentThreadMessages() {
return this.currentThreadId ? (this.threadMessages[this.currentThreadId] || []) : [];
}
},
actions: {
// 初始化store
async initialize() {
if (this.isInitialized) return;
try {
await Promise.all([
this.fetchAgents(),
this.fetchDefaultAgent(),
this.fetchTools()
]);
this.isInitialized = true;
} catch (error) {
console.error('Failed to initialize agent store:', error);
this.error = error.message;
}
},
// 获取智能体列表
async fetchAgents() {
this.isLoadingAgents = true;
this.error = null;
try {
const response = await agentApi.getAgents();
// 将数组转换为以ID为键的对象
this.agents = response.agents.reduce((acc, agent) => {
acc[agent.id] = agent;
return acc;
}, {});
} catch (error) {
console.error('Failed to fetch agents:', error);
this.error = error.message;
throw error;
} finally {
this.isLoadingAgents = false;
}
},
// 获取默认智能体
async fetchDefaultAgent() {
try {
const response = await agentApi.getDefaultAgent();
this.defaultAgentId = response.default_agent_id;
// 如果没有选中的智能体,则选择默认智能体
if (!this.selectedAgentId && this.defaultAgentId) {
this.selectedAgentId = this.defaultAgentId;
}
} catch (error) {
console.error('Failed to fetch default agent:', error);
this.error = error.message;
}
},
// 设置默认智能体
async setDefaultAgent(agentId) {
try {
await agentConfigApi.setDefaultAgent(agentId);
this.defaultAgentId = agentId;
} catch (error) {
console.error('Failed to set default agent:', error);
this.error = error.message;
throw error;
}
},
// 选择智能体
selectAgent(agentId) {
if (this.agents[agentId]) {
this.selectedAgentId = agentId;
// 清空之前的配置
this.agentConfig = {};
this.originalAgentConfig = {};
}
},
// 加载智能体配置
async loadAgentConfig(agentId = null) {
const targetAgentId = agentId || this.selectedAgentId;
if (!targetAgentId) return;
this.isLoadingConfig = true;
this.error = null;
try {
const response = await agentApi.getAgentConfig(targetAgentId);
this.agentConfig = { ...response.config };
this.originalAgentConfig = { ...response.config };
} catch (error) {
console.error('Failed to load agent config:', error);
this.error = error.message;
throw error;
} finally {
this.isLoadingConfig = false;
}
},
// 保存智能体配置
async saveAgentConfig(agentId = null) {
const targetAgentId = agentId || this.selectedAgentId;
if (!targetAgentId) return;
try {
await agentApi.saveAgentConfig(targetAgentId, this.agentConfig);
this.originalAgentConfig = { ...this.agentConfig };
} catch (error) {
console.error('Failed to save agent config:', error);
this.error = error.message;
throw error;
}
},
// 重置智能体配置
resetAgentConfig() {
this.agentConfig = { ...this.originalAgentConfig };
},
// 更新配置项
updateConfigItem(key, value) {
this.agentConfig[key] = value;
},
// 更新智能体配置(支持批量更新)
updateAgentConfig(updates) {
Object.assign(this.agentConfig, updates);
},
// 获取工具列表
async fetchTools() {
this.isLoadingTools = true;
this.error = null;
try {
const response = await agentApi.getTools();
this.availableTools = response.tools;
} catch (error) {
console.error('Failed to fetch tools:', error);
this.error = error.message;
throw error;
} finally {
this.isLoadingTools = false;
}
},
// 清除错误状态
clearError() {
this.error = null;
},
// ==================== 线程管理方法 ====================
// 获取智能体的线程列表
async fetchThreads(agentId = null) {
const targetAgentId = agentId || this.selectedAgentId;
if (!targetAgentId) return;
this.isLoadingThreads = true;
this.error = null;
try {
const threads = await threadApi.getThreads(targetAgentId);
this.threads[targetAgentId] = threads || [];
} catch (error) {
console.error('Failed to fetch threads:', error);
this.error = error.message;
throw error;
} finally {
this.isLoadingThreads = false;
}
},
// 创建新线程
async createThread(agentId, title = '新的对话') {
if (!agentId) return null;
try {
const thread = await threadApi.createThread(agentId, title);
if (thread) {
// 更新线程列表
if (!this.threads[agentId]) {
this.threads[agentId] = [];
}
this.threads[agentId].unshift(thread);
// 设置为当前线程
this.currentThreadId = thread.id;
// 初始化消息列表
this.threadMessages[thread.id] = [];
}
return thread;
} catch (error) {
console.error('Failed to create thread:', error);
this.error = error.message;
throw error;
}
},
// 删除线程
async deleteThread(threadId) {
if (!threadId) return;
try {
await threadApi.deleteThread(threadId);
// 从所有智能体的线程列表中移除
Object.keys(this.threads).forEach(agentId => {
this.threads[agentId] = this.threads[agentId].filter(thread => thread.id !== threadId);
});
// 清理消息
delete this.threadMessages[threadId];
// 如果删除的是当前线程重置当前线程ID
if (this.currentThreadId === threadId) {
this.currentThreadId = null;
}
} catch (error) {
console.error('Failed to delete thread:', error);
this.error = error.message;
throw error;
}
},
// 更新线程标题
async updateThread(threadId, title) {
if (!threadId || !title) return;
try {
await threadApi.updateThread(threadId, title);
// 更新本地线程列表中的标题
Object.keys(this.threads).forEach(agentId => {
const thread = this.threads[agentId].find(t => t.id === threadId);
if (thread) {
thread.title = title;
}
});
} catch (error) {
console.error('Failed to update thread:', error);
this.error = error.message;
throw error;
}
},
// 选择线程
selectThread(threadId) {
this.currentThreadId = threadId;
// 如果没有该线程的消息,初始化空数组
if (threadId && !this.threadMessages[threadId]) {
this.threadMessages[threadId] = [];
}
},
// 获取线程消息
async fetchThreadMessages(threadId) {
if (!threadId) return;
this.isLoadingMessages = true;
this.error = null;
try {
const response = await agentApi.getAgentHistory(this.selectedAgentId, threadId);
this.threadMessages[threadId] = response.history || [];
} catch (error) {
console.error('Failed to fetch thread messages:', error);
this.error = error.message;
throw error;
} finally {
this.isLoadingMessages = false;
}
},
// 发送消息
async sendMessage(agentId, requestData) {
if (!agentId) return null;
try {
const response = await agentApi.sendAgentMessage(agentId, requestData);
return response;
} catch (error) {
console.error('Failed to send message:', error);
this.error = error.message;
throw error;
}
},
// 添加消息到线程
addMessageToThread(threadId, message) {
if (!threadId || !message) return;
if (!this.threadMessages[threadId]) {
this.threadMessages[threadId] = [];
}
this.threadMessages[threadId].push(message);
},
// 更新线程中的消息
updateMessageInThread(threadId, messageIndex, updatedMessage) {
if (!threadId || messageIndex < 0 || !this.threadMessages[threadId]) return;
if (messageIndex < this.threadMessages[threadId].length) {
this.threadMessages[threadId][messageIndex] = updatedMessage;
}
},
// 重置store状态
reset() {
this.agents = {};
this.selectedAgentId = null;
this.defaultAgentId = null;
this.agentConfig = {};
this.originalAgentConfig = {};
this.availableTools = [];
this.threads = {};
this.currentThreadId = null;
this.threadMessages = {};
this.isLoadingAgents = false;
this.isLoadingConfig = false;
this.isLoadingTools = false;
this.isLoadingThreads = false;
this.isLoadingMessages = false;
this.error = null;
this.isInitialized = false;
}
},
// 持久化配置
persist: {
key: 'agent-store',
storage: localStorage,
paths: ['selectedAgentId', 'defaultAgentId'] // 只持久化关键状态
}
});

View File

@ -5,7 +5,7 @@
<div class="header-item"> <div class="header-item">
<a-button class="header-button" @click="openAgentModal"> <a-button class="header-button" @click="openAgentModal">
<Bot size="18" stroke-width="1.75" /> <Bot size="18" stroke-width="1.75" />
选择{{ selectedAgent.name || '选择智能体' }} 选择{{ selectedAgent?.name || '选择智能体' }}
</a-button> </a-button>
</div> </div>
</div> </div>
@ -62,8 +62,6 @@
<!-- 中间内容区域 --> <!-- 中间内容区域 -->
<div class="content"> <div class="content">
<AgentChatComponent <AgentChatComponent
:agent-id="selectedAgentId"
:config="agentConfig"
:state="state" :state="state"
:single-mode="false" :single-mode="false"
@open-config="toggleConf" @open-config="toggleConf"
@ -76,13 +74,7 @@
<!-- 配置侧边栏 --> <!-- 配置侧边栏 -->
<AgentConfigSidebar <AgentConfigSidebar
:isOpen="state.isConfigSidebarOpen" :isOpen="state.isConfigSidebarOpen"
:selectedAgent="selectedAgent"
:selectedAgentId="selectedAgentId"
:agentConfig="agentConfig"
@close="() => state.isConfigSidebarOpen = false" @close="() => state.isConfigSidebarOpen = false"
@update:agentConfig="(config) => agentConfig = config"
@config-saved="loadAgentConfig"
@config-reset="loadAgentConfig"
/> />
</div> </div>
</div> </div>
@ -92,72 +84,53 @@
import { ref, onMounted, reactive, watch, computed, h } from 'vue'; import { ref, onMounted, reactive, watch, computed, h } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { import {
CloseOutlined,
SettingOutlined, SettingOutlined,
LinkOutlined, LinkOutlined,
StarOutlined, StarOutlined,
StarFilled, StarFilled,
CheckCircleOutlined,
PlusCircleOutlined
} from '@ant-design/icons-vue'; } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { Bot } from 'lucide-vue-next'; import { Bot } from 'lucide-vue-next';
import AgentChatComponent from '@/components/AgentChatComponent.vue'; import AgentChatComponent from '@/components/AgentChatComponent.vue';
import ModelSelectorComponent from '@/components/ModelSelectorComponent.vue';
import AgentConfigSidebar from '@/components/AgentConfigSidebar.vue'; import AgentConfigSidebar from '@/components/AgentConfigSidebar.vue';
import { useUserStore } from '@/stores/user'; import { useUserStore } from '@/stores/user';
import { agentApi } from '@/apis/agent'; import { useAgentStore } from '@/stores/agent';
import { storeToRefs } from 'pinia';
// // stores
const router = useRouter(); const router = useRouter();
const userStore = useUserStore(); const userStore = useUserStore();
const agentStore = useAgentStore();
// // store
const agents = ref({}); const {
const selectedAgentId = ref(null); agents,
const defaultAgentId = ref(null); // ID selectedAgentId,
defaultAgentId,
agentConfig,
selectedAgent,
configurableItems,
} = storeToRefs(agentStore);
const state = reactive({ const state = reactive({
debug_mode: false, debug_mode: false,
agentModalOpen: false, agentModalOpen: false,
isConfigSidebarOpen: false, isConfigSidebarOpen: false,
isEmptyConfig: computed(() => isEmptyConfig: computed(() =>
!selectedAgentId.value || !selectedAgentId.value ||
Object.keys(configurableItems.value).length === 0 Object.keys(configurableItems.value || {}).length === 0
) )
}); });
const selectedAgent = computed(() => agents.value[selectedAgentId.value] || {}); // UI
const configSchema = computed(() => selectedAgent.value.config_schema || {});
const configurableItems = computed(() => {
const items = configSchema.value.configurable_items || {};
// x_oap_ui_config
Object.keys(items).forEach(key => {
const item = items[key];
if (item.x_oap_ui_config) {
items[key] = { ...item, ...item.x_oap_ui_config };
delete items[key].x_oap_ui_config;
}
});
return items;
});
//
const agentConfig = ref({});
//
const isDefaultAgent = computed(() => {
return selectedAgentId.value === defaultAgentId.value;
});
// //
const setAsDefaultAgent = async () => { const setAsDefaultAgent = async () => {
if (!selectedAgentId.value || !userStore.isAdmin) return; if (!selectedAgentId.value || !userStore.isAdmin) return;
try { try {
await agentApi.setDefaultAgent(selectedAgentId.value); await agentStore.setDefaultAgent(selectedAgentId.value);
defaultAgentId.value = selectedAgentId.value;
message.success('已将当前智能体设为默认'); message.success('已将当前智能体设为默认');
} catch (error) { } catch (error) {
console.error('设置默认智能体错误:', error); console.error('设置默认智能体错误:', error);
@ -169,97 +142,15 @@ const setAsDefaultAgent = async () => {
// ID // agentStore
const fetchDefaultAgent = async () => {
try {
const data = await agentApi.getDefaultAgent();
defaultAgentId.value = data.default_agent_id;
console.debug("Default agent ID:", defaultAgentId.value);
} catch (error) {
console.error('获取默认智能体错误:', error);
}
};
// // 使store
const fetchAgents = async () => {
try {
const data = await agentApi.getAgents();
//
agents.value = data.agents.reduce((acc, agent) => {
acc[agent.id] = agent;
return acc;
}, {});
// console.log("agents", agents.value);
//
if (selectedAgentId.value) {
loadAgentConfig();
}
} catch (error) {
console.error('获取智能体错误:', error);
}
};
//
const loadAgentConfig = async () => { const loadAgentConfig = async () => {
// BUG:
if (!selectedAgentId.value || !agents.value[selectedAgentId.value]) return;
const agent = agents.value[selectedAgentId.value];
const schema = agent.config_schema || {};
const items = schema.configurable_items || {};
//
agentConfig.value = {};
//
if (schema.system_prompt) {
agentConfig.value.system_prompt = schema.system_prompt;
}
if (schema.model) {
agentConfig.value.model = schema.model;
}
if (schema.tools && schema.tools.length > 0 && schema.tools[0] != 'undefined') {
agentConfig.value.tools = schema.tools;
}
//
Object.keys(items).forEach(key => {
const item = items[key];
//
if (typeof item.default === 'boolean') {
agentConfig.value[key] = item.default;
} else if (item.type === 'list') {
// list
agentConfig.value[key] = Array.isArray(item.default) ? item.default : [];
} else {
agentConfig.value[key] = item.default || '';
}
});
try { try {
// await agentStore.loadAgentConfig();
const response = await agentApi.getAgentConfig(selectedAgentId.value);
if (response.success && response.config) {
//
Object.keys(response.config).forEach(key => {
if (key in agentConfig.value) {
const item = items[key];
// list
if (item && item.type === 'list') {
agentConfig.value[key] = Array.isArray(response.config[key]) ? response.config[key] : [];
} else {
agentConfig.value[key] = response.config[key];
}
}
});
// console.log(` ${selectedAgentId.value} , ${JSON.stringify(agentConfig.value)}`);
}
} catch (error) { } catch (error) {
console.error('从服务器加载配置出错:', error); console.error('加载配置出错:', error);
message.error('加载配置失败');
} }
}; };
@ -267,43 +158,6 @@ const handleModelChange = (data) => {
agentConfig.value.model = `${data.provider}/${data.name}`; agentConfig.value.model = `${data.provider}/${data.name}`;
} }
//
const saveConfig = async () => {
if (!selectedAgentId.value) {
message.error('没有选择智能体');
return;
}
try {
//
await agentApi.saveAgentConfig(selectedAgentId.value, agentConfig.value);
//
message.success('配置已保存到服务器');
console.log("保存配置:", agentConfig.value);
} catch (error) {
console.error('保存配置到服务器出错:', error);
message.error('保存配置到服务器失败');
}
};
//
const resetConfig = async () => {
if (!selectedAgentId.value) {
message.error('没有选择智能体');
return;
}
try {
//
await agentApi.saveAgentConfig(selectedAgentId.value, {});
//
await loadAgentConfig();
message.info('配置已重置');
} catch (error) {
console.error('重置配置出错:', error);
message.error('重置配置失败');
}
};
// //
watch( watch(
@ -313,11 +167,9 @@ watch(
} }
); );
// // 使store
const selectAgent = (agentId) => { const selectAgent = (agentId) => {
selectedAgentId.value = agentId; agentStore.selectAgent(agentId);
//
localStorage.setItem('last-selected-agent', agentId);
// //
loadAgentConfig(); loadAgentConfig();
}; };
@ -335,29 +187,27 @@ const selectAgentFromModal = (agentId) => {
// // 使store
onMounted(async () => { onMounted(async () => {
// try {
await fetchDefaultAgent(); //
// const lastSelectedAgent = localStorage.getItem('last-selected-agent');
await fetchAgents(); if (lastSelectedAgent && agents.value[lastSelectedAgent]) {
// agentStore.selectAgent(lastSelectedAgent);
} else if (defaultAgentId.value && agents.value[defaultAgentId.value]) {
//
agentStore.selectAgent(defaultAgentId.value);
} else if (Object.keys(agents.value).length > 0) {
//
agentStore.selectAgent(Object.keys(agents.value)[0]);
}
//
// await loadAgentConfig();
const lastSelectedAgent = localStorage.getItem('last-selected-agent'); } catch (error) {
if (lastSelectedAgent && agents.value[lastSelectedAgent]) { console.error('初始化失败:', error);
selectedAgentId.value = lastSelectedAgent; message.error('初始化失败');
} else if (defaultAgentId.value && agents.value[defaultAgentId.value]) {
//
selectedAgentId.value = defaultAgentId.value;
} else if (Object.keys(agents.value).length > 0) {
//
selectedAgentId.value = Object.keys(agents.value)[0];
} }
//
loadAgentConfig();
}); });
// //
@ -445,6 +295,35 @@ const toggleConf = () => {
// padding: var(--gap-radius); // padding: var(--gap-radius);
// gap: var(--gap-radius); // gap: var(--gap-radius);
.content {
flex: 1;
display: flex;
flex-direction: column;
}
.no-agent-selected {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
background-color: var(--bg-content);
}
.no-agent-content {
text-align: center;
color: var(--text-secondary);
svg {
margin-bottom: 16px;
opacity: 0.6;
}
h3 {
margin-bottom: 16px;
color: var(--text-primary);
}
}
// .content { // .content {
// border-radius: var(--gap-radius); // border-radius: var(--gap-radius);
// border: 1px solid var(--gray-300); // border: 1px solid var(--gray-300);

View File

@ -64,12 +64,13 @@ import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import { useInfoStore } from '@/stores/info' import { useInfoStore } from '@/stores/info'
import { agentApi } from '@/apis/agent' import { useAgentStore } from '@/stores/agent'
import UserInfoComponent from '@/components/UserInfoComponent.vue' import UserInfoComponent from '@/components/UserInfoComponent.vue'
const router = useRouter() const router = useRouter()
const userStore = useUserStore() const userStore = useUserStore()
const infoStore = useInfoStore() const infoStore = useInfoStore()
const agentStore = useAgentStore()
const goToChat = async () => { const goToChat = async () => {
// //
@ -90,23 +91,11 @@ const goToChat = async () => {
// //
try { try {
// //
const data = await agentApi.getDefaultAgent(); const defaultAgent = agentStore.defaultAgent;
if (data.default_agent_id) { router.push(`/agent/${defaultAgent.id}`);
// 使
router.push(`/agent/${data.default_agent_id}`);
} else {
//
const agentData = await agentApi.getAgents();
if (agentData.agents && agentData.agents.length > 0) {
router.push(`/agent/${agentData.agents[0].id}`);
} else {
// 退chat
router.push("/chat");
}
}
} catch (error) { } catch (error) {
console.error('跳转到智能体页面失败:', error); console.error('跳转到智能体页面失败:', error);
router.push("/chat"); router.push("/");
} }
}; };

View File

@ -170,13 +170,14 @@ import { ref, reactive, onMounted, computed } from 'vue';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { useUserStore } from '@/stores/user'; import { useUserStore } from '@/stores/user';
import { useInfoStore } from '@/stores/info'; import { useInfoStore } from '@/stores/info';
import { useAgentStore } from '@/stores/agent';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { agentApi } from '@/apis/agent';
import { healthApi } from '@/apis/system_api'; import { healthApi } from '@/apis/system_api';
import { UserOutlined, LockOutlined, WechatOutlined, QrcodeOutlined, ThunderboltOutlined, ExclamationCircleOutlined } from '@ant-design/icons-vue'; import { UserOutlined, LockOutlined, WechatOutlined, QrcodeOutlined, ThunderboltOutlined, ExclamationCircleOutlined } from '@ant-design/icons-vue';
const router = useRouter(); const router = useRouter();
const userStore = useUserStore(); const userStore = useUserStore();
const infoStore = useInfoStore(); const infoStore = useInfoStore();
const agentStore = useAgentStore();
// infoStore // infoStore
const loginBgImage = computed(() => { const loginBgImage = computed(() => {
@ -248,18 +249,20 @@ const handleLogin = async () => {
// //
try { try {
// agentStore
await agentStore.initialize();
// //
const data = await agentApi.getDefaultAgent(); if (agentStore.defaultAgentId) {
if (data.default_agent_id) {
// //
router.push(`/agent/${data.default_agent_id}`); router.push(`/agent/${agentStore.defaultAgentId}`);
return; return;
} }
// //
const agentData = await agentApi.getAgents(); const agentIds = Object.keys(agentStore.agents);
if (agentData.agents && agentData.agents.length > 0) { if (agentIds.length > 0) {
router.push(`/agent/${agentData.agents[0].id}`); router.push(`/agent/${agentIds[0]}`);
return; return;
} }