refactor: 前端登录/权限/界面状态迁移至 zustand

This commit is contained in:
2026-08-04 14:41:27 +08:00
parent ce1dcc07ea
commit f07ffdc64c
39 changed files with 970 additions and 211 deletions

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useMemo, useState } from 'react';
import React, { useEffect, useMemo } from 'react';
import type { DragEndEvent } from '@dnd-kit/core';
import { closestCenter, DndContext, PointerSensor, useSensor, useSensors } from '@dnd-kit/core';
import {
@@ -12,13 +12,7 @@ import { Tabs } from 'antd';
import type { TabsProps } from 'antd';
import type { Location } from 'react-router-dom';
import type { AppMenuItem } from '../../auth/menu-policy';
const STORAGE_KEY = 'gongxue-route-dock';
interface DockTab {
key: string;
label: string;
}
import { useAppStore } from '../../store';
interface RouteDockProps {
location: Location;
@@ -50,19 +44,6 @@ function getRouteLabel(items: readonly AppMenuItem[], pathname: string): string
return pathname === '/' ? '首页' : '页面';
}
function readStoredTabs(): DockTab[] {
try {
const parsed = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
if (!Array.isArray(parsed)) return [];
return parsed.filter(
(tab): tab is DockTab =>
typeof tab?.key === 'string' && tab.key.startsWith('/') && typeof tab?.label === 'string',
);
} catch {
return [];
}
}
const DraggableTabNode: React.FC<Readonly<DraggableTabNodeProps>> = ({ ...props }) => {
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
id: props['data-node-key'],
@@ -87,28 +68,20 @@ const DraggableTabNode: React.FC<Readonly<DraggableTabNodeProps>> = ({ ...props
const RouteDock: React.FC<RouteDockProps> = ({ location, menuItems, onNavigate, draggable }) => {
const activeKey = `${location.pathname}${location.search}`;
const [tabs, setTabs] = useState<DockTab[]>(() => {
const storedTabs = readStoredTabs();
if (location.pathname === '/') return storedTabs;
if (storedTabs.some((tab) => tab.key === activeKey)) return storedTabs;
return [...storedTabs, { key: activeKey, label: getRouteLabel(menuItems, location.pathname) }];
});
const tabs = useAppStore((state) => state.routeDockTabs);
const setRouteDockTabs = useAppStore((state) => state.setRouteDockTabs);
const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 8 } }));
useEffect(() => {
if (location.pathname === '/') return;
setTabs((currentTabs) => {
setRouteDockTabs((currentTabs) => {
const label = getRouteLabel(menuItems, location.pathname);
const existing = currentTabs.find((tab) => tab.key === activeKey);
if (!existing) return [...currentTabs, { key: activeKey, label }];
if (existing.label === label) return currentTabs;
return currentTabs.map((tab) => (tab.key === activeKey ? { ...tab, label } : tab));
});
}, [activeKey, location.pathname, menuItems]);
useEffect(() => {
localStorage.setItem(STORAGE_KEY, JSON.stringify(tabs));
}, [tabs]);
}, [activeKey, location.pathname, menuItems, setRouteDockTabs]);
const tabItems = useMemo<NonNullable<TabsProps['items']>>(
() =>
@@ -124,7 +97,7 @@ const RouteDock: React.FC<RouteDockProps> = ({ location, menuItems, onNavigate,
const targetIndex = tabs.findIndex((tab) => tab.key === targetKey);
if (targetIndex < 0 || tabs.length === 1) return;
const nextTabs = tabs.filter((tab) => tab.key !== targetKey);
setTabs(nextTabs);
setRouteDockTabs(nextTabs);
if (targetKey === activeKey) {
const nextActiveTab = nextTabs[Math.min(targetIndex, nextTabs.length - 1)];
if (nextActiveTab) onNavigate(nextActiveTab.key);
@@ -133,7 +106,7 @@ const RouteDock: React.FC<RouteDockProps> = ({ location, menuItems, onNavigate,
const handleDragEnd = ({ active, over }: DragEndEvent) => {
if (!over || active.id === over.id) return;
setTabs((currentTabs) => {
setRouteDockTabs((currentTabs) => {
const activeIndex = currentTabs.findIndex((tab) => tab.key === active.id);
const overIndex = currentTabs.findIndex((tab) => tab.key === over.id);
return activeIndex < 0 || overIndex < 0