Просмотр исходного кода

🎨 refactor: migrate sidebar inline styles to CSS classes and improve code organization

This commit improves the codebase structure by:
- Moving all inline styles from SiderBar.js to CSS classes in index.css
- Organizing CSS with clear section comments for better maintainability
- Removing unused imports and components
- Improving sidebar design with cleaner styling and consistent color management
- Restructuring CSS to group related styles together
- Adjusting sidebar width from 200px to 180px
- Replacing Text components with semantic divs for group labels
- Creating a color management function for sidebar icons
Apple\Apple 9 месяцев назад
Родитель
Сommit
4b3791e6dc
4 измененных файлов с 511 добавлено и 379 удалено
  1. 1 1
      web/src/components/layout/PageLayout.js
  2. 176 235
      web/src/components/layout/SiderBar.js
  3. 74 0
      web/src/helpers/render.js
  4. 260 143
      web/src/index.css

+ 1 - 1
web/src/components/layout/PageLayout.js

@@ -124,7 +124,7 @@ const PageLayout = () => {
               : styleState.showSider
                 ? styleState.siderCollapsed
                   ? '60px'
-                  : '200px'
+                  : '180px'
                 : '0',
             transition: 'margin-left 0.3s ease',
             flex: '1 1 auto',

+ 176 - 235
web/src/components/layout/SiderBar.js

@@ -1,66 +1,21 @@
-import React, { useContext, useEffect, useMemo, useState } from 'react';
+import React, { useEffect, useMemo, useState } from 'react';
 import { Link, useLocation } from 'react-router-dom';
-import { StatusContext } from '../../context/Status/index.js';
 import { useTranslation } from 'react-i18next';
-
+import { getLucideIcon, sidebarIconColors } from '../../helpers/render.js';
+import { ChevronLeft } from 'lucide-react';
+import { useStyle, styleActions } from '../../context/Style/index.js';
 import {
   isAdmin,
   isRoot,
   showError
 } from '../../helpers/index.js';
 
-import {
-  IconCalendarClock,
-  IconChecklistStroked,
-  IconComment,
-  IconTerminal,
-  IconCreditCard,
-  IconGift,
-  IconHistogram,
-  IconImage,
-  IconKey,
-  IconLayers,
-  IconSetting,
-  IconUser,
-} from '@douyinfe/semi-icons';
 import {
   Nav,
   Divider,
+  Tooltip,
 } from '@douyinfe/semi-ui';
-import { useSetTheme, useTheme } from '../../context/Theme/index.js';
-import { useStyle, styleActions } from '../../context/Style/index.js';
-import Text from '@douyinfe/semi-ui/lib/es/typography/text';
-
-// 自定义侧边栏按钮样式
-const navItemStyle = {
-  borderRadius: '6px',
-  margin: '4px 8px',
-};
-
-// 自定义侧边栏按钮悬停样式
-const navItemHoverStyle = {
-  backgroundColor: 'var(--semi-color-primary-light-default)',
-  color: 'var(--semi-color-primary)',
-};
-
-// 自定义侧边栏按钮选中样式
-const navItemSelectedStyle = {
-  backgroundColor: 'var(--semi-color-primary-light-default)',
-  color: 'var(--semi-color-primary)',
-  fontWeight: '600',
-};
-
-// 自定义图标样式
-const iconStyle = (itemKey, selectedKeys) => {
-  return {
-    fontSize: '18px',
-    color: selectedKeys.includes(itemKey)
-      ? 'var(--semi-color-primary)'
-      : 'var(--semi-color-text-2)',
-  };
-};
 
-// Define routerMap as a constant outside the component
 const routerMap = {
   home: '/',
   channel: '/console/channel',
@@ -82,60 +37,20 @@ const routerMap = {
 const SiderBar = () => {
   const { t } = useTranslation();
   const { state: styleState, dispatch: styleDispatch } = useStyle();
-  const [statusState, statusDispatch] = useContext(StatusContext);
 
   const [selectedKeys, setSelectedKeys] = useState(['home']);
   const [isCollapsed, setIsCollapsed] = useState(styleState.siderCollapsed);
   const [chatItems, setChatItems] = useState([]);
   const [openedKeys, setOpenedKeys] = useState([]);
-  const theme = useTheme();
-  const setTheme = useSetTheme();
   const location = useLocation();
   const [routerMapState, setRouterMapState] = useState(routerMap);
 
-  // 预先计算所有可能的图标样式
-  const allItemKeys = useMemo(() => {
-    const keys = [
-      'home',
-      'channel',
-      'token',
-      'redemption',
-      'topup',
-      'user',
-      'log',
-      'midjourney',
-      'setting',
-      'about',
-      'chat',
-      'detail',
-      'pricing',
-      'task',
-      'playground',
-      'personal',
-    ];
-    // 添加聊天项的keys
-    for (let i = 0; i < chatItems.length; i++) {
-      keys.push('chat' + i);
-    }
-    return keys;
-  }, [chatItems]);
-
-  // 使用useMemo一次性计算所有图标样式
-  const iconStyles = useMemo(() => {
-    const styles = {};
-    allItemKeys.forEach((key) => {
-      styles[key] = iconStyle(key, selectedKeys);
-    });
-    return styles;
-  }, [allItemKeys, selectedKeys]);
-
   const workspaceItems = useMemo(
     () => [
       {
         text: t('数据看板'),
         itemKey: 'detail',
         to: '/detail',
-        icon: <IconCalendarClock />,
         className:
           localStorage.getItem('enable_data_export') === 'true'
             ? ''
@@ -145,19 +60,16 @@ const SiderBar = () => {
         text: t('API令牌'),
         itemKey: 'token',
         to: '/token',
-        icon: <IconKey />,
       },
       {
         text: t('使用日志'),
         itemKey: 'log',
         to: '/log',
-        icon: <IconHistogram />,
       },
       {
         text: t('绘图日志'),
         itemKey: 'midjourney',
         to: '/midjourney',
-        icon: <IconImage />,
         className:
           localStorage.getItem('enable_drawing') === 'true'
             ? ''
@@ -167,7 +79,6 @@ const SiderBar = () => {
         text: t('任务日志'),
         itemKey: 'task',
         to: '/task',
-        icon: <IconChecklistStroked />,
         className:
           localStorage.getItem('enable_task') === 'true' ? '' : 'tableHiddle',
       },
@@ -186,13 +97,11 @@ const SiderBar = () => {
         text: t('钱包'),
         itemKey: 'topup',
         to: '/topup',
-        icon: <IconCreditCard />,
       },
       {
         text: t('个人设置'),
         itemKey: 'personal',
         to: '/personal',
-        icon: <IconUser />,
       },
     ],
     [t],
@@ -204,28 +113,24 @@ const SiderBar = () => {
         text: t('渠道'),
         itemKey: 'channel',
         to: '/channel',
-        icon: <IconLayers />,
         className: isAdmin() ? '' : 'tableHiddle',
       },
       {
         text: t('兑换码'),
         itemKey: 'redemption',
         to: '/redemption',
-        icon: <IconGift />,
         className: isAdmin() ? '' : 'tableHiddle',
       },
       {
         text: t('用户管理'),
         itemKey: 'user',
         to: '/user',
-        icon: <IconUser />,
         className: isAdmin() ? '' : 'tableHiddle',
       },
       {
         text: t('系统设置'),
         itemKey: 'setting',
         to: '/setting',
-        icon: <IconSetting />,
         className: isRoot() ? '' : 'tableHiddle',
       },
     ],
@@ -238,19 +143,17 @@ const SiderBar = () => {
         text: t('操练场'),
         itemKey: 'playground',
         to: '/playground',
-        icon: <IconTerminal />,
       },
       {
         text: t('聊天'),
         itemKey: 'chat',
         items: chatItems,
-        icon: <IconComment />,
       },
     ],
     [chatItems, t],
   );
 
-  // Function to update router map with chat routes
+  // 更新路由映射,添加聊天路由
   const updateRouterMapWithChats = (chats) => {
     const newRouterMap = { ...routerMap };
 
@@ -264,7 +167,7 @@ const SiderBar = () => {
     return newRouterMap;
   };
 
-  // Update the useEffect for chat items
+  // 加载聊天项
   useEffect(() => {
     let chats = localStorage.getItem('chats');
     if (chats) {
@@ -282,8 +185,6 @@ const SiderBar = () => {
             chatItems.push(chat);
           }
           setChatItems(chatItems);
-
-          // Update router map with chat routes
           updateRouterMapWithChats(chats);
         }
       } catch (e) {
@@ -293,14 +194,14 @@ const SiderBar = () => {
     }
   }, []);
 
-  // Update the useEffect for route selection
+  // 根据当前路径设置选中的菜单项
   useEffect(() => {
     const currentPath = location.pathname;
     let matchingKey = Object.keys(routerMapState).find(
       (key) => routerMapState[key] === currentPath,
     );
 
-    // Handle chat routes
+    // 处理聊天路由
     if (!matchingKey && currentPath.startsWith('/console/chat/')) {
       const chatIndex = currentPath.split('/').pop();
       if (!isNaN(chatIndex)) {
@@ -310,54 +211,129 @@ const SiderBar = () => {
       }
     }
 
-    // If we found a matching key, update the selected keys
+    // 如果找到匹配的键,更新选中的键
     if (matchingKey) {
       setSelectedKeys([matchingKey]);
     }
   }, [location.pathname, routerMapState]);
 
+  // 同步折叠状态
   useEffect(() => {
     setIsCollapsed(styleState.siderCollapsed);
   }, [styleState.siderCollapsed]);
 
-  // Custom divider style
-  const dividerStyle = {
-    margin: '8px 0',
-    opacity: 0.6,
+  // 获取菜单项对应的颜色
+  const getItemColor = (itemKey) => {
+    switch (itemKey) {
+      case 'detail': return sidebarIconColors.dashboard;
+      case 'playground': return sidebarIconColors.terminal;
+      case 'chat': return sidebarIconColors.message;
+      case 'token': return sidebarIconColors.key;
+      case 'log': return sidebarIconColors.chart;
+      case 'midjourney': return sidebarIconColors.image;
+      case 'task': return sidebarIconColors.check;
+      case 'topup': return sidebarIconColors.credit;
+      case 'channel': return sidebarIconColors.layers;
+      case 'redemption': return sidebarIconColors.gift;
+      case 'user':
+      case 'personal': return sidebarIconColors.user;
+      case 'setting': return sidebarIconColors.settings;
+      default:
+        // 处理聊天项
+        if (itemKey && itemKey.startsWith('chat')) return sidebarIconColors.message;
+        return 'currentColor';
+    }
   };
 
-  // Custom group label style
-  const groupLabelStyle = {
-    padding: '8px 16px',
-    color: 'var(--semi-color-text-2)',
-    fontSize: '12px',
-    fontWeight: 'bold',
-    textTransform: 'uppercase',
-    letterSpacing: '0.5px',
+  // 渲染自定义菜单项
+  const renderNavItem = (item) => {
+    // 跳过隐藏的项目
+    if (item.className === 'tableHiddle') return null;
+
+    const isSelected = selectedKeys.includes(item.itemKey);
+    const textColor = isSelected ? getItemColor(item.itemKey) : 'inherit';
+
+    return (
+      <Nav.Item
+        key={item.itemKey}
+        itemKey={item.itemKey}
+        text={
+          <div className="flex items-center">
+            <span className="truncate font-medium text-sm" style={{ color: textColor }}>
+              {item.text}
+            </span>
+          </div>
+        }
+        icon={
+          <div className="sidebar-icon-container flex-shrink-0">
+            {getLucideIcon(item.itemKey, isSelected)}
+          </div>
+        }
+        className={item.className}
+      />
+    );
+  };
+
+  // 渲染子菜单项
+  const renderSubItem = (item) => {
+    if (item.items && item.items.length > 0) {
+      const isSelected = selectedKeys.includes(item.itemKey);
+      const textColor = isSelected ? getItemColor(item.itemKey) : 'inherit';
+
+      return (
+        <Nav.Sub
+          key={item.itemKey}
+          itemKey={item.itemKey}
+          text={
+            <div className="flex items-center">
+              <span className="truncate font-medium text-sm" style={{ color: textColor }}>
+                {item.text}
+              </span>
+            </div>
+          }
+          icon={
+            <div className="sidebar-icon-container flex-shrink-0">
+              {getLucideIcon(item.itemKey, isSelected)}
+            </div>
+          }
+        >
+          {item.items.map((subItem) => {
+            const isSubSelected = selectedKeys.includes(subItem.itemKey);
+            const subTextColor = isSubSelected ? getItemColor(subItem.itemKey) : 'inherit';
+
+            return (
+              <Nav.Item
+                key={subItem.itemKey}
+                itemKey={subItem.itemKey}
+                text={
+                  <span className="truncate font-medium text-sm" style={{ color: subTextColor }}>
+                    {subItem.text}
+                  </span>
+                }
+              />
+            );
+          })}
+        </Nav.Sub>
+      );
+    } else {
+      return renderNavItem(item);
+    }
   };
 
   return (
-    <>
+    <div
+      className="sidebar-container"
+      style={{ width: isCollapsed ? '60px' : '180px' }}
+    >
       <Nav
-        className='custom-sidebar-nav'
-        style={{
-          width: isCollapsed ? '60px' : '200px',
-          borderRight: '1px solid var(--semi-color-border)',
-          background: 'var(--semi-color-bg-0)',
-          borderRadius: styleState.isMobile ? '0' : '0 8px 8px 0',
-          position: 'relative',
-          zIndex: 95,
-          height: '100%',
-          overflowY: 'auto',
-          WebkitOverflowScrolling: 'touch', // Improve scrolling on iOS devices
-        }}
+        className="sidebar-nav custom-sidebar-nav"
         defaultIsCollapsed={styleState.siderCollapsed}
         isCollapsed={isCollapsed}
         onCollapseChange={(collapsed) => {
           setIsCollapsed(collapsed);
           styleDispatch(styleActions.setSiderCollapsed(collapsed));
 
-          // 确保在收起侧边栏时有选中的项目,避免不必要的计算
+          // 确保在收起侧边栏时有选中的项目
           if (selectedKeys.length === 0) {
             const currentPath = location.pathname;
             const matchingKey = Object.keys(routerMapState).find(
@@ -374,14 +350,19 @@ const SiderBar = () => {
           }
         }}
         selectedKeys={selectedKeys}
-        itemStyle={navItemStyle}
-        hoverStyle={navItemHoverStyle}
-        selectedStyle={navItemSelectedStyle}
-        renderWrapper={({ itemElement, isSubNav, isInSubNav, props }) => {
+        itemStyle="sidebar-nav-item"
+        hoverStyle="sidebar-nav-item:hover"
+        selectedStyle="sidebar-nav-item-selected"
+        renderWrapper={({ itemElement, props }) => {
+          const to = routerMapState[props.itemKey] || routerMap[props.itemKey];
+
+          // 如果没有路由,直接返回元素
+          if (!to) return itemElement;
+
           return (
             <Link
               style={{ textDecoration: 'none' }}
-              to={routerMapState[props.itemKey] || routerMap[props.itemKey]}
+              to={to}
             >
               {itemElement}
             </Link>
@@ -400,107 +381,67 @@ const SiderBar = () => {
           setOpenedKeys(data.openKeys);
         }}
       >
-        {/* Chat Section - Only show if there are chat items */}
-        {chatMenuItems.map((item) => {
-          if (item.items && item.items.length > 0) {
-            return (
-              <Nav.Sub
-                key={item.itemKey}
-                itemKey={item.itemKey}
-                text={item.text}
-                icon={React.cloneElement(item.icon, {
-                  style: iconStyles[item.itemKey],
-                })}
-              >
-                {item.items.map((subItem) => (
-                  <Nav.Item
-                    key={subItem.itemKey}
-                    itemKey={subItem.itemKey}
-                    text={subItem.text}
-                  />
-                ))}
-              </Nav.Sub>
-            );
-          } else {
-            return (
-              <Nav.Item
-                key={item.itemKey}
-                itemKey={item.itemKey}
-                text={item.text}
-                icon={React.cloneElement(item.icon, {
-                  style: iconStyles[item.itemKey],
-                })}
-              />
-            );
-          }
-        })}
-
-        {/* Divider */}
-        <Divider style={dividerStyle} />
-
-        {/* Workspace Section */}
-        {!isCollapsed && <Text style={groupLabelStyle}>{t('控制台')}</Text>}
-        {workspaceItems.map((item) => (
-          <Nav.Item
-            key={item.itemKey}
-            itemKey={item.itemKey}
-            text={item.text}
-            icon={React.cloneElement(item.icon, {
-              style: iconStyles[item.itemKey],
-            })}
-            className={item.className}
-          />
-        ))}
-
+        {/* 聊天区域 */}
+        <div className="sidebar-section">
+          {!isCollapsed && (
+            <div className="sidebar-group-label">{t('聊天')}</div>
+          )}
+          {chatMenuItems.map((item) => renderSubItem(item))}
+        </div>
+
+        {/* 控制台区域 */}
+        <Divider className="sidebar-divider" />
+        <div>
+          {!isCollapsed && (
+            <div className="sidebar-group-label">{t('控制台')}</div>
+          )}
+          {workspaceItems.map((item) => renderNavItem(item))}
+        </div>
+
+        {/* 管理员区域 - 只在管理员时显示 */}
         {isAdmin() && (
           <>
-            {/* Divider */}
-            <Divider style={dividerStyle} />
-
-            {/* Admin Section */}
-            {!isCollapsed && <Text style={groupLabelStyle}>{t('管理员')}</Text>}
-            {adminItems.map((item) => (
-              <Nav.Item
-                key={item.itemKey}
-                itemKey={item.itemKey}
-                text={item.text}
-                icon={React.cloneElement(item.icon, {
-                  style: iconStyles[item.itemKey],
-                })}
-                className={item.className}
-              />
-            ))}
+            <Divider className="sidebar-divider" />
+            <div>
+              {!isCollapsed && (
+                <div className="sidebar-group-label">{t('管理员')}</div>
+              )}
+              {adminItems.map((item) => renderNavItem(item))}
+            </div>
           </>
         )}
 
-        {/* Divider */}
-        <Divider style={dividerStyle} />
-
-        {/* Finance Management Section */}
-        {!isCollapsed && <Text style={groupLabelStyle}>{t('个人中心')}</Text>}
-        {financeItems.map((item) => (
-          <Nav.Item
-            key={item.itemKey}
-            itemKey={item.itemKey}
-            text={item.text}
-            icon={React.cloneElement(item.icon, {
-              style: iconStyles[item.itemKey],
-            })}
-            className={item.className}
-          />
-        ))}
-
-        <Nav.Footer
-          collapseButton={true}
-          collapseText={(collapsed) => {
-            if (collapsed) {
-              return t('展开侧边栏');
-            }
-            return t('收起侧边栏');
-          }}
-        />
+        {/* 个人中心区域 */}
+        <Divider className="sidebar-divider" />
+        <div>
+          {!isCollapsed && (
+            <div className="sidebar-group-label">{t('个人中心')}</div>
+          )}
+          {financeItems.map((item) => renderNavItem(item))}
+        </div>
       </Nav>
-    </>
+
+      {/* 底部折叠按钮 */}
+      <div
+        className="sidebar-collapse-button"
+        onClick={() => {
+          const newCollapsed = !isCollapsed;
+          setIsCollapsed(newCollapsed);
+          styleDispatch(styleActions.setSiderCollapsed(newCollapsed));
+        }}
+      >
+        <Tooltip content={isCollapsed ? t('展开侧边栏') : t('收起侧边栏')} position="right">
+          <div className="sidebar-collapse-button-inner">
+            <span
+              className="sidebar-collapse-icon-container"
+              style={{ transform: isCollapsed ? 'rotate(180deg)' : 'rotate(0deg)' }}
+            >
+              <ChevronLeft size={16} strokeWidth={2.5} color="var(--semi-color-text-2)" />
+            </span>
+          </div>
+        </Tooltip>
+      </div>
+    </div>
   );
 };
 

+ 74 - 0
web/src/helpers/render.js

@@ -26,6 +26,80 @@ import {
   Doubao,
 } from '@lobehub/icons';
 
+import {
+  LayoutDashboard,
+  TerminalSquare,
+  MessageSquare,
+  Key,
+  BarChart3,
+  Image as ImageIcon,
+  CheckSquare,
+  CreditCard,
+  Layers,
+  Gift,
+  User,
+  Settings,
+  CircleUser,
+} from 'lucide-react';
+
+// 侧边栏图标颜色映射
+export const sidebarIconColors = {
+  dashboard: "#4F46E5", // 紫蓝色
+  terminal: "#10B981", // 绿色
+  message: "#06B6D4", // 青色
+  key: "#3B82F6", // 蓝色
+  chart: "#8B5CF6", // 紫色
+  image: "#EC4899", // 粉色
+  check: "#F59E0B", // 琥珀色
+  credit: "#F97316", // 橙色
+  layers: "#EF4444", // 红色
+  gift: "#F43F5E", // 玫红色
+  user: "#6366F1", // 靛蓝色
+  settings: "#6B7280", // 灰色
+};
+
+// 获取侧边栏Lucide图标组件
+export function getLucideIcon(key, selected = false) {
+  const size = 16;
+  const strokeWidth = 2;
+  const commonProps = {
+    size,
+    strokeWidth,
+    className: `transition-colors duration-200 ${selected ? 'transition-transform duration-200 scale-105' : ''}`,
+  };
+
+  // 根据不同的key返回不同的图标
+  switch (key) {
+    case 'detail':
+      return <LayoutDashboard {...commonProps} color={selected ? sidebarIconColors.dashboard : 'currentColor'} />;
+    case 'playground':
+      return <TerminalSquare {...commonProps} color={selected ? sidebarIconColors.terminal : 'currentColor'} />;
+    case 'chat':
+      return <MessageSquare {...commonProps} color={selected ? sidebarIconColors.message : 'currentColor'} />;
+    case 'token':
+      return <Key {...commonProps} color={selected ? sidebarIconColors.key : 'currentColor'} />;
+    case 'log':
+      return <BarChart3 {...commonProps} color={selected ? sidebarIconColors.chart : 'currentColor'} />;
+    case 'midjourney':
+      return <ImageIcon {...commonProps} color={selected ? sidebarIconColors.image : 'currentColor'} />;
+    case 'task':
+      return <CheckSquare {...commonProps} color={selected ? sidebarIconColors.check : 'currentColor'} />;
+    case 'topup':
+      return <CreditCard {...commonProps} color={selected ? sidebarIconColors.credit : 'currentColor'} />;
+    case 'channel':
+      return <Layers {...commonProps} color={selected ? sidebarIconColors.layers : 'currentColor'} />;
+    case 'redemption':
+      return <Gift {...commonProps} color={selected ? sidebarIconColors.gift : 'currentColor'} />;
+    case 'user':
+    case 'personal':
+      return <User {...commonProps} color={selected ? sidebarIconColors.user : 'currentColor'} />;
+    case 'setting':
+      return <Settings {...commonProps} color={selected ? sidebarIconColors.settings : 'currentColor'} />;
+    default:
+      return <CircleUser {...commonProps} color={selected ? sidebarIconColors.user : 'currentColor'} />;
+  }
+}
+
 // 获取模型分类
 export const getModelCategories = (() => {
   let categoriesCache = null;

+ 260 - 143
web/src/index.css

@@ -1,3 +1,4 @@
+/* ==================== Tailwind CSS 配置 ==================== */
 @layer tailwind-base, semi, tailwind-components, tailwind-utils;
 
 @layer tailwind-base {
@@ -12,6 +13,7 @@
   @tailwind utilities;
 }
 
+/* ==================== 全局基础样式 ==================== */
 body {
   margin: 0;
   padding-top: 0;
@@ -25,6 +27,15 @@ body {
   height: 100vh;
 }
 
+body::-webkit-scrollbar {
+  display: none;
+}
+
+code {
+  font-family:
+    source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
+}
+
 #root {
   height: 100%;
   display: flex;
@@ -32,121 +43,184 @@ body {
   overflow: hidden;
 }
 
-#root>section>header>section>div>div>div>div.semi-navigation-header-list-outer>div.semi-navigation-list-wrapper>ul>div>a>li>span {
-  font-weight: 600 !important;
+/* ==================== 布局相关样式 ==================== */
+.semi-layout-content::-webkit-scrollbar,
+.semi-sider::-webkit-scrollbar {
+  width: 6px;
+  height: 6px;
 }
 
-@media only screen and (max-width: 767px) {
-
-  #root>section>header>section>div>div>div>div.semi-navigation-footer>div>a>li {
-    padding: 0 0;
-  }
+.semi-layout-content::-webkit-scrollbar-thumb,
+.semi-sider::-webkit-scrollbar-thumb {
+  background: var(--semi-color-tertiary-light-default);
+  border-radius: 3px;
+}
 
-  #root>section>header>section>div>div>div>div.semi-navigation-header-list-outer>div.semi-navigation-list-wrapper>ul>div>a>li {
-    padding: 0 5px;
-  }
+.semi-layout-content::-webkit-scrollbar-thumb:hover,
+.semi-sider::-webkit-scrollbar-thumb:hover {
+  background: var(--semi-color-tertiary);
+}
 
-  #root>section>header>section>div>div>div>div.semi-navigation-footer>div:nth-child(1)>a>li {
-    padding: 0 5px;
-  }
+.semi-layout-content::-webkit-scrollbar-track,
+.semi-sider::-webkit-scrollbar-track {
+  background: transparent;
+}
 
-  .semi-navigation-horizontal .semi-navigation-header {
-    margin-right: 0;
-  }
+/* ==================== 导航和侧边栏样式 ==================== */
+/* 导航项样式 */
+.semi-navigation-sub-title,
+.semi-chat-inputBox-sendButton,
+.semi-page-item,
+.semi-navigation-item,
+.semi-tag-closable,
+.semi-datepicker-range-input {
+  border-radius: 9999px !important;
+}
 
-  /* 确保移动端内容可滚动 */
-  .semi-layout-content {
-    -webkit-overflow-scrolling: touch !important;
-    overscroll-behavior-y: auto !important;
-  }
+.semi-navigation-item {
+  margin-bottom: 4px !important;
+}
 
-  /* 修复移动端下拉刷新 */
-  body {
-    overflow: visible !important;
-    overscroll-behavior-y: auto !important;
-    position: static !important;
-    height: 100% !important;
-  }
+.semi-navigation-item-icon {
+  justify-items: center;
+  align-items: center;
+}
 
-  /* 确保内容区域在移动端可以正常滚动 */
-  #root {
-    overflow: visible !important;
-    height: 100% !important;
-  }
+.semi-navigation-item-icon-info {
+  margin-right: 0;
+}
 
-  .semi-table-tbody,
-  .semi-table-row,
-  .semi-table-row-cell {
-    display: block !important;
-    width: auto !important;
-    padding: 2px !important;
-  }
+.semi-navigation-sub-title {
+  height: 100% !important;
+}
 
-  .semi-table-row-cell {
-    border-bottom: 0 !important;
-  }
+.semi-navigation-item-collapsed {
+  height: 44px !important;
+}
 
-  .semi-table-tbody>.semi-table-row {
-    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
-  }
+#root>section>header>section>div>div>div>div.semi-navigation-header-list-outer>div.semi-navigation-list-wrapper>ul>div>a>li>span {
+  font-weight: 600 !important;
 }
 
-.tableShow {
-  display: revert;
+/* 自定义侧边栏样式 */
+.sidebar-container {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  transition: width 0.3s ease;
 }
 
-.tableHiddle {
-  display: none !important;
+.sidebar-nav {
+  flex: 1;
+  width: 100%;
+  background: var(--semi-color-bg-0);
+  height: 100%;
+  overflow: hidden;
+  border-right: none;
+  overflow-y: auto;
+  scrollbar-width: none;
+  -ms-overflow-style: none;
 }
 
-body::-webkit-scrollbar {
+.sidebar-nav::-webkit-scrollbar {
   display: none;
 }
 
-code {
-  font-family:
-    source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
+/* 侧边栏导航项样式 */
+.sidebar-nav-item {
+  border-radius: 6px;
+  margin: 3px 8px;
+  transition: all 0.15s ease;
+  padding: 8px 12px;
 }
 
-.custom-footer {
-  font-size: 1.1em;
+.sidebar-nav-item:hover {
+  background-color: rgba(var(--semi-blue-0), 0.08);
+  color: var(--semi-color-primary);
 }
 
-.semi-layout-content::-webkit-scrollbar,
-.semi-sider::-webkit-scrollbar {
-  width: 6px;
-  height: 6px;
+.sidebar-nav-item-selected {
+  background-color: rgba(var(--semi-blue-0), 0.12);
+  color: var(--semi-color-primary);
+  font-weight: 500;
 }
 
-.semi-layout-content::-webkit-scrollbar-thumb,
-.semi-sider::-webkit-scrollbar-thumb {
-  background: var(--semi-color-tertiary-light-default);
-  border-radius: 3px;
+/* 图标容器样式 */
+.sidebar-icon-container {
+  width: 22px;
+  height: 22px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 10px;
+  transition: all 0.2s ease;
 }
 
-.semi-layout-content::-webkit-scrollbar-thumb:hover,
-.semi-sider::-webkit-scrollbar-thumb:hover {
-  background: var(--semi-color-tertiary);
+.sidebar-sub-icon-container {
+  width: 18px;
+  height: 18px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 10px;
+  margin-left: 1px;
+  transition: all 0.2s ease;
 }
 
-.semi-layout-content::-webkit-scrollbar-track,
-.semi-sider::-webkit-scrollbar-track {
-  background: transparent;
+/* 分割线样式 */
+.sidebar-divider {
+  margin: 4px 8px;
+  opacity: 0.15;
 }
 
-.semi-chat-inputBox-sendButton,
-.semi-page-item,
-.semi-navigation-item,
-.semi-tag-closable,
-.semi-datepicker-range-input {
+/* 分组标签样式 */
+.sidebar-group-label {
+  padding: 4px 15px 8px;
+  color: var(--semi-color-text-2);
+  font-size: 12px;
+  text-transform: uppercase;
+  letter-spacing: 0.5px;
+  opacity: 0.8;
+}
+
+/* 底部折叠按钮 */
+.sidebar-collapse-button {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding: 12px;
+  cursor: pointer;
+  background-color: var(--semi-color-bg-0);
+  position: sticky;
+  bottom: 0;
+  z-index: 10;
+  box-shadow: 0 -10px 10px -5px var(--semi-color-bg-0);
+  backdrop-filter: blur(4px);
+  border-top: 1px solid rgba(var(--semi-grey-0), 0.08);
+}
+
+.sidebar-collapse-button-inner {
+  width: 28px;
+  height: 28px;
   border-radius: 9999px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-color: var(--semi-color-fill-0);
+  transition: all 0.2s ease;
 }
 
-.semi-tabs-content {
-  padding: 0 !important;
+.sidebar-collapse-icon-container {
+  display: inline-block;
+  transition: transform 0.3s ease;
 }
 
-/* 聊天 */
+/* 侧边栏区域容器 */
+.sidebar-section {
+  padding-top: 12px;
+}
+
+/* ==================== 聊天界面样式 ==================== */
 .semi-chat {
   padding-top: 0 !important;
   padding-bottom: 0 !important;
@@ -183,13 +257,26 @@ code {
   overflow-x: hidden !important;
 }
 
+.semi-chat-container {
+  overflow-x: hidden !important;
+}
+
+.semi-chat-chatBox-action {
+  column-gap: 0 !important;
+}
+
+.semi-chat-inputBox-clearButton.semi-button .semi-icon {
+  font-size: 20px !important;
+}
+
 /* 隐藏所有聊天相关区域的滚动条 */
 .semi-chat::-webkit-scrollbar,
 .semi-chat-chatBox::-webkit-scrollbar,
 .semi-chat-chatBox-wrap::-webkit-scrollbar,
 .semi-chat-chatBox-content::-webkit-scrollbar,
 .semi-chat-content::-webkit-scrollbar,
-.semi-chat-list::-webkit-scrollbar {
+.semi-chat-list::-webkit-scrollbar,
+.semi-chat-container::-webkit-scrollbar {
   display: none;
 }
 
@@ -198,40 +285,72 @@ code {
 .semi-chat-chatBox-wrap,
 .semi-chat-chatBox-content,
 .semi-chat-content,
-.semi-chat-list {
+.semi-chat-list,
+.semi-chat-container {
   -ms-overflow-style: none;
   scrollbar-width: none;
 }
 
-.semi-chat-container {
-  overflow-x: hidden !important;
+/* ==================== 组件特定样式 ==================== */
+/* Tabs组件样式 */
+.semi-tabs-content {
+  padding: 0 !important;
+  height: calc(100% - 40px) !important;
+  flex: 1 !important;
 }
 
-.semi-chat-container::-webkit-scrollbar {
-  display: none;
+.semi-tabs-content .semi-tabs-pane {
+  height: 100% !important;
+  overflow: hidden !important;
 }
 
-.semi-chat-container {
-  -ms-overflow-style: none;
-  scrollbar-width: none;
+.semi-tabs-content .semi-tabs-pane>div {
+  height: 100% !important;
 }
 
-/* 隐藏模型设置区域的滚动条 */
-.model-settings-scroll::-webkit-scrollbar {
-  display: none;
+/* 表格样式 */
+.tableShow {
+  display: revert;
 }
 
-.model-settings-scroll {
-  -ms-overflow-style: none;
-  scrollbar-width: none;
+.tableHiddle {
+  display: none !important;
+}
+
+/* 页脚样式 */
+.custom-footer {
+  font-size: 1.1em;
+}
+
+/* ==================== 调试面板特定样式 ==================== */
+.debug-panel .semi-tabs {
+  height: 100% !important;
+  display: flex !important;
+  flex-direction: column !important;
+}
+
+.debug-panel .semi-tabs-bar {
+  flex-shrink: 0 !important;
 }
 
-/* 思考内容区域滚动条样式 */
-.thinking-content-scroll::-webkit-scrollbar {
+.debug-panel .semi-tabs-content {
+  flex: 1 !important;
+  overflow: hidden !important;
+}
+
+/* ==================== 滚动条样式统一管理 ==================== */
+/* 隐藏模型设置区域的滚动条 */
+.model-settings-scroll::-webkit-scrollbar,
+.thinking-content-scroll::-webkit-scrollbar,
+.custom-request-textarea .semi-input::-webkit-scrollbar,
+.custom-request-textarea textarea::-webkit-scrollbar {
   display: none;
 }
 
-.thinking-content-scroll {
+.model-settings-scroll,
+.thinking-content-scroll,
+.custom-request-textarea .semi-input,
+.custom-request-textarea textarea {
   -ms-overflow-style: none;
   scrollbar-width: none;
 }
@@ -255,60 +374,58 @@ code {
   background: transparent;
 }
 
-/* 隐藏请求体 JSON TextArea 的滚动条 */
-.custom-request-textarea .semi-input::-webkit-scrollbar {
-  display: none;
-}
-
-.custom-request-textarea .semi-input {
-  -ms-overflow-style: none;
-  scrollbar-width: none;
-}
-
-.custom-request-textarea textarea::-webkit-scrollbar {
-  display: none;
-}
+/* ==================== 响应式/移动端样式 ==================== */
+@media only screen and (max-width: 767px) {
+  #root>section>header>section>div>div>div>div.semi-navigation-footer>div>a>li {
+    padding: 0 0;
+  }
 
-.custom-request-textarea textarea {
-  -ms-overflow-style: none;
-  scrollbar-width: none;
-}
+  #root>section>header>section>div>div>div>div.semi-navigation-header-list-outer>div.semi-navigation-list-wrapper>ul>div>a>li {
+    padding: 0 5px;
+  }
 
-/* 调试面板标签样式 */
-.semi-tabs-content {
-  height: calc(100% - 40px) !important;
-  flex: 1 !important;
-}
+  #root>section>header>section>div>div>div>div.semi-navigation-footer>div:nth-child(1)>a>li {
+    padding: 0 5px;
+  }
 
-.semi-tabs-content .semi-tabs-pane {
-  height: 100% !important;
-  overflow: hidden !important;
-}
+  .semi-navigation-horizontal .semi-navigation-header {
+    margin-right: 0;
+  }
 
-.semi-tabs-content .semi-tabs-pane>div {
-  height: 100% !important;
-}
+  /* 确保移动端内容可滚动 */
+  .semi-layout-content {
+    -webkit-overflow-scrolling: touch !important;
+    overscroll-behavior-y: auto !important;
+  }
 
-/* 调试面板特定样式 */
-.debug-panel .semi-tabs {
-  height: 100% !important;
-  display: flex !important;
-  flex-direction: column !important;
-}
+  /* 修复移动端下拉刷新 */
+  body {
+    overflow: visible !important;
+    overscroll-behavior-y: auto !important;
+    position: static !important;
+    height: 100% !important;
+  }
 
-.debug-panel .semi-tabs-bar {
-  flex-shrink: 0 !important;
-}
+  /* 确保内容区域在移动端可以正常滚动 */
+  #root {
+    overflow: visible !important;
+    height: 100% !important;
+  }
 
-.debug-panel .semi-tabs-content {
-  flex: 1 !important;
-  overflow: hidden !important;
-}
+  /* 移动端表格样式调整 */
+  .semi-table-tbody,
+  .semi-table-row,
+  .semi-table-row-cell {
+    display: block !important;
+    width: auto !important;
+    padding: 2px !important;
+  }
 
-.semi-chat-chatBox-action {
-  column-gap: 0 !important;
-}
+  .semi-table-row-cell {
+    border-bottom: 0 !important;
+  }
 
-.semi-chat-inputBox-clearButton.semi-button .semi-icon {
-  font-size: 20px !important;
+  .semi-table-tbody>.semi-table-row {
+    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+  }
 }