@zat-design/sisyphus-react 4.6.0-beta.3 → 4.6.0
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/index.esm.css +1 -1
- package/dist/less.esm.css +1 -1
- package/es/ProEditTable/utils/tools.d.ts +1 -1
- package/es/ProEditTable/utils/tools.js +5 -4
- package/es/ProLayout/components/TabsManager/hooks/useTabsState.js +15 -12
- package/es/ProLayout/components/TabsManager/hooks/useTabsUrlSync.js +19 -2
- package/es/ProLayout/components/TabsManager/style/index.less +7 -7
- package/es/ProLayout/components/TabsManager/utils/index.d.ts +2 -0
- package/es/ProLayout/components/TabsManager/utils/index.js +47 -7
- package/es/ProLayout/propTypes.d.ts +6 -2
- package/es/ProLayout/style/index.less +6 -22
- package/package.json +1 -1
|
@@ -32,7 +32,7 @@ export declare const difference: <T extends Record<string, any>, U extends Recor
|
|
|
32
32
|
* @param rowName 行名称路径
|
|
33
33
|
* @returns 校验结果Promise
|
|
34
34
|
*/
|
|
35
|
-
export declare const customValidate: (validateKeys:
|
|
35
|
+
export declare const customValidate: (validateKeys: NamePath[], form: FormInstance, rowName: NamePath) => Promise<any>;
|
|
36
36
|
/**
|
|
37
37
|
* 拆解数组names
|
|
38
38
|
* @param names 名称数组
|
|
@@ -47,10 +47,11 @@ const difference = (object, base) => {
|
|
|
47
47
|
return changes(object, base);
|
|
48
48
|
};
|
|
49
49
|
const customValidate = (validateKeys, form, rowName) => {
|
|
50
|
-
const
|
|
51
|
-
validateKeys.
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
const rowNamePath = Array.isArray(rowName) ? rowName : [rowName];
|
|
51
|
+
const array = validateKeys.map((key) => [
|
|
52
|
+
...rowNamePath,
|
|
53
|
+
...Array.isArray(key) ? key : [key]
|
|
54
|
+
]);
|
|
54
55
|
return form.validateFields(array);
|
|
55
56
|
};
|
|
56
57
|
const splitNames = (names) => {
|
|
@@ -13,8 +13,8 @@ import {
|
|
|
13
13
|
flattenMenuData,
|
|
14
14
|
canOpenAsTab,
|
|
15
15
|
getMenuRoute,
|
|
16
|
-
hasRelationKey,
|
|
17
16
|
findExistingTab,
|
|
17
|
+
getUrlSyncTabIdentity,
|
|
18
18
|
getComparableTabUrl,
|
|
19
19
|
normalizeTabUrl,
|
|
20
20
|
isSafeExternalUrl,
|
|
@@ -69,18 +69,20 @@ const useTabsState = (options) => {
|
|
|
69
69
|
const retainedTabByIdentity = /* @__PURE__ */ new Map();
|
|
70
70
|
const retainedIdByOriginalId = /* @__PURE__ */ new Map();
|
|
71
71
|
restoredState.tabsList.forEach((tab) => {
|
|
72
|
-
var _a2;
|
|
73
72
|
const normalizedUrl = normalizeTabUrl(tab.url);
|
|
74
73
|
if (!normalizedUrl)
|
|
75
74
|
return;
|
|
76
75
|
const comparableUrl = getComparableTabUrl(normalizedUrl, urlSyncBasename);
|
|
77
76
|
if (!comparableUrl)
|
|
78
77
|
return;
|
|
79
|
-
const
|
|
80
|
-
|
|
78
|
+
const identity = getUrlSyncTabIdentity(
|
|
79
|
+
{ ...tab, url: normalizedUrl },
|
|
80
|
+
{ identity: urlSyncIdentity, basename: urlSyncBasename }
|
|
81
|
+
);
|
|
82
|
+
if (!identity)
|
|
83
|
+
return;
|
|
81
84
|
const retainedTab = retainedTabByIdentity.get(identity);
|
|
82
85
|
if (retainedTab) {
|
|
83
|
-
retainedTabByIdentity.set(identity, { ...retainedTab, url: normalizedUrl });
|
|
84
86
|
retainedIdByOriginalId.set(tab.id, retainedTab.id);
|
|
85
87
|
return;
|
|
86
88
|
}
|
|
@@ -181,12 +183,7 @@ const useTabsState = (options) => {
|
|
|
181
183
|
}) : void 0;
|
|
182
184
|
if (existingTab) {
|
|
183
185
|
const shouldFixClosable = resolvedMenuItem.closable === false && existingTab.closable !== false;
|
|
184
|
-
const
|
|
185
|
-
const correctedTab = shouldFixClosable || shouldUpdateUrl ? {
|
|
186
|
-
...existingTab,
|
|
187
|
-
...shouldFixClosable ? { closable: false } : {},
|
|
188
|
-
...shouldUpdateUrl ? { url: normalizedUrl } : {}
|
|
189
|
-
} : existingTab;
|
|
186
|
+
const correctedTab = shouldFixClosable ? { ...existingTab, closable: false } : existingTab;
|
|
190
187
|
const updatedTabsList = correctedTab !== existingTab ? prevState.tabsList.map((t) => t.id === existingTab.id ? correctedTab : t) : prevState.tabsList;
|
|
191
188
|
return {
|
|
192
189
|
...prevState,
|
|
@@ -257,7 +254,10 @@ const useTabsState = (options) => {
|
|
|
257
254
|
...updates.icon !== void 0 && { icon: updates.icon },
|
|
258
255
|
...updates.extra !== void 0 && { extra: updates.extra },
|
|
259
256
|
...updates.closable !== void 0 && { closable: updates.closable },
|
|
260
|
-
...updates.pinned !== void 0 && { pinned: updates.pinned }
|
|
257
|
+
...updates.pinned !== void 0 && { pinned: updates.pinned },
|
|
258
|
+
...updates.menuCodeSource !== void 0 && {
|
|
259
|
+
menuCodeSource: updates.menuCodeSource
|
|
260
|
+
}
|
|
261
261
|
} : target.menuItem;
|
|
262
262
|
const nextTab = {
|
|
263
263
|
...target,
|
|
@@ -265,6 +265,9 @@ const useTabsState = (options) => {
|
|
|
265
265
|
...updates.icon !== void 0 && { icon: updates.icon },
|
|
266
266
|
...updates.closable !== void 0 && { closable: updates.closable },
|
|
267
267
|
...updates.pinned !== void 0 && { pinned: updates.pinned },
|
|
268
|
+
...updates.menuCodeSource !== void 0 && {
|
|
269
|
+
menuCodeSource: updates.menuCodeSource
|
|
270
|
+
},
|
|
268
271
|
menuItem: nextMenuItem
|
|
269
272
|
};
|
|
270
273
|
const newTabsList = [...prevState.tabsList];
|
|
@@ -3,6 +3,7 @@ import * as ReactRouterDom from "react-router-dom";
|
|
|
3
3
|
import {
|
|
4
4
|
canOpenAsTab,
|
|
5
5
|
flattenMenuData,
|
|
6
|
+
findExistingTab,
|
|
6
7
|
getBrowserTabUrl,
|
|
7
8
|
getComparableTabUrl,
|
|
8
9
|
getCurrentTabUrl,
|
|
@@ -95,6 +96,7 @@ const useTabsUrlSync = ({
|
|
|
95
96
|
const enabled = config.urlSync === true || typeof config.urlSync === "object" && config.urlSync !== null;
|
|
96
97
|
const historyMode = typeof config.urlSync === "object" ? config.urlSync.history || "replace" : "replace";
|
|
97
98
|
const basename = typeof config.urlSync === "object" ? config.urlSync.basename : void 0;
|
|
99
|
+
const identity = typeof config.urlSync === "object" ? config.urlSync.identity : void 0;
|
|
98
100
|
const bootstrappedRef = useRef(false);
|
|
99
101
|
const writingUrlRef = useRef(false);
|
|
100
102
|
const preserveUrlInteractionRef = useRef(null);
|
|
@@ -201,7 +203,21 @@ const useTabsUrlSync = ({
|
|
|
201
203
|
handledLocationSequenceRef.current = locationChange.sequence;
|
|
202
204
|
bootstrappedRef.current = true;
|
|
203
205
|
preserveUrlInteractionRef.current = interactionSequence;
|
|
204
|
-
|
|
206
|
+
const resolvedTargetMenu = { ...targetMenu, url: currentUrl, redirectUrl: void 0 };
|
|
207
|
+
const existingTargetTab = findExistingTab(state.tabsList, resolvedTargetMenu, {
|
|
208
|
+
urlSync: true,
|
|
209
|
+
identity,
|
|
210
|
+
basename
|
|
211
|
+
});
|
|
212
|
+
if (existingTargetTab) {
|
|
213
|
+
if (getComparableTabUrl(existingTargetTab.url, basename) !== comparableCurrentUrl) {
|
|
214
|
+
updateTabUrl(existingTargetTab.id, currentUrl);
|
|
215
|
+
}
|
|
216
|
+
if (state.activeKey !== existingTargetTab.id)
|
|
217
|
+
switchTab(existingTargetTab.id);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
addTab(resolvedTargetMenu);
|
|
205
221
|
return;
|
|
206
222
|
}
|
|
207
223
|
if (!bootstrappedRef.current) {
|
|
@@ -230,7 +246,8 @@ const useTabsUrlSync = ({
|
|
|
230
246
|
switchTab,
|
|
231
247
|
updateTabUrl,
|
|
232
248
|
interactionSequence,
|
|
233
|
-
basename
|
|
249
|
+
basename,
|
|
250
|
+
identity
|
|
234
251
|
]);
|
|
235
252
|
useEffect(() => {
|
|
236
253
|
if (!enabled || !isInitialized || !bootstrappedRef.current || typeof window === "undefined") {
|
|
@@ -86,13 +86,6 @@
|
|
|
86
86
|
border-bottom: none;
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
// 横线右端缩短,空出圆角半径,让横线在圆弧起点处结束
|
|
90
|
-
// 用属性选择器提升权重,确保覆盖 antd :where() 规则
|
|
91
|
-
.@{ant-prefix}-tabs-nav[class]::before {
|
|
92
|
-
right: var(--zaui-border-radius, 8px) !important;
|
|
93
|
-
left: 0 !important;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
89
|
// 覆盖 nav-list 样式,使其匹配 pro-layout-tab-list
|
|
97
90
|
.@{ant-prefix}-tabs-nav-list {
|
|
98
91
|
display: flex;
|
|
@@ -167,6 +160,13 @@
|
|
|
167
160
|
|
|
168
161
|
.pro-layout-tabs-antd {
|
|
169
162
|
&.@{ant-prefix}-tabs {
|
|
163
|
+
// TabsHeader 通过 Portal 渲染到 ProLayout 挂载点,需要在全局作用域截断横线。
|
|
164
|
+
// 右侧空出一个圆角半径,与内容区圆弧的顶部边线无缝连接。
|
|
165
|
+
.@{ant-prefix}-tabs-nav[class]::before {
|
|
166
|
+
right: var(--zaui-border-radius, 8px) !important;
|
|
167
|
+
left: 0 !important;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
170
|
.@{ant-prefix}-tabs-tab {
|
|
171
171
|
padding: 4px 0 5px !important;
|
|
172
172
|
margin-right: 8px !important;
|
|
@@ -24,6 +24,8 @@ export interface FindExistingTabOptions {
|
|
|
24
24
|
identity?: TabsUrlSyncConfig['identity'];
|
|
25
25
|
basename?: TabsUrlSyncConfig['basename'];
|
|
26
26
|
}
|
|
27
|
+
/** URL 同步模式的唯一身份;relationKey 模式兼容无关联键的旧菜单 code。 */
|
|
28
|
+
export declare const getUrlSyncTabIdentity: (item: MenusType | TabItem, options?: Pick<FindExistingTabOptions, 'identity' | 'basename'>) => string | undefined;
|
|
27
29
|
export declare const findExistingTab: (tabsList: TabItem[], menuItem: MenusType, options?: FindExistingTabOptions) => TabItem | undefined;
|
|
28
30
|
/**
|
|
29
31
|
* 根据菜单项生成TabItem
|
|
@@ -78,20 +78,59 @@ const getTabUrlPathname = (value) => {
|
|
|
78
78
|
return void 0;
|
|
79
79
|
return new URL(normalized, window.location.origin).pathname;
|
|
80
80
|
};
|
|
81
|
+
const getIdentityRelationKey = (item) => {
|
|
82
|
+
var _a;
|
|
83
|
+
return item.relationKey ?? ("menuItem" in item ? (_a = item.menuItem) == null ? void 0 : _a.relationKey : void 0);
|
|
84
|
+
};
|
|
85
|
+
const getIdentityCode = (item) => {
|
|
86
|
+
var _a;
|
|
87
|
+
return item.code ?? ("menuItem" in item ? (_a = item.menuItem) == null ? void 0 : _a.code : void 0);
|
|
88
|
+
};
|
|
89
|
+
const getTypedIdentity = (prefix, value) => `${prefix}:${typeof value}:${String(value)}`;
|
|
90
|
+
const isTabItem = (item) => typeof item.id === "string";
|
|
91
|
+
const getUrlSyncTabIdentity = (item, options = {}) => {
|
|
92
|
+
if (options.identity === "relationKey") {
|
|
93
|
+
const relationKey = getIdentityRelationKey(item);
|
|
94
|
+
if (hasRelationKey(relationKey))
|
|
95
|
+
return getTypedIdentity("relationKey", relationKey);
|
|
96
|
+
const code = getIdentityCode(item);
|
|
97
|
+
if (code !== void 0 && code !== null && code !== "") {
|
|
98
|
+
return getTypedIdentity("code", code);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const route = isTabItem(item) ? item.url : getMenuRoute(item);
|
|
102
|
+
const comparableUrl = getComparableTabUrl(route, options.basename);
|
|
103
|
+
return comparableUrl ? `url:${comparableUrl}` : void 0;
|
|
104
|
+
};
|
|
81
105
|
const findExistingTab = (tabsList, menuItem, options = {}) => {
|
|
82
106
|
if (options.urlSync) {
|
|
83
|
-
if (options.identity === "relationKey"
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
107
|
+
if (options.identity === "relationKey") {
|
|
108
|
+
const relationKey = getIdentityRelationKey(menuItem);
|
|
109
|
+
if (hasRelationKey(relationKey)) {
|
|
110
|
+
const relatedTab = tabsList.find(
|
|
111
|
+
(tab) => getIdentityRelationKey(tab) === relationKey
|
|
112
|
+
);
|
|
113
|
+
if (relatedTab)
|
|
114
|
+
return relatedTab;
|
|
115
|
+
const legacyCode = getIdentityCode(menuItem);
|
|
116
|
+
if (legacyCode !== void 0 && legacyCode !== null && legacyCode !== "") {
|
|
117
|
+
return tabsList.find(
|
|
118
|
+
(tab) => !hasRelationKey(getIdentityRelationKey(tab)) && getIdentityCode(tab) === legacyCode
|
|
119
|
+
);
|
|
88
120
|
}
|
|
89
|
-
|
|
121
|
+
return void 0;
|
|
122
|
+
}
|
|
123
|
+
const code = getIdentityCode(menuItem);
|
|
124
|
+
if (code !== void 0 && code !== null && code !== "") {
|
|
125
|
+
return tabsList.find((tab) => getIdentityCode(tab) === code);
|
|
126
|
+
}
|
|
90
127
|
}
|
|
91
128
|
const targetUrl = getComparableTabUrl(getMenuRoute(menuItem), options.basename);
|
|
92
129
|
if (!targetUrl)
|
|
93
130
|
return void 0;
|
|
94
|
-
return tabsList.find(
|
|
131
|
+
return tabsList.find(
|
|
132
|
+
(tab) => getComparableTabUrl(tab.url, options.basename) === targetUrl
|
|
133
|
+
);
|
|
95
134
|
}
|
|
96
135
|
if (hasRelationKey(menuItem.relationKey)) {
|
|
97
136
|
return tabsList.find(
|
|
@@ -282,6 +321,7 @@ export {
|
|
|
282
321
|
getPinnedBoundary,
|
|
283
322
|
getRightTabs,
|
|
284
323
|
getTabUrlPathname,
|
|
324
|
+
getUrlSyncTabIdentity,
|
|
285
325
|
handleExternalOpen,
|
|
286
326
|
hasRelationKey,
|
|
287
327
|
isLeafMenuItem,
|
|
@@ -369,7 +369,7 @@ export interface TabsUrlSyncConfig {
|
|
|
369
369
|
*/
|
|
370
370
|
history?: 'replace' | 'push';
|
|
371
371
|
/**
|
|
372
|
-
* @description 页签复用身份。url
|
|
372
|
+
* @description 页签复用身份。url 保持完整 URL 唯一语义;relationKey 按关联键、无关联键的菜单 code、完整 URL 依次匹配
|
|
373
373
|
* @default 'url'
|
|
374
374
|
*/
|
|
375
375
|
identity?: 'url' | 'relationKey';
|
|
@@ -425,7 +425,7 @@ export interface AddTabOptions {
|
|
|
425
425
|
silent?: boolean;
|
|
426
426
|
}
|
|
427
427
|
/**
|
|
428
|
-
* @description
|
|
428
|
+
* @description 更新标签页的展示、扩展数据与来源菜单身份
|
|
429
429
|
*/
|
|
430
430
|
export interface UpdateTabParams {
|
|
431
431
|
/**
|
|
@@ -448,6 +448,10 @@ export interface UpdateTabParams {
|
|
|
448
448
|
* @description 是否固定标签(本质状态,driven closable)
|
|
449
449
|
*/
|
|
450
450
|
pinned?: boolean;
|
|
451
|
+
/**
|
|
452
|
+
* @description 最初打开当前业务标签链路的左侧菜单 code;用于兼容性迁移已缓存的来源身份
|
|
453
|
+
*/
|
|
454
|
+
menuCodeSource?: string;
|
|
451
455
|
}
|
|
452
456
|
/**
|
|
453
457
|
* @description 获取标签页信息返回值
|
|
@@ -327,24 +327,6 @@
|
|
|
327
327
|
overflow: hidden;
|
|
328
328
|
min-height: 0;
|
|
329
329
|
}
|
|
330
|
-
|
|
331
|
-
// 稳定遮住 ant-tabs-nav::before 横线的右端,使其在内容区圆弧起点处截止
|
|
332
|
-
&.tabs-visible::before {
|
|
333
|
-
content: '';
|
|
334
|
-
position: absolute;
|
|
335
|
-
bottom: 0;
|
|
336
|
-
right: 0;
|
|
337
|
-
width: var(--zaui-border-radius, 8px);
|
|
338
|
-
height: 2px;
|
|
339
|
-
background-color: #f7f9fd;
|
|
340
|
-
background-image: var(--header-bg-image);
|
|
341
|
-
background-repeat: no-repeat;
|
|
342
|
-
background-position: top center;
|
|
343
|
-
background-size: 100% auto;
|
|
344
|
-
background-attachment: fixed;
|
|
345
|
-
z-index: 1;
|
|
346
|
-
pointer-events: none;
|
|
347
|
-
}
|
|
348
330
|
}
|
|
349
331
|
|
|
350
332
|
/** 内容区 */
|
|
@@ -370,17 +352,19 @@
|
|
|
370
352
|
border-top-color: transparent;
|
|
371
353
|
border-radius: 0;
|
|
372
354
|
padding-top: var(--zaui-space-size-md, 16px);
|
|
373
|
-
border-top-right-radius: var(--zaui-border-radius, 8px);
|
|
374
355
|
|
|
375
356
|
// 圆弧由内容区独立绘制并随内容滚动;sticky Tab 栏始终保持在其上方。
|
|
376
|
-
//
|
|
357
|
+
// 与内容区外边框对齐,同时遮住底层直角,避免缩放时出现断线或双重圆弧。
|
|
377
358
|
&::before {
|
|
378
359
|
content: '';
|
|
379
360
|
position: absolute;
|
|
380
|
-
|
|
381
|
-
|
|
361
|
+
box-sizing: border-box;
|
|
362
|
+
top: -1px;
|
|
363
|
+
right: -1px;
|
|
382
364
|
width: var(--zaui-border-radius, 8px);
|
|
383
365
|
height: var(--zaui-border-radius, 8px);
|
|
366
|
+
background: #ffffff;
|
|
367
|
+
border-top: 1px solid rgb(235 236 238);
|
|
384
368
|
border-right: 1px solid rgb(235 236 238);
|
|
385
369
|
border-top-right-radius: var(--zaui-border-radius, 8px);
|
|
386
370
|
pointer-events: none;
|