110 lines
3.1 KiB
TypeScript
110 lines
3.1 KiB
TypeScript
/**
|
|
* KRA - Tabs Component
|
|
* Tab-based page management
|
|
*/
|
|
import React, { useState, useEffect } from 'react';
|
|
import { Tabs as AntTabs, Dropdown } from 'antd';
|
|
import type { MenuProps } from 'antd';
|
|
import { useLocation, history } from '@umijs/max';
|
|
import { CloseOutlined, ReloadOutlined, CloseCircleOutlined } from '@ant-design/icons';
|
|
|
|
interface TabItem {
|
|
key: string;
|
|
label: string;
|
|
closable?: boolean;
|
|
}
|
|
|
|
const Tabs: React.FC = () => {
|
|
const location = useLocation();
|
|
const [activeKey, setActiveKey] = useState(location.pathname);
|
|
const [tabs, setTabs] = useState<TabItem[]>([
|
|
{ key: '/dashboard', label: '首页', closable: false },
|
|
]);
|
|
|
|
useEffect(() => {
|
|
const path = location.pathname;
|
|
setActiveKey(path);
|
|
|
|
// Add new tab if not exists
|
|
if (!tabs.find((tab) => tab.key === path) && path !== '/') {
|
|
const title = document.title?.replace(' - Kratos Admin', '') || path;
|
|
setTabs((prev) => [...prev, { key: path, label: title, closable: true }]);
|
|
}
|
|
}, [location.pathname]);
|
|
|
|
const handleTabChange = (key: string) => {
|
|
setActiveKey(key);
|
|
history.push(key);
|
|
};
|
|
|
|
const handleTabEdit = (targetKey: string | React.MouseEvent | React.KeyboardEvent, action: 'add' | 'remove') => {
|
|
if (action === 'remove' && typeof targetKey === 'string') {
|
|
const newTabs = tabs.filter((tab) => tab.key !== targetKey);
|
|
setTabs(newTabs);
|
|
|
|
// Navigate to previous tab if current tab is closed
|
|
if (targetKey === activeKey && newTabs.length > 0) {
|
|
const lastTab = newTabs[newTabs.length - 1];
|
|
setActiveKey(lastTab.key);
|
|
history.push(lastTab.key);
|
|
}
|
|
}
|
|
};
|
|
|
|
const closeOthers = () => {
|
|
const newTabs = tabs.filter((tab) => !tab.closable || tab.key === activeKey);
|
|
setTabs(newTabs);
|
|
};
|
|
|
|
const closeAll = () => {
|
|
const newTabs = tabs.filter((tab) => !tab.closable);
|
|
setTabs(newTabs);
|
|
if (newTabs.length > 0) {
|
|
setActiveKey(newTabs[0].key);
|
|
history.push(newTabs[0].key);
|
|
}
|
|
};
|
|
|
|
const contextMenuItems: MenuProps['items'] = [
|
|
{ key: 'reload', icon: <ReloadOutlined />, label: '刷新当前' },
|
|
{ key: 'closeOthers', icon: <CloseCircleOutlined />, label: '关闭其他' },
|
|
{ key: 'closeAll', icon: <CloseOutlined />, label: '关闭所有' },
|
|
];
|
|
|
|
const handleContextMenu: MenuProps['onClick'] = ({ key }) => {
|
|
switch (key) {
|
|
case 'reload':
|
|
history.push(activeKey);
|
|
break;
|
|
case 'closeOthers':
|
|
closeOthers();
|
|
break;
|
|
case 'closeAll':
|
|
closeAll();
|
|
break;
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Dropdown menu={{ items: contextMenuItems, onClick: handleContextMenu }} trigger={['contextMenu']}>
|
|
<div style={{ padding: '0 16px', background: '#fff', borderBottom: '1px solid #f0f0f0' }}>
|
|
<AntTabs
|
|
type="editable-card"
|
|
hideAdd
|
|
activeKey={activeKey}
|
|
onChange={handleTabChange}
|
|
onEdit={handleTabEdit}
|
|
items={tabs.map((tab) => ({
|
|
key: tab.key,
|
|
label: tab.label,
|
|
closable: tab.closable,
|
|
}))}
|
|
size="small"
|
|
/>
|
|
</div>
|
|
</Dropdown>
|
|
);
|
|
};
|
|
|
|
export default Tabs;
|