refactor: Enhance UI layout and styling with responsive design improvements
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React, { useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { Link, useNavigate, useLocation } from 'react-router-dom';
|
||||
import { UserContext } from '../context/User';
|
||||
import { StatusContext } from '../context/Status';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -34,7 +34,34 @@ import { stringToColor } from '../helpers/render.js';
|
||||
import { useSetTheme, useTheme } from '../context/Theme/index.js';
|
||||
import { StyleContext } from '../context/Style/index.js';
|
||||
|
||||
// HeaderBar Buttons
|
||||
// 自定义侧边栏按钮样式
|
||||
const navItemStyle = {
|
||||
borderRadius: '6px',
|
||||
margin: '4px 8px',
|
||||
transition: 'all 0.3s ease'
|
||||
};
|
||||
|
||||
// 自定义侧边栏按钮悬停样式
|
||||
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)',
|
||||
transition: 'all 0.3s ease'
|
||||
};
|
||||
};
|
||||
|
||||
const SiderBar = () => {
|
||||
const { t } = useTranslation();
|
||||
@@ -46,8 +73,30 @@ const SiderBar = () => {
|
||||
const [selectedKeys, setSelectedKeys] = useState(['home']);
|
||||
const [isCollapsed, setIsCollapsed] = useState(defaultIsCollapsed);
|
||||
const [chatItems, setChatItems] = useState([]);
|
||||
const [openedKeys, setOpenedKeys] = useState([]);
|
||||
const theme = useTheme();
|
||||
const setTheme = useSetTheme();
|
||||
const location = useLocation();
|
||||
|
||||
// 预先计算所有可能的图标样式
|
||||
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 routerMap = {
|
||||
home: '/',
|
||||
@@ -190,11 +239,14 @@ const SiderBar = () => {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
let localKey = window.location.pathname.split('/')[1];
|
||||
if (localKey === '') {
|
||||
localKey = 'home';
|
||||
const currentPath = location.pathname;
|
||||
const matchingKey = Object.keys(routerMap).find(key => routerMap[key] === currentPath);
|
||||
|
||||
if (matchingKey) {
|
||||
setSelectedKeys([matchingKey]);
|
||||
} else if (currentPath.startsWith('/chat/')) {
|
||||
setSelectedKeys(['chat']);
|
||||
}
|
||||
setSelectedKeys([localKey]);
|
||||
|
||||
let chats = localStorage.getItem('chats');
|
||||
if (chats) {
|
||||
@@ -222,7 +274,7 @@ const SiderBar = () => {
|
||||
}
|
||||
|
||||
setIsCollapsed(localStorage.getItem('default_collapse_sidebar') === 'true');
|
||||
}, []);
|
||||
}, [location.pathname]);
|
||||
|
||||
// Custom divider style
|
||||
const dividerStyle = {
|
||||
@@ -235,21 +287,54 @@ const SiderBar = () => {
|
||||
padding: '8px 16px',
|
||||
color: 'var(--semi-color-text-2)',
|
||||
fontSize: '12px',
|
||||
fontWeight: 'normal',
|
||||
fontWeight: 'bold',
|
||||
textTransform: 'uppercase',
|
||||
letterSpacing: '0.5px',
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Nav
|
||||
style={{ maxWidth: 200, height: '100%' }}
|
||||
className="custom-sidebar-nav"
|
||||
style={{
|
||||
width: isCollapsed ? '60px' : '200px',
|
||||
height: '100%',
|
||||
boxShadow: '0 1px 6px rgba(0, 0, 0, 0.08)',
|
||||
borderRight: '1px solid var(--semi-color-border)',
|
||||
background: 'var(--semi-color-bg-0)',
|
||||
borderRadius: '0 8px 8px 0',
|
||||
transition: 'all 0.3s ease',
|
||||
position: 'relative',
|
||||
zIndex: 95
|
||||
}}
|
||||
defaultIsCollapsed={
|
||||
localStorage.getItem('default_collapse_sidebar') === 'true'
|
||||
}
|
||||
isCollapsed={isCollapsed}
|
||||
onCollapseChange={(collapsed) => {
|
||||
setIsCollapsed(collapsed);
|
||||
localStorage.setItem('default_collapse_sidebar', collapsed);
|
||||
// 始终保持侧边栏显示,只是宽度不同
|
||||
styleDispatch({ type: 'SET_SIDER', payload: true });
|
||||
|
||||
// 确保在收起侧边栏时有选中的项目,避免不必要的计算
|
||||
if (selectedKeys.length === 0) {
|
||||
const currentPath = location.pathname;
|
||||
const matchingKey = Object.keys(routerMap).find(key => routerMap[key] === currentPath);
|
||||
|
||||
if (matchingKey) {
|
||||
setSelectedKeys([matchingKey]);
|
||||
} else if (currentPath.startsWith('/chat/')) {
|
||||
setSelectedKeys(['chat']);
|
||||
} else {
|
||||
setSelectedKeys(['home']); // 默认选中首页
|
||||
}
|
||||
}
|
||||
}}
|
||||
selectedKeys={selectedKeys}
|
||||
itemStyle={navItemStyle}
|
||||
hoverStyle={navItemHoverStyle}
|
||||
selectedStyle={navItemSelectedStyle}
|
||||
renderWrapper={({ itemElement, isSubNav, isInSubNav, props }) => {
|
||||
let chats = localStorage.getItem('chats');
|
||||
if (chats) {
|
||||
@@ -284,8 +369,18 @@ const SiderBar = () => {
|
||||
} else {
|
||||
styleDispatch({ type: 'SET_INNER_PADDING', payload: true });
|
||||
}
|
||||
|
||||
// 如果点击的是已经展开的子菜单的父项,则收起子菜单
|
||||
if (openedKeys.includes(key.itemKey)) {
|
||||
setOpenedKeys(openedKeys.filter(k => k !== key.itemKey));
|
||||
}
|
||||
|
||||
setSelectedKeys([key.itemKey]);
|
||||
}}
|
||||
openKeys={openedKeys}
|
||||
onOpenChange={(data) => {
|
||||
setOpenedKeys(data.openKeys);
|
||||
}}
|
||||
>
|
||||
{/* Chat Section - Only show if there are chat items */}
|
||||
{chatMenuItems.map((item) => {
|
||||
@@ -295,7 +390,7 @@ const SiderBar = () => {
|
||||
key={item.itemKey}
|
||||
itemKey={item.itemKey}
|
||||
text={item.text}
|
||||
icon={item.icon}
|
||||
icon={React.cloneElement(item.icon, { style: iconStyles[item.itemKey] })}
|
||||
>
|
||||
{item.items.map((subItem) => (
|
||||
<Nav.Item
|
||||
@@ -312,13 +407,12 @@ const SiderBar = () => {
|
||||
key={item.itemKey}
|
||||
itemKey={item.itemKey}
|
||||
text={item.text}
|
||||
icon={item.icon}
|
||||
icon={React.cloneElement(item.icon, { style: iconStyles[item.itemKey] })}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
|
||||
|
||||
{/* Divider */}
|
||||
<Divider style={dividerStyle} />
|
||||
|
||||
@@ -329,11 +423,30 @@ const SiderBar = () => {
|
||||
key={item.itemKey}
|
||||
itemKey={item.itemKey}
|
||||
text={item.text}
|
||||
icon={item.icon}
|
||||
icon={React.cloneElement(item.icon, { style: iconStyles[item.itemKey] })}
|
||||
className={item.className}
|
||||
/>
|
||||
))}
|
||||
|
||||
{isAdmin() && (
|
||||
<>
|
||||
{/* Divider */}
|
||||
<Divider style={dividerStyle} />
|
||||
|
||||
{/* Admin Section */}
|
||||
{!isCollapsed && <div style={groupLabelStyle}>{t('管理员')}</div>}
|
||||
{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 */}
|
||||
<Divider style={dividerStyle} />
|
||||
|
||||
@@ -344,31 +457,18 @@ const SiderBar = () => {
|
||||
key={item.itemKey}
|
||||
itemKey={item.itemKey}
|
||||
text={item.text}
|
||||
icon={item.icon}
|
||||
icon={React.cloneElement(item.icon, { style: iconStyles[item.itemKey] })}
|
||||
className={item.className}
|
||||
/>
|
||||
))}
|
||||
|
||||
{isAdmin() && (
|
||||
<>
|
||||
{/* Divider */}
|
||||
<Divider style={dividerStyle} />
|
||||
|
||||
{/* Admin Section */}
|
||||
{adminItems.map((item) => (
|
||||
<Nav.Item
|
||||
key={item.itemKey}
|
||||
itemKey={item.itemKey}
|
||||
text={item.text}
|
||||
icon={item.icon}
|
||||
className={item.className}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
|
||||
<Nav.Footer
|
||||
collapseButton={true}
|
||||
style={{
|
||||
borderTop: '1px solid var(--semi-color-border)',
|
||||
padding: '12px 0',
|
||||
marginTop: 'auto'
|
||||
}}
|
||||
collapseText={(collapsed)=>
|
||||
{
|
||||
if(collapsed){
|
||||
|
||||
Reference in New Issue
Block a user