@gx-design-vue/pro-layout 0.1.0-alpha.6 → 0.1.0-alpha.8
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 +50 -38
- package/dist/components/AppPage/index.d.ts +7 -1
- package/dist/components/AppPage/index.js +10 -5
- package/dist/components/Breadcrumb/index.js +6 -22
- package/dist/components/CollapseButton/index.js +3 -3
- package/dist/components/CollapseButton/interface.d.ts +0 -1
- package/dist/components/Header/index.js +9 -5
- package/dist/components/Header/interface.d.ts +6 -0
- package/dist/components/Logo/index.d.ts +1 -1
- package/dist/components/Logo/index.js +4 -6
- package/dist/components/Logo/interface.d.ts +1 -2
- package/dist/components/{Sider → Menu}/SiderMenu.d.ts +4 -2
- package/dist/components/{Sider → Menu}/SiderMenu.js +18 -26
- package/dist/components/Menu/iconRender.d.ts +10 -0
- package/dist/components/Menu/iconRender.js +23 -0
- package/dist/components/Menu/index.js +3 -17
- package/dist/components/{Sider/style.d.ts → Menu/style/siderMenu.d.ts} +1 -1
- package/dist/components/{Sider/style.js → Menu/style/siderMenu.js} +6 -6
- package/dist/components/PageContainer/PageHeader.d.ts +0 -3
- package/dist/components/PageContainer/PageHeader.js +5 -13
- package/dist/components/PageContainer/index.js +5 -7
- package/dist/components/PageContainer/style.js +7 -2
- package/dist/components/PageTransition/index.d.ts +3 -3
- package/dist/components/PageTransition/index.js +1 -1
- package/dist/components/Sider/index.js +58 -16
- package/dist/components/Sider/interface.d.ts +6 -3
- package/dist/components/Tabs/contextMenu.js +12 -10
- package/dist/components/Tabs/index.js +21 -17
- package/dist/context/index.d.ts +4 -2
- package/dist/defaultConfig.d.ts +1 -1
- package/dist/defaultConfig.js +4 -4
- package/dist/hooks/useLayoutBase.d.ts +14 -13
- package/dist/hooks/useLayoutBase.js +5 -1
- package/dist/hooks/useTabs.d.ts +2 -6
- package/dist/hooks/useTabs.js +4 -6
- package/dist/index.d.ts +2 -2
- package/dist/interface.d.ts +18 -21
- package/dist/interface.js +0 -2
- package/dist/pro-layout.esm.js +1280 -1759
- package/dist/pro-layout.js +15 -1
- package/dist/style/content.js +3 -1
- package/dist/style/logo.js +1 -2
- package/dist/style/sider.js +29 -14
- package/dist/theme/augment.d.ts +6 -6
- package/dist/theme/interface/components.d.ts +6 -6
- package/dist/utils/themeComponents.d.ts +1 -1
- package/package.json +8 -9
package/dist/ProLayout.js
CHANGED
|
@@ -13,7 +13,7 @@ import { normalizeSectionConfig } from "./utils/config.js";
|
|
|
13
13
|
import { computed, createVNode, defineComponent, mergeDefaults, ref } from "vue";
|
|
14
14
|
import { ThemeContext, useThemeContext } from "@gx-design-vue/context";
|
|
15
15
|
import { GProConfigProvider, useProToken } from "@gx-design-vue/pro-provider";
|
|
16
|
-
import { classNames,
|
|
16
|
+
import { classNames, getSlotsProps } from "@gx-design-vue/pro-utils";
|
|
17
17
|
import { useDark, useMediaQuery, useVModel } from "@vueuse/core";
|
|
18
18
|
import { ConfigProvider, Layout } from "antdv-next";
|
|
19
19
|
import { useBaseConfig } from "antdv-next/config-provider/context";
|
|
@@ -112,18 +112,39 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
|
|
|
112
112
|
}
|
|
113
113
|
if (target.path && router.currentRoute.value.fullPath !== target.path) router.push(target.path);
|
|
114
114
|
}
|
|
115
|
+
const logoConfig = computed(() => normalizeSectionConfig(props.logo, DEFAULT_LAYOUT_CONFIG.logo));
|
|
116
|
+
const collapseConfig = computed(() => normalizeSectionConfig(props.collapse, DEFAULT_LAYOUT_CONFIG.collapse));
|
|
117
|
+
const footerConfig = computed(() => normalizeSectionConfig(props.footerConfig, DEFAULT_LAYOUT_CONFIG.footerConfig));
|
|
118
|
+
const breadcrumbConfig = computed(() => normalizeSectionConfig(props.breadcrumb, DEFAULT_LAYOUT_CONFIG.breadcrumb));
|
|
119
|
+
const pageContainerConfig = computed(() => ({
|
|
120
|
+
...DEFAULT_LAYOUT_CONFIG.pageContainerConfig,
|
|
121
|
+
...props.pageContainer || {}
|
|
122
|
+
}));
|
|
115
123
|
const tabsConf = computed(() => normalizeSectionConfig(props.tabsConfig, DEFAULT_LAYOUT_CONFIG.tabsConfig));
|
|
116
124
|
const tabsStateConfig = computed(() => tabsConf.value ?? {});
|
|
125
|
+
const tabsModel = useVModel(props, "tabs", emit, {
|
|
126
|
+
passive: true,
|
|
127
|
+
defaultValue: []
|
|
128
|
+
});
|
|
129
|
+
const collapsedWidth = computed(() => collapseConfig.value?.width ?? DEFAULT_LAYOUT_CONFIG.collapse.width);
|
|
130
|
+
/** Sider 实际渲染宽度:0 表示不可见,否则 = 折叠/展开的真实宽度 */
|
|
131
|
+
const siderWidth = computed(() => {
|
|
132
|
+
const width = props.sider?.width ?? DEFAULT_LAYOUT_CONFIG.sider.width;
|
|
133
|
+
if (!width) return 0;
|
|
134
|
+
if (contentFullscreen.value) return 0;
|
|
135
|
+
if (![
|
|
136
|
+
"side",
|
|
137
|
+
"mix",
|
|
138
|
+
"wide"
|
|
139
|
+
].includes(layout.value ?? "")) return 0;
|
|
140
|
+
if (props.sider?.hideWhenEmpty && menuState.siderMenus.value.length === 0) return 0;
|
|
141
|
+
return collapsed.value ? collapsedWidth.value : width;
|
|
142
|
+
});
|
|
117
143
|
const tabsState = useTabsState({
|
|
118
|
-
tabs:
|
|
119
|
-
passive: true,
|
|
120
|
-
defaultValue: []
|
|
121
|
-
}),
|
|
144
|
+
tabs: tabsModel,
|
|
122
145
|
config: tabsStateConfig,
|
|
123
146
|
flatMenus: menuState.flatMenus,
|
|
124
|
-
|
|
125
|
-
siderWidth: props.sider?.width ?? DEFAULT_LAYOUT_CONFIG.sider.width,
|
|
126
|
-
collapseWidth: props.collapseConfig ? props.collapseConfig.width ?? DEFAULT_LAYOUT_CONFIG.collapseConfig.width : DEFAULT_LAYOUT_CONFIG.collapseConfig.width,
|
|
147
|
+
siderWidth,
|
|
127
148
|
isMobile: computed(() => isMobile.value),
|
|
128
149
|
contentFullscreen,
|
|
129
150
|
onTabsChange: (val) => {
|
|
@@ -163,14 +184,6 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
|
|
|
163
184
|
function handleLogoClick() {
|
|
164
185
|
emit("logoClick");
|
|
165
186
|
}
|
|
166
|
-
const logoConfig = computed(() => normalizeSectionConfig(props.logoConfig, DEFAULT_LAYOUT_CONFIG.logoConfig));
|
|
167
|
-
const collapseConfig = computed(() => normalizeSectionConfig(props.collapseConfig, DEFAULT_LAYOUT_CONFIG.collapseConfig));
|
|
168
|
-
const footerConfig = computed(() => normalizeSectionConfig(props.footerConfig, DEFAULT_LAYOUT_CONFIG.footerConfig));
|
|
169
|
-
const breadcrumbConfig = computed(() => normalizeSectionConfig(props.breadcrumbConfig, DEFAULT_LAYOUT_CONFIG.breadcrumbConfig));
|
|
170
|
-
const pageContainerConfig = computed(() => ({
|
|
171
|
-
...DEFAULT_LAYOUT_CONFIG.pageContainerConfig,
|
|
172
|
-
...props.pageContainerConfig || {}
|
|
173
|
-
}));
|
|
174
187
|
const showHeader = computed(() => !contentFullscreen.value && layout.value !== "simple" && props.header?.height !== 0);
|
|
175
188
|
const showSider = computed(() => !contentFullscreen.value && [
|
|
176
189
|
"side",
|
|
@@ -194,6 +207,8 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
|
|
|
194
207
|
})),
|
|
195
208
|
menuState,
|
|
196
209
|
logoConfig,
|
|
210
|
+
siderWidth,
|
|
211
|
+
collapsedWidth,
|
|
197
212
|
breadcrumbConfig,
|
|
198
213
|
pageContainerConfig,
|
|
199
214
|
watermark: computed(() => props.watermark),
|
|
@@ -227,17 +242,7 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
|
|
|
227
242
|
keys: LAYOUT_SLOT_KEYS,
|
|
228
243
|
render: false
|
|
229
244
|
});
|
|
230
|
-
|
|
231
|
-
if (slots.logo) normalized.logo = slots.logo;
|
|
232
|
-
if (props.collapseConfig) {
|
|
233
|
-
const collapseIconSlot = getSlot({
|
|
234
|
-
slots,
|
|
235
|
-
props: { collapseIcon: props.collapseConfig.icon },
|
|
236
|
-
key: "collapseIcon"
|
|
237
|
-
});
|
|
238
|
-
if (collapseIconSlot) normalized.collapseIcon = collapseIconSlot;
|
|
239
|
-
}
|
|
240
|
-
return normalized;
|
|
245
|
+
return Object.fromEntries(Object.entries(merged).map(([key, value]) => [key, typeof value === "function" ? value : () => value]));
|
|
241
246
|
}
|
|
242
247
|
return () => {
|
|
243
248
|
if (props.pure) return slots.default?.();
|
|
@@ -253,12 +258,17 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
|
|
|
253
258
|
"showSider": showSider.value,
|
|
254
259
|
"proClasses": props.proClasses,
|
|
255
260
|
"proStyles": props.proStyles
|
|
256
|
-
}, { default: () => [showSider.value && createVNode(LayoutSider$1, {
|
|
261
|
+
}, { default: () => [showSider.value && siderWidth.value && createVNode(LayoutSider$1, {
|
|
257
262
|
"onSelect": handleSelect,
|
|
258
263
|
"onOpenChange": handleOpenChange,
|
|
259
264
|
"onLogoClick": handleLogoClick,
|
|
260
265
|
"onUpdate:collapsed": handleCollapsedUpdate
|
|
261
|
-
},
|
|
266
|
+
}, {
|
|
267
|
+
...layoutSlots,
|
|
268
|
+
logo: slots.logo,
|
|
269
|
+
collapse: slots.collapse,
|
|
270
|
+
collapseIcon: slots.collapseIcon
|
|
271
|
+
}), createVNode("div", {
|
|
262
272
|
"style": { position: "relative" },
|
|
263
273
|
"class": `${prefixCls.value}-container`,
|
|
264
274
|
"data-testid": "pro-layout-container"
|
|
@@ -267,7 +277,13 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
|
|
|
267
277
|
"onLogoClick": handleLogoClick,
|
|
268
278
|
"onUpdate:collapsed": handleCollapsedUpdate,
|
|
269
279
|
"onSelect": handleSelect
|
|
270
|
-
},
|
|
280
|
+
}, {
|
|
281
|
+
...layoutSlots,
|
|
282
|
+
logo: slots.logo,
|
|
283
|
+
collapse: slots.collapse,
|
|
284
|
+
breadcrumb: slots.breadcrumb,
|
|
285
|
+
collapseIcon: slots.collapseIcon
|
|
286
|
+
}),
|
|
271
287
|
tabsConf.value && createVNode(LayoutTabs, {
|
|
272
288
|
"config": tabsConf.value,
|
|
273
289
|
"onReloadPage": () => emit("reloadPage"),
|
|
@@ -364,7 +380,7 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
|
|
|
364
380
|
type: Object,
|
|
365
381
|
required: false
|
|
366
382
|
},
|
|
367
|
-
|
|
383
|
+
breadcrumb: {
|
|
368
384
|
type: [Object, Boolean],
|
|
369
385
|
required: false,
|
|
370
386
|
default: void 0
|
|
@@ -374,17 +390,17 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
|
|
|
374
390
|
required: false,
|
|
375
391
|
default: void 0
|
|
376
392
|
},
|
|
377
|
-
|
|
393
|
+
logo: {
|
|
378
394
|
type: [Object, Boolean],
|
|
379
395
|
required: false,
|
|
380
396
|
default: void 0
|
|
381
397
|
},
|
|
382
|
-
|
|
398
|
+
collapse: {
|
|
383
399
|
type: [Object, Boolean],
|
|
384
400
|
required: false,
|
|
385
401
|
default: void 0
|
|
386
402
|
},
|
|
387
|
-
|
|
403
|
+
pageContainer: {
|
|
388
404
|
type: Object,
|
|
389
405
|
required: false
|
|
390
406
|
},
|
|
@@ -444,10 +460,6 @@ const GProLayout = /* @__PURE__ */ defineComponent((props, { emit, expose, slots
|
|
|
444
460
|
type: Function,
|
|
445
461
|
required: false
|
|
446
462
|
},
|
|
447
|
-
breadcrumb: {
|
|
448
|
-
type: Function,
|
|
449
|
-
required: false
|
|
450
|
-
},
|
|
451
463
|
tabBarItem: {
|
|
452
464
|
type: Function,
|
|
453
465
|
required: false
|
|
@@ -3,6 +3,8 @@ import * as _$vue from "vue";
|
|
|
3
3
|
import { CSSProperties, PropType, SlotsType } from "vue";
|
|
4
4
|
import { CustomRender } from "@gx-design-vue/pro-utils";
|
|
5
5
|
import * as _$vue_jsx_runtime0 from "vue/jsx-runtime";
|
|
6
|
+
import { ConfigOptions } from "antdv-next/dist/message/interface";
|
|
7
|
+
import { NotificationConfig } from "antdv-next/dist/notification/interface";
|
|
6
8
|
|
|
7
9
|
//#region src/components/AppPage/index.d.ts
|
|
8
10
|
/**
|
|
@@ -23,6 +25,8 @@ declare const ProAppPage: _$vue.DefineComponent<_$vue.ExtractPropTypes<{
|
|
|
23
25
|
type: PropType<CustomRender>;
|
|
24
26
|
default: any;
|
|
25
27
|
};
|
|
28
|
+
message: PropType<ConfigOptions>;
|
|
29
|
+
notification: PropType<NotificationConfig>;
|
|
26
30
|
}>, () => _$vue_jsx_runtime0.JSX.Element, {}, {}, {}, _$vue.ComponentOptionsMixin, _$vue.ComponentOptionsMixin, {}, string, _$vue.PublicProps, Readonly<_$vue.ExtractPropTypes<{
|
|
27
31
|
class: PropType<string>;
|
|
28
32
|
style: PropType<CSSProperties>;
|
|
@@ -35,9 +39,11 @@ declare const ProAppPage: _$vue.DefineComponent<_$vue.ExtractPropTypes<{
|
|
|
35
39
|
type: PropType<CustomRender>;
|
|
36
40
|
default: any;
|
|
37
41
|
};
|
|
42
|
+
message: PropType<ConfigOptions>;
|
|
43
|
+
notification: PropType<NotificationConfig>;
|
|
38
44
|
}>> & Readonly<{}>, {
|
|
39
|
-
indicator: CustomRender;
|
|
40
45
|
spinning: AppPageSpinning;
|
|
46
|
+
indicator: CustomRender;
|
|
41
47
|
}, SlotsType<{
|
|
42
48
|
default: () => any;
|
|
43
49
|
indicator: () => any;
|
|
@@ -3,6 +3,7 @@ import style_default from "./style.js";
|
|
|
3
3
|
import { computed, createVNode, defineComponent } from "vue";
|
|
4
4
|
import { useProConfigContext } from "@gx-design-vue/pro-provider";
|
|
5
5
|
import { classNames, getSlotVNode } from "@gx-design-vue/pro-utils";
|
|
6
|
+
import { App } from "antdv-next";
|
|
6
7
|
import { useBaseConfig } from "antdv-next/config-provider/context";
|
|
7
8
|
import useCSSVarCls from "antdv-next/config-provider/hooks/useCSSVarCls";
|
|
8
9
|
import { GIcon } from "@gx-design-vue/icon";
|
|
@@ -43,7 +44,9 @@ const ProAppPage = /* @__PURE__ */ defineComponent({
|
|
|
43
44
|
String
|
|
44
45
|
],
|
|
45
46
|
default: void 0
|
|
46
|
-
}
|
|
47
|
+
},
|
|
48
|
+
message: Object,
|
|
49
|
+
notification: Object
|
|
47
50
|
},
|
|
48
51
|
slots: Object,
|
|
49
52
|
setup(props, { slots }) {
|
|
@@ -72,16 +75,18 @@ const ProAppPage = /* @__PURE__ */ defineComponent({
|
|
|
72
75
|
backgroundColor: props.spinning?.blur ? void 0 : "unset",
|
|
73
76
|
...props.spinning?.style || {}
|
|
74
77
|
}));
|
|
75
|
-
return () => createVNode(
|
|
78
|
+
return () => createVNode(App, {
|
|
76
79
|
"class": rootClassNames.value,
|
|
77
|
-
"style": props.style
|
|
78
|
-
|
|
80
|
+
"style": props.style,
|
|
81
|
+
"message": props.message,
|
|
82
|
+
"notification": props.notification
|
|
83
|
+
}, { default: () => [createVNode(GScrollbar, {
|
|
79
84
|
"barStyle": SCROLLBAR_BAR_STYLE,
|
|
80
85
|
"viewStyle": SCROLLBAR_VIEW_STYLE
|
|
81
86
|
}, { default: () => [slots.default?.()] }), loading.value && createVNode("div", {
|
|
82
87
|
"class": classNames(`${prefixCls.value}-blur`, hashId.value),
|
|
83
88
|
"style": maskStyle.value
|
|
84
|
-
}, [createVNode("div", { "class": `${prefixCls.value}-spin` }, [resolvedIndicator.value])])]);
|
|
89
|
+
}, [createVNode("div", { "class": `${prefixCls.value}-spin` }, [resolvedIndicator.value])])] });
|
|
85
90
|
}
|
|
86
91
|
});
|
|
87
92
|
//#endregion
|
|
@@ -1,31 +1,15 @@
|
|
|
1
1
|
import { useLayoutBase } from "../../hooks/useLayoutBase.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { renderMenuIcon } from "../Menu/iconRender.js";
|
|
3
|
+
import { Fragment, computed, createTextVNode, createVNode, defineComponent } from "vue";
|
|
4
|
+
import { classNames } from "@gx-design-vue/pro-utils";
|
|
4
5
|
import { Dropdown } from "antdv-next";
|
|
5
|
-
import { DownOutlined } from "@antdv-next/icons";
|
|
6
6
|
import { GIcon } from "@gx-design-vue/icon";
|
|
7
7
|
//#region src/components/Breadcrumb/index.tsx
|
|
8
8
|
/** 首页占位路由名 */
|
|
9
9
|
const BREADCRUMB_HOME_KEY = "__breadcrumb_home__";
|
|
10
|
-
/**
|
|
11
|
-
* 渲染面包屑图标:兼容 VNode / 组件 / 图片地址 / 图标名。
|
|
12
|
-
* 复用 LayoutMenu 的 renderMenuIcon 模式。
|
|
13
|
-
*/
|
|
14
|
-
function renderBreadcrumbIcon(icon) {
|
|
15
|
-
if (!icon) return null;
|
|
16
|
-
if (isVNode(icon)) return icon;
|
|
17
|
-
if (isString(icon)) {
|
|
18
|
-
if (isUrl(icon) || isImg(icon)) return createVNode("img", {
|
|
19
|
-
"src": icon,
|
|
20
|
-
"alt": "icon"
|
|
21
|
-
}, null);
|
|
22
|
-
return createVNode(GIcon, { "type": icon }, null);
|
|
23
|
-
}
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
10
|
/** 渲染默认面包屑项内容(图标 + 标题) */
|
|
27
11
|
function renderBreadcrumbItemContent(prefixCls, route) {
|
|
28
|
-
const iconNode =
|
|
12
|
+
const iconNode = renderMenuIcon(route.meta?.icon);
|
|
29
13
|
return createVNode(Fragment, null, [iconNode && createVNode("span", { "class": `${prefixCls}-item-icon` }, [iconNode]), createVNode("span", null, [route.meta?.title])]);
|
|
30
14
|
}
|
|
31
15
|
/**
|
|
@@ -94,11 +78,11 @@ const Breadcrumb = /* @__PURE__ */ defineComponent((props, { slots }) => {
|
|
|
94
78
|
items: dropdownChildren.map((child) => ({
|
|
95
79
|
key: child.name,
|
|
96
80
|
label: child.meta?.title ?? child.name,
|
|
97
|
-
icon:
|
|
81
|
+
icon: renderMenuIcon(child.meta?.icon)
|
|
98
82
|
})),
|
|
99
83
|
selectedKeys: dropdownSelectedKeys,
|
|
100
84
|
onClick: ({ key }) => handleDropdownMenuClick(String(key))
|
|
101
|
-
} }, { default: () => [createVNode("span", { "class": `${breadcrumbCls}-overlay-link` }, [itemContent, createVNode(
|
|
85
|
+
} }, { default: () => [createVNode("span", { "class": `${breadcrumbCls}-overlay-link` }, [itemContent, createVNode(GIcon, { "type": "DownOutlined" }, null)])] }) : createVNode("span", {
|
|
102
86
|
"class": classNames(`${breadcrumbCls}-link`, isLast && `${breadcrumbCls}-link-last`),
|
|
103
87
|
"onClick": () => handleItemClick(route, isLast)
|
|
104
88
|
}, [itemContent])]), !isLast && createVNode("li", { "class": `${breadcrumbCls}-separator` }, [createTextVNode("/")])]);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { useLayoutBase } from "../../hooks/useLayoutBase.js";
|
|
2
|
+
import { ArrowSvgIcon } from "./Arrow.js";
|
|
2
3
|
import { computed, createVNode, defineComponent } from "vue";
|
|
3
4
|
import { classNames } from "@gx-design-vue/pro-utils";
|
|
4
|
-
import {
|
|
5
|
+
import { GIcon } from "@gx-design-vue/icon";
|
|
5
6
|
//#region src/components/CollapseButton/index.tsx
|
|
6
7
|
const LayoutCollapseButton = /* @__PURE__ */ defineComponent((props, { emit, slots }) => {
|
|
7
8
|
const { prefixCls, collapseConfig } = useLayoutBase();
|
|
@@ -11,7 +12,6 @@ const LayoutCollapseButton = /* @__PURE__ */ defineComponent((props, { emit, slo
|
|
|
11
12
|
emit("click", event);
|
|
12
13
|
}
|
|
13
14
|
return () => {
|
|
14
|
-
if (slots.default) return slots.default();
|
|
15
15
|
return createVNode("div", {
|
|
16
16
|
"class": collapseButtonClassNames.value,
|
|
17
17
|
"role": "button",
|
|
@@ -24,7 +24,7 @@ const LayoutCollapseButton = /* @__PURE__ */ defineComponent((props, { emit, slo
|
|
|
24
24
|
handleClick(event);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
-
}, [
|
|
27
|
+
}, [slots.icon?.({ collapsed: props.collapsed }) || placement.value === "sider" ? createVNode(ArrowSvgIcon, null, null) : createVNode(GIcon, { "type": props.collapsed ? "MenuUnfoldOutlined" : "MenuFoldOutlined" }, null)]);
|
|
28
28
|
};
|
|
29
29
|
}, {
|
|
30
30
|
props: {
|
|
@@ -14,7 +14,6 @@ interface LayoutCollapseButtonSlots {
|
|
|
14
14
|
icon?: (props: {
|
|
15
15
|
collapsed: boolean;
|
|
16
16
|
}) => any;
|
|
17
|
-
default?: () => any;
|
|
18
17
|
}
|
|
19
18
|
//#endregion
|
|
20
19
|
export { LayoutCollapseButtonEmits, LayoutCollapseButtonEmitsProps, LayoutCollapseButtonProps, LayoutCollapseButtonSlots };
|
|
@@ -63,7 +63,8 @@ const Header = /* @__PURE__ */ defineComponent((props, { emit, slots }) => {
|
|
|
63
63
|
items,
|
|
64
64
|
routes
|
|
65
65
|
};
|
|
66
|
-
const
|
|
66
|
+
const breadcrumb = slots.breadcrumb || breadcrumbConfig.value?.render;
|
|
67
|
+
const customBreadcrumb = breadcrumbRender.value?.(slotProps) ?? breadcrumb?.(slotProps);
|
|
67
68
|
if (customBreadcrumb) return createVNode("div", {
|
|
68
69
|
"class": classNames(`${prefixCls.value}-header-breadcrumb`, proClasses.value.breadcrumb),
|
|
69
70
|
"style": proStyles.value.breadcrumb
|
|
@@ -76,6 +77,9 @@ const Header = /* @__PURE__ */ defineComponent((props, { emit, slots }) => {
|
|
|
76
77
|
}
|
|
77
78
|
return () => {
|
|
78
79
|
const showHeaderMenu = ["top", "mix"].includes(layout.value ?? "");
|
|
80
|
+
const logoRender = slots.logo || logoConfig.value?.render;
|
|
81
|
+
const collapseRender = slots.collapse || collapseConfig.value?.render;
|
|
82
|
+
const collapseIconRender = slots.collapseIcon || collapseConfig.value?.icon;
|
|
79
83
|
return createVNode(Fragment, null, [header.value?.fixed && createVNode(LayoutHeader, { "style": {
|
|
80
84
|
background: "transparent",
|
|
81
85
|
lineHeight: unit(header.value?.height),
|
|
@@ -88,14 +92,14 @@ const Header = /* @__PURE__ */ defineComponent((props, { emit, slots }) => {
|
|
|
88
92
|
}
|
|
89
93
|
}, { default: () => [createVNode("div", { "class": `${prefixCls.value}-header-main` }, [
|
|
90
94
|
createVNode("div", { "class": `${prefixCls.value}-header-main-left` }, [
|
|
91
|
-
showLogo.value && (
|
|
95
|
+
showLogo.value && logoConfig.value && (typeof logoRender === "function" ? logoRender?.({
|
|
92
96
|
collapsed: collapsed.value,
|
|
93
97
|
title: logoTitle.value
|
|
94
|
-
})
|
|
95
|
-
collapseConfig.value && collapseConfig.value.placement === "header" && (
|
|
98
|
+
}) : createVNode(LayoutLogo, { "onClick": handleLogoClick }, slots)),
|
|
99
|
+
collapseConfig.value && collapseConfig.value.placement === "header" && (collapseRender?.({ collapsed: collapsed.value }) ?? createVNode(LayoutCollapseButton, {
|
|
96
100
|
"collapsed": collapsed.value,
|
|
97
101
|
"onClick": handleToggleCollapsed
|
|
98
|
-
}, { icon:
|
|
102
|
+
}, { icon: collapseIconRender })),
|
|
99
103
|
renderBreadcrumb()
|
|
100
104
|
]),
|
|
101
105
|
slots.headerContent?.({
|
|
@@ -20,6 +20,12 @@ interface LayoutHeaderSlots {
|
|
|
20
20
|
collapsed: boolean;
|
|
21
21
|
title?: string;
|
|
22
22
|
}) => any;
|
|
23
|
+
collapse?: (props: {
|
|
24
|
+
collapsed: boolean;
|
|
25
|
+
}) => any;
|
|
26
|
+
collapseIcon?: (props: {
|
|
27
|
+
collapsed: boolean;
|
|
28
|
+
}) => any;
|
|
23
29
|
breadcrumb?: (props: LayoutBreadcrumbSlotProps) => any;
|
|
24
30
|
headerContent?: (props: LayoutHeaderSlotProps) => any;
|
|
25
31
|
headerRight?: (props: LayoutHeaderSlotProps) => any;
|
|
@@ -4,7 +4,7 @@ import { SlotsType } from "vue";
|
|
|
4
4
|
import { CustomRender } from "@gx-design-vue/pro-utils";
|
|
5
5
|
|
|
6
6
|
//#region src/components/Logo/index.d.ts
|
|
7
|
-
declare function defaultRenderLogo(logo?: CustomRender | false):
|
|
7
|
+
declare function defaultRenderLogo(logo?: CustomRender | false): any;
|
|
8
8
|
declare const LayoutLogo: _$vue.DefineSetupFnComponent<LayoutLogoEmitsProps, LayoutLogoEmits, SlotsType<LayoutLogoSlots>, LayoutLogoEmitsProps, _$vue.PublicProps>;
|
|
9
9
|
//#endregion
|
|
10
10
|
export { LayoutLogo as default, defaultRenderLogo };
|
|
@@ -18,7 +18,7 @@ const LayoutLogo = /* @__PURE__ */ defineComponent((_props, { emit, slots }) =>
|
|
|
18
18
|
if (!logoConfig?.value) return "";
|
|
19
19
|
return logoConfig?.value.title;
|
|
20
20
|
});
|
|
21
|
-
const logoSource = computed(() =>
|
|
21
|
+
const logoSource = computed(() => logoConfig?.value.image);
|
|
22
22
|
const hideTitle = computed(() => collapsed.value && ["side", "simple"].includes(layout.value ?? ""));
|
|
23
23
|
const showLogo = computed(() => !collapsed.value || !!collapsedWidth.value);
|
|
24
24
|
const logoClassNames = computed(() => classNames(`${prefixCls.value}-logo`, proClasses.value.logo, {
|
|
@@ -29,15 +29,13 @@ const LayoutLogo = /* @__PURE__ */ defineComponent((_props, { emit, slots }) =>
|
|
|
29
29
|
emit("click");
|
|
30
30
|
}
|
|
31
31
|
return () => {
|
|
32
|
-
|
|
33
|
-
const logo = slots.logo ? slots.logo() : defaultRenderLogo(logoSource.value);
|
|
34
|
-
const hasLogo = logo !== null && logo !== void 0 && logo !== false;
|
|
32
|
+
const image = slots.image ? slots.image() : defaultRenderLogo(logoSource.value);
|
|
35
33
|
const titleDom = title.value && !hideTitle.value ? createVNode("h1", null, [title.value]) : null;
|
|
36
34
|
return createVNode("div", {
|
|
37
35
|
"class": logoClassNames.value,
|
|
38
36
|
"style": proStyles.value.logo,
|
|
39
37
|
"onClick": handleClick
|
|
40
|
-
}, [createVNode("a", null, [showLogo.value &&
|
|
38
|
+
}, [createVNode("a", null, [showLogo.value && image && createVNode("span", {
|
|
41
39
|
"class": proClasses.value.logoImage,
|
|
42
40
|
"style": {
|
|
43
41
|
display: "inline-flex",
|
|
@@ -45,7 +43,7 @@ const LayoutLogo = /* @__PURE__ */ defineComponent((_props, { emit, slots }) =>
|
|
|
45
43
|
fontSize: 22,
|
|
46
44
|
...proStyles.value.logoImage
|
|
47
45
|
}
|
|
48
|
-
}, [
|
|
46
|
+
}, [image]), titleDom])]);
|
|
49
47
|
};
|
|
50
48
|
}, {
|
|
51
49
|
props: { onClick: {
|
|
@@ -7,8 +7,7 @@ interface LayoutLogoEmitsProps {
|
|
|
7
7
|
}
|
|
8
8
|
type LayoutLogoProps = LayoutLogoEmitsProps;
|
|
9
9
|
interface LayoutLogoSlots {
|
|
10
|
-
|
|
11
|
-
default?: () => any;
|
|
10
|
+
image?: () => any;
|
|
12
11
|
}
|
|
13
12
|
//#endregion
|
|
14
13
|
export { LayoutLogoEmits, LayoutLogoEmitsProps, LayoutLogoProps, LayoutLogoSlots };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { LayoutMenuEmits, LayoutMenuSlots } from "
|
|
1
|
+
import { LayoutMenuEmits, LayoutMenuSlots } from "./interface.js";
|
|
2
2
|
import { LayoutClassNamesType, LayoutMenuItemSlotProps, LayoutMenuRoute, LayoutStylesType } from "../../interface.js";
|
|
3
3
|
import * as _$vue from "vue";
|
|
4
4
|
import { CSSProperties, SlotsType } from "vue";
|
|
5
5
|
import { CustomRender } from "@gx-design-vue/pro-utils";
|
|
6
6
|
|
|
7
|
-
//#region src/components/
|
|
7
|
+
//#region src/components/Menu/SiderMenu.d.ts
|
|
8
8
|
interface SiderMenuProps {
|
|
9
9
|
class?: string;
|
|
10
10
|
prefixCls?: string;
|
|
@@ -15,6 +15,8 @@ interface SiderMenuProps {
|
|
|
15
15
|
collapsed?: boolean;
|
|
16
16
|
proClasses?: LayoutClassNamesType;
|
|
17
17
|
proStyles?: LayoutStylesType;
|
|
18
|
+
/** 由外部(如 Sider)传入,用于获取包裹 SiderMenu 的滚动容器 DOM 引用 */
|
|
19
|
+
getScrollContainer?: () => HTMLElement | undefined;
|
|
18
20
|
menuItemIcon?: (slotProps: LayoutMenuItemSlotProps) => CustomRender;
|
|
19
21
|
menuItemLabel?: (slotProps: LayoutMenuItemSlotProps) => CustomRender;
|
|
20
22
|
menuItemExtra?: (slotProps: LayoutMenuItemSlotProps) => CustomRender;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { useLayoutBase } from "../../hooks/useLayoutBase.js";
|
|
2
|
+
import { renderMenuIcon } from "./iconRender.js";
|
|
2
3
|
import { buildSiderProMenuTokens } from "../../utils/themeComponents.js";
|
|
3
|
-
import { LAYOUT_MENU_SLOT_KEYS } from "
|
|
4
|
-
import
|
|
5
|
-
import { Transition, computed, createVNode, defineComponent,
|
|
4
|
+
import { LAYOUT_MENU_SLOT_KEYS } from "./interface.js";
|
|
5
|
+
import siderMenu_default from "./style/siderMenu.js";
|
|
6
|
+
import { Transition, computed, createVNode, defineComponent, isVNode, mergeProps, nextTick, onBeforeUnmount, ref, vShow, watch, withDirectives } from "vue";
|
|
6
7
|
import { useProToken } from "@gx-design-vue/pro-provider";
|
|
7
|
-
import { classNames, getSlotsProps
|
|
8
|
+
import { classNames, getSlotsProps } from "@gx-design-vue/pro-utils";
|
|
8
9
|
import { ConfigProvider, Popover } from "antdv-next";
|
|
9
10
|
import { useBaseConfig } from "antdv-next/config-provider/context";
|
|
10
11
|
import useCSSVarCls from "antdv-next/config-provider/hooks/useCSSVarCls";
|
|
11
|
-
import { GIcon } from "@gx-design-vue/icon";
|
|
12
12
|
import { omit } from "es-toolkit";
|
|
13
|
-
//#region src/components/
|
|
13
|
+
//#region src/components/Menu/SiderMenu.tsx
|
|
14
14
|
function _isSlot(s) {
|
|
15
15
|
return typeof s === "function" || Object.prototype.toString.call(s) === "[object Object]" && !isVNode(s);
|
|
16
16
|
}
|
|
@@ -47,19 +47,6 @@ function getPathKey(path) {
|
|
|
47
47
|
function getMenuTitleSymbol(title) {
|
|
48
48
|
return title ? title.charAt(0).toUpperCase() : null;
|
|
49
49
|
}
|
|
50
|
-
function renderMenuIcon(icon) {
|
|
51
|
-
if (!icon) return null;
|
|
52
|
-
if (isVNode(icon)) return icon;
|
|
53
|
-
if (isString(icon)) {
|
|
54
|
-
if (isUrl(icon) || isImg(icon)) return createVNode("img", {
|
|
55
|
-
"src": icon,
|
|
56
|
-
"alt": "icon"
|
|
57
|
-
}, null);
|
|
58
|
-
return createVNode(GIcon, { "type": icon }, null);
|
|
59
|
-
}
|
|
60
|
-
if (typeof icon === "function" || typeof icon === "object") return h(icon);
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
50
|
function getVisibleChildren(route) {
|
|
64
51
|
return route.children?.filter((child) => !child.meta?.hideInMenu) ?? [];
|
|
65
52
|
}
|
|
@@ -130,7 +117,7 @@ const SiderMenuContent = /* @__PURE__ */ defineComponent((props, { emit, slots }
|
|
|
130
117
|
const { proClasses, proStyles, collapsed } = useLayoutBase(props);
|
|
131
118
|
const { prefixCls } = useBaseConfig("pro-sider-menu", props);
|
|
132
119
|
const rootCls = useCSSVarCls(prefixCls);
|
|
133
|
-
const [hashId, cssVarCls] =
|
|
120
|
+
const [hashId, cssVarCls] = siderMenu_default(prefixCls, rootCls);
|
|
134
121
|
const { proLayoutToken } = useProToken();
|
|
135
122
|
const popupOpenPath = ref([]);
|
|
136
123
|
const menuRef = ref();
|
|
@@ -146,7 +133,7 @@ const SiderMenuContent = /* @__PURE__ */ defineComponent((props, { emit, slots }
|
|
|
146
133
|
keys: LAYOUT_MENU_SLOT_KEYS,
|
|
147
134
|
render: false
|
|
148
135
|
}));
|
|
149
|
-
const menuClassNames = computed(() => classNames(`${prefixCls.value}`, proClasses.value.menu, hashId.value, cssVarCls.value, rootCls.value, { [`${prefixCls.value}-collapsed`]: collapsed.value }, props.class));
|
|
136
|
+
const menuClassNames = computed(() => classNames(`${prefixCls.value}`, `${prefixCls.value}-root`, proClasses.value.menu, hashId.value, cssVarCls.value, rootCls.value, { [`${prefixCls.value}-collapsed`]: collapsed.value }, props.class));
|
|
150
137
|
const popupClassName = computed(() => classNames(`${prefixCls.value}-popover`, hashId.value, cssVarCls.value, rootCls.value));
|
|
151
138
|
function isPopupInteraction(ctx) {
|
|
152
139
|
return collapsed.value || ctx.popup;
|
|
@@ -246,7 +233,7 @@ const SiderMenuContent = /* @__PURE__ */ defineComponent((props, { emit, slots }
|
|
|
246
233
|
if (hasInitialScrolled.value || hasSiderMenuInitialAutoScrolled || collapsed.value) return;
|
|
247
234
|
const menuEl = menuRef.value;
|
|
248
235
|
if (!menuEl) return;
|
|
249
|
-
const scrollContainer = menuEl.closest(`.${prefixCls.value}-container`);
|
|
236
|
+
const scrollContainer = props.getScrollContainer?.() ?? menuEl.closest(`.${prefixCls.value}-container`);
|
|
250
237
|
const selectedItem = menuEl.querySelector(`.${prefixCls.value}-item-selected`);
|
|
251
238
|
if (!scrollContainer || !selectedItem) return;
|
|
252
239
|
hasInitialScrolled.value = true;
|
|
@@ -376,10 +363,7 @@ const SiderMenuContent = /* @__PURE__ */ defineComponent((props, { emit, slots }
|
|
|
376
363
|
renderIcon(route, item, firstLevelCollapsed),
|
|
377
364
|
!firstLevelCollapsed && renderLabel(route, item),
|
|
378
365
|
!firstLevelCollapsed && renderExtra(route, item),
|
|
379
|
-
!firstLevelCollapsed && createVNode("
|
|
380
|
-
"class": `${prefixCls.value}-arrow`,
|
|
381
|
-
"aria-hidden": "true"
|
|
382
|
-
}, null)
|
|
366
|
+
!firstLevelCollapsed && createVNode("i", { "class": `${prefixCls.value}-arrow` }, null)
|
|
383
367
|
]);
|
|
384
368
|
}
|
|
385
369
|
function renderSubMenu(route, children, ctx) {
|
|
@@ -540,6 +524,10 @@ const SiderMenuContent = /* @__PURE__ */ defineComponent((props, { emit, slots }
|
|
|
540
524
|
type: Object,
|
|
541
525
|
required: false
|
|
542
526
|
},
|
|
527
|
+
getScrollContainer: {
|
|
528
|
+
type: Function,
|
|
529
|
+
required: false
|
|
530
|
+
},
|
|
543
531
|
menuItemIcon: {
|
|
544
532
|
type: Function,
|
|
545
533
|
required: false
|
|
@@ -632,6 +620,10 @@ const SiderMenu = /* @__PURE__ */ defineComponent((props, { emit, slots }) => {
|
|
|
632
620
|
type: Object,
|
|
633
621
|
required: false
|
|
634
622
|
},
|
|
623
|
+
getScrollContainer: {
|
|
624
|
+
type: Function,
|
|
625
|
+
required: false
|
|
626
|
+
},
|
|
635
627
|
menuItemIcon: {
|
|
636
628
|
type: Function,
|
|
637
629
|
required: false
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { VNode } from "vue";
|
|
2
|
+
|
|
3
|
+
//#region src/components/Menu/iconRender.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* 渲染菜单/面包屑图标:兼容 VNode / 组件 / 图片地址 / 图标名。
|
|
6
|
+
* 被 LayoutMenu、SiderMenu、Breadcrumb 等组件共用。
|
|
7
|
+
*/
|
|
8
|
+
declare function renderMenuIcon(icon: any): VNode | string | null;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { renderMenuIcon };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createVNode, h, isVNode } from "vue";
|
|
2
|
+
import { isImg, isString, isUrl } from "@gx-design-vue/pro-utils";
|
|
3
|
+
import { GIcon } from "@gx-design-vue/icon";
|
|
4
|
+
//#region src/components/Menu/iconRender.tsx
|
|
5
|
+
/**
|
|
6
|
+
* 渲染菜单/面包屑图标:兼容 VNode / 组件 / 图片地址 / 图标名。
|
|
7
|
+
* 被 LayoutMenu、SiderMenu、Breadcrumb 等组件共用。
|
|
8
|
+
*/
|
|
9
|
+
function renderMenuIcon(icon) {
|
|
10
|
+
if (!icon) return null;
|
|
11
|
+
if (isVNode(icon)) return icon;
|
|
12
|
+
if (isString(icon)) {
|
|
13
|
+
if (isUrl(icon) || isImg(icon)) return createVNode("img", {
|
|
14
|
+
"src": icon,
|
|
15
|
+
"alt": "icon"
|
|
16
|
+
}, null);
|
|
17
|
+
return createVNode(GIcon, { "type": icon }, null);
|
|
18
|
+
}
|
|
19
|
+
if (typeof icon === "function" || typeof icon === "object") return h(icon);
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { renderMenuIcon };
|
|
@@ -1,31 +1,17 @@
|
|
|
1
1
|
import { useLayoutBase } from "../../hooks/useLayoutBase.js";
|
|
2
|
+
import { renderMenuIcon } from "./iconRender.js";
|
|
2
3
|
import { buildHeaderMenuTokens, buildSiderMenuTokens } from "../../utils/themeComponents.js";
|
|
3
4
|
import { buildMenuItems } from "./convert.js";
|
|
4
5
|
import { LAYOUT_MENU_SLOT_KEYS } from "./interface.js";
|
|
5
|
-
import { computed, createVNode, defineComponent
|
|
6
|
+
import { computed, createVNode, defineComponent } from "vue";
|
|
6
7
|
import { useProToken } from "@gx-design-vue/pro-provider";
|
|
7
|
-
import { classNames, getSlotsProps
|
|
8
|
+
import { classNames, getSlotsProps } from "@gx-design-vue/pro-utils";
|
|
8
9
|
import { ConfigProvider, Menu } from "antdv-next";
|
|
9
|
-
import { GIcon } from "@gx-design-vue/icon";
|
|
10
10
|
//#region src/components/Menu/index.tsx
|
|
11
11
|
/** 取菜单项首字母(折叠态无图标时的兜底展示)。 */
|
|
12
12
|
function getMenuTitleSymbol(title) {
|
|
13
13
|
return title ? title.charAt(0).toUpperCase() : null;
|
|
14
14
|
}
|
|
15
|
-
/** 渲染菜单图标:兼容 VNode / 组件 / 图片地址 / 图标名。 */
|
|
16
|
-
function renderMenuIcon(icon) {
|
|
17
|
-
if (!icon) return null;
|
|
18
|
-
if (isVNode(icon)) return icon;
|
|
19
|
-
if (isString(icon)) {
|
|
20
|
-
if (isUrl(icon) || isImg(icon)) return createVNode("img", {
|
|
21
|
-
"src": icon,
|
|
22
|
-
"alt": "icon"
|
|
23
|
-
}, null);
|
|
24
|
-
return createVNode(GIcon, { "type": icon }, null);
|
|
25
|
-
}
|
|
26
|
-
if (typeof icon === "function" || typeof icon === "object") return h(icon);
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
15
|
/**
|
|
30
16
|
* 将携带 `route` 的菜单项扁平为 `key → route`,供 `select` 事件回填 `LayoutSelectInfo.route`。
|
|
31
17
|
*/
|
|
@@ -2,7 +2,7 @@ import * as _$vue from "vue";
|
|
|
2
2
|
import { CSSProperties } from "vue";
|
|
3
3
|
import { GetDefaultToken } from "antdv-next/dist/theme/internal";
|
|
4
4
|
|
|
5
|
-
//#region src/components/
|
|
5
|
+
//#region src/components/Menu/style/siderMenu.d.ts
|
|
6
6
|
/** Component only token. Which will handle additional calculation of alias token */
|
|
7
7
|
interface ComponentToken {
|
|
8
8
|
/**
|