feat(admin): RouteDock 页签增强——pathname 唯一 key、20 上限 LRU、关闭其他/左/右/全部、登出清理、持久化校验收紧
This commit is contained in:
@@ -14,10 +14,12 @@ import {
|
||||
useSortable,
|
||||
} from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
import { Tabs, type TabsProps } from 'antd';
|
||||
import { Button, Dropdown, Tabs, type TabsProps } from 'antd';
|
||||
import { DownOutlined } from '@ant-design/icons';
|
||||
import type { Location } from 'react-router';
|
||||
import type { AppMenuItem } from '../../auth/menu-policy';
|
||||
import { useAppStore } from '../../store';
|
||||
import { upsertDockTab } from './dockTabs';
|
||||
|
||||
interface RouteDockProps {
|
||||
location: Location;
|
||||
@@ -72,20 +74,16 @@ const DraggableTabNode: React.FC<Readonly<DraggableTabNodeProps>> = ({ ...props
|
||||
};
|
||||
|
||||
const RouteDock: React.FC<RouteDockProps> = ({ location, menuItems, onNavigate, draggable }) => {
|
||||
const activeKey = `${location.pathname}${location.search}`;
|
||||
// 与 RouteKeeper 缓存 key 保持一致:只按 pathname 建 tab,避免 query 变化产生重复页签。
|
||||
const activeKey = 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;
|
||||
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));
|
||||
});
|
||||
const label = getRouteLabel(menuItems, location.pathname);
|
||||
setRouteDockTabs((currentTabs) => upsertDockTab(currentTabs, activeKey, label));
|
||||
}, [activeKey, location.pathname, menuItems, setRouteDockTabs]);
|
||||
|
||||
const tabItems = useMemo<NonNullable<TabsProps['items']>>(
|
||||
@@ -109,6 +107,27 @@ const RouteDock: React.FC<RouteDockProps> = ({ location, menuItems, onNavigate,
|
||||
}
|
||||
};
|
||||
|
||||
const closeOthers = () => {
|
||||
setRouteDockTabs(tabs.filter((tab) => tab.key === activeKey));
|
||||
};
|
||||
|
||||
const closeLeft = () => {
|
||||
const index = tabs.findIndex((tab) => tab.key === activeKey);
|
||||
if (index <= 0) return;
|
||||
setRouteDockTabs(tabs.filter((_, i) => i >= index));
|
||||
};
|
||||
|
||||
const closeRight = () => {
|
||||
const index = tabs.findIndex((tab) => tab.key === activeKey);
|
||||
if (index < 0 || index === tabs.length - 1) return;
|
||||
setRouteDockTabs(tabs.filter((_, i) => i <= index));
|
||||
};
|
||||
|
||||
const closeAll = () => {
|
||||
setRouteDockTabs([]);
|
||||
onNavigate('/dashboard');
|
||||
};
|
||||
|
||||
const handleDragEnd = ({ active, over }: DragEndEvent) => {
|
||||
if (!over || active.id === over.id) return;
|
||||
setRouteDockTabs((currentTabs) => {
|
||||
@@ -166,6 +185,32 @@ const RouteDock: React.FC<RouteDockProps> = ({ location, menuItems, onNavigate,
|
||||
if (action === 'remove') closeTab(String(targetKey));
|
||||
}}
|
||||
renderTabBar={renderTabBar}
|
||||
tabBarExtraContent={
|
||||
tabs.length > 1 ? (
|
||||
<Dropdown
|
||||
trigger={['click']}
|
||||
menu={{
|
||||
items: [
|
||||
{ key: 'close-others', label: '关闭其他' },
|
||||
{ key: 'close-left', label: '关闭左侧' },
|
||||
{ key: 'close-right', label: '关闭右侧' },
|
||||
{ type: 'divider' },
|
||||
{ key: 'close-all', label: '关闭全部' },
|
||||
],
|
||||
onClick: ({ key }) => {
|
||||
if (key === 'close-others') closeOthers();
|
||||
else if (key === 'close-left') closeLeft();
|
||||
else if (key === 'close-right') closeRight();
|
||||
else if (key === 'close-all') closeAll();
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Button type="text" size="small" icon={<DownOutlined />} aria-label="更多页签操作">
|
||||
更多
|
||||
</Button>
|
||||
</Dropdown>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
</nav>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user