feat: 侧边栏移动端优化
This commit is contained in:
@@ -9,17 +9,19 @@ import '../index.css';
|
|||||||
import fireworks from 'react-fireworks';
|
import fireworks from 'react-fireworks';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
IconClose,
|
||||||
IconHelpCircle,
|
IconHelpCircle,
|
||||||
IconHome,
|
IconHome,
|
||||||
IconHomeStroked,
|
IconHomeStroked, IconIndentLeft,
|
||||||
IconKey,
|
IconKey, IconMenu,
|
||||||
IconNoteMoneyStroked,
|
IconNoteMoneyStroked,
|
||||||
IconPriceTag,
|
IconPriceTag,
|
||||||
IconUser
|
IconUser
|
||||||
} from '@douyinfe/semi-icons';
|
} from '@douyinfe/semi-icons';
|
||||||
import { Avatar, Dropdown, Layout, Nav, Switch } from '@douyinfe/semi-ui';
|
import { Avatar, Button, Dropdown, Layout, Nav, Switch } from '@douyinfe/semi-ui';
|
||||||
import { stringToColor } from '../helpers/render';
|
import { stringToColor } from '../helpers/render';
|
||||||
import Text from '@douyinfe/semi-ui/lib/es/typography/text';
|
import Text from '@douyinfe/semi-ui/lib/es/typography/text';
|
||||||
|
import { StyleContext } from '../context/Style/index.js';
|
||||||
|
|
||||||
// HeaderBar Buttons
|
// HeaderBar Buttons
|
||||||
let headerButtons = [
|
let headerButtons = [
|
||||||
@@ -36,14 +38,7 @@ let buttons = [
|
|||||||
text: '首页',
|
text: '首页',
|
||||||
itemKey: 'home',
|
itemKey: 'home',
|
||||||
to: '/',
|
to: '/',
|
||||||
// icon: <IconHomeStroked />,
|
|
||||||
},
|
},
|
||||||
// {
|
|
||||||
// text: 'Playground',
|
|
||||||
// itemKey: 'playground',
|
|
||||||
// to: '/playground',
|
|
||||||
// // icon: <IconNoteMoneyStroked />,
|
|
||||||
// },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
if (localStorage.getItem('chat_link')) {
|
if (localStorage.getItem('chat_link')) {
|
||||||
@@ -56,9 +51,9 @@ if (localStorage.getItem('chat_link')) {
|
|||||||
|
|
||||||
const HeaderBar = () => {
|
const HeaderBar = () => {
|
||||||
const [userState, userDispatch] = useContext(UserContext);
|
const [userState, userDispatch] = useContext(UserContext);
|
||||||
|
const [styleState, styleDispatch] = useContext(StyleContext);
|
||||||
let navigate = useNavigate();
|
let navigate = useNavigate();
|
||||||
|
|
||||||
const [showSidebar, setShowSidebar] = useState(false);
|
|
||||||
const systemName = getSystemName();
|
const systemName = getSystemName();
|
||||||
const logo = getLogo();
|
const logo = getLogo();
|
||||||
const currentDate = new Date();
|
const currentDate = new Date();
|
||||||
@@ -70,7 +65,6 @@ const HeaderBar = () => {
|
|||||||
currentDate.getDate() <= 24);
|
currentDate.getDate() <= 24);
|
||||||
|
|
||||||
async function logout() {
|
async function logout() {
|
||||||
setShowSidebar(false);
|
|
||||||
await API.get('/api/user/logout');
|
await API.get('/api/user/logout');
|
||||||
showSuccess('注销成功!');
|
showSuccess('注销成功!');
|
||||||
userDispatch({ type: 'logout' });
|
userDispatch({ type: 'logout' });
|
||||||
@@ -128,16 +122,25 @@ const HeaderBar = () => {
|
|||||||
selectedKeys={[]}
|
selectedKeys={[]}
|
||||||
// items={headerButtons}
|
// items={headerButtons}
|
||||||
onSelect={(key) => {}}
|
onSelect={(key) => {}}
|
||||||
header={isMobile()?{
|
header={styleState.isMobile?{
|
||||||
logo: (
|
logo: (
|
||||||
<img src={logo} alt='logo' style={{ marginRight: '0.75em' }} />
|
<>
|
||||||
|
{
|
||||||
|
styleState.showSider ?
|
||||||
|
<Button icon={<IconMenu />} theme="light" style={{ marginRight: 10 }} aria-label="展开侧边栏" onClick={
|
||||||
|
() => styleDispatch({ type: 'SET_SIDER', payload: false })
|
||||||
|
} />:
|
||||||
|
<Button icon={<IconIndentLeft />} theme="light" style={{ marginRight: 10 }} aria-label="关闭侧边栏" onClick={
|
||||||
|
() => styleDispatch({ type: 'SET_SIDER', payload: true })
|
||||||
|
} />
|
||||||
|
}
|
||||||
|
</>
|
||||||
),
|
),
|
||||||
}:{
|
}:{
|
||||||
logo: (
|
logo: (
|
||||||
<img src={logo} alt='logo' />
|
<img src={logo} alt='logo' />
|
||||||
),
|
),
|
||||||
text: systemName,
|
text: systemName,
|
||||||
|
|
||||||
}}
|
}}
|
||||||
items={buttons}
|
items={buttons}
|
||||||
footer={
|
footer={
|
||||||
@@ -159,17 +162,15 @@ const HeaderBar = () => {
|
|||||||
)}
|
)}
|
||||||
<Nav.Item itemKey={'about'} icon={<IconHelpCircle />} />
|
<Nav.Item itemKey={'about'} icon={<IconHelpCircle />} />
|
||||||
<>
|
<>
|
||||||
{!isMobile() && (
|
<Switch
|
||||||
<Switch
|
checkedText='🌞'
|
||||||
checkedText='🌞'
|
size={'large'}
|
||||||
size={'large'}
|
checked={theme === 'dark'}
|
||||||
checked={theme === 'dark'}
|
uncheckedText='🌙'
|
||||||
uncheckedText='🌙'
|
onChange={(checked) => {
|
||||||
onChange={(checked) => {
|
setTheme(checked);
|
||||||
setTheme(checked);
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
{userState.user ? (
|
{userState.user ? (
|
||||||
<>
|
<>
|
||||||
|
|||||||
40
web/src/components/PageLayout.js
Normal file
40
web/src/components/PageLayout.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import HeaderBar from './HeaderBar.js';
|
||||||
|
import { Layout } from '@douyinfe/semi-ui';
|
||||||
|
import SiderBar from './SiderBar.js';
|
||||||
|
import App from '../App.js';
|
||||||
|
import FooterBar from './Footer.js';
|
||||||
|
import { ToastContainer } from 'react-toastify';
|
||||||
|
import React, { useContext } from 'react';
|
||||||
|
import { StyleContext } from '../context/Style/index.js';
|
||||||
|
const { Sider, Content, Header, Footer } = Layout;
|
||||||
|
|
||||||
|
|
||||||
|
const PageLayout = () => {
|
||||||
|
const [styleState, styleDispatch] = useContext(StyleContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}>
|
||||||
|
<Header>
|
||||||
|
<HeaderBar />
|
||||||
|
</Header>
|
||||||
|
<Layout style={{ flex: 1, overflow: 'hidden' }}>
|
||||||
|
<Sider>
|
||||||
|
{styleState.showSider ? null : <SiderBar />}
|
||||||
|
</Sider>
|
||||||
|
<Layout>
|
||||||
|
<Content
|
||||||
|
style={{ overflowY: 'auto', padding: '24px' }}
|
||||||
|
>
|
||||||
|
<App />
|
||||||
|
</Content>
|
||||||
|
<Layout.Footer>
|
||||||
|
<FooterBar></FooterBar>
|
||||||
|
</Layout.Footer>
|
||||||
|
</Layout>
|
||||||
|
</Layout>
|
||||||
|
<ToastContainer />
|
||||||
|
</Layout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PageLayout;
|
||||||
@@ -31,14 +31,15 @@ import { Avatar, Dropdown, Layout, Nav, Switch } from '@douyinfe/semi-ui';
|
|||||||
import { setStatusData } from '../helpers/data.js';
|
import { setStatusData } from '../helpers/data.js';
|
||||||
import { stringToColor } from '../helpers/render.js';
|
import { stringToColor } from '../helpers/render.js';
|
||||||
import { useSetTheme, useTheme } from '../context/Theme/index.js';
|
import { useSetTheme, useTheme } from '../context/Theme/index.js';
|
||||||
|
import { StyleContext } from '../context/Style/index.js';
|
||||||
|
|
||||||
// HeaderBar Buttons
|
// HeaderBar Buttons
|
||||||
|
|
||||||
const SiderBar = () => {
|
const SiderBar = () => {
|
||||||
const [userState, userDispatch] = useContext(UserContext);
|
const [styleState, styleDispatch] = useContext(StyleContext);
|
||||||
const [statusState, statusDispatch] = useContext(StatusContext);
|
const [statusState, statusDispatch] = useContext(StatusContext);
|
||||||
const defaultIsCollapsed =
|
const defaultIsCollapsed =
|
||||||
isMobile() || localStorage.getItem('default_collapse_sidebar') === 'true';
|
localStorage.getItem('default_collapse_sidebar') === 'true';
|
||||||
|
|
||||||
const [selectedKeys, setSelectedKeys] = useState(['home']);
|
const [selectedKeys, setSelectedKeys] = useState(['home']);
|
||||||
const [isCollapsed, setIsCollapsed] = useState(defaultIsCollapsed);
|
const [isCollapsed, setIsCollapsed] = useState(defaultIsCollapsed);
|
||||||
@@ -196,7 +197,6 @@ const SiderBar = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadStatus().then(() => {
|
loadStatus().then(() => {
|
||||||
setIsCollapsed(
|
setIsCollapsed(
|
||||||
isMobile() ||
|
|
||||||
localStorage.getItem('default_collapse_sidebar') === 'true',
|
localStorage.getItem('default_collapse_sidebar') === 'true',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -239,7 +239,6 @@ const SiderBar = () => {
|
|||||||
<Nav
|
<Nav
|
||||||
style={{ maxWidth: 220, height: '100%' }}
|
style={{ maxWidth: 220, height: '100%' }}
|
||||||
defaultIsCollapsed={
|
defaultIsCollapsed={
|
||||||
isMobile() ||
|
|
||||||
localStorage.getItem('default_collapse_sidebar') === 'true'
|
localStorage.getItem('default_collapse_sidebar') === 'true'
|
||||||
}
|
}
|
||||||
isCollapsed={isCollapsed}
|
isCollapsed={isCollapsed}
|
||||||
@@ -284,17 +283,6 @@ const SiderBar = () => {
|
|||||||
}}
|
}}
|
||||||
footer={
|
footer={
|
||||||
<>
|
<>
|
||||||
{isMobile() && (
|
|
||||||
<Switch
|
|
||||||
checkedText='🌞'
|
|
||||||
size={'small'}
|
|
||||||
checked={theme === 'dark'}
|
|
||||||
uncheckedText='🌙'
|
|
||||||
onChange={(checked) => {
|
|
||||||
setTheme(checked);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
57
web/src/context/Style/index.js
Normal file
57
web/src/context/Style/index.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// contexts/User/index.jsx
|
||||||
|
|
||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { isMobile } from '../../helpers/index.js';
|
||||||
|
|
||||||
|
export const StyleContext = React.createContext({
|
||||||
|
dispatch: () => null,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const StyleProvider = ({ children }) => {
|
||||||
|
const [state, setState] = useState({
|
||||||
|
isMobile: false,
|
||||||
|
showSider: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const dispatch = (action) => {
|
||||||
|
if ('type' in action) {
|
||||||
|
switch (action.type) {
|
||||||
|
case 'TOGGLE_SIDER':
|
||||||
|
setState(prev => ({ ...prev, showSider: !prev.showSider }));
|
||||||
|
break;
|
||||||
|
case 'SET_SIDER':
|
||||||
|
setState(prev => ({ ...prev, showSider: action.payload }));
|
||||||
|
break;
|
||||||
|
case 'SET_MOBILE':
|
||||||
|
setState(prev => ({ ...prev, isMobile: action.payload }));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setState(prev => ({ ...prev, ...action }));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setState(prev => ({ ...prev, ...action }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const updateIsMobile = () => {
|
||||||
|
dispatch({ type: 'SET_MOBILE', payload: isMobile() });
|
||||||
|
};
|
||||||
|
|
||||||
|
updateIsMobile();
|
||||||
|
|
||||||
|
// Optionally, add event listeners to handle window resize
|
||||||
|
window.addEventListener('resize', updateIsMobile);
|
||||||
|
|
||||||
|
// Cleanup event listener on component unmount
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('resize', updateIsMobile);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyleContext.Provider value={[state, dispatch]}>
|
||||||
|
{children}
|
||||||
|
</StyleContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -13,6 +13,8 @@ import { Layout } from '@douyinfe/semi-ui';
|
|||||||
import SiderBar from './components/SiderBar';
|
import SiderBar from './components/SiderBar';
|
||||||
import { ThemeProvider } from './context/Theme';
|
import { ThemeProvider } from './context/Theme';
|
||||||
import FooterBar from './components/Footer';
|
import FooterBar from './components/Footer';
|
||||||
|
import { StyleProvider } from './context/Style/index.js';
|
||||||
|
import PageLayout from './components/PageLayout.js';
|
||||||
|
|
||||||
// initialization
|
// initialization
|
||||||
|
|
||||||
@@ -24,27 +26,9 @@ root.render(
|
|||||||
<UserProvider>
|
<UserProvider>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<Layout style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}>
|
<StyleProvider>
|
||||||
<Header>
|
<PageLayout/>
|
||||||
<HeaderBar />
|
</StyleProvider>
|
||||||
</Header>
|
|
||||||
<Layout style={{ flex: 1, overflow: 'hidden' }}>
|
|
||||||
<Sider>
|
|
||||||
<SiderBar />
|
|
||||||
</Sider>
|
|
||||||
<Layout>
|
|
||||||
<Content
|
|
||||||
style={{ overflowY: 'auto', padding: '24px' }}
|
|
||||||
>
|
|
||||||
<App />
|
|
||||||
</Content>
|
|
||||||
<Layout.Footer>
|
|
||||||
<FooterBar></FooterBar>
|
|
||||||
</Layout.Footer>
|
|
||||||
</Layout>
|
|
||||||
</Layout>
|
|
||||||
<ToastContainer />
|
|
||||||
</Layout>
|
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</UserProvider>
|
</UserProvider>
|
||||||
|
|||||||
Reference in New Issue
Block a user