refactor: 拆分 sidebar 和内容区为独立组件

This commit is contained in:
Linkon
2026-01-18 20:36:18 +08:00
parent 24e9e6f6f8
commit df5b94bdb4
3 changed files with 201 additions and 103 deletions
+13 -103
View File
@@ -1,18 +1,11 @@
import { Layout, Menu, Space, Dialog, Input, Button, Tag, MessagePlugin } from 'tdesign-react'
import { Layout, Dialog, Input, MessagePlugin } from 'tdesign-react'
import { useEffect, useMemo, useState } from 'react'
import { UserIcon, SettingIcon, HistoryIcon, RootListIcon, ViewListIcon } from 'tdesign-icons-react'
import { HashRouter, Navigate, Route, Routes, useLocation, useNavigate } from 'react-router-dom'
import { StudentManager } from './components/StudentManager'
import { Settings } from './components/Settings'
import { ReasonManager } from './components/ReasonManager'
import { ScoreManager } from './components/ScoreManager'
import { Leaderboard } from './components/Leaderboard'
import { SettlementHistory } from './components/SettlementHistory'
import { HashRouter, useLocation, useNavigate } from 'react-router-dom'
import { Sidebar } from './components/Sidebar'
import { ContentArea } from './components/ContentArea'
import { Wizard } from './components/Wizard'
import { ThemeProvider } from './contexts/ThemeContext'
const { Header, Content, Aside } = Layout
function MainContent(): React.JSX.Element {
const navigate = useNavigate()
const location = useLocation()
@@ -95,99 +88,16 @@ function MainContent(): React.JSX.Element {
if (key === 'settings') navigate('/settings')
}
const permissionTag = (
<Tag
theme={permission === 'admin' ? 'success' : permission === 'points' ? 'warning' : 'default'}
variant="light"
>
{permission === 'admin' ? '管理权限' : permission === 'points' ? '积分权限' : '只读'}
</Tag>
)
return (
<Layout style={{ height: '100vh', backgroundColor: 'var(--ss-bg-color)' }}>
<Aside
className="ss-sidebar"
style={{
backgroundColor: 'var(--ss-sidebar-bg)',
borderRight: '1px solid var(--ss-border-color)'
}}
>
<div style={{ padding: '24px', textAlign: 'center' }}>
<h2 style={{ color: 'var(--ss-sidebar-text, var(--ss-text-main))', margin: 0 }}>
SecScore
</h2>
<div
style={{
fontSize: '12px',
color: 'var(--ss-sidebar-text, var(--ss-text-main))'
}}
>
</div>
</div>
<Menu value={activeMenu} onChange={onMenuChange} style={{ width: '100%', border: 'none' }}>
<Menu.MenuItem value="students" icon={<UserIcon />} disabled={permission !== 'admin'}>
</Menu.MenuItem>
<Menu.MenuItem value="score" icon={<HistoryIcon />}>
</Menu.MenuItem>
<Menu.MenuItem value="leaderboard" icon={<ViewListIcon />}>
</Menu.MenuItem>
<Menu.MenuItem value="settlements" icon={<HistoryIcon />}>
</Menu.MenuItem>
<Menu.MenuItem value="reasons" icon={<RootListIcon />} disabled={permission !== 'admin'}>
</Menu.MenuItem>
<Menu.MenuItem value="settings" icon={<SettingIcon />} disabled={permission !== 'admin'}>
</Menu.MenuItem>
</Menu>
</Aside>
<Layout>
<Header
style={{
backgroundColor: 'var(--ss-header-bg)',
borderBottom: '1px solid var(--ss-border-color)',
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: '0 24px'
}}
>
<Space>
{permissionTag}
{hasAnyPassword && (
<>
<Button size="small" variant="outline" onClick={() => setAuthVisible(true)}>
</Button>
<Button size="small" variant="outline" theme="danger" onClick={logout}>
</Button>
</>
)}
</Space>
</Header>
<Content style={{ overflowY: 'auto' }}>
<Routes>
<Route path="/" element={<Navigate to="/score" replace />} />
<Route path="/students" element={<StudentManager canEdit={permission === 'admin'} />} />
<Route
path="/score"
element={<ScoreManager canEdit={permission === 'admin' || permission === 'points'} />}
/>
<Route path="/leaderboard" element={<Leaderboard />} />
<Route path="/settlements" element={<SettlementHistory />} />
<Route path="/reasons" element={<ReasonManager canEdit={permission === 'admin'} />} />
<Route path="/settings" element={<Settings permission={permission} />} />
<Route path="*" element={<Navigate to="/score" replace />} />
</Routes>
</Content>
</Layout>
<Layout style={{ height: '100vh', flexDirection: 'row', overflow: 'hidden' }}>
<Sidebar activeMenu={activeMenu} permission={permission} onMenuChange={onMenuChange} />
<ContentArea
permission={permission}
hasAnyPassword={hasAnyPassword}
onAuthClick={() => setAuthVisible(true)}
onLogout={logout}
/>
<Wizard visible={wizardVisible} onComplete={() => setWizardVisible(false)} />
<Dialog
+103
View File
@@ -0,0 +1,103 @@
import { Layout, Space, Button, Tag } from 'tdesign-react'
import { Routes, Route, Navigate } from 'react-router-dom'
import { StudentManager } from './StudentManager'
import { Settings } from './Settings'
import { ReasonManager } from './ReasonManager'
import { ScoreManager } from './ScoreManager'
import { Leaderboard } from './Leaderboard'
import { SettlementHistory } from './SettlementHistory'
import { WindowControls } from './WindowControls'
const { Content } = Layout
interface ContentAreaProps {
permission: 'admin' | 'points' | 'view'
hasAnyPassword: boolean
onAuthClick: () => void
onLogout: () => void
}
export function ContentArea({
permission,
hasAnyPassword,
onAuthClick,
onLogout
}: ContentAreaProps): React.JSX.Element {
const permissionTag = (
<Tag
theme={permission === 'admin' ? 'success' : permission === 'points' ? 'warning' : 'default'}
variant="light"
>
{permission === 'admin' ? '管理权限' : permission === 'points' ? '积分权限' : '只读'}
</Tag>
)
return (
<Layout
style={{
flex: 1,
display: 'flex',
flexDirection: 'column',
overflow: 'hidden',
backgroundColor: 'var(--ss-bg-color)'
}}
>
<div
style={
{
height: '32px',
WebkitAppRegion: 'drag',
display: 'flex',
alignItems: 'center',
backgroundColor: 'var(--ss-header-bg)',
borderBottom: '1px solid var(--ss-border-color)',
flexShrink: 0
} as React.CSSProperties
}
>
<div style={{ flex: 1 }} />
<div
style={
{
display: 'flex',
alignItems: 'center',
paddingRight: '0px',
WebkitAppRegion: 'no-drag'
} as React.CSSProperties
}
>
<Space size="small" style={{ marginRight: '12px' }}>
{permissionTag}
{hasAnyPassword && (
<>
<Button size="small" variant="outline" onClick={onAuthClick}>
</Button>
<Button size="small" variant="outline" theme="danger" onClick={onLogout}>
</Button>
</>
)}
</Space>
<WindowControls />
</div>
</div>
<Content style={{ flex: 1, overflowY: 'auto' }}>
<Routes>
<Route path="/" element={<Navigate to="/score" replace />} />
<Route path="/students" element={<StudentManager canEdit={permission === 'admin'} />} />
<Route
path="/score"
element={<ScoreManager canEdit={permission === 'admin' || permission === 'points'} />}
/>
<Route path="/leaderboard" element={<Leaderboard />} />
<Route path="/settlements" element={<SettlementHistory />} />
<Route path="/reasons" element={<ReasonManager canEdit={permission === 'admin'} />} />
<Route path="/settings" element={<Settings permission={permission} />} />
<Route path="*" element={<Navigate to="/score" replace />} />
</Routes>
</Content>
</Layout>
)
}
+85
View File
@@ -0,0 +1,85 @@
import { Layout, Menu } from 'tdesign-react'
import { UserIcon, SettingIcon, HistoryIcon, RootListIcon, ViewListIcon } from 'tdesign-icons-react'
import appLogo from '../assets/logo.svg'
const { Aside } = Layout
interface SidebarProps {
activeMenu: string
permission: 'admin' | 'points' | 'view'
onMenuChange: (value: string | number) => void
}
export function Sidebar({ activeMenu, permission, onMenuChange }: SidebarProps): React.JSX.Element {
return (
<Aside
className="ss-sidebar"
style={{
backgroundColor: 'var(--ss-sidebar-bg)',
borderRight: '1px solid var(--ss-border-color)',
display: 'flex',
flexDirection: 'column'
}}
>
<div
style={
{
padding: '32px 24px 16px',
textAlign: 'center',
WebkitAppRegion: 'drag',
userSelect: 'none',
flexShrink: 0
} as React.CSSProperties
}
>
<img
src={appLogo}
style={{ width: '48px', height: '48px', marginBottom: '12px' }}
alt="logo"
/>
<h2
style={{
color: 'var(--ss-sidebar-text, var(--ss-text-main))',
margin: 0,
fontSize: '20px'
}}
>
SecScore
</h2>
<div
style={{
fontSize: '12px',
color: 'var(--ss-sidebar-text, var(--ss-text-main))',
opacity: 0.8,
marginTop: '4px'
}}
>
</div>
</div>
<div style={{ flex: 1, overflowY: 'auto', display: 'flex', flexDirection: 'column' }}>
<Menu value={activeMenu} onChange={onMenuChange} style={{ width: '100%', border: 'none' }}>
<Menu.MenuItem value="students" icon={<UserIcon />} disabled={permission !== 'admin'}>
</Menu.MenuItem>
<Menu.MenuItem value="score" icon={<HistoryIcon />}>
</Menu.MenuItem>
<Menu.MenuItem value="leaderboard" icon={<ViewListIcon />}>
</Menu.MenuItem>
<Menu.MenuItem value="settlements" icon={<HistoryIcon />}>
</Menu.MenuItem>
<Menu.MenuItem value="reasons" icon={<RootListIcon />} disabled={permission !== 'admin'}>
</Menu.MenuItem>
<Menu.MenuItem value="settings" icon={<SettingIcon />} disabled={permission !== 'admin'}>
</Menu.MenuItem>
</Menu>
</div>
</Aside>
)
}