@zat-design/sisyphus-react 4.2.0 → 4.3.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,29 +1,57 @@
1
- import { useEffect, useState } from 'react';
1
+ import React, { createElement as _createElement } from 'react';
2
2
  import { Tabs } from 'antd';
3
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
- /**
5
- * 标签页头部:监听 window 滚动(实际滚动在 window/documentElement,非 body),
6
- * 滚动时添加阴影类名;仅此组件随滚动重渲染,不带动父组件内容区。
7
- */
3
+ import { closestCenter, DndContext, PointerSensor, useSensor } from '@dnd-kit/core';
4
+ import { horizontalListSortingStrategy, SortableContext, useSortable } from '@dnd-kit/sortable';
5
+ import { CSS } from '@dnd-kit/utilities';
6
+ import { jsx as _jsx } from "react/jsx-runtime";
7
+ const DraggableTabNode = props => {
8
+ const {
9
+ attributes,
10
+ listeners,
11
+ setNodeRef,
12
+ transform,
13
+ transition
14
+ } = useSortable({
15
+ id: props['data-node-key']
16
+ });
17
+ const style = {
18
+ ...props.style,
19
+ transform: CSS.Translate.toString(transform),
20
+ transition,
21
+ cursor: 'move'
22
+ };
23
+ return /*#__PURE__*/React.cloneElement(props.children, {
24
+ ref: setNodeRef,
25
+ style,
26
+ ...attributes,
27
+ ...listeners
28
+ });
29
+ };
8
30
  export function TabsHeader({
9
31
  activeKey,
10
32
  tabsItems,
11
33
  onTabChange,
12
- onTabEdit
34
+ onTabEdit,
35
+ draggable = true,
36
+ onReorder
13
37
  }) {
14
- const [windowScrollY, setWindowScrollY] = useState(() => typeof window !== 'undefined' ? window.scrollY : 0);
15
- useEffect(() => {
16
- if (typeof window === 'undefined') return;
17
- const onScroll = () => setWindowScrollY(window.scrollY);
18
- window.addEventListener('scroll', onScroll, {
19
- passive: true
20
- });
21
- return () => window.removeEventListener('scroll', onScroll);
22
- }, []);
23
- const isScrolled = windowScrollY > 0;
24
- return /*#__PURE__*/_jsxs("div", {
38
+ const sensor = useSensor(PointerSensor, {
39
+ activationConstraint: {
40
+ distance: 10
41
+ }
42
+ });
43
+ const onDragEnd = ({
44
+ active,
45
+ over
46
+ }) => {
47
+ if (over && active.id !== over.id) {
48
+ onReorder?.(String(active.id), String(over.id));
49
+ }
50
+ };
51
+ const tabKeys = (tabsItems ?? []).map(item => item.key);
52
+ return /*#__PURE__*/_jsx("div", {
25
53
  className: "pro-layout-tabs-header",
26
- children: [/*#__PURE__*/_jsx(Tabs, {
54
+ children: /*#__PURE__*/_jsx(Tabs, {
27
55
  activeKey: activeKey,
28
56
  onChange: onTabChange,
29
57
  onEdit: onTabEdit,
@@ -35,10 +63,23 @@ export function TabsHeader({
35
63
  popup: {
36
64
  root: 'pro-layout-tabs-dropdown-menu'
37
65
  }
38
- }
39
- }), isScrolled && /*#__PURE__*/_jsx("div", {
40
- className: "pro-layout-tabs-header-shadow",
41
- "aria-hidden": true
42
- })]
66
+ },
67
+ renderTabBar: draggable ? (tabBarProps, DefaultTabBar) => /*#__PURE__*/_jsx(DndContext, {
68
+ sensors: [sensor],
69
+ onDragEnd: onDragEnd,
70
+ collisionDetection: closestCenter,
71
+ children: /*#__PURE__*/_jsx(SortableContext, {
72
+ items: tabKeys,
73
+ strategy: horizontalListSortingStrategy,
74
+ children: /*#__PURE__*/_jsx(DefaultTabBar, {
75
+ ...tabBarProps,
76
+ children: node => /*#__PURE__*/_createElement(DraggableTabNode, {
77
+ ...node.props,
78
+ key: node.key
79
+ }, node)
80
+ })
81
+ })
82
+ }) : undefined
83
+ })
43
84
  });
44
85
  }
@@ -1,9 +1,10 @@
1
1
  import { useState, useCallback, useEffect, useRef } from 'react';
2
2
  import { message } from 'antd';
3
+ import { arrayMove } from '@dnd-kit/sortable';
3
4
  import { DEFAULT_TABS_CONFIG } from "../propTypes";
4
5
  import { useTabsCache } from "./useTabsCache";
5
6
  import locale, { formatMessage } from "../../../../locale";
6
- import { createTabFromMenu, generateTabId, shouldOpenExternal, handleExternalOpen, checkTabLimit, getRightTabs, flattenMenuData, isLeafMenuItem } from "../utils";
7
+ import { createTabFromMenu, generateTabId, shouldOpenExternal, handleExternalOpen, checkTabLimit, getRightTabs, flattenMenuData, canOpenAsTab, getMenuRoute } from "../utils";
7
8
 
8
9
  /**
9
10
  * 标签页状态管理Hook
@@ -106,8 +107,8 @@ export const useTabsState = options => {
106
107
  forceNew = false
107
108
  } = options || {};
108
109
 
109
- // 只有最后一级菜单(叶子节点)才能添加标签页
110
- if (!isLeafMenuItem(menuItem)) {
110
+ // 非叶子节点默认不入签,仅根节点特例可入签
111
+ if (!canOpenAsTab(menuItem)) {
111
112
  return false;
112
113
  }
113
114
 
@@ -118,7 +119,8 @@ export const useTabsState = options => {
118
119
 
119
120
  // 如果 forceNew = false(默认),检查是否已存在相同的标签页
120
121
  if (!forceNew) {
121
- const existingTab = state.tabsList.find(tab => tab.menuItem?.code === menuItem.code || tab.url === menuItem.url);
122
+ const menuRoute = getMenuRoute(menuItem);
123
+ const existingTab = state.tabsList.find(tab => tab.menuItem?.code === menuItem.code || tab.url === menuRoute);
122
124
  if (existingTab) {
123
125
  // 如果已存在,可以添加(会切换到已存在的标签页)
124
126
  return true;
@@ -141,48 +143,41 @@ export const useTabsState = options => {
141
143
  forceNew = false
142
144
  } = options || {};
143
145
  setState(prevState => {
144
- // 只有最后一级菜单(叶子节点)才能添加标签页
145
- if (!isLeafMenuItem(menuItem)) {
146
- return prevState;
147
- }
148
-
149
- // 检查是否需要外部跳转
146
+ if (!canOpenAsTab(menuItem)) return prevState;
150
147
  if (shouldOpenExternal(menuItem)) {
151
148
  handleExternalOpen(menuItem);
152
149
  return prevState;
153
150
  }
154
- const existingIds = prevState.tabsList.map(tab => tab.id);
155
- const tabId = generateTabId(menuItem, existingIds);
156
151
 
157
- // 如果 forceNew = false(默认),检查是否已存在相同的标签页
158
- if (!forceNew) {
159
- const existingTab = prevState.tabsList.find(tab => tab.menuItem?.code === menuItem.code || tab.url === menuItem.url);
160
- if (existingTab) {
161
- // 如果已存在,直接切换到该标签页
162
- const newState = {
163
- ...prevState,
164
- activeKey: existingTab.id,
165
- activeTabInfo: existingTab,
166
- activeComponent: existingTab.menuItem?.code || existingTab.id
167
- };
168
- return newState;
169
- }
152
+ // 非强制新建时,查找已存在的标签页并切换
153
+ const menuRoute = getMenuRoute(menuItem);
154
+ const existingTab = !forceNew ? prevState.tabsList.find(tab => tab.menuItem?.code === menuItem.code || tab.url === menuRoute) : undefined;
155
+ if (existingTab) {
156
+ // 若来源菜单明确标记 closable: false 而已有 tab 未继承,则修正
157
+ const correctedTab = menuItem.closable === false && existingTab.closable !== false ? {
158
+ ...existingTab,
159
+ closable: false
160
+ } : existingTab;
161
+ const updatedTabsList = correctedTab !== existingTab ? prevState.tabsList.map(t => t.id === existingTab.id ? correctedTab : t) : prevState.tabsList;
162
+ return {
163
+ ...prevState,
164
+ tabsList: updatedTabsList,
165
+ activeKey: existingTab.id,
166
+ activeTabInfo: correctedTab,
167
+ activeComponent: existingTab.menuItem?.code || existingTab.id
168
+ };
170
169
  }
171
-
172
- // 检查是否超出限制
173
170
  if (checkTabLimit(prevState.tabsList, finalConfig.max)) {
174
- // 达到最大值时,不添加新标签页,显示提示信息
175
- const messageText = formatMessage(locale.ProLayout.tabMaxLimitMessage, {
171
+ message.info(formatMessage(locale.ProLayout.tabMaxLimitMessage, {
176
172
  max: finalConfig.max
177
- });
178
- message.info(messageText);
173
+ }));
179
174
  return prevState;
180
175
  }
181
-
182
- // 创建新标签页
176
+ const existingIds = prevState.tabsList.map(tab => tab.id);
177
+ const tabId = generateTabId(menuItem, existingIds);
183
178
  const newTab = createTabFromMenu(menuItem, prevState.newTabIndex);
184
179
  newTab.id = tabId;
185
- const newState = {
180
+ return {
186
181
  ...prevState,
187
182
  tabsList: [...prevState.tabsList, newTab],
188
183
  activeKey: tabId,
@@ -190,7 +185,6 @@ export const useTabsState = options => {
190
185
  newTabIndex: prevState.newTabIndex + 1,
191
186
  activeComponent: menuItem.code || tabId
192
187
  };
193
- return newState;
194
188
  });
195
189
  }, [finalConfig]);
196
190
 
@@ -316,22 +310,38 @@ export const useTabsState = options => {
316
310
  shouldSkipSaveRef.current = true;
317
311
  clearCache();
318
312
 
319
- // 如果关闭后没有标签页了,直接调用 onTabChange
320
- if (newTabs.length === 0 && finalConfig.onTabChange) {
313
+ // 直接调用 onTabChange(useEffect 因 shouldSkipSaveRef 会跳过)
314
+ if (finalConfig.onTabChange) {
315
+ const firstTab = newTabs.length > 0 ? newTabs[0] : undefined;
321
316
  setTimeout(() => {
322
- finalConfig.onTabChange('', undefined, []);
317
+ finalConfig.onTabChange(firstTab?.id ?? '', firstTab, newTabs);
323
318
  }, 0);
324
319
  }
325
320
  return {
326
321
  ...prevState,
327
322
  tabsList: newTabs,
328
323
  activeKey: newActiveKey,
329
- activeTabInfo: undefined,
324
+ activeTabInfo: newTabs.length > 0 ? newTabs[0] : undefined,
330
325
  activeComponent: newActiveComponent
331
326
  };
332
327
  });
333
328
  }, [clearCache, finalConfig]);
334
329
 
330
+ /**
331
+ * 重排标签页顺序(拖拽结束后调用)
332
+ */
333
+ const reorderTabs = useCallback((activeId, overId) => {
334
+ setState(prevState => {
335
+ const activeIndex = prevState.tabsList.findIndex(tab => tab.id === activeId);
336
+ const overIndex = prevState.tabsList.findIndex(tab => tab.id === overId);
337
+ if (activeIndex === -1 || overIndex === -1 || activeIndex === overIndex) return prevState;
338
+ return {
339
+ ...prevState,
340
+ tabsList: arrayMove(prevState.tabsList, activeIndex, overIndex)
341
+ };
342
+ });
343
+ }, []);
344
+
335
345
  /**
336
346
  * 重置状态
337
347
  */
@@ -347,40 +357,23 @@ export const useTabsState = options => {
347
357
  // 监听 URL 变化并同步 Tabs (仅在初始化和 location 变化时)
348
358
  // 放在最后以确保可以使用 addTab 和 switchTab
349
359
  useEffect(() => {
350
- // 确保数据源存在
351
360
  if (!dataSource || !('menus' in dataSource)) return;
352
361
  const currentPath = window.location.pathname;
353
-
354
- // 如果当前没有 activeKey 或者 activeKey 对应的 URL 与当前 URL 不一致
355
362
  const activeTab = state.tabsList.find(tab => tab.id === state.activeKey);
356
-
357
- // 如果当前已经在正确的 Tab 上,无需处理
358
363
  if (activeTab && activeTab.url === currentPath) return;
359
-
360
- // 在菜单中查找当前 URL 对应的菜单项
361
364
  const flatMenus = flattenMenuData(dataSource.menus || []);
362
- const targetMenu = flatMenus.find(item => item.url === currentPath);
363
- if (targetMenu) {
364
- // 只有最后一级菜单(叶子节点)才能添加标签页
365
- if (!isLeafMenuItem(targetMenu)) {
366
- return;
367
- }
368
-
369
- // 检查是否已在 Tabs 中
370
- const existingTab = state.tabsList.find(tab => tab.menuItem?.code === targetMenu.code || tab.url === targetMenu.url);
371
- if (existingTab) {
372
- // 如果已存在,切换到该 Tab
373
- if (state.activeKey !== existingTab.id) {
374
- switchTab(existingTab.id);
375
- }
376
- } else {
377
- // 如果不存在,添加新 Tab
378
- addTab(targetMenu);
379
- }
365
+ const targetMenu = flatMenus.find(item => getMenuRoute(item) === currentPath);
366
+ if (!targetMenu || !canOpenAsTab(targetMenu)) return;
367
+ const existingTab = state.tabsList.find(tab => tab.menuItem?.code === targetMenu.code || tab.url === getMenuRoute(targetMenu));
368
+ if (existingTab) {
369
+ if (state.activeKey !== existingTab.id) switchTab(existingTab.id);
370
+ return;
380
371
  }
372
+ addTab(targetMenu);
381
373
  }, [dataSource, state.tabsList, state.activeKey, addTab, switchTab]);
382
374
  return {
383
375
  state,
376
+ isInitialized,
384
377
  addTab,
385
378
  canAddTab,
386
379
  removeTab,
@@ -388,6 +381,7 @@ export const useTabsState = options => {
388
381
  closeOtherTabs,
389
382
  closeRightTabs,
390
383
  closeAllTabs,
391
- resetTabs
384
+ resetTabs,
385
+ reorderTabs
392
386
  };
393
387
  };
@@ -1,5 +1,7 @@
1
- import { useCallback, useMemo, forwardRef, useImperativeHandle, useState, useEffect } from 'react';
1
+ import { useCallback, useMemo, forwardRef, useImperativeHandle, useState, useEffect, useRef } from 'react';
2
+ import { createPortal } from 'react-dom';
2
3
  import { useTabsState } from "./hooks/useTabsState";
4
+ import { flattenMenuData } from "./utils";
3
5
  import { TabItemComponent } from "./components/TabItem";
4
6
  import { TabsHeader } from "./components/TabsHeader";
5
7
  import { TabsContext } from "./components/TabsContext";
@@ -12,18 +14,21 @@ const TabsManager = /*#__PURE__*/forwardRef(({
12
14
  config,
13
15
  children,
14
16
  dataSource,
15
- originalOnMenuClick
17
+ originalOnMenuClick,
18
+ tabsBarContainer,
19
+ onTabsChange
16
20
  }, ref) => {
17
- // 使用标签页状态管理Hook
18
21
  const {
19
22
  state,
23
+ isInitialized,
20
24
  addTab,
21
25
  canAddTab,
22
26
  removeTab,
23
27
  switchTab,
24
28
  closeOtherTabs,
25
29
  closeRightTabs,
26
- closeAllTabs
30
+ closeAllTabs,
31
+ reorderTabs
27
32
  } = useTabsState({
28
33
  config,
29
34
  dataSource
@@ -44,14 +49,42 @@ const TabsManager = /*#__PURE__*/forwardRef(({
44
49
  }
45
50
  }, [state.activeKey]);
46
51
 
52
+ // 通知父级(ProLayout)当前是否有 Tab
53
+ useEffect(() => {
54
+ onTabsChange?.(state.tabsList.length > 0);
55
+ }, [state.tabsList.length, onTabsChange]);
56
+
57
+ // 处理声明式固定标签页:config.fixed 中的 code 在初始化完成后自动添加,强制 closable=false
58
+ // 用 ref 持有 addTab,避免 addTab 引用变化引起无限循环
59
+ const addTabRef = useRef(addTab);
60
+ addTabRef.current = addTab;
61
+ useEffect(() => {
62
+ if (!isInitialized || !config.fixed || config.fixed.length === 0) return;
63
+ const sourceMenus = Array.isArray(dataSource?.menus) ? flattenMenuData(dataSource.menus) : [];
64
+
65
+ // menus 为空说明异步数据尚未返回,等 dataSource 变化后重跑,避免用 code 创建占位 tab
66
+ if (sourceMenus.length === 0) return;
67
+ config.fixed.forEach(code => {
68
+ const matchedMenu = sourceMenus.find(item => item.code === code);
69
+ const menuItem = matchedMenu ? {
70
+ ...matchedMenu,
71
+ closable: false
72
+ } : {
73
+ id: Date.now(),
74
+ code,
75
+ name: code,
76
+ url: `/${code}`,
77
+ closable: false
78
+ };
79
+ addTabRef.current(menuItem);
80
+ });
81
+ }, [isInitialized, config.fixed, dataSource]);
82
+
47
83
  // 处理菜单点击 - 拦截原有的菜单点击逻辑
48
84
  const handleMenuClick = useCallback(params => {
49
85
  if (params.item) {
50
- // 添加到标签页
51
86
  addTab(params.item);
52
87
  }
53
-
54
- // 如果有原始的菜单点击处理函数,也调用它
55
88
  originalOnMenuClick?.(params);
56
89
  }, [addTab, originalOnMenuClick]);
57
90
 
@@ -63,13 +96,18 @@ const TabsManager = /*#__PURE__*/forwardRef(({
63
96
  name,
64
97
  extra
65
98
  } = params;
66
- const menuItem = {
99
+ const sourceMenus = Array.isArray(dataSource?.menus) ? flattenMenuData(dataSource.menus) : [];
100
+ const matchedMenu = sourceMenus.find(item => item.code === code);
101
+ const menuItem = matchedMenu ? {
102
+ ...matchedMenu,
103
+ // 允许外部覆盖展示名称和附加数据,但保留菜单原始 closable/url 等配置
104
+ name: name || matchedMenu.name,
105
+ extra: extra ?? matchedMenu.extra
106
+ } : {
67
107
  id: Date.now(),
68
- // 生成临时 ID
69
108
  code,
70
109
  name,
71
110
  url: `/${code}`,
72
- // 生成 URL
73
111
  extra
74
112
  };
75
113
  addTab(menuItem, options);
@@ -80,14 +118,10 @@ const TabsManager = /*#__PURE__*/forwardRef(({
80
118
  activeTabInfo: state.tabsList.find(tab => tab.id === state.activeKey),
81
119
  activeComponent: state.activeComponent
82
120
  })
83
- }), [addTab, removeTab, state.tabsList, state.activeKey, state.activeComponent]);
84
-
85
- // 处理标签切换
121
+ }), [addTab, removeTab, state.tabsList, state.activeKey, state.activeComponent, dataSource]);
86
122
  const handleTabChange = useCallback(activeKey => {
87
123
  switchTab(activeKey);
88
124
  }, [switchTab]);
89
-
90
- // 处理标签关闭
91
125
  const handleTabEdit = useCallback((targetKey, action) => {
92
126
  if (action === 'remove') {
93
127
  removeTab(targetKey);
@@ -111,12 +145,11 @@ const TabsManager = /*#__PURE__*/forwardRef(({
111
145
  menuItems: config?.menuItems,
112
146
  tabMenuClick: config?.tabMenuClick
113
147
  }),
114
- closable: tab.closable,
115
- children: null // 内容在 renderContent() 中渲染
148
+ closable: false,
149
+ // 关闭 antd 内置 ×,由 TabItemComponent 自定义按钮处理
150
+ children: null
116
151
  }));
117
152
  }, [state.tabsList, state.activeKey, config?.menuItems, config?.tabMenuClick, switchTab, removeTab, closeOtherTabs, closeRightTabs, closeAllTabs]);
118
-
119
- // 从 config 中获取组件解析函数和空状态组件
120
153
  const activeComponent = config?.activeComponent;
121
154
  const emptyComponent = config?.empty;
122
155
 
@@ -127,8 +160,6 @@ const TabsManager = /*#__PURE__*/forwardRef(({
127
160
  children: [state.tabsList.map(tab => {
128
161
  const isActive = tab.id === state.activeKey;
129
162
  const hasVisited = visitedTabIds.has(tab.id);
130
-
131
- // 如果既不是当前激活,也没有访问过,则只渲染占位符(懒加载)
132
163
  if (!isActive && !hasVisited) {
133
164
  return /*#__PURE__*/_jsx("div", {
134
165
  className: "tab-pane hidden",
@@ -136,12 +167,9 @@ const TabsManager = /*#__PURE__*/forwardRef(({
136
167
  }, tab.id);
137
168
  }
138
169
  let content = children;
139
-
140
- // 如果提供了组件解析函数,尝试解析组件
141
170
  if (activeComponent && tab.menuItem?.code) {
142
171
  const ResolvedComponent = activeComponent(tab.menuItem.code);
143
172
  if (ResolvedComponent) {
144
- // 将 extra 中的所有属性作为 props 传递给组件
145
173
  content = /*#__PURE__*/_jsx(ResolvedComponent, {
146
174
  ...(tab.menuItem.extra || {})
147
175
  });
@@ -160,23 +188,27 @@ const TabsManager = /*#__PURE__*/forwardRef(({
160
188
  });
161
189
  };
162
190
 
163
- // 暴露方法给父组件
191
+ // TabsHeader 节点:有 tabsBarContainer 时用 Portal 渲染到外部,否则原位 fallback
192
+ const tabsHeaderNode = state.tabsList.length > 0 ? /*#__PURE__*/_jsx(TabsHeader, {
193
+ activeKey: state.activeKey || undefined,
194
+ tabsItems: tabsItems,
195
+ onTabChange: handleTabChange,
196
+ onTabEdit: handleTabEdit,
197
+ draggable: config?.draggable !== false,
198
+ onReorder: reorderTabs
199
+ }) : null;
164
200
  useImperativeHandle(ref, () => ({
165
201
  handleMenuClick,
166
- canAddTab
167
- }), [handleMenuClick, canAddTab]);
168
- return /*#__PURE__*/_jsx(TabsContext.Provider, {
202
+ canAddTab,
203
+ getTabInfo: tabsInstance.getTabInfo
204
+ }), [handleMenuClick, canAddTab, tabsInstance]);
205
+ return /*#__PURE__*/_jsxs(TabsContext.Provider, {
169
206
  value: tabsInstance,
170
- children: /*#__PURE__*/_jsxs("div", {
207
+ children: [tabsBarContainer ? /*#__PURE__*/createPortal(tabsHeaderNode, tabsBarContainer) : tabsHeaderNode, /*#__PURE__*/_jsx("div", {
171
208
  className: "pro-layout-tabs",
172
209
  "data-testid": "tabs-manager",
173
- children: [state.tabsList.length > 0 && /*#__PURE__*/_jsx(TabsHeader, {
174
- activeKey: state.activeKey || undefined,
175
- tabsItems: tabsItems,
176
- onTabChange: handleTabChange,
177
- onTabEdit: handleTabEdit
178
- }), renderContent()]
179
- })
210
+ children: renderContent()
211
+ })]
180
212
  });
181
213
  });
182
214
  TabsManager.displayName = 'TabsManager';
@@ -14,6 +14,10 @@ export interface TabsManagerProps {
14
14
  key: string;
15
15
  keyPath: string[];
16
16
  }) => void;
17
+ /** Tab 栏的 Portal 挂载目标(由 ProLayout 提供),不传则原位渲染 */
18
+ tabsBarContainer?: HTMLElement | null;
19
+ /** Tab 有无变化回调,供 ProLayout 控制 wrapper 动画 */
20
+ onTabsChange?: (hasTabs: boolean) => void;
17
21
  }
18
22
  export interface TabContextMenuProps {
19
23
  tabId: string;
@@ -47,6 +51,8 @@ export interface UseTabsStateOptions {
47
51
  export interface UseTabsStateReturn {
48
52
  /** 当前状态 */
49
53
  state: TabsState;
54
+ /** 缓存是否已恢复完成 */
55
+ isInitialized: boolean;
50
56
  /** 添加标签页 */
51
57
  addTab: (menuItem: MenusType, options?: AddTabOptions) => void;
52
58
  /** 检查是否可以添加标签页 */
@@ -63,6 +69,8 @@ export interface UseTabsStateReturn {
63
69
  closeAllTabs: () => void;
64
70
  /** 重置状态 */
65
71
  resetTabs: () => void;
72
+ /** 重排标签页顺序 */
73
+ reorderTabs: (activeId: string, overId: string) => void;
66
74
  }
67
75
  export interface TabsCacheManager {
68
76
  save: (state: TabsState) => void;