feat(agent-ui): 添加智能体选择弹窗并优化默认标记样式
- 在AgentSingleView中添加智能体选择弹窗功能 - 重构默认智能体标记的样式和位置 - 仅管理员显示系统设置菜单项
This commit is contained in:
parent
5f1f6be7c4
commit
abf4414a0f
@ -29,8 +29,8 @@
|
|||||||
<component :is="themeStore.isDark ? Sun : Moon" size="16"/>
|
<component :is="themeStore.isDark ? Sun : Moon" size="16"/>
|
||||||
<span class="menu-text">{{ themeStore.isDark ? '切换到浅色模式' : '切换到深色模式 (Beta)' }}</span>
|
<span class="menu-text">{{ themeStore.isDark ? '切换到浅色模式' : '切换到深色模式 (Beta)' }}</span>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
<a-menu-divider />
|
<a-menu-divider v-if="userStore.isAdmin"/>
|
||||||
<a-menu-item key="setting" @click="goToSetting">
|
<a-menu-item v-if="userStore.isAdmin" key="setting" @click="goToSetting">
|
||||||
<Settings size="16"/>
|
<Settings size="16"/>
|
||||||
<span class="menu-text">系统设置</span>
|
<span class="menu-text">系统设置</span>
|
||||||
</a-menu-item>
|
</a-menu-item>
|
||||||
|
|||||||
@ -1,7 +1,47 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="agent-single-view">
|
<div class="agent-single-view">
|
||||||
|
<!-- 智能体选择弹窗 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:open="agentModalOpen"
|
||||||
|
title="选择智能体"
|
||||||
|
:width="800"
|
||||||
|
:footer="null"
|
||||||
|
:maskClosable="true"
|
||||||
|
class="agent-modal"
|
||||||
|
>
|
||||||
|
<div class="agent-modal-content">
|
||||||
|
<div class="agents-grid">
|
||||||
|
<div
|
||||||
|
v-for="agent in agents"
|
||||||
|
:key="agent.id"
|
||||||
|
class="agent-card"
|
||||||
|
:class="{ 'selected': agent.id === agentId }"
|
||||||
|
@click="selectAgentFromModal(agent.id)"
|
||||||
|
>
|
||||||
|
<div class="agent-card-header">
|
||||||
|
<div class="agent-card-title">
|
||||||
|
<span class="agent-card-name">{{ agent.name || 'Unknown' }}</span>
|
||||||
|
</div>
|
||||||
|
<StarFilled v-if="agent.id === defaultAgentId" class="default-icon" />
|
||||||
|
<StarOutlined v-else @click.prevent="setAsDefaultAgent(agent.id)" class="default-icon" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="agent-card-description">
|
||||||
|
{{ agent.description || '' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
|
||||||
<!-- 智能体聊天界面 -->
|
<!-- 智能体聊天界面 -->
|
||||||
<AgentChatComponent ref="chatComponentRef" :agent-id="agentId" :single-mode="true">
|
<AgentChatComponent ref="chatComponentRef" :agent-id="agentId" :single-mode="true">
|
||||||
|
<template #header-left>
|
||||||
|
<div type="button" class="agent-nav-btn" @click="openAgentModal">
|
||||||
|
<span class="text">{{ currentAgentName || '选择智能体' }}</span>
|
||||||
|
<ChevronDown size="16" class="switch-icon" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<template #header-right>
|
<template #header-right>
|
||||||
<div type="button" class="agent-nav-btn" @click="handleShareChat">
|
<div type="button" class="agent-nav-btn" @click="handleShareChat">
|
||||||
<Share2 size="18" class="nav-btn-icon" />
|
<Share2 size="18" class="nav-btn-icon" />
|
||||||
@ -14,19 +54,66 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref, onMounted } from 'vue';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
import { Share2 } from 'lucide-vue-next';
|
import { Share2, ChevronDown } from 'lucide-vue-next';
|
||||||
|
import { StarFilled, StarOutlined } from '@ant-design/icons-vue';
|
||||||
import AgentChatComponent from '@/components/AgentChatComponent.vue';
|
import AgentChatComponent from '@/components/AgentChatComponent.vue';
|
||||||
import UserInfoComponent from '@/components/UserInfoComponent.vue';
|
import UserInfoComponent from '@/components/UserInfoComponent.vue';
|
||||||
import { ChatExporter } from '@/utils/chatExporter';
|
import { ChatExporter } from '@/utils/chatExporter';
|
||||||
import { handleChatError } from '@/utils/errorHandler';
|
import { handleChatError } from '@/utils/errorHandler';
|
||||||
|
import { useAgentStore } from '@/stores/agent';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const agentStore = useAgentStore();
|
||||||
|
|
||||||
const agentId = computed(() => route.params.agent_id);
|
const agentId = computed(() => route.params.agent_id);
|
||||||
const chatComponentRef = ref(null);
|
const chatComponentRef = ref(null);
|
||||||
|
|
||||||
|
// 智能体选择弹窗状态
|
||||||
|
const agentModalOpen = ref(false);
|
||||||
|
|
||||||
|
// 从 store 获取智能体数据
|
||||||
|
const { agents, defaultAgentId } = storeToRefs(agentStore);
|
||||||
|
|
||||||
|
// 当前智能体名称
|
||||||
|
const currentAgentName = computed(() => {
|
||||||
|
if (!agentId.value || !agents.value?.length) return '智能体加载中……';
|
||||||
|
const agent = agents.value.find(a => a.id === agentId.value);
|
||||||
|
return agent ? agent.name : '未知智能体';
|
||||||
|
});
|
||||||
|
|
||||||
|
// 打开智能体选择弹窗
|
||||||
|
const openAgentModal = () => {
|
||||||
|
agentModalOpen.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 从弹窗中选择智能体 - 切换路由
|
||||||
|
const selectAgentFromModal = (newAgentId) => {
|
||||||
|
if (newAgentId === agentId.value) {
|
||||||
|
// 如果选择的是当前智能体,只需要关闭弹窗
|
||||||
|
agentModalOpen.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跳转到新的智能体页面
|
||||||
|
router.push(`/agent/${newAgentId}`);
|
||||||
|
agentModalOpen.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 设置默认智能体
|
||||||
|
const setAsDefaultAgent = async (agentIdToSet) => {
|
||||||
|
try {
|
||||||
|
await agentStore.setDefaultAgent(agentIdToSet);
|
||||||
|
message.success('已设置为默认智能体');
|
||||||
|
} catch (error) {
|
||||||
|
handleChatError(error, 'save');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleShareChat = async () => {
|
const handleShareChat = async () => {
|
||||||
try {
|
try {
|
||||||
const exportData = chatComponentRef.value?.getExportPayload?.();
|
const exportData = chatComponentRef.value?.getExportPayload?.();
|
||||||
@ -54,6 +141,17 @@ const handleShareChat = async () => {
|
|||||||
handleChatError(error, 'export');
|
handleChatError(error, 'export');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 初始化时确保智能体 store 已加载
|
||||||
|
onMounted(async () => {
|
||||||
|
if (!agentStore.isInitialized) {
|
||||||
|
try {
|
||||||
|
await agentStore.initialize();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('初始化智能体 store 失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
@ -73,6 +171,115 @@ const handleShareChat = async () => {
|
|||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 智能体选择弹窗样式
|
||||||
|
.agent-modal {
|
||||||
|
:deep(.ant-modal-content) {
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-modal-header) {
|
||||||
|
background: var(--gray-0);
|
||||||
|
border-bottom: 1px solid var(--gray-200);
|
||||||
|
padding: 16px 20px;
|
||||||
|
|
||||||
|
.ant-modal-title {
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--gray-900);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-modal-body) {
|
||||||
|
padding: 20px;
|
||||||
|
background: var(--gray-0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-modal-content {
|
||||||
|
.agents-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
max-height: 500px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-card {
|
||||||
|
border: 1px solid var(--gray-200);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
background: var(--gray-0);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: var(--main-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.agent-card-title {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.agent-card-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--gray-900);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.default-icon {
|
||||||
|
color: var(--color-warning-500);
|
||||||
|
font-size: 16px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-left: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-warning-600);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-card-description {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--gray-700);
|
||||||
|
line-height: 1.5;
|
||||||
|
word-break: break-word;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.selected {
|
||||||
|
border-color: var(--main-color);
|
||||||
|
background: var(--main-20);
|
||||||
|
|
||||||
|
.agent-card-header .agent-card-title .agent-card-name {
|
||||||
|
color: var(--main-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-card-description {
|
||||||
|
color: var(--gray-900);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应式适配智能体弹窗
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.agent-modal {
|
||||||
|
.agent-modal-content {
|
||||||
|
.agents-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 侧边栏样式
|
// 侧边栏样式
|
||||||
.sidebar {
|
.sidebar {
|
||||||
// position: absolute;
|
// position: absolute;
|
||||||
@ -129,3 +336,38 @@ const handleShareChat = async () => {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.agent-nav-btn {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 6px 14px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 12px;
|
||||||
|
color: var(--gray-900);
|
||||||
|
cursor: pointer;
|
||||||
|
width: auto;
|
||||||
|
font-size: 15px;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--gray-50);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-btn-icon {
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-icon {
|
||||||
|
color: var(--gray-500);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover .switch-icon {
|
||||||
|
color: var(--main-500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|||||||
@ -22,9 +22,9 @@
|
|||||||
<div class="agent-card-header">
|
<div class="agent-card-header">
|
||||||
<div class="agent-card-title">
|
<div class="agent-card-title">
|
||||||
<span class="agent-card-name">{{ agent.name || 'Unknown' }}</span>
|
<span class="agent-card-name">{{ agent.name || 'Unknown' }}</span>
|
||||||
<StarFilled v-if="agent.id === defaultAgentId" class="default-icon" />
|
|
||||||
<StarOutlined v-else @click.prevent="setAsDefaultAgent(agent.id)" class="default-icon" />
|
|
||||||
</div>
|
</div>
|
||||||
|
<StarFilled v-if="agent.id === defaultAgentId" class="default-icon" />
|
||||||
|
<StarOutlined v-else @click.prevent="setAsDefaultAgent(agent.id)" class="default-icon" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="agent-card-description">
|
<div class="agent-card-description">
|
||||||
@ -428,12 +428,6 @@ const handlePreview = () => {
|
|||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.default-icon {
|
|
||||||
color: var(--color-warning-500);
|
|
||||||
font-size: 14px;
|
|
||||||
margin-left: 4px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// 工具选择器样式(与项目风格一致)
|
// 工具选择器样式(与项目风格一致)
|
||||||
.tools-selector {
|
.tools-selector {
|
||||||
@ -754,11 +748,6 @@ const handlePreview = () => {
|
|||||||
color: var(--gray-900);
|
color: var(--gray-900);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.default-icon {
|
|
||||||
color: var(--color-warning-500);
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -814,9 +803,6 @@ const handlePreview = () => {
|
|||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
|
||||||
.agent-card-title {
|
.agent-card-title {
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
||||||
.agent-card-name {
|
.agent-card-name {
|
||||||
@ -825,11 +811,17 @@ const handlePreview = () => {
|
|||||||
color: var(--gray-900);
|
color: var(--gray-900);
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.default-icon {
|
.default-icon {
|
||||||
color: var(--color-warning-500);
|
color: var(--color-warning-500);
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
margin-left: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--color-warning-600);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user