🎨 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
This commit is contained in:
@@ -124,7 +124,7 @@ const PageLayout = () => {
|
|||||||
: styleState.showSider
|
: styleState.showSider
|
||||||
? styleState.siderCollapsed
|
? styleState.siderCollapsed
|
||||||
? '60px'
|
? '60px'
|
||||||
: '200px'
|
: '180px'
|
||||||
: '0',
|
: '0',
|
||||||
transition: 'margin-left 0.3s ease',
|
transition: 'margin-left 0.3s ease',
|
||||||
flex: '1 1 auto',
|
flex: '1 1 auto',
|
||||||
|
|||||||
@@ -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 { Link, useLocation } from 'react-router-dom';
|
||||||
import { StatusContext } from '../../context/Status/index.js';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
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 {
|
import {
|
||||||
isAdmin,
|
isAdmin,
|
||||||
isRoot,
|
isRoot,
|
||||||
showError
|
showError
|
||||||
} from '../../helpers/index.js';
|
} from '../../helpers/index.js';
|
||||||
|
|
||||||
import {
|
|
||||||
IconCalendarClock,
|
|
||||||
IconChecklistStroked,
|
|
||||||
IconComment,
|
|
||||||
IconTerminal,
|
|
||||||
IconCreditCard,
|
|
||||||
IconGift,
|
|
||||||
IconHistogram,
|
|
||||||
IconImage,
|
|
||||||
IconKey,
|
|
||||||
IconLayers,
|
|
||||||
IconSetting,
|
|
||||||
IconUser,
|
|
||||||
} from '@douyinfe/semi-icons';
|
|
||||||
import {
|
import {
|
||||||
Nav,
|
Nav,
|
||||||
Divider,
|
Divider,
|
||||||
|
Tooltip,
|
||||||
} from '@douyinfe/semi-ui';
|
} 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 = {
|
const routerMap = {
|
||||||
home: '/',
|
home: '/',
|
||||||
channel: '/console/channel',
|
channel: '/console/channel',
|
||||||
@@ -82,60 +37,20 @@ const routerMap = {
|
|||||||
const SiderBar = () => {
|
const SiderBar = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { state: styleState, dispatch: styleDispatch } = useStyle();
|
const { state: styleState, dispatch: styleDispatch } = useStyle();
|
||||||
const [statusState, statusDispatch] = useContext(StatusContext);
|
|
||||||
|
|
||||||
const [selectedKeys, setSelectedKeys] = useState(['home']);
|
const [selectedKeys, setSelectedKeys] = useState(['home']);
|
||||||
const [isCollapsed, setIsCollapsed] = useState(styleState.siderCollapsed);
|
const [isCollapsed, setIsCollapsed] = useState(styleState.siderCollapsed);
|
||||||
const [chatItems, setChatItems] = useState([]);
|
const [chatItems, setChatItems] = useState([]);
|
||||||
const [openedKeys, setOpenedKeys] = useState([]);
|
const [openedKeys, setOpenedKeys] = useState([]);
|
||||||
const theme = useTheme();
|
|
||||||
const setTheme = useSetTheme();
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const [routerMapState, setRouterMapState] = useState(routerMap);
|
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(
|
const workspaceItems = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
text: t('数据看板'),
|
text: t('数据看板'),
|
||||||
itemKey: 'detail',
|
itemKey: 'detail',
|
||||||
to: '/detail',
|
to: '/detail',
|
||||||
icon: <IconCalendarClock />,
|
|
||||||
className:
|
className:
|
||||||
localStorage.getItem('enable_data_export') === 'true'
|
localStorage.getItem('enable_data_export') === 'true'
|
||||||
? ''
|
? ''
|
||||||
@@ -145,19 +60,16 @@ const SiderBar = () => {
|
|||||||
text: t('API令牌'),
|
text: t('API令牌'),
|
||||||
itemKey: 'token',
|
itemKey: 'token',
|
||||||
to: '/token',
|
to: '/token',
|
||||||
icon: <IconKey />,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: t('使用日志'),
|
text: t('使用日志'),
|
||||||
itemKey: 'log',
|
itemKey: 'log',
|
||||||
to: '/log',
|
to: '/log',
|
||||||
icon: <IconHistogram />,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: t('绘图日志'),
|
text: t('绘图日志'),
|
||||||
itemKey: 'midjourney',
|
itemKey: 'midjourney',
|
||||||
to: '/midjourney',
|
to: '/midjourney',
|
||||||
icon: <IconImage />,
|
|
||||||
className:
|
className:
|
||||||
localStorage.getItem('enable_drawing') === 'true'
|
localStorage.getItem('enable_drawing') === 'true'
|
||||||
? ''
|
? ''
|
||||||
@@ -167,7 +79,6 @@ const SiderBar = () => {
|
|||||||
text: t('任务日志'),
|
text: t('任务日志'),
|
||||||
itemKey: 'task',
|
itemKey: 'task',
|
||||||
to: '/task',
|
to: '/task',
|
||||||
icon: <IconChecklistStroked />,
|
|
||||||
className:
|
className:
|
||||||
localStorage.getItem('enable_task') === 'true' ? '' : 'tableHiddle',
|
localStorage.getItem('enable_task') === 'true' ? '' : 'tableHiddle',
|
||||||
},
|
},
|
||||||
@@ -186,13 +97,11 @@ const SiderBar = () => {
|
|||||||
text: t('钱包'),
|
text: t('钱包'),
|
||||||
itemKey: 'topup',
|
itemKey: 'topup',
|
||||||
to: '/topup',
|
to: '/topup',
|
||||||
icon: <IconCreditCard />,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: t('个人设置'),
|
text: t('个人设置'),
|
||||||
itemKey: 'personal',
|
itemKey: 'personal',
|
||||||
to: '/personal',
|
to: '/personal',
|
||||||
icon: <IconUser />,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[t],
|
[t],
|
||||||
@@ -204,28 +113,24 @@ const SiderBar = () => {
|
|||||||
text: t('渠道'),
|
text: t('渠道'),
|
||||||
itemKey: 'channel',
|
itemKey: 'channel',
|
||||||
to: '/channel',
|
to: '/channel',
|
||||||
icon: <IconLayers />,
|
|
||||||
className: isAdmin() ? '' : 'tableHiddle',
|
className: isAdmin() ? '' : 'tableHiddle',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: t('兑换码'),
|
text: t('兑换码'),
|
||||||
itemKey: 'redemption',
|
itemKey: 'redemption',
|
||||||
to: '/redemption',
|
to: '/redemption',
|
||||||
icon: <IconGift />,
|
|
||||||
className: isAdmin() ? '' : 'tableHiddle',
|
className: isAdmin() ? '' : 'tableHiddle',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: t('用户管理'),
|
text: t('用户管理'),
|
||||||
itemKey: 'user',
|
itemKey: 'user',
|
||||||
to: '/user',
|
to: '/user',
|
||||||
icon: <IconUser />,
|
|
||||||
className: isAdmin() ? '' : 'tableHiddle',
|
className: isAdmin() ? '' : 'tableHiddle',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: t('系统设置'),
|
text: t('系统设置'),
|
||||||
itemKey: 'setting',
|
itemKey: 'setting',
|
||||||
to: '/setting',
|
to: '/setting',
|
||||||
icon: <IconSetting />,
|
|
||||||
className: isRoot() ? '' : 'tableHiddle',
|
className: isRoot() ? '' : 'tableHiddle',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -238,19 +143,17 @@ const SiderBar = () => {
|
|||||||
text: t('操练场'),
|
text: t('操练场'),
|
||||||
itemKey: 'playground',
|
itemKey: 'playground',
|
||||||
to: '/playground',
|
to: '/playground',
|
||||||
icon: <IconTerminal />,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: t('聊天'),
|
text: t('聊天'),
|
||||||
itemKey: 'chat',
|
itemKey: 'chat',
|
||||||
items: chatItems,
|
items: chatItems,
|
||||||
icon: <IconComment />,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[chatItems, t],
|
[chatItems, t],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Function to update router map with chat routes
|
// 更新路由映射,添加聊天路由
|
||||||
const updateRouterMapWithChats = (chats) => {
|
const updateRouterMapWithChats = (chats) => {
|
||||||
const newRouterMap = { ...routerMap };
|
const newRouterMap = { ...routerMap };
|
||||||
|
|
||||||
@@ -264,7 +167,7 @@ const SiderBar = () => {
|
|||||||
return newRouterMap;
|
return newRouterMap;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update the useEffect for chat items
|
// 加载聊天项
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let chats = localStorage.getItem('chats');
|
let chats = localStorage.getItem('chats');
|
||||||
if (chats) {
|
if (chats) {
|
||||||
@@ -282,8 +185,6 @@ const SiderBar = () => {
|
|||||||
chatItems.push(chat);
|
chatItems.push(chat);
|
||||||
}
|
}
|
||||||
setChatItems(chatItems);
|
setChatItems(chatItems);
|
||||||
|
|
||||||
// Update router map with chat routes
|
|
||||||
updateRouterMapWithChats(chats);
|
updateRouterMapWithChats(chats);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -293,14 +194,14 @@ const SiderBar = () => {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Update the useEffect for route selection
|
// 根据当前路径设置选中的菜单项
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const currentPath = location.pathname;
|
const currentPath = location.pathname;
|
||||||
let matchingKey = Object.keys(routerMapState).find(
|
let matchingKey = Object.keys(routerMapState).find(
|
||||||
(key) => routerMapState[key] === currentPath,
|
(key) => routerMapState[key] === currentPath,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle chat routes
|
// 处理聊天路由
|
||||||
if (!matchingKey && currentPath.startsWith('/console/chat/')) {
|
if (!matchingKey && currentPath.startsWith('/console/chat/')) {
|
||||||
const chatIndex = currentPath.split('/').pop();
|
const chatIndex = currentPath.split('/').pop();
|
||||||
if (!isNaN(chatIndex)) {
|
if (!isNaN(chatIndex)) {
|
||||||
@@ -310,54 +211,129 @@ const SiderBar = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we found a matching key, update the selected keys
|
// 如果找到匹配的键,更新选中的键
|
||||||
if (matchingKey) {
|
if (matchingKey) {
|
||||||
setSelectedKeys([matchingKey]);
|
setSelectedKeys([matchingKey]);
|
||||||
}
|
}
|
||||||
}, [location.pathname, routerMapState]);
|
}, [location.pathname, routerMapState]);
|
||||||
|
|
||||||
|
// 同步折叠状态
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsCollapsed(styleState.siderCollapsed);
|
setIsCollapsed(styleState.siderCollapsed);
|
||||||
}, [styleState.siderCollapsed]);
|
}, [styleState.siderCollapsed]);
|
||||||
|
|
||||||
// Custom divider style
|
// 获取菜单项对应的颜色
|
||||||
const dividerStyle = {
|
const getItemColor = (itemKey) => {
|
||||||
margin: '8px 0',
|
switch (itemKey) {
|
||||||
opacity: 0.6,
|
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 = {
|
const renderNavItem = (item) => {
|
||||||
padding: '8px 16px',
|
// 跳过隐藏的项目
|
||||||
color: 'var(--semi-color-text-2)',
|
if (item.className === 'tableHiddle') return null;
|
||||||
fontSize: '12px',
|
|
||||||
fontWeight: 'bold',
|
const isSelected = selectedKeys.includes(item.itemKey);
|
||||||
textTransform: 'uppercase',
|
const textColor = isSelected ? getItemColor(item.itemKey) : 'inherit';
|
||||||
letterSpacing: '0.5px',
|
|
||||||
|
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 (
|
return (
|
||||||
<>
|
<div
|
||||||
|
className="sidebar-container"
|
||||||
|
style={{ width: isCollapsed ? '60px' : '180px' }}
|
||||||
|
>
|
||||||
<Nav
|
<Nav
|
||||||
className='custom-sidebar-nav'
|
className="sidebar-nav 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
|
|
||||||
}}
|
|
||||||
defaultIsCollapsed={styleState.siderCollapsed}
|
defaultIsCollapsed={styleState.siderCollapsed}
|
||||||
isCollapsed={isCollapsed}
|
isCollapsed={isCollapsed}
|
||||||
onCollapseChange={(collapsed) => {
|
onCollapseChange={(collapsed) => {
|
||||||
setIsCollapsed(collapsed);
|
setIsCollapsed(collapsed);
|
||||||
styleDispatch(styleActions.setSiderCollapsed(collapsed));
|
styleDispatch(styleActions.setSiderCollapsed(collapsed));
|
||||||
|
|
||||||
// 确保在收起侧边栏时有选中的项目,避免不必要的计算
|
// 确保在收起侧边栏时有选中的项目
|
||||||
if (selectedKeys.length === 0) {
|
if (selectedKeys.length === 0) {
|
||||||
const currentPath = location.pathname;
|
const currentPath = location.pathname;
|
||||||
const matchingKey = Object.keys(routerMapState).find(
|
const matchingKey = Object.keys(routerMapState).find(
|
||||||
@@ -374,14 +350,19 @@ const SiderBar = () => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
selectedKeys={selectedKeys}
|
selectedKeys={selectedKeys}
|
||||||
itemStyle={navItemStyle}
|
itemStyle="sidebar-nav-item"
|
||||||
hoverStyle={navItemHoverStyle}
|
hoverStyle="sidebar-nav-item:hover"
|
||||||
selectedStyle={navItemSelectedStyle}
|
selectedStyle="sidebar-nav-item-selected"
|
||||||
renderWrapper={({ itemElement, isSubNav, isInSubNav, props }) => {
|
renderWrapper={({ itemElement, props }) => {
|
||||||
|
const to = routerMapState[props.itemKey] || routerMap[props.itemKey];
|
||||||
|
|
||||||
|
// 如果没有路由,直接返回元素
|
||||||
|
if (!to) return itemElement;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
style={{ textDecoration: 'none' }}
|
style={{ textDecoration: 'none' }}
|
||||||
to={routerMapState[props.itemKey] || routerMap[props.itemKey]}
|
to={to}
|
||||||
>
|
>
|
||||||
{itemElement}
|
{itemElement}
|
||||||
</Link>
|
</Link>
|
||||||
@@ -400,107 +381,67 @@ const SiderBar = () => {
|
|||||||
setOpenedKeys(data.openKeys);
|
setOpenedKeys(data.openKeys);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* Chat Section - Only show if there are chat items */}
|
{/* 聊天区域 */}
|
||||||
{chatMenuItems.map((item) => {
|
<div className="sidebar-section">
|
||||||
if (item.items && item.items.length > 0) {
|
{!isCollapsed && (
|
||||||
return (
|
<div className="sidebar-group-label">{t('聊天')}</div>
|
||||||
<Nav.Sub
|
)}
|
||||||
key={item.itemKey}
|
{chatMenuItems.map((item) => renderSubItem(item))}
|
||||||
itemKey={item.itemKey}
|
</div>
|
||||||
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} />
|
<Divider className="sidebar-divider" />
|
||||||
|
<div>
|
||||||
{/* Workspace Section */}
|
{!isCollapsed && (
|
||||||
{!isCollapsed && <Text style={groupLabelStyle}>{t('控制台')}</Text>}
|
<div className="sidebar-group-label">{t('控制台')}</div>
|
||||||
{workspaceItems.map((item) => (
|
)}
|
||||||
<Nav.Item
|
{workspaceItems.map((item) => renderNavItem(item))}
|
||||||
key={item.itemKey}
|
</div>
|
||||||
itemKey={item.itemKey}
|
|
||||||
text={item.text}
|
|
||||||
icon={React.cloneElement(item.icon, {
|
|
||||||
style: iconStyles[item.itemKey],
|
|
||||||
})}
|
|
||||||
className={item.className}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
|
|
||||||
|
{/* 管理员区域 - 只在管理员时显示 */}
|
||||||
{isAdmin() && (
|
{isAdmin() && (
|
||||||
<>
|
<>
|
||||||
{/* Divider */}
|
<Divider className="sidebar-divider" />
|
||||||
<Divider style={dividerStyle} />
|
<div>
|
||||||
|
{!isCollapsed && (
|
||||||
{/* Admin Section */}
|
<div className="sidebar-group-label">{t('管理员')}</div>
|
||||||
{!isCollapsed && <Text style={groupLabelStyle}>{t('管理员')}</Text>}
|
)}
|
||||||
{adminItems.map((item) => (
|
{adminItems.map((item) => renderNavItem(item))}
|
||||||
<Nav.Item
|
</div>
|
||||||
key={item.itemKey}
|
|
||||||
itemKey={item.itemKey}
|
|
||||||
text={item.text}
|
|
||||||
icon={React.cloneElement(item.icon, {
|
|
||||||
style: iconStyles[item.itemKey],
|
|
||||||
})}
|
|
||||||
className={item.className}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Divider */}
|
{/* 个人中心区域 */}
|
||||||
<Divider style={dividerStyle} />
|
<Divider className="sidebar-divider" />
|
||||||
|
<div>
|
||||||
{/* Finance Management Section */}
|
{!isCollapsed && (
|
||||||
{!isCollapsed && <Text style={groupLabelStyle}>{t('个人中心')}</Text>}
|
<div className="sidebar-group-label">{t('个人中心')}</div>
|
||||||
{financeItems.map((item) => (
|
)}
|
||||||
<Nav.Item
|
{financeItems.map((item) => renderNavItem(item))}
|
||||||
key={item.itemKey}
|
</div>
|
||||||
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('收起侧边栏');
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Nav>
|
</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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,80 @@ import {
|
|||||||
Doubao,
|
Doubao,
|
||||||
} from '@lobehub/icons';
|
} 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 = (() => {
|
export const getModelCategories = (() => {
|
||||||
let categoriesCache = null;
|
let categoriesCache = null;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* ==================== Tailwind CSS 配置 ==================== */
|
||||||
@layer tailwind-base, semi, tailwind-components, tailwind-utils;
|
@layer tailwind-base, semi, tailwind-components, tailwind-utils;
|
||||||
|
|
||||||
@layer tailwind-base {
|
@layer tailwind-base {
|
||||||
@@ -12,6 +13,7 @@
|
|||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ==================== 全局基础样式 ==================== */
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
@@ -25,80 +27,6 @@ body {
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
#root {
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 767px) {
|
|
||||||
|
|
||||||
#root>section>header>section>div>div>div>div.semi-navigation-footer>div>a>li {
|
|
||||||
padding: 0 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
|
|
||||||
#root>section>header>section>div>div>div>div.semi-navigation-footer>div:nth-child(1)>a>li {
|
|
||||||
padding: 0 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.semi-navigation-horizontal .semi-navigation-header {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 确保移动端内容可滚动 */
|
|
||||||
.semi-layout-content {
|
|
||||||
-webkit-overflow-scrolling: touch !important;
|
|
||||||
overscroll-behavior-y: auto !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 修复移动端下拉刷新 */
|
|
||||||
body {
|
|
||||||
overflow: visible !important;
|
|
||||||
overscroll-behavior-y: auto !important;
|
|
||||||
position: static !important;
|
|
||||||
height: 100% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 确保内容区域在移动端可以正常滚动 */
|
|
||||||
#root {
|
|
||||||
overflow: visible !important;
|
|
||||||
height: 100% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.semi-table-tbody,
|
|
||||||
.semi-table-row,
|
|
||||||
.semi-table-row-cell {
|
|
||||||
display: block !important;
|
|
||||||
width: auto !important;
|
|
||||||
padding: 2px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.semi-table-row-cell {
|
|
||||||
border-bottom: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.semi-table-tbody>.semi-table-row {
|
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tableShow {
|
|
||||||
display: revert;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tableHiddle {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
body::-webkit-scrollbar {
|
body::-webkit-scrollbar {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@@ -108,10 +36,14 @@ code {
|
|||||||
source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
|
source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-footer {
|
#root {
|
||||||
font-size: 1.1em;
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ==================== 布局相关样式 ==================== */
|
||||||
.semi-layout-content::-webkit-scrollbar,
|
.semi-layout-content::-webkit-scrollbar,
|
||||||
.semi-sider::-webkit-scrollbar {
|
.semi-sider::-webkit-scrollbar {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
@@ -134,19 +66,161 @@ code {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ==================== 导航和侧边栏样式 ==================== */
|
||||||
|
/* 导航项样式 */
|
||||||
|
.semi-navigation-sub-title,
|
||||||
.semi-chat-inputBox-sendButton,
|
.semi-chat-inputBox-sendButton,
|
||||||
.semi-page-item,
|
.semi-page-item,
|
||||||
.semi-navigation-item,
|
.semi-navigation-item,
|
||||||
.semi-tag-closable,
|
.semi-tag-closable,
|
||||||
.semi-datepicker-range-input {
|
.semi-datepicker-range-input {
|
||||||
|
border-radius: 9999px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.semi-navigation-item {
|
||||||
|
margin-bottom: 4px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.semi-navigation-item-icon {
|
||||||
|
justify-items: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.semi-navigation-item-icon-info {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.semi-navigation-sub-title {
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.semi-navigation-item-collapsed {
|
||||||
|
height: 44px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 自定义侧边栏样式 */
|
||||||
|
.sidebar-container {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-nav::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 侧边栏导航项样式 */
|
||||||
|
.sidebar-nav-item {
|
||||||
|
border-radius: 6px;
|
||||||
|
margin: 3px 8px;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
padding: 8px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-nav-item:hover {
|
||||||
|
background-color: rgba(var(--semi-blue-0), 0.08);
|
||||||
|
color: var(--semi-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-nav-item-selected {
|
||||||
|
background-color: rgba(var(--semi-blue-0), 0.12);
|
||||||
|
color: var(--semi-color-primary);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 图标容器样式 */
|
||||||
|
.sidebar-icon-container {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-right: 10px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分割线样式 */
|
||||||
|
.sidebar-divider {
|
||||||
|
margin: 4px 8px;
|
||||||
|
opacity: 0.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分组标签样式 */
|
||||||
|
.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;
|
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 {
|
.sidebar-collapse-icon-container {
|
||||||
padding: 0 !important;
|
display: inline-block;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 聊天 */
|
/* 侧边栏区域容器 */
|
||||||
|
.sidebar-section {
|
||||||
|
padding-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==================== 聊天界面样式 ==================== */
|
||||||
.semi-chat {
|
.semi-chat {
|
||||||
padding-top: 0 !important;
|
padding-top: 0 !important;
|
||||||
padding-bottom: 0 !important;
|
padding-bottom: 0 !important;
|
||||||
@@ -183,13 +257,26 @@ code {
|
|||||||
overflow-x: hidden !important;
|
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::-webkit-scrollbar,
|
||||||
.semi-chat-chatBox::-webkit-scrollbar,
|
.semi-chat-chatBox::-webkit-scrollbar,
|
||||||
.semi-chat-chatBox-wrap::-webkit-scrollbar,
|
.semi-chat-chatBox-wrap::-webkit-scrollbar,
|
||||||
.semi-chat-chatBox-content::-webkit-scrollbar,
|
.semi-chat-chatBox-content::-webkit-scrollbar,
|
||||||
.semi-chat-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;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,40 +285,72 @@ code {
|
|||||||
.semi-chat-chatBox-wrap,
|
.semi-chat-chatBox-wrap,
|
||||||
.semi-chat-chatBox-content,
|
.semi-chat-chatBox-content,
|
||||||
.semi-chat-content,
|
.semi-chat-content,
|
||||||
.semi-chat-list {
|
.semi-chat-list,
|
||||||
-ms-overflow-style: none;
|
|
||||||
scrollbar-width: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.semi-chat-container {
|
|
||||||
overflow-x: hidden !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.semi-chat-container::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.semi-chat-container {
|
.semi-chat-container {
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ==================== 组件特定样式 ==================== */
|
||||||
|
/* Tabs组件样式 */
|
||||||
|
.semi-tabs-content {
|
||||||
|
padding: 0 !important;
|
||||||
|
height: calc(100% - 40px) !important;
|
||||||
|
flex: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.semi-tabs-content .semi-tabs-pane {
|
||||||
|
height: 100% !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.semi-tabs-content .semi-tabs-pane>div {
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格样式 */
|
||||||
|
.tableShow {
|
||||||
|
display: revert;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.debug-panel .semi-tabs-content {
|
||||||
|
flex: 1 !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==================== 滚动条样式统一管理 ==================== */
|
||||||
/* 隐藏模型设置区域的滚动条 */
|
/* 隐藏模型设置区域的滚动条 */
|
||||||
.model-settings-scroll::-webkit-scrollbar {
|
.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;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.model-settings-scroll {
|
.model-settings-scroll,
|
||||||
-ms-overflow-style: none;
|
.thinking-content-scroll,
|
||||||
scrollbar-width: none;
|
.custom-request-textarea .semi-input,
|
||||||
}
|
.custom-request-textarea textarea {
|
||||||
|
|
||||||
/* 思考内容区域滚动条样式 */
|
|
||||||
.thinking-content-scroll::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.thinking-content-scroll {
|
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
}
|
}
|
||||||
@@ -255,60 +374,58 @@ code {
|
|||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 隐藏请求体 JSON TextArea 的滚动条 */
|
/* ==================== 响应式/移动端样式 ==================== */
|
||||||
.custom-request-textarea .semi-input::-webkit-scrollbar {
|
@media only screen and (max-width: 767px) {
|
||||||
display: none;
|
#root>section>header>section>div>div>div>div.semi-navigation-footer>div>a>li {
|
||||||
}
|
padding: 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
.custom-request-textarea .semi-input {
|
#root>section>header>section>div>div>div>div.semi-navigation-header-list-outer>div.semi-navigation-list-wrapper>ul>div>a>li {
|
||||||
-ms-overflow-style: none;
|
padding: 0 5px;
|
||||||
scrollbar-width: none;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.custom-request-textarea textarea::-webkit-scrollbar {
|
#root>section>header>section>div>div>div>div.semi-navigation-footer>div:nth-child(1)>a>li {
|
||||||
display: none;
|
padding: 0 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.custom-request-textarea textarea {
|
.semi-navigation-horizontal .semi-navigation-header {
|
||||||
-ms-overflow-style: none;
|
margin-right: 0;
|
||||||
scrollbar-width: none;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* 调试面板标签样式 */
|
/* 确保移动端内容可滚动 */
|
||||||
.semi-tabs-content {
|
.semi-layout-content {
|
||||||
height: calc(100% - 40px) !important;
|
-webkit-overflow-scrolling: touch !important;
|
||||||
flex: 1 !important;
|
overscroll-behavior-y: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.semi-tabs-content .semi-tabs-pane {
|
/* 修复移动端下拉刷新 */
|
||||||
height: 100% !important;
|
body {
|
||||||
overflow: hidden !important;
|
overflow: visible !important;
|
||||||
}
|
overscroll-behavior-y: auto !important;
|
||||||
|
position: static !important;
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
.semi-tabs-content .semi-tabs-pane>div {
|
/* 确保内容区域在移动端可以正常滚动 */
|
||||||
height: 100% !important;
|
#root {
|
||||||
}
|
overflow: visible !important;
|
||||||
|
height: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* 调试面板特定样式 */
|
/* 移动端表格样式调整 */
|
||||||
.debug-panel .semi-tabs {
|
.semi-table-tbody,
|
||||||
height: 100% !important;
|
.semi-table-row,
|
||||||
display: flex !important;
|
.semi-table-row-cell {
|
||||||
flex-direction: column !important;
|
display: block !important;
|
||||||
}
|
width: auto !important;
|
||||||
|
padding: 2px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.debug-panel .semi-tabs-bar {
|
.semi-table-row-cell {
|
||||||
flex-shrink: 0 !important;
|
border-bottom: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.debug-panel .semi-tabs-content {
|
.semi-table-tbody>.semi-table-row {
|
||||||
flex: 1 !important;
|
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||||
overflow: hidden !important;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.semi-chat-chatBox-action {
|
|
||||||
column-gap: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.semi-chat-inputBox-clearButton.semi-button .semi-icon {
|
|
||||||
font-size: 20px !important;
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user