实现侧边栏分组展开折叠状态持久化记忆

This commit is contained in:
yml2213
2026-08-04 11:27:40 +08:00
parent aa2eac8d51
commit 54f05c15a4
+40 -8
View File
@@ -170,8 +170,29 @@ function getInitialOpenKeys(sections: SidebarSection[], selectedKey: string) {
.map((section) => section.key) .map((section) => section.key)
} }
const SIDEBAR_OPEN_KEYS_KEY = 'sidebar_open_keys'
const SIDEBAR_COLLAPSED_KEY = 'sidebar_collapsed'
function loadStoredOpenKeys(sections: SidebarSection[], selectedKey: string): string[] {
const initial = getInitialOpenKeys(sections, selectedKey)
try {
const raw = localStorage.getItem(SIDEBAR_OPEN_KEYS_KEY)
if (raw) {
const parsed = JSON.parse(raw)
if (Array.isArray(parsed)) {
return Array.from(new Set([...parsed, ...initial]))
}
}
} catch (e) {
// ignore
}
return initial
}
export default function MainLayout() { export default function MainLayout() {
const [collapsed, setCollapsed] = useState(false) const [collapsed, setCollapsed] = useState<boolean>(() => {
return localStorage.getItem(SIDEBAR_COLLAPSED_KEY) === 'true'
})
const { user, logout, isAdmin } = useAuth() const { user, logout, isAdmin } = useAuth()
const navigate = useNavigate() const navigate = useNavigate()
const location = useLocation() const location = useLocation()
@@ -179,11 +200,15 @@ export default function MainLayout() {
const sections = useMemo(() => (isAdmin ? adminSections : merchantSections), [isAdmin]) const sections = useMemo(() => (isAdmin ? adminSections : merchantSections), [isAdmin])
const selectedKey = getSelectedKey(location.pathname, location.search) const selectedKey = getSelectedKey(location.pathname, location.search)
const [openKeys, setOpenKeys] = useState<string[]>(() => getInitialOpenKeys(sections, selectedKey)) const [openKeys, setOpenKeys] = useState<string[]>(() => loadStoredOpenKeys(sections, selectedKey))
useEffect(() => { useEffect(() => {
const nextOpenKeys = getInitialOpenKeys(sections, selectedKey) const nextOpenKeys = getInitialOpenKeys(sections, selectedKey)
setOpenKeys((current) => Array.from(new Set([...current, ...nextOpenKeys]))) setOpenKeys((current) => {
const merged = Array.from(new Set([...current, ...nextOpenKeys]))
localStorage.setItem(SIDEBAR_OPEN_KEYS_KEY, JSON.stringify(merged))
return merged
})
}, [sections, selectedKey]) }, [sections, selectedKey])
const toggleSection = (section: SidebarSection) => { const toggleSection = (section: SidebarSection) => {
@@ -197,11 +222,18 @@ export default function MainLayout() {
if (firstEnabledChild?.path) navigate(firstEnabledChild.path) if (firstEnabledChild?.path) navigate(firstEnabledChild.path)
return return
} }
setOpenKeys((current) => setOpenKeys((current) => {
current.includes(section.key) const next = current.includes(section.key)
? current.filter((key) => key !== section.key) ? current.filter((key) => key !== section.key)
: [...current, section.key], : [...current, section.key]
) localStorage.setItem(SIDEBAR_OPEN_KEYS_KEY, JSON.stringify(next))
return next
})
}
const handleToggleCollapse = (next: boolean) => {
setCollapsed(next)
localStorage.setItem(SIDEBAR_COLLAPSED_KEY, String(next))
} }
const handleChildClick = (child: SidebarChild) => { const handleChildClick = (child: SidebarChild) => {
@@ -300,7 +332,7 @@ export default function MainLayout() {
<button <button
className="app-header__collapse" className="app-header__collapse"
type="button" type="button"
onClick={() => setCollapsed(!collapsed)} onClick={() => handleToggleCollapse(!collapsed)}
aria-label={collapsed ? '展开侧边栏' : '收起侧边栏'} aria-label={collapsed ? '展开侧边栏' : '收起侧边栏'}
title={collapsed ? '展开侧边栏' : '收起侧边栏'} title={collapsed ? '展开侧边栏' : '收起侧边栏'}
> >