@gx-design-vue/pro-layout 0.1.0-alpha.8 → 0.1.0-alpha.9

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.
package/dist/ProLayout.js CHANGED
@@ -117,15 +117,11 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
117
117
  const footerConfig = computed(() => normalizeSectionConfig(props.footerConfig, DEFAULT_LAYOUT_CONFIG.footerConfig));
118
118
  const breadcrumbConfig = computed(() => normalizeSectionConfig(props.breadcrumb, DEFAULT_LAYOUT_CONFIG.breadcrumb));
119
119
  const pageContainerConfig = computed(() => ({
120
- ...DEFAULT_LAYOUT_CONFIG.pageContainerConfig,
120
+ ...DEFAULT_LAYOUT_CONFIG.pageContainer,
121
121
  ...props.pageContainer || {}
122
122
  }));
123
123
  const tabsConf = computed(() => normalizeSectionConfig(props.tabsConfig, DEFAULT_LAYOUT_CONFIG.tabsConfig));
124
124
  const tabsStateConfig = computed(() => tabsConf.value ?? {});
125
- const tabsModel = useVModel(props, "tabs", emit, {
126
- passive: true,
127
- defaultValue: []
128
- });
129
125
  const collapsedWidth = computed(() => collapseConfig.value?.width ?? DEFAULT_LAYOUT_CONFIG.collapse.width);
130
126
  /** Sider 实际渲染宽度:0 表示不可见,否则 = 折叠/展开的真实宽度 */
131
127
  const siderWidth = computed(() => {
@@ -141,7 +137,6 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
141
137
  return collapsed.value ? collapsedWidth.value : width;
142
138
  });
143
139
  const tabsState = useTabsState({
144
- tabs: tabsModel,
145
140
  config: tabsStateConfig,
146
141
  flatMenus: menuState.flatMenus,
147
142
  siderWidth,
@@ -150,9 +145,6 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
150
145
  onTabsChange: (val) => {
151
146
  emit("tabsChange", val);
152
147
  },
153
- onUpdateTabs: (val) => {
154
- emit("update:tabs", val);
155
- },
156
148
  onReloadPage: () => {
157
149
  emit("reloadPage");
158
150
  renderRouterView.value = false;
@@ -346,10 +338,6 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
346
338
  type: Array,
347
339
  required: false
348
340
  },
349
- tabs: {
350
- type: Array,
351
- required: false
352
- },
353
341
  token: { required: false },
354
342
  proStyles: {
355
343
  type: Object,
@@ -494,7 +482,6 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
494
482
  "update:theme",
495
483
  "update:selectedKeys",
496
484
  "update:openKeys",
497
- "update:tabs",
498
485
  "select",
499
486
  "openChange",
500
487
  "darkChange",
@@ -49,8 +49,8 @@ declare const GPageTransition: _$vue.DefineComponent<_$vue.ExtractPropTypes<{
49
49
  default: string;
50
50
  };
51
51
  }>> & Readonly<{}>, {
52
- direction: string;
53
52
  reverse: boolean;
53
+ direction: string;
54
54
  name: string;
55
55
  disabled: boolean;
56
56
  }, SlotsType<GPageTransitionSlots>, {}, {}, string, _$vue.ComponentProvideOptions, true, {}, any>;
@@ -155,7 +155,6 @@ const LayoutTabs = /* @__PURE__ */ defineComponent((props, { slots }) => {
155
155
  }
156
156
  },
157
157
  emits: [
158
- "update:tabs",
159
158
  "tabsChange",
160
159
  "reloadPage",
161
160
  "contentFullscreenChange"
@@ -4,13 +4,11 @@ import { Tab } from "antdv-next/dist/tabs";
4
4
 
5
5
  //#region src/components/Tabs/interface.d.ts
6
6
  interface LayoutTabsEmits {
7
- 'update:tabs': (tabs: LayoutMenuRoute[]) => void;
8
7
  'tabsChange': (tabs: LayoutMenuRoute[]) => void;
9
8
  'reloadPage': () => void;
10
9
  'contentFullscreenChange': (fullscreen: boolean) => void;
11
10
  }
12
11
  interface LayoutTabsEmitsProps {
13
- 'onUpdate:tabs'?: LayoutTabsEmits['update:tabs'];
14
12
  onTabsChange?: LayoutTabsEmits['tabsChange'];
15
13
  onReloadPage?: LayoutTabsEmits['reloadPage'];
16
14
  onContentFullscreenChange?: LayoutTabsEmits['contentFullscreenChange'];
@@ -40,33 +40,33 @@ declare function useLayoutBase(props?: {
40
40
  openKeys: _$vue.ComputedRef<string[]>;
41
41
  proClasses: _$vue.ComputedRef<{
42
42
  content?: string;
43
- root?: string;
44
43
  header?: string;
45
44
  sider?: string;
45
+ breadcrumb?: string;
46
+ logo?: string;
47
+ root?: string;
46
48
  siderChildren?: string;
47
49
  siderHeader?: string;
48
50
  siderFooter?: string;
49
- logo?: string;
50
51
  logoImage?: string;
51
52
  footer?: string;
52
53
  menu?: string;
53
54
  tabs?: string;
54
- breadcrumb?: string;
55
55
  }>;
56
56
  proStyles: _$vue.ComputedRef<{
57
57
  content?: _$vue.CSSProperties;
58
- root?: _$vue.CSSProperties;
59
58
  header?: _$vue.CSSProperties;
60
59
  sider?: _$vue.CSSProperties;
60
+ breadcrumb?: _$vue.CSSProperties;
61
+ logo?: _$vue.CSSProperties;
62
+ root?: _$vue.CSSProperties;
61
63
  siderChildren?: _$vue.CSSProperties;
62
64
  siderHeader?: _$vue.CSSProperties;
63
65
  siderFooter?: _$vue.CSSProperties;
64
- logo?: _$vue.CSSProperties;
65
66
  logoImage?: _$vue.CSSProperties;
66
67
  footer?: _$vue.CSSProperties;
67
68
  menu?: _$vue.CSSProperties;
68
69
  tabs?: _$vue.CSSProperties;
69
- breadcrumb?: _$vue.CSSProperties;
70
70
  }>;
71
71
  breadcrumbRender: _$vue.Ref<(props: LayoutBreadcrumbSlotProps) => any, (props: LayoutBreadcrumbSlotProps) => any>;
72
72
  breadcrumbConfig: _$vue.ComputedRef<LayoutBreadcrumbConfig>;
@@ -8,11 +8,9 @@ interface MenuDataState {
8
8
  normal: LayoutMenuRoute[];
9
9
  }
10
10
  interface UseTabsStateOptions {
11
- /** v-model:tabs 绑定值 */
12
- tabs: Ref<LayoutMenuRoute[]>;
13
11
  /** 标签栏配置 */
14
12
  config: ComputedRef<LayoutTabsConfig>;
15
- /** 扁平菜单列表(用于固定标签扫描 & 过滤无效标签) */
13
+ /** 扁平菜单列表(用于固定标签扫描 & 过滤无效 fixed 标签) */
16
14
  flatMenus: ComputedRef<LayoutMenuRoute[]>;
17
15
  /** 侧边栏展开宽度(支持响应式 — 路由切换时 Sider 可见性可能变化) */
18
16
  siderWidth: ComputedRef<number | string>;
@@ -20,14 +18,13 @@ interface UseTabsStateOptions {
20
18
  isMobile: ComputedRef<boolean>;
21
19
  /** 内容是否全屏 */
22
20
  contentFullscreen: Ref<boolean>;
23
- /** 事件回调 */
21
+ /** 事件回调(单向通知,不再有 v-model 回流) */
24
22
  onTabsChange: (tabs: LayoutMenuRoute[]) => void;
25
- onUpdateTabs: (tabs: LayoutMenuRoute[]) => void;
26
23
  onReloadPage: () => void;
27
24
  onContentFullscreenChange: (fullscreen: boolean) => void;
28
25
  }
29
26
  interface UseTabsStateReturn {
30
- /** 标签原始状态(fixed + normal) */
27
+ /** 标签原始状态(fixed + normal) — 单一真相源 */
31
28
  menuDataState: MenuDataState;
32
29
  /** 合并后的完整标签列表 */
33
30
  dataSource: ComputedRef<LayoutMenuRoute[]>;
@@ -1,5 +1,5 @@
1
1
  import { useInjectLayoutContext } from "../context/index.js";
2
- import { computed, reactive, toRaw, watch } from "vue";
2
+ import { computed, reactive, ref, toRaw, watch } from "vue";
3
3
  import { unit } from "@gx-design-vue/pro-provider";
4
4
  import { isBoolean } from "@gx-design-vue/pro-utils";
5
5
  import { useLocalStorage, useThrottleFn } from "@vueuse/core";
@@ -86,22 +86,8 @@ function isFixedTabRoute(route) {
86
86
  const { fixed } = route?.meta?.tabState ?? {};
87
87
  return isBoolean(fixed) ? fixed : false;
88
88
  }
89
- function splitTabsByFixed(tabs) {
90
- const state = {
91
- fixed: [],
92
- normal: []
93
- };
94
- const seenNames = /* @__PURE__ */ new Set();
95
- tabs?.forEach((tab) => {
96
- if (!tab?.name || seenNames.has(tab.name)) return;
97
- seenNames.add(tab.name);
98
- const normalized = normalizeTabRoute(tab);
99
- state[isFixedTabRoute(normalized) ? "fixed" : "normal"].push(normalized);
100
- });
101
- return state;
102
- }
103
- function getInitialTabsState(tabs, storageTabs, persistent) {
104
- if (tabs.value?.length) return splitTabsByFixed(tabs.value);
89
+ /** 初始状态:仅从 localStorage 恢复(单一外部源,不再有 v-model 优先级) */
90
+ function getInitialTabsState(storageTabs, persistent) {
105
91
  if (persistent && storageTabs.value) return {
106
92
  fixed: storageTabs.value.fixed?.map(normalizeTabRoute) ?? [],
107
93
  normal: storageTabs.value.normal?.map(normalizeTabRoute) ?? []
@@ -111,11 +97,8 @@ function getInitialTabsState(tabs, storageTabs, persistent) {
111
97
  normal: []
112
98
  };
113
99
  }
114
- function getTabsSignature(tabs) {
115
- return JSON.stringify((tabs ?? []).map((tab) => toStorageTabRoute(tab)));
116
- }
117
100
  function useTabsState(options) {
118
- const { tabs, config, flatMenus, siderWidth, isMobile, contentFullscreen, onTabsChange, onUpdateTabs, onReloadPage, onContentFullscreenChange } = options;
101
+ const { config, flatMenus, siderWidth, isMobile, contentFullscreen, onTabsChange, onReloadPage, onContentFullscreenChange } = options;
119
102
  const router = useRouter();
120
103
  const currentRoute = useRoute();
121
104
  const isPersistent = computed(() => config.value.persistent !== false);
@@ -123,11 +106,8 @@ function useTabsState(options) {
123
106
  fixed: [],
124
107
  normal: []
125
108
  });
126
- const initialTabsState = getInitialTabsState(tabs, storageTabs, isPersistent.value);
127
- const menuDataState = reactive({
128
- fixed: initialTabsState.fixed,
129
- normal: initialTabsState.normal
130
- });
109
+ const menuDataState = reactive(getInitialTabsState(storageTabs, isPersistent.value));
110
+ const menusReady = ref(flatMenus.value.length > 0);
131
111
  const dataSource = computed(() => [...menuDataState.fixed, ...menuDataState.normal]);
132
112
  const activeKey = computed(() => currentRoute.name);
133
113
  const tabsWidth = computed(() => {
@@ -168,12 +148,21 @@ function useTabsState(options) {
168
148
  const fixed = checkIsFixed(record);
169
149
  const name = getTabName(record);
170
150
  const tabData = normalizeTabRoute(record);
171
- if (dataSource.value.some((item) => getTabName(item) === name)) changeTabsList(record, {
151
+ const existing = dataSource.value.find((item) => getTabName(item) === name);
152
+ if (!existing) {
153
+ menuDataState[fixed ? "fixed" : "normal"].push(tabData);
154
+ return;
155
+ }
156
+ if (checkIsFixed(existing) !== fixed) {
157
+ changeTabsList(existing, {
158
+ type: "remove",
159
+ params: existing
160
+ });
161
+ menuDataState[fixed ? "fixed" : "normal"].push(mergeTabRoute(existing, tabData));
162
+ } else changeTabsList(record, {
172
163
  type: "merge",
173
164
  params: tabData
174
165
  });
175
- else if (fixed) menuDataState.fixed.push(tabData);
176
- else menuDataState.normal.push(tabData);
177
166
  }
178
167
  /** 导航到最后一个标签 */
179
168
  function navigateToLastTab() {
@@ -265,11 +254,12 @@ function useTabsState(options) {
265
254
  }, 500);
266
255
  watch(flatMenus, (menus) => {
267
256
  if (!menus.length) return;
268
- const menuNameSet = new Set(menus.map((m) => m.name));
257
+ if (!menusReady.value) menusReady.value = true;
258
+ const menuNameSet = new Set(menus.map((menuItem) => menuItem.name));
269
259
  if (menuDataState.fixed.length) menuDataState.fixed = menuDataState.fixed.filter((item) => menuNameSet.has(item.name));
270
- if (menuDataState.normal.length) menuDataState.normal = menuDataState.normal.filter((item) => menuNameSet.has(item.name));
260
+ const openedNames = new Set(dataSource.value.map((tab) => getTabName(tab)));
271
261
  menus.forEach((item) => {
272
- if (checkIsFixed(item)) handleAddTabs(item);
262
+ if (openedNames.has(getTabName(item)) || checkIsFixed(item)) handleAddTabs(item);
273
263
  });
274
264
  }, { immediate: true });
275
265
  watch(() => {
@@ -287,16 +277,9 @@ function useTabsState(options) {
287
277
  }, () => {
288
278
  if (currentRoute?.name) handleAddTabs(currentRoute);
289
279
  }, { immediate: true });
290
- watch(() => getTabsSignature(tabs.value), (modelSignature) => {
291
- if (modelSignature === getTabsSignature(dataSource.value)) return;
292
- const nextState = splitTabsByFixed(tabs.value);
293
- menuDataState.fixed = nextState.fixed;
294
- menuDataState.normal = nextState.normal;
295
- });
296
280
  watch(dataSource, (allTabs) => {
297
- if (isPersistent.value) storageTabs.value = toStorageMenuDataState(menuDataState);
281
+ if (isPersistent.value && menusReady.value) storageTabs.value = toStorageMenuDataState(menuDataState);
298
282
  onTabsChange(allTabs);
299
- onUpdateTabs(allTabs);
300
283
  });
301
284
  return {
302
285
  menuDataState,
@@ -309,20 +292,51 @@ function useTabsState(options) {
309
292
  handleContextMenuAction,
310
293
  checkIsFixed,
311
294
  tabsController: {
312
- clean() {
295
+ getTabs: () => dataSource.value.map((item) => normalizeTabRoute(item)),
296
+ getActive: () => activeKey.value,
297
+ isActive: (name) => activeKey.value === name,
298
+ isFixed: (name) => {
299
+ const route = getCurrentRoute(name);
300
+ return route ? checkIsFixed(route) : false;
301
+ },
302
+ add: (record) => handleAddTabs(record),
303
+ close: (name) => handleTabRemove(name ?? activeKey.value),
304
+ closeOthers: (name) => handleContextMenuAction("closeOthersTabs", name ?? activeKey.value),
305
+ closeLeft: (name) => handleContextMenuAction("closeLeftTabs", name ?? activeKey.value),
306
+ closeRight: (name) => handleContextMenuAction("closeRightTabs", name ?? activeKey.value),
307
+ closeAll: () => handleContextMenuAction("closeAllTabs", activeKey.value),
308
+ clean: () => {
313
309
  menuDataState.fixed = [];
314
310
  menuDataState.normal = [];
315
311
  },
316
- close(name) {
317
- handleTabRemove(name ?? activeKey.value);
312
+ switchTo: (name) => handleTabClick(name),
313
+ toggleFixed: (name, fixed) => {
314
+ const route = getCurrentRoute(name);
315
+ if (!route) return;
316
+ const currentFixed = checkIsFixed(route);
317
+ if ((fixed ?? !currentFixed) !== currentFixed) handleContextMenuAction("fixed", name);
318
318
  },
319
- update(record, operation) {
320
- changeTabsList(record, {
321
- type: operation.type,
322
- addType: operation.addType,
323
- params: operation.params ?? record
324
- });
325
- }
319
+ reload: (name) => {
320
+ const target = name ?? activeKey.value;
321
+ const run = () => throttledReload();
322
+ if (target !== activeKey.value) {
323
+ const route = getCurrentRoute(target);
324
+ if (route) {
325
+ router.push({
326
+ path: getRoutePath(route),
327
+ ...getExtraProps(route)
328
+ }).then(run);
329
+ return;
330
+ }
331
+ }
332
+ run();
333
+ },
334
+ toggleFullscreen: () => handleContextMenuAction("fullScreen", activeKey.value),
335
+ update: (record, op) => changeTabsList(record, {
336
+ type: op.type,
337
+ addType: op.addType,
338
+ params: op.params ?? record
339
+ })
326
340
  }
327
341
  };
328
342
  }
@@ -331,8 +345,21 @@ function useTabsState(options) {
331
345
  * 通过 inject 获取 `GProLayout` 暴露的 tabs 控制器。
332
346
  */
333
347
  const fallbackController = {
334
- clean: () => {},
348
+ getTabs: () => [],
349
+ getActive: () => "",
350
+ isActive: () => false,
351
+ isFixed: () => false,
352
+ add: () => {},
335
353
  close: () => {},
354
+ closeOthers: () => {},
355
+ closeLeft: () => {},
356
+ closeRight: () => {},
357
+ closeAll: () => {},
358
+ clean: () => {},
359
+ switchTo: () => {},
360
+ toggleFixed: () => {},
361
+ reload: () => {},
362
+ toggleFullscreen: () => {},
336
363
  update: () => {}
337
364
  };
338
365
  /**
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@ import { LAYOUT_MENU_SLOT_KEYS, LayoutMenuEmits, LayoutMenuEmitsProps, LayoutMen
7
7
  import { LayoutSiderEmits, LayoutSiderEmitsProps, LayoutSiderProps, LayoutSiderSlots } from "./components/Sider/interface.js";
8
8
  import { LayoutTabsEmits, LayoutTabsEmitsProps, LayoutTabsProps, LayoutTabsSlots } from "./components/Tabs/interface.js";
9
9
  import { LayoutContentEmits, LayoutContentEmitsProps, LayoutContentProps, LayoutContentSlots } from "./components/WrapContent/interface.js";
10
- import { APP_PAGE_SLOT_KEYS, AppPageSlotKey, AppPageSpinning, BreadcrumbItemType, CardProps, DeepPartial, EmptyEmits, FOOTER_TOOLBAR_SLOT_KEYS, FooterToolbarClassNamesType, FooterToolbarSemanticName, FooterToolbarSlotKey, FooterToolbarStylesType, GFooterToolbarProps, GFooterToolbarSlots, GPageContainerEmits, GPageContainerEmitsProps, GPageContainerProps, GPageContainerSlots, GPageTransitionProps, GPageTransitionSlots, GProAppPageProps, GProAppPageSlots, GProLayoutEmits, GProLayoutEmitsProps, GProLayoutProps, GProLayoutRef, GProLayoutSlots, LAYOUT_SLOT_KEYS, LayoutBreadcrumbConfig, LayoutBreadcrumbSlotProps, LayoutClassNamesType, LayoutCollapseConfig, LayoutFooterConfig, LayoutFooterLink, LayoutHeaderConfig, LayoutHeaderSlotProps, LayoutLogoConfig, LayoutMenuConfig, LayoutMenuItemSlotProps, LayoutMenuRoute, LayoutPageContainerConfig, LayoutSelectInfo, LayoutSemanticName, LayoutSiderConfig, LayoutSiderSlotProps, LayoutStylesType, LayoutTabsConfig, LayoutTabsController, MenuClassNamesType, MenuClickInfo, MenuProps, MenuRenderItem, MenuSelectInfo, MenuSemanticName, MenuStylesType, Meta, PAGE_CONTAINER_SLOT_KEYS, PageContainerCard, PageContainerClassNamesType, PageContainerLoading, PageContainerSemanticName, PageContainerSlotKey, PageContainerStylesType, SiderProps, SpinProps, Tab, TabsMeta, TabsProps, WatermarkProps } from "./interface.js";
10
+ import { APP_PAGE_SLOT_KEYS, AppPageSlotKey, AppPageSpinning, BreadcrumbItemType, CardProps, DeepPartial, EmptyEmits, FOOTER_TOOLBAR_SLOT_KEYS, FooterToolbarClassNamesType, FooterToolbarSemanticName, FooterToolbarSlotKey, FooterToolbarStylesType, GFooterToolbarProps, GFooterToolbarSlots, GPageContainerEmits, GPageContainerEmitsProps, GPageContainerProps, GPageContainerSlots, GPageTransitionProps, GPageTransitionSlots, GProAppPageProps, GProAppPageSlots, GProLayoutEmits, GProLayoutEmitsProps, GProLayoutProps, GProLayoutRef, GProLayoutSlots, LAYOUT_SLOT_KEYS, LayoutBreadcrumbConfig, LayoutBreadcrumbSlotProps, LayoutClassNamesType, LayoutCollapseConfig, LayoutFooterConfig, LayoutFooterLink, LayoutHeaderConfig, LayoutHeaderSlotProps, LayoutLogoConfig, LayoutMenuConfig, LayoutMenuItemSlotProps, LayoutMenuRoute, LayoutPageContainerConfig, LayoutSelectInfo, LayoutSemanticName, LayoutSiderConfig, LayoutSiderSlotProps, LayoutStylesType, LayoutTabsConfig, LayoutTabsController, LayoutTabsUpdateOp, MenuClassNamesType, MenuClickInfo, MenuProps, MenuRenderItem, MenuSelectInfo, MenuSemanticName, MenuStylesType, Meta, PAGE_CONTAINER_SLOT_KEYS, PageContainerCard, PageContainerClassNamesType, PageContainerLoading, PageContainerSemanticName, PageContainerSlotKey, PageContainerStylesType, SiderProps, SpinProps, Tab, TabsMeta, TabsProps, WatermarkProps } from "./interface.js";
11
11
  import GProLayout from "./ProLayout.js";
12
12
  import { ProAppPageContext, useAppPageContext } from "./components/AppPage/context.js";
13
13
  import ProAppPage from "./components/AppPage/index.js";
@@ -19,4 +19,4 @@ import { MenuLookupEntry, MenuMetaPatch, MenuModel, MenuRoutePatch, MenuTreeMuta
19
19
  import { LayoutMenuState, UseLayoutMenuOptions, UseLayoutMenuReturn, useMenu } from "./hooks/useMenu.js";
20
20
  import { useTabs } from "./hooks/useTabs.js";
21
21
  import { LayoutContextProps, useInjectLayoutContext } from "./context/index.js";
22
- export { APP_PAGE_SLOT_KEYS, AppPageSlotKey, AppPageSpinning, BreadcrumbItemType, CardProps, DeepPartial, EmptyEmits, FOOTER_TOOLBAR_SLOT_KEYS, FooterToolbarClassNamesType, FooterToolbarSemanticName, FooterToolbarSlotKey, FooterToolbarStylesType, GFooterToolbar, GFooterToolbarProps, GFooterToolbarSlots, GPageContainerEmits, GPageContainerEmitsProps, GPageContainerProps, GPageContainerSlots, GPageTransition, GPageTransitionProps, GPageTransitionSlots, ProAppPage as GProAppPage, GProAppPageProps, GProAppPageSlots, GProLayout, GProLayoutEmits, GProLayoutEmitsProps, GProLayoutProps, GProLayoutRef, GProLayoutSlots, ProPageContainer as GProPageContainer, LAYOUT_MENU_SLOT_KEYS, LAYOUT_SLOT_KEYS, LayoutBreadcrumbConfig, LayoutBreadcrumbProps, LayoutBreadcrumbSlotProps, LayoutBreadcrumbSlots, LayoutClassNamesType, LayoutCollapseButtonEmits, LayoutCollapseButtonEmitsProps, LayoutCollapseButtonProps, LayoutCollapseButtonSlots, LayoutCollapseConfig, LayoutContentEmits, LayoutContentEmitsProps, LayoutContentProps, LayoutContentSlots, type LayoutContextProps, LayoutFooterConfig, LayoutFooterLink, LayoutFooterProps, LayoutFooterSlots, LayoutHeaderConfig, LayoutHeaderEmits, LayoutHeaderEmitsProps, LayoutHeaderProps, LayoutHeaderSlotProps, LayoutHeaderSlots, LayoutLogoConfig, LayoutLogoEmits, LayoutLogoEmitsProps, LayoutLogoProps, LayoutLogoSlots, LayoutMenu, LayoutMenuConfig, LayoutMenuEmits, LayoutMenuEmitsProps, LayoutMenuItemSlotProps, LayoutMenuProps, LayoutMenuRoute, LayoutMenuSlots, type LayoutMenuState, LayoutPageContainerConfig, LayoutSelectInfo, LayoutSemanticName, LayoutSiderConfig, LayoutSiderEmits, LayoutSiderEmitsProps, LayoutSiderProps, LayoutSiderSlotProps, LayoutSiderSlots, LayoutStylesType, LayoutTabsConfig, LayoutTabsController, LayoutTabsEmits, LayoutTabsEmitsProps, LayoutTabsProps, LayoutTabsSlots, MenuClassNamesType, MenuClickInfo, MenuLookupEntry, MenuMetaPatch, MenuModel, MenuProps, MenuRenderItem, MenuRoutePatch, MenuSelectInfo, MenuSemanticName, MenuStylesType, MenuTreeMutationOptions, Meta, PAGE_CONTAINER_SLOT_KEYS, PageContainerCard, PageContainerClassNamesType, PageContainerLoading, PageContainerSemanticName, PageContainerSlotKey, PageContainerStylesType, type ProAppPageContext, ResolveRouteResult, SiderProps, SpinProps, Tab, TabsMeta, TabsProps, type UseLayoutMenuOptions, type UseLayoutMenuReturn, WatermarkProps, buildBreadcrumbRoutes, buildMenuLookup, cleanMenus, collectMenuNames, getLastPath, insertMenuAtPath, normalizeMenus, partitionFixedMenus, removeMenuAtPath, resolveMatchedChain, resolveRouteTarget, routesToMenus, selectMenuBranch, sortMenusByOrder, toBreadcrumbItems, updateMenuAtPath, updateMenuMetaAtPath, useAppPageContext, useInjectLayoutContext, useMenu, useTabs };
22
+ export { APP_PAGE_SLOT_KEYS, AppPageSlotKey, AppPageSpinning, BreadcrumbItemType, CardProps, DeepPartial, EmptyEmits, FOOTER_TOOLBAR_SLOT_KEYS, FooterToolbarClassNamesType, FooterToolbarSemanticName, FooterToolbarSlotKey, FooterToolbarStylesType, GFooterToolbar, GFooterToolbarProps, GFooterToolbarSlots, GPageContainerEmits, GPageContainerEmitsProps, GPageContainerProps, GPageContainerSlots, GPageTransition, GPageTransitionProps, GPageTransitionSlots, ProAppPage as GProAppPage, GProAppPageProps, GProAppPageSlots, GProLayout, GProLayoutEmits, GProLayoutEmitsProps, GProLayoutProps, GProLayoutRef, GProLayoutSlots, ProPageContainer as GProPageContainer, LAYOUT_MENU_SLOT_KEYS, LAYOUT_SLOT_KEYS, LayoutBreadcrumbConfig, LayoutBreadcrumbProps, LayoutBreadcrumbSlotProps, LayoutBreadcrumbSlots, LayoutClassNamesType, LayoutCollapseButtonEmits, LayoutCollapseButtonEmitsProps, LayoutCollapseButtonProps, LayoutCollapseButtonSlots, LayoutCollapseConfig, LayoutContentEmits, LayoutContentEmitsProps, LayoutContentProps, LayoutContentSlots, type LayoutContextProps, LayoutFooterConfig, LayoutFooterLink, LayoutFooterProps, LayoutFooterSlots, LayoutHeaderConfig, LayoutHeaderEmits, LayoutHeaderEmitsProps, LayoutHeaderProps, LayoutHeaderSlotProps, LayoutHeaderSlots, LayoutLogoConfig, LayoutLogoEmits, LayoutLogoEmitsProps, LayoutLogoProps, LayoutLogoSlots, LayoutMenu, LayoutMenuConfig, LayoutMenuEmits, LayoutMenuEmitsProps, LayoutMenuItemSlotProps, LayoutMenuProps, LayoutMenuRoute, LayoutMenuSlots, type LayoutMenuState, LayoutPageContainerConfig, LayoutSelectInfo, LayoutSemanticName, LayoutSiderConfig, LayoutSiderEmits, LayoutSiderEmitsProps, LayoutSiderProps, LayoutSiderSlotProps, LayoutSiderSlots, LayoutStylesType, LayoutTabsConfig, LayoutTabsController, LayoutTabsEmits, LayoutTabsEmitsProps, LayoutTabsProps, LayoutTabsSlots, LayoutTabsUpdateOp, MenuClassNamesType, MenuClickInfo, MenuLookupEntry, MenuMetaPatch, MenuModel, MenuProps, MenuRenderItem, MenuRoutePatch, MenuSelectInfo, MenuSemanticName, MenuStylesType, MenuTreeMutationOptions, Meta, PAGE_CONTAINER_SLOT_KEYS, PageContainerCard, PageContainerClassNamesType, PageContainerLoading, PageContainerSemanticName, PageContainerSlotKey, PageContainerStylesType, type ProAppPageContext, ResolveRouteResult, SiderProps, SpinProps, Tab, TabsMeta, TabsProps, type UseLayoutMenuOptions, type UseLayoutMenuReturn, WatermarkProps, buildBreadcrumbRoutes, buildMenuLookup, cleanMenus, collectMenuNames, getLastPath, insertMenuAtPath, normalizeMenus, partitionFixedMenus, removeMenuAtPath, resolveMatchedChain, resolveRouteTarget, routesToMenus, selectMenuBranch, sortMenusByOrder, toBreadcrumbItems, updateMenuAtPath, updateMenuMetaAtPath, useAppPageContext, useInjectLayoutContext, useMenu, useTabs };
@@ -224,7 +224,6 @@ interface GProLayoutEmits {
224
224
  'update:theme': (theme: ThemeValue) => void;
225
225
  'update:selectedKeys': (keys: string[]) => void;
226
226
  'update:openKeys': (keys: string[]) => void;
227
- 'update:tabs': (tabs: LayoutMenuRoute[]) => void;
228
227
  'select': (info: LayoutSelectInfo) => void;
229
228
  'openChange': (keys: string[]) => void;
230
229
  'darkChange': (dark: boolean) => void;
@@ -238,7 +237,6 @@ interface GProLayoutEmitsProps {
238
237
  'onUpdate:theme'?: GProLayoutEmits['update:theme'];
239
238
  'onUpdate:selectedKeys'?: GProLayoutEmits['update:selectedKeys'];
240
239
  'onUpdate:openKeys'?: GProLayoutEmits['update:openKeys'];
241
- 'onUpdate:tabs'?: GProLayoutEmits['update:tabs'];
242
240
  onSelect?: GProLayoutEmits['select'];
243
241
  onOpenChange?: GProLayoutEmits['openChange'];
244
242
  onDarkChange?: GProLayoutEmits['darkChange'];
@@ -266,8 +264,6 @@ interface GProLayoutProps extends GProLayoutEmitsProps {
266
264
  selectedKeys?: string[];
267
265
  /** v-model:openKeys 展开菜单 */
268
266
  openKeys?: string[];
269
- /** v-model:tabs 多标签数据 */
270
- tabs?: LayoutMenuRoute[];
271
267
  token?: DeepPartial<ProAliasToken>;
272
268
  proStyles?: LayoutStylesType;
273
269
  proClasses?: LayoutClassNamesType;
@@ -278,7 +274,7 @@ interface GProLayoutProps extends GProLayoutEmitsProps {
278
274
  menu?: LayoutMenuConfig;
279
275
  /** 面包屑配置 */
280
276
  breadcrumb?: LayoutBreadcrumbConfig | false;
281
- /** 多标签配置(数据走 v-model:tabs);false 关闭标签栏 */
277
+ /** 多标签配置;false 关闭标签栏。标签数据由组件内部管理,通过 expose.tabs 控制、tabsChange 通知 */
282
278
  tabsConfig?: LayoutTabsConfig | false;
283
279
  /** Logo 组件配置;false 不渲染默认 Logo 区域 */
284
280
  logo?: LayoutLogoConfig | false;
@@ -348,15 +344,50 @@ interface GProLayoutSlots {
348
344
  footer?: () => any;
349
345
  copyright?: () => any;
350
346
  }
351
- /** tabs 命令式控制器 */
347
+ /** tabs 底层操作参数(add/merge/remove) */
348
+ interface LayoutTabsUpdateOp {
349
+ type: 'add' | 'merge' | 'remove';
350
+ addType?: 'push' | 'unshift';
351
+ params?: Partial<LayoutMenuRoute>;
352
+ }
353
+ /**
354
+ * tabs 命令式控制器。
355
+ * 通过 GProLayout 实例的 expose.tabs,或 useTabs() hook 获取。
356
+ * 标签数据内部管理(单一真相源),外部仅能通过本控制器修改、通过 tabsChange 监听。
357
+ */
352
358
  interface LayoutTabsController {
353
- clean: () => void;
359
+ /** 当前全部标签(fixed + normal) */
360
+ getTabs: () => LayoutMenuRoute[];
361
+ /** 当前激活标签 name(route.name) */
362
+ getActive: () => string;
363
+ /** 指定标签是否激活 */
364
+ isActive: (name: string) => boolean;
365
+ /** 指定标签是否固定 */
366
+ isFixed: (name: string) => boolean;
367
+ /** 新增标签(已存在则合并更新) */
368
+ add: (record: LayoutMenuRoute) => void;
369
+ /** 关闭标签,默认关闭当前激活项 */
354
370
  close: (name?: string) => void;
355
- update: (record: LayoutMenuRoute, op: {
356
- type: 'add' | 'merge' | 'remove';
357
- addType?: 'push' | 'unshift';
358
- params?: Partial<LayoutMenuRoute>;
359
- }) => void;
371
+ /** 关闭其它标签(保留目标 + fixed) */
372
+ closeOthers: (name?: string) => void;
373
+ /** 关闭左侧标签 */
374
+ closeLeft: (name?: string) => void;
375
+ /** 关闭右侧标签 */
376
+ closeRight: (name?: string) => void;
377
+ /** 关闭全部 normal 标签(保留 fixed) */
378
+ closeAll: () => void;
379
+ /** 清空全部标签(含 fixed) */
380
+ clean: () => void;
381
+ /** 切换到指定标签(等价于点击) */
382
+ switchTo: (name: string) => void;
383
+ /** 切换固定状态;省略 fixed 时取反 */
384
+ toggleFixed: (name: string, fixed?: boolean) => void;
385
+ /** 刷新标签,默认刷新当前激活项 */
386
+ reload: (name?: string) => void;
387
+ /** 切换内容全屏 */
388
+ toggleFullscreen: () => void;
389
+ /** 底层增删改(add/merge/remove),语义化方法的兜底 */
390
+ update: (record: LayoutMenuRoute, op: LayoutTabsUpdateOp) => void;
360
391
  }
361
392
  interface GProLayoutRef {
362
393
  tabs: LayoutTabsController;
@@ -486,4 +517,4 @@ interface GPageTransitionSlots {
486
517
  default?: () => any;
487
518
  }
488
519
  //#endregion
489
- export { APP_PAGE_SLOT_KEYS, AppPageSlotKey, AppPageSpinning, type BreadcrumbItemType, type CardProps, DeepPartial, EmptyEmits, FOOTER_TOOLBAR_SLOT_KEYS, FooterToolbarClassNamesType, FooterToolbarSemanticName, FooterToolbarSlotKey, FooterToolbarStylesType, GFooterToolbarProps, GFooterToolbarSlots, GPageContainerEmits, GPageContainerEmitsProps, GPageContainerProps, GPageContainerSlots, GPageTransitionProps, GPageTransitionSlots, GProAppPageProps, GProAppPageSlots, GProLayoutEmits, GProLayoutEmitsProps, GProLayoutProps, GProLayoutRef, GProLayoutSlots, LAYOUT_MENU_SLOT_KEYS, LAYOUT_SLOT_KEYS, LayoutBreadcrumbConfig, LayoutBreadcrumbProps, LayoutBreadcrumbSlotProps, LayoutBreadcrumbSlots, LayoutClassNamesType, LayoutCollapseButtonEmits, LayoutCollapseButtonEmitsProps, LayoutCollapseButtonProps, LayoutCollapseButtonSlots, LayoutCollapseConfig, LayoutContentEmits, LayoutContentEmitsProps, LayoutContentProps, LayoutContentSlots, LayoutFooterConfig, LayoutFooterLink, LayoutFooterProps, LayoutFooterSlots, LayoutHeaderConfig, LayoutHeaderEmits, LayoutHeaderEmitsProps, LayoutHeaderProps, LayoutHeaderSlotProps, LayoutHeaderSlots, LayoutLogoConfig, LayoutLogoEmits, LayoutLogoEmitsProps, LayoutLogoProps, LayoutLogoSlots, LayoutMenuConfig, LayoutMenuEmits, LayoutMenuEmitsProps, LayoutMenuItemSlotProps, LayoutMenuProps, LayoutMenuRoute, LayoutMenuSlots, LayoutPageContainerConfig, LayoutSelectInfo, LayoutSemanticName, LayoutSiderConfig, LayoutSiderEmits, LayoutSiderEmitsProps, LayoutSiderProps, LayoutSiderSlotProps, LayoutSiderSlots, LayoutStylesType, LayoutTabsConfig, LayoutTabsController, LayoutTabsEmits, LayoutTabsEmitsProps, LayoutTabsProps, LayoutTabsSlots, MenuClassNamesType, MenuClickInfo, type MenuProps, MenuRenderItem, MenuSelectInfo, MenuSemanticName, MenuStylesType, Meta, PAGE_CONTAINER_SLOT_KEYS, PageContainerCard, PageContainerClassNamesType, PageContainerLoading, PageContainerSemanticName, PageContainerSlotKey, PageContainerStylesType, type SiderProps, type SpinProps, type Tab, TabsMeta, type TabsProps, type WatermarkProps };
520
+ export { APP_PAGE_SLOT_KEYS, AppPageSlotKey, AppPageSpinning, type BreadcrumbItemType, type CardProps, DeepPartial, EmptyEmits, FOOTER_TOOLBAR_SLOT_KEYS, FooterToolbarClassNamesType, FooterToolbarSemanticName, FooterToolbarSlotKey, FooterToolbarStylesType, GFooterToolbarProps, GFooterToolbarSlots, GPageContainerEmits, GPageContainerEmitsProps, GPageContainerProps, GPageContainerSlots, GPageTransitionProps, GPageTransitionSlots, GProAppPageProps, GProAppPageSlots, GProLayoutEmits, GProLayoutEmitsProps, GProLayoutProps, GProLayoutRef, GProLayoutSlots, LAYOUT_MENU_SLOT_KEYS, LAYOUT_SLOT_KEYS, LayoutBreadcrumbConfig, LayoutBreadcrumbProps, LayoutBreadcrumbSlotProps, LayoutBreadcrumbSlots, LayoutClassNamesType, LayoutCollapseButtonEmits, LayoutCollapseButtonEmitsProps, LayoutCollapseButtonProps, LayoutCollapseButtonSlots, LayoutCollapseConfig, LayoutContentEmits, LayoutContentEmitsProps, LayoutContentProps, LayoutContentSlots, LayoutFooterConfig, LayoutFooterLink, LayoutFooterProps, LayoutFooterSlots, LayoutHeaderConfig, LayoutHeaderEmits, LayoutHeaderEmitsProps, LayoutHeaderProps, LayoutHeaderSlotProps, LayoutHeaderSlots, LayoutLogoConfig, LayoutLogoEmits, LayoutLogoEmitsProps, LayoutLogoProps, LayoutLogoSlots, LayoutMenuConfig, LayoutMenuEmits, LayoutMenuEmitsProps, LayoutMenuItemSlotProps, LayoutMenuProps, LayoutMenuRoute, LayoutMenuSlots, LayoutPageContainerConfig, LayoutSelectInfo, LayoutSemanticName, LayoutSiderConfig, LayoutSiderEmits, LayoutSiderEmitsProps, LayoutSiderProps, LayoutSiderSlotProps, LayoutSiderSlots, LayoutStylesType, LayoutTabsConfig, LayoutTabsController, LayoutTabsEmits, LayoutTabsEmitsProps, LayoutTabsProps, LayoutTabsSlots, LayoutTabsUpdateOp, MenuClassNamesType, MenuClickInfo, type MenuProps, MenuRenderItem, MenuSelectInfo, MenuSemanticName, MenuStylesType, Meta, PAGE_CONTAINER_SLOT_KEYS, PageContainerCard, PageContainerClassNamesType, PageContainerLoading, PageContainerSemanticName, PageContainerSlotKey, PageContainerStylesType, type SiderProps, type SpinProps, type Tab, TabsMeta, type TabsProps, type WatermarkProps };