feat(ui): 新增暗色模式支持并调整智能体选择器位置

将智能体选择器从侧边栏移动到主聊天界面头部
调整相关样式以匹配新的布局
This commit is contained in:
Wenjie Zhang 2025-11-23 19:42:51 +08:00
parent 0ff771dc19
commit fb3b864565
5 changed files with 46 additions and 59 deletions

View File

@ -11,7 +11,7 @@
- 开发与生产环境隔离,构建生产镜像 <Badge type="info" text="0.4" /> - 开发与生产环境隔离,构建生产镜像 <Badge type="info" text="0.4" />
- 集成 LangFuse (观望) 添加用户日志与用户反馈模块,可以在 AgentView 中查看信息 - 集成 LangFuse (观望) 添加用户日志与用户反馈模块,可以在 AgentView 中查看信息
- 集成 neo4j mcp (或者自己构建工具) - 集成 neo4j mcp (或者自己构建工具)
- 文档解析部分的 markdown 中的图片替换为内部可访问的链接 - 文档解析部分的 markdown 中的图片替换为内部可访问的链接 (2/4)
### Bugs ### Bugs
- 部分异常状态下,智能体的模型名称出现重叠[#279](https://github.com/xerrors/Yuxi-Know/issues/279) - 部分异常状态下,智能体的模型名称出现重叠[#279](https://github.com/xerrors/Yuxi-Know/issues/279)
@ -28,6 +28,7 @@
- 新增基于知识库文件生成示例问题功能([#335](https://github.com/xerrors/Yuxi-Know/pull/335#issuecomment-3530976425) - 新增基于知识库文件生成示例问题功能([#335](https://github.com/xerrors/Yuxi-Know/pull/335#issuecomment-3530976425)
- 新增知识库支持文件夹/压缩包上传的功能([#335](https://github.com/xerrors/Yuxi-Know/pull/335#issuecomment-3530976425) - 新增知识库支持文件夹/压缩包上传的功能([#335](https://github.com/xerrors/Yuxi-Know/pull/335#issuecomment-3530976425)
- 新增自定义模型支持、新增 dashscope rerank/embeddings 模型的支持 - 新增自定义模型支持、新增 dashscope rerank/embeddings 模型的支持
- 新增暗色模式支持并调整整体 UI[#343](https://github.com/xerrors/Yuxi-Know/pull/343)
### 修复 ### 修复
- 修复重排序模型实际未生效的问题 - 修复重排序模型实际未生效的问题

View File

@ -30,7 +30,7 @@
<div type="button" class="agent-nav-btn" v-if="!chatUIStore.isSidebarOpen" @click="toggleSidebar"> <div type="button" class="agent-nav-btn" v-if="!chatUIStore.isSidebarOpen" @click="toggleSidebar">
<PanelLeftOpen class="nav-btn-icon" size="18"/> <PanelLeftOpen class="nav-btn-icon" size="18"/>
</div> </div>
<div <div
type="button" type="button"
class="agent-nav-btn" class="agent-nav-btn"
v-if="!chatUIStore.isSidebarOpen" v-if="!chatUIStore.isSidebarOpen"
@ -41,6 +41,15 @@
<MessageCirclePlus v-else class="nav-btn-icon" size="18"/> <MessageCirclePlus v-else class="nav-btn-icon" size="18"/>
<span class="text" :class="{'hide-text': isMediumContainer}">新对话</span> <span class="text" :class="{'hide-text': isMediumContainer}">新对话</span>
</div> </div>
<div
v-if="!props.singleMode"
class="agent-nav-btn"
@click="openAgentModal"
>
<span class="text">{{ currentAgentName || '选择智能体' }}</span>
<ChevronDown size="16" class="switch-icon" />
</div>
</div> </div>
<div class="header__right"> <div class="header__right">
<!-- AgentState 显示按钮 - 只在智能体支持 todo files 能力时显示 --> <!-- AgentState 显示按钮 - 只在智能体支持 todo files 能力时显示 -->
@ -234,7 +243,7 @@ import AgentMessageComponent from '@/components/AgentMessageComponent.vue'
import ImagePreviewComponent from '@/components/ImagePreviewComponent.vue' import ImagePreviewComponent from '@/components/ImagePreviewComponent.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 { PanelLeftOpen, MessageCirclePlus, LoaderCircle, Layers } from 'lucide-vue-next'; import { PanelLeftOpen, MessageCirclePlus, LoaderCircle, Layers, ChevronDown } from 'lucide-vue-next';
import { handleChatError, handleValidationError } from '@/utils/errorHandler'; import { handleChatError, handleValidationError } from '@/utils/errorHandler';
import { ScrollController } from '@/utils/scrollController'; import { ScrollController } from '@/utils/scrollController';
import { AgentValidator } from '@/utils/agentValidator'; import { AgentValidator } from '@/utils/agentValidator';
@ -1432,10 +1441,19 @@ watch(conversations, () => {
align-items: center; align-items: center;
padding: 1rem 8px; padding: 1rem 8px;
.header__left, .header__right, .header__center { .header__left, .header__right {
display: flex; display: flex;
align-items: center; align-items: center;
} }
.switch-icon {
color: var(--gray-500);
transition: all 0.2s ease;
}
.agent-nav-btn:hover .switch-icon {
color: var(--main-500);
}
} }
} }

View File

@ -22,7 +22,7 @@
<p class="agent-description">{{ selectedAgent.description }}</p> <p class="agent-description">{{ selectedAgent.description }}</p>
</div> </div>
<a-divider /> <!-- <a-divider /> -->
<div v-if="selectedAgentId && configurableItems" class="config-form-content"> <div v-if="selectedAgentId && configurableItems" class="config-form-content">
<!-- 配置表单 --> <!-- 配置表单 -->
@ -357,7 +357,8 @@ const closeSidebar = () => {
const getConfigLabel = (key, value) => { const getConfigLabel = (key, value) => {
// console.log(configurableItems) // console.log(configurableItems)
if (value.description && value.name !== key) { if (value.description && value.name !== key) {
return `${value.name}${key}`; return `${value.name}`;
// return `${value.name}${key}`;
} }
return key; return key;
}; };
@ -695,7 +696,14 @@ watch(() => props.isOpen, (newVal) => {
} }
.config-item { .config-item {
margin: 30px 0; background-color: var(--gray-50);
padding: 12px;
border-radius: 8px;
box-shadow: 0px 1px 1px var(--shadow-2);
:deep(label.form_item_model) {
font-weight: 600;
}
.config-description { .config-description {
margin: 4px 0 8px 0; margin: 4px 0 8px 0;

View File

@ -2,15 +2,7 @@
<div class="chat-sidebar" :class="{'sidebar-open': chatUIStore.isSidebarOpen, 'no-transition': isInitialRender}"> <div class="chat-sidebar" :class="{'sidebar-open': chatUIStore.isSidebarOpen, 'no-transition': isInitialRender}">
<div class="sidebar-content"> <div class="sidebar-content">
<div class="sidebar-header"> <div class="sidebar-header">
<div class="header-title" v-if="singleMode">{{ selectedAgentName }}</div> <div class="header-title">{{ branding.name }}</div>
<div
v-else
class="agent-selector"
@click="openAgentModal"
>
<span class="agent-name">{{ selectedAgentName || '选择智能体' }}</span>
<ChevronDown size="16" class="switch-icon" />
</div>
<div class="header-actions"> <div class="header-actions">
<div class="toggle-sidebar nav-btn" v-if="chatUIStore.isSidebarOpen" @click="toggleCollapse"> <div class="toggle-sidebar nav-btn" v-if="chatUIStore.isSidebarOpen" @click="toggleCollapse">
<PanelLeftClose size="20" color="var(--gray-800)"/> <PanelLeftClose size="20" color="var(--gray-800)"/>
@ -73,12 +65,17 @@ import {
MoreOutlined MoreOutlined
} from '@ant-design/icons-vue'; } from '@ant-design/icons-vue';
import { message, Modal } from 'ant-design-vue'; import { message, Modal } from 'ant-design-vue';
import { PanelLeftClose, MessageSquarePlus, ChevronDown, LoaderCircle } from 'lucide-vue-next'; import { PanelLeftClose, MessageSquarePlus, LoaderCircle } from 'lucide-vue-next';
import dayjs, { parseToShanghai } from '@/utils/time'; import dayjs, { parseToShanghai } from '@/utils/time';
import { useChatUIStore } from '@/stores/chatUI'; import { useChatUIStore } from '@/stores/chatUI';
import { useInfoStore } from '@/stores/info';
import { storeToRefs } from 'pinia';
// 使 chatUI store // 使 chatUI store
const chatUIStore = useChatUIStore(); const chatUIStore = useChatUIStore();
const infoStore = useInfoStore();
const { branding } = storeToRefs(infoStore);
const props = defineProps({ const props = defineProps({
currentAgentId: { currentAgentId: {
@ -113,13 +110,7 @@ const props = defineProps({
const emit = defineEmits(['create-chat', 'select-chat', 'delete-chat', 'rename-chat', 'toggle-sidebar', 'open-agent-modal']); const emit = defineEmits(['create-chat', 'select-chat', 'delete-chat', 'rename-chat', 'toggle-sidebar', 'open-agent-modal']);
const selectedAgentName = computed(() => {
if (props.selectedAgentId && props.agents && props.agents.length > 0) {
const agent = props.agents.find(a => a.id === props.selectedAgentId);
return agent ? agent.name : '';
}
return '';
});
const groupedChats = computed(() => { const groupedChats = computed(() => {
const groups = { const groups = {
@ -221,16 +212,14 @@ const toggleCollapse = () => {
emit('toggle-sidebar'); emit('toggle-sidebar');
}; };
const openAgentModal = () => {
emit('open-agent-modal');
};
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.chat-sidebar { .chat-sidebar {
width: 0; width: 0;
height: 100%; height: 100%;
background-color: var(--gray-25); background-color: var(--gray-0);
transition: all 0.3s ease; transition: all 0.3s ease;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -280,7 +269,7 @@ const openAgentModal = () => {
flex-shrink: 0; flex-shrink: 0;
.header-title { .header-title {
font-weight: 500; font-weight: 600;
font-size: 16px; font-size: 16px;
color: var(--gray-900); color: var(--gray-900);
white-space: nowrap; white-space: nowrap;
@ -479,34 +468,5 @@ const openAgentModal = () => {
} }
} }
//
.agent-selector {
cursor: pointer;
font-size: 15px;
color: var(--gray-900);
transition: color 0.2s ease;
font-weight: 500;
display: flex;
align-items: center;
gap: 6px;
.agent-name {
transition: color 0.2s ease;
}
.switch-icon {
color: var(--gray-500);
transition: all 0.2s ease;
}
&:hover {
.agent-name {
color: var(--main-500);
}
.switch-icon {
color: var(--main-500);
transform: translateY(1px);
}
}
}
</style> </style>

View File

@ -202,7 +202,7 @@ const mainList = [{
</RouterLink> </RouterLink>
</div> </div>
<div class="header-mobile"> <div class="header-mobile">
<RouterLink to="/chat" class="nav-item" active-class="active">对话</RouterLink> <RouterLink to="/agent" class="nav-item" active-class="active">对话</RouterLink>
<RouterLink to="/database" class="nav-item" active-class="active">知识</RouterLink> <RouterLink to="/database" class="nav-item" active-class="active">知识</RouterLink>
<RouterLink to="/setting" class="nav-item" active-class="active">设置</RouterLink> <RouterLink to="/setting" class="nav-item" active-class="active">设置</RouterLink>
</div> </div>