优化界面显示

This commit is contained in:
Wenjie Zhang 2025-04-01 22:16:07 +08:00
parent 2d06c59008
commit 04ee276331
4 changed files with 158 additions and 27 deletions

View File

@ -60,6 +60,15 @@ class BaseAgent():
def __init__(self, **kwargs):
self.check_requirements()
@classmethod
def get_info(cls):
return {
"name": cls.name,
"description": cls.description,
"config_schema": cls.config_schema.to_dict(),
"requirements": cls.requirements if hasattr(cls, "requirements") else [],
}
def check_requirements(self):
if not hasattr(self, "requirements") or not self.requirements:
return

View File

@ -124,11 +124,7 @@ async def call(query: str = Body(...), meta: dict = Body(None)):
@chat.get("/agent")
async def get_agent():
agents = [{
"name": agent.name,
"description": agent.description,
"config_schema": agent.config_schema.to_dict()
} for agent in agent_manager.agents.values()]
agents = [agent.get_info() for agent in agent_manager.agents.values()]
return {"agents": agents}
@chat.post("/agent/{agent_name}")

View File

@ -3,6 +3,7 @@
<div class="chat">
<div class="chat-header">
<div class="header__left">
<slot name="header-left" class="nav-btn"></slot>
<div class="newchat nav-btn" @click="resetThread" :disabled="isProcessing">
<PlusCircleOutlined /> <span class="text">新对话</span>
</div>

View File

@ -1,37 +1,73 @@
<template>
<div class="agent-view">
<div class="sidebar">
<h2 class="sidebar-title">智能体列表</h2>
<div class="agent-list">
<div v-for="(agent, name) in agents"
:key="name"
class="agent-item"
:class="{ active: selectedAgentId === name }"
@click="selectAgent(name)">
<a-avatar :style="{ backgroundColor: getAgentColor(name) }">
<template #icon><RobotOutlined /></template>
</a-avatar>
<div class="agent-info">
<div class="agent-name">{{ agent.name }}</div>
<div class="agent-desc">{{ agent.short_description || '智能助手' }}</div>
<div class="sidebar" :class="{ 'is-open': state.isSidebarOpen }">
<h2 class="sidebar-title">
智能体列表
<div class="toggle-sidebar" @click="toggleSidebar">
<img src="@/assets/icons/sidebar_left.svg" class="iconfont icon-20" alt="设置" />
</div>
</h2>
<div class="agent-info">
<a-select
v-model:value="selectedAgentId"
class="agent-list"
style="width: 100%"
@change="selectAgent"
>
<a-select-option
v-for="(agent, name) in agents"
:key="name"
:value="name"
>
{{ agent.name }}
</a-select-option>
</a-select>
<p style="padding: 0 4px;">
{{ agents[selectedAgentId]?.description }}
</p>
<!-- 添加requirements显示部分 -->
<div v-if="agents[selectedAgentId]?.requirements && agents[selectedAgentId]?.requirements.length > 0" class="requirements-section">
<h3>所需环境变量:</h3>
<div class="requirements-list">
<a-tag v-for="req in agents[selectedAgentId].requirements" :key="req">
{{ req }}
</a-tag>
</div>
</div>
</div>
</div>
<div class="content">
<AgentChatComponent :agent-id="selectedAgentId" />
<AgentChatComponent :agent-id="selectedAgentId">
<template #header-left>
<div class="toggle-sidebar nav-btn" @click="toggleSidebar" v-if="!state.isSidebarOpen">
<img src="@/assets/icons/sidebar_left.svg" class="iconfont icon-20" alt="设置" />
</div>
</template>
</AgentChatComponent>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { RobotOutlined } from '@ant-design/icons-vue';
import { ref, onMounted, reactive, watch } from 'vue';
import { RobotOutlined, MenuFoldOutlined, MenuUnfoldOutlined, CloseOutlined } from '@ant-design/icons-vue';
import AgentChatComponent from '@/components/AgentChatComponent.vue';
//
const agents = ref({});
const selectedAgentId = ref(null);
const state = reactive({
isSidebarOpen: JSON.parse(localStorage.getItem('agent-sidebar-open') || 'true'),
});
// localStorage
watch(
() => state.isSidebarOpen,
(newValue) => {
localStorage.setItem('agent-sidebar-open', JSON.stringify(newValue));
}
);
//
const fetchAgents = async () => {
@ -53,6 +89,11 @@ const fetchAgents = async () => {
}
};
//
const toggleSidebar = () => {
state.isSidebarOpen = !state.isSidebarOpen;
};
//
const selectAgent = (agentId) => {
selectedAgentId.value = agentId;
@ -91,15 +132,22 @@ onMounted(async () => {
width: 100%;
height: 100vh;
overflow: hidden;
--agent-sidebar-width: 230px;
}
.sidebar {
width: 230px;
max-width: 230px;
width: 0;
max-width: var(--agent-sidebar-width);
border-right: 1px solid var(--main-light-3);
background-color: var(--bg-sider);
padding: 16px;
box-sizing: content-box;
overflow-y: auto;
transition: width 0.3s ease;
overflow: hidden;
&.is-open {
width: var(--agent-sidebar-width);
}
}
.sidebar-title {
@ -110,12 +158,50 @@ onMounted(async () => {
padding-bottom: 1rem;
font-size: 1rem;
border-bottom: 1px solid var(--main-light-3);
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px;
margin: 0;
}
.toggle-sidebar {
cursor: pointer;
&.nav-btn {
height: 2.5rem;
display: flex;
justify-content: center;
align-items: center;
border-radius: 8px;
color: var(--gray-900);
cursor: pointer;
font-size: 15px;
width: auto;
padding: 0.5rem 1rem;
transition: background-color 0.3s;
overflow: hidden;
.text {
margin-left: 10px;
}
&:hover {
background-color: var(--main-light-3);
}
.nav-btn-icon {
width: 1.5rem;
height: 1.5rem;
}
}
}
.agent-list {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 1rem;
}
.agent-item {
@ -136,8 +222,8 @@ onMounted(async () => {
}
.agent-info {
margin-left: 12px;
overflow: hidden;
padding: 16px;
min-width: calc(var(--agent-sidebar-width) - 16px);
}
.agent-name {
@ -159,6 +245,45 @@ onMounted(async () => {
flex: 1;
overflow: hidden;
}
// requirements
.requirements-section {
margin-top: 16px;
border-top: 1px solid var(--main-light-3);
padding-top: 12px;
h3 {
font-size: 14px;
margin-bottom: 8px;
font-weight: 500;
}
}
.requirements-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
.ant-tag {
user-select: all;
}
}
@media (max-width: 520px) {
.sidebar {
position: absolute;
z-index: 101;
width: 0;
height: 100%;
border-radius: 0 16px 16px 0;
box-shadow: 0 0 10px 1px rgba(0, 0, 0, 0.05);
&.is-open {
width: var(--agent-sidebar-width);
padding: 16px;
}
}
}
</style>