@nocobase/flow-engine 2.2.0-beta.15 → 2.2.0-beta.17
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/lib/acl/Acl.d.ts +2 -1
- package/lib/acl/Acl.js +28 -0
- package/lib/components/FlowContextSelector.js +7 -1
- package/lib/components/MobilePopup.js +14 -3
- package/lib/components/subModel/LazyDropdown.js +41 -26
- package/lib/flowContext.d.ts +5 -1
- package/lib/flowContext.js +18 -6
- package/lib/locale/en-US.json +2 -0
- package/lib/locale/index.d.ts +4 -0
- package/lib/locale/zh-CN.json +2 -0
- package/lib/utils/associationObjectVariable.d.ts +10 -0
- package/lib/utils/associationObjectVariable.js +10 -7
- package/lib/utils/dateVariable.d.ts +22 -0
- package/lib/utils/dateVariable.js +123 -16
- package/lib/utils/index.d.ts +3 -3
- package/lib/utils/index.js +8 -0
- package/lib/utils/params-resolvers.d.ts +3 -0
- package/lib/utils/params-resolvers.js +10 -0
- package/lib/utils/variablesParams.js +5 -0
- package/lib/views/createViewMeta.d.ts +1 -0
- package/lib/views/createViewMeta.js +53 -22
- package/package.json +4 -4
- package/src/__tests__/createViewMeta.popup.test.ts +84 -1
- package/src/__tests__/flowContext.test.ts +8 -0
- package/src/__tests__/objectVariable.test.ts +6 -1
- package/src/acl/Acl.tsx +36 -1
- package/src/acl/__tests__/Acl.test.tsx +70 -0
- package/src/components/FlowContextSelector.tsx +7 -1
- package/src/components/MobilePopup.tsx +16 -4
- package/src/components/__tests__/MobilePopup.test.tsx +42 -1
- package/src/components/subModel/LazyDropdown.tsx +44 -26
- package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +85 -2
- package/src/components/variables/__tests__/FlowContextSelector.test.tsx +35 -0
- package/src/flowContext.ts +31 -5
- package/src/locale/__tests__/index.test.ts +21 -0
- package/src/locale/en-US.json +2 -0
- package/src/locale/zh-CN.json +2 -0
- package/src/utils/__tests__/dateVariable.test.ts +57 -4
- package/src/utils/__tests__/variablesParams.test.ts +28 -1
- package/src/utils/associationObjectVariable.ts +9 -6
- package/src/utils/dateVariable.ts +145 -18
- package/src/utils/index.ts +17 -2
- package/src/utils/params-resolvers.ts +12 -0
- package/src/utils/variablesParams.ts +10 -0
- package/src/views/createViewMeta.ts +52 -18
package/lib/acl/Acl.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ interface CheckOptions {
|
|
|
5
5
|
actionName: string;
|
|
6
6
|
fields?: string[];
|
|
7
7
|
recordPkValue?: string | number;
|
|
8
|
-
allowedActions
|
|
8
|
+
allowedActions?: Record<string, Array<string | number>>;
|
|
9
9
|
}
|
|
10
10
|
export declare class ACL {
|
|
11
11
|
private flowEngine;
|
|
@@ -26,6 +26,7 @@ export declare class ACL {
|
|
|
26
26
|
verifyScope: (actionName: string, recordPkValue: any, allowedActions: any) => boolean;
|
|
27
27
|
parseAction(options: CheckOptions): any;
|
|
28
28
|
parseField(options: CheckOptions): boolean;
|
|
29
|
+
can(options: CheckOptions): boolean;
|
|
29
30
|
aclCheck(options: CheckOptions): Promise<boolean>;
|
|
30
31
|
}
|
|
31
32
|
export {};
|
package/lib/acl/Acl.js
CHANGED
|
@@ -154,6 +154,34 @@ const _ACL = class _ACL {
|
|
|
154
154
|
const allowed = whitelist.includes(fields[0]);
|
|
155
155
|
return allowed;
|
|
156
156
|
}
|
|
157
|
+
can(options) {
|
|
158
|
+
var _a;
|
|
159
|
+
const { allowAll } = this.data;
|
|
160
|
+
if (allowAll) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
const { actionName, allowedActions, recordPkValue } = options;
|
|
164
|
+
const hasRecordPkValue = recordPkValue !== void 0 && recordPkValue !== null;
|
|
165
|
+
const recordPermission = hasRecordPkValue && allowedActions ? this.verifyScope(actionName, recordPkValue, allowedActions) : null;
|
|
166
|
+
if (hasRecordPkValue && allowedActions && recordPermission !== true) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
const params = this.parseAction(options);
|
|
170
|
+
if (!params) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
if (!import_lodash.default.isEmpty(params.filter) && recordPermission !== true) {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
if (!((_a = options.fields) == null ? void 0 : _a.length)) {
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
const allowedFields = [].concat(params.whitelist || []).concat(params.fields || []).concat(params.appends || []);
|
|
180
|
+
if (!allowedFields.length) {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
return options.fields.every((field) => allowedFields.includes(field));
|
|
184
|
+
}
|
|
157
185
|
async aclCheck(options) {
|
|
158
186
|
const { allowAll } = this.data;
|
|
159
187
|
if (allowAll) {
|
|
@@ -247,6 +247,7 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
247
247
|
}, [active, cascaderProps.disabled, currentPath]);
|
|
248
248
|
const handleChange = (0, import_react.useCallback)(
|
|
249
249
|
(selectedValues, selectedOptions) => {
|
|
250
|
+
var _a;
|
|
250
251
|
const lastOption = selectedOptions == null ? void 0 : selectedOptions[selectedOptions.length - 1];
|
|
251
252
|
if (!selectedValues || selectedValues.length === 0) {
|
|
252
253
|
onChange == null ? void 0 : onChange("", lastOption == null ? void 0 : lastOption.meta);
|
|
@@ -256,6 +257,7 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
256
257
|
const path = selectedValues.map(String);
|
|
257
258
|
const pathString = path.join(".");
|
|
258
259
|
const isLeaf = lastOption == null ? void 0 : lastOption.isLeaf;
|
|
260
|
+
const isSelectable = ((_a = lastOption == null ? void 0 : lastOption.meta) == null ? void 0 : _a.selectable) !== false;
|
|
259
261
|
const now = Date.now();
|
|
260
262
|
let formattedValue;
|
|
261
263
|
if (customFormatPathToValue) {
|
|
@@ -267,12 +269,16 @@ const FlowContextSelectorComponent = /* @__PURE__ */ __name(({
|
|
|
267
269
|
formattedValue = (0, import_utils.formatPathToValue)(lastOption == null ? void 0 : lastOption.meta);
|
|
268
270
|
}
|
|
269
271
|
if (isLeaf) {
|
|
272
|
+
if (!isSelectable) {
|
|
273
|
+
setTempSelectedPath(path);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
270
276
|
onChange == null ? void 0 : onChange(formattedValue, lastOption == null ? void 0 : lastOption.meta);
|
|
271
277
|
setTempSelectedPath([]);
|
|
272
278
|
return;
|
|
273
279
|
}
|
|
274
280
|
const lastSelected = lastSelectedRef.current;
|
|
275
|
-
const isDoubleClick = !onlyLeafSelectable && (lastSelected == null ? void 0 : lastSelected.path) === pathString && now - lastSelected.time < 300;
|
|
281
|
+
const isDoubleClick = isSelectable && !onlyLeafSelectable && (lastSelected == null ? void 0 : lastSelected.path) === pathString && now - lastSelected.time < 300;
|
|
276
282
|
if (isDoubleClick) {
|
|
277
283
|
onChange == null ? void 0 : onChange(formattedValue, lastOption == null ? void 0 : lastOption.meta);
|
|
278
284
|
lastSelectedRef.current = null;
|
|
@@ -47,25 +47,36 @@ var import_react_i18next = require("react-i18next");
|
|
|
47
47
|
var import_lazy_helper = require("../lazy-helper");
|
|
48
48
|
const { Popup } = (0, import_lazy_helper.lazy)(() => import("antd-mobile"), "Popup");
|
|
49
49
|
const { CloseOutline } = (0, import_lazy_helper.lazy)(() => import("antd-mobile-icons"), "CloseOutline");
|
|
50
|
+
const getMobilePopupMaxHeight = /* @__PURE__ */ __name(() => {
|
|
51
|
+
var _a;
|
|
52
|
+
if (typeof CSS !== "undefined" && ((_a = CSS.supports) == null ? void 0 : _a.call(CSS, "height", "100dvh"))) {
|
|
53
|
+
return "calc(100dvh - var(--nb-mobile-page-header-height, 46px))";
|
|
54
|
+
}
|
|
55
|
+
return "calc(100vh - var(--nb-mobile-page-header-height, 46px))";
|
|
56
|
+
}, "getMobilePopupMaxHeight");
|
|
50
57
|
const MobilePopup = /* @__PURE__ */ __name((props) => {
|
|
51
58
|
var _a;
|
|
52
59
|
const { title, visible, onClose: closePopup, children, minHeight, className, footer } = props;
|
|
53
60
|
const { t } = (0, import_react_i18next.useTranslation)();
|
|
54
61
|
const { componentCls, hashId } = (0, import_MobilePopup.useMobileActionDrawerStyle)();
|
|
55
62
|
const bodyStyles = (_a = props.styles) == null ? void 0 : _a.body;
|
|
63
|
+
const defaultMaxHeight = getMobilePopupMaxHeight();
|
|
56
64
|
const popupStyle = (0, import_react.useMemo)(() => {
|
|
57
65
|
return {
|
|
58
66
|
minHeight: (bodyStyles == null ? void 0 : bodyStyles.minHeight) ?? minHeight,
|
|
59
67
|
height: bodyStyles == null ? void 0 : bodyStyles.height,
|
|
60
|
-
maxHeight: bodyStyles == null ? void 0 : bodyStyles.maxHeight
|
|
68
|
+
maxHeight: (bodyStyles == null ? void 0 : bodyStyles.maxHeight) ?? defaultMaxHeight
|
|
61
69
|
};
|
|
62
|
-
}, [bodyStyles == null ? void 0 : bodyStyles.height, bodyStyles == null ? void 0 : bodyStyles.maxHeight, bodyStyles == null ? void 0 : bodyStyles.minHeight, minHeight]);
|
|
70
|
+
}, [bodyStyles == null ? void 0 : bodyStyles.height, bodyStyles == null ? void 0 : bodyStyles.maxHeight, bodyStyles == null ? void 0 : bodyStyles.minHeight, defaultMaxHeight, minHeight]);
|
|
63
71
|
const bodyStyle = (0, import_react.useMemo)(() => {
|
|
64
72
|
return {
|
|
65
73
|
padding: 0,
|
|
74
|
+
maxHeight: defaultMaxHeight,
|
|
75
|
+
overflowY: "auto",
|
|
76
|
+
overflowX: "hidden",
|
|
66
77
|
...bodyStyles
|
|
67
78
|
};
|
|
68
|
-
}, [bodyStyles]);
|
|
79
|
+
}, [bodyStyles, defaultMaxHeight]);
|
|
69
80
|
const handleCloseKeyDown = (0, import_react.useCallback)(
|
|
70
81
|
(event) => {
|
|
71
82
|
if (event.key !== "Enter" && event.key !== " ") {
|
|
@@ -347,19 +347,15 @@ const createSearchItem = /* @__PURE__ */ __name((item, searchKey, currentSearchV
|
|
|
347
347
|
e.stopPropagation();
|
|
348
348
|
},
|
|
349
349
|
onChange: (e) => {
|
|
350
|
-
var _a;
|
|
351
350
|
e.stopPropagation();
|
|
352
351
|
const value = e.target.value;
|
|
353
352
|
if (shouldActivateSearchSubmenu) {
|
|
354
353
|
activateSearchSubmenu(searchKey);
|
|
355
354
|
}
|
|
356
|
-
if (
|
|
355
|
+
if (e.nativeEvent.isComposing || searchHandlers.isComposing(searchKey)) {
|
|
357
356
|
searchHandlers.updateInputValue(searchKey, value);
|
|
358
357
|
return;
|
|
359
358
|
}
|
|
360
|
-
if (!value && shouldActivateSearchSubmenu) {
|
|
361
|
-
deactivateSearchSubmenu(searchKey);
|
|
362
|
-
}
|
|
363
359
|
searchHandlers.updateSearchValue(searchKey, value);
|
|
364
360
|
},
|
|
365
361
|
onCompositionStart: (e) => {
|
|
@@ -373,11 +369,7 @@ const createSearchItem = /* @__PURE__ */ __name((item, searchKey, currentSearchV
|
|
|
373
369
|
e.stopPropagation();
|
|
374
370
|
const value = e.currentTarget.value;
|
|
375
371
|
if (shouldActivateSearchSubmenu) {
|
|
376
|
-
|
|
377
|
-
activateSearchSubmenu(searchKey);
|
|
378
|
-
} else {
|
|
379
|
-
deactivateSearchSubmenu(searchKey);
|
|
380
|
-
}
|
|
372
|
+
activateSearchSubmenu(searchKey);
|
|
381
373
|
}
|
|
382
374
|
searchHandlers.endComposition(searchKey, value);
|
|
383
375
|
},
|
|
@@ -385,10 +377,20 @@ const createSearchItem = /* @__PURE__ */ __name((item, searchKey, currentSearchV
|
|
|
385
377
|
e.stopPropagation();
|
|
386
378
|
},
|
|
387
379
|
onKeyDown: (e) => {
|
|
380
|
+
if (e.key === "Escape" || e.key === "Tab") {
|
|
381
|
+
deactivateSearchSubmenu(searchKey);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (shouldActivateSearchSubmenu) {
|
|
385
|
+
activateSearchSubmenu(searchKey);
|
|
386
|
+
}
|
|
388
387
|
e.stopPropagation();
|
|
389
388
|
},
|
|
390
389
|
onMouseDown: (e) => {
|
|
391
390
|
e.stopPropagation();
|
|
391
|
+
if (shouldActivateSearchSubmenu) {
|
|
392
|
+
activateSearchSubmenu(searchKey);
|
|
393
|
+
}
|
|
392
394
|
},
|
|
393
395
|
size: "small",
|
|
394
396
|
style: {
|
|
@@ -409,7 +411,7 @@ const KEEP_OPEN_LABEL_STYLE = {
|
|
|
409
411
|
width: "100%"
|
|
410
412
|
};
|
|
411
413
|
const DROPDOWN_PERSIST_TTL_MS = 350;
|
|
412
|
-
const
|
|
414
|
+
const MENU_CLOSE_DELAY = 0.3;
|
|
413
415
|
const SUBMENU_MOTION_DISABLED = {
|
|
414
416
|
motionEnter: false,
|
|
415
417
|
motionLeave: false
|
|
@@ -417,13 +419,18 @@ const SUBMENU_MOTION_DISABLED = {
|
|
|
417
419
|
const dropdownPersistRegistry = /* @__PURE__ */ new Map();
|
|
418
420
|
const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
419
421
|
const engine = (0, import_provider.useFlowEngine)();
|
|
422
|
+
const { getPrefixCls } = import_react.default.useContext(import_antd.ConfigProvider.ConfigContext);
|
|
423
|
+
const triggerId = import_react.default.useId();
|
|
420
424
|
const [menuVisible, setMenuVisible] = (0, import_react.useState)(false);
|
|
421
425
|
const [openKeys, setOpenKeys] = (0, import_react.useState)(/* @__PURE__ */ new Set());
|
|
422
|
-
const [activeSearchKey, setActiveSearchKey] = (0, import_react.useState)(null);
|
|
423
426
|
const [rootItems, setRootItems] = (0, import_react.useState)([]);
|
|
424
427
|
const [rootLoading, setRootLoading] = (0, import_react.useState)(false);
|
|
428
|
+
const activeSearchKeyRef = (0, import_react.useRef)(null);
|
|
425
429
|
const closeByOutsideClickRef = (0, import_react.useRef)(false);
|
|
426
430
|
const skipPreserveActiveSearchRef = (0, import_react.useRef)(false);
|
|
431
|
+
const triggerOpenClassName = `nb-lazy-dropdown-trigger-${triggerId.replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
432
|
+
const defaultOpenClassName = `${getPrefixCls("dropdown", props.prefixCls)}-open`;
|
|
433
|
+
const mergedOpenClassName = [props.openClassName ?? defaultOpenClassName, triggerOpenClassName].filter(Boolean).join(" ");
|
|
427
434
|
const dropdownMaxHeight = useNiceDropdownMaxHeight();
|
|
428
435
|
const t = engine.translate.bind(engine);
|
|
429
436
|
const { items: menuItems, keepDropdownOpen, persistKey, stateVersion, refreshKeys, ...dropdownMenuProps } = menu;
|
|
@@ -440,12 +447,12 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
440
447
|
useSubmenuStyles(menuVisible, dropdownMaxHeight);
|
|
441
448
|
const closeMenu = (0, import_react.useCallback)(() => {
|
|
442
449
|
setMenuVisible(false);
|
|
443
|
-
|
|
450
|
+
activeSearchKeyRef.current = null;
|
|
444
451
|
setOpenKeys(/* @__PURE__ */ new Set());
|
|
445
452
|
clearAllSearchValues();
|
|
446
453
|
}, [clearAllSearchValues]);
|
|
447
454
|
const activateSearchSubmenu = (0, import_react.useCallback)((key) => {
|
|
448
|
-
|
|
455
|
+
activeSearchKeyRef.current = key;
|
|
449
456
|
setOpenKeys((prev) => {
|
|
450
457
|
if (prev.has(key)) return prev;
|
|
451
458
|
const next = new Set(prev);
|
|
@@ -454,34 +461,38 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
454
461
|
});
|
|
455
462
|
}, []);
|
|
456
463
|
const deactivateSearchSubmenu = (0, import_react.useCallback)((key) => {
|
|
457
|
-
|
|
464
|
+
if (activeSearchKeyRef.current === key) {
|
|
465
|
+
activeSearchKeyRef.current = null;
|
|
466
|
+
}
|
|
458
467
|
}, []);
|
|
459
468
|
const closeActiveSearchForPath = (0, import_react.useCallback)(
|
|
460
469
|
(keyPath) => {
|
|
470
|
+
const activeSearchKey = activeSearchKeyRef.current;
|
|
461
471
|
if (!activeSearchKey || keyPath === activeSearchKey || keyPath.startsWith(`${activeSearchKey}/`) || activeSearchKey.startsWith(`${keyPath}/`)) {
|
|
462
472
|
return;
|
|
463
473
|
}
|
|
464
474
|
skipPreserveActiveSearchRef.current = true;
|
|
465
475
|
clearSearchValue(activeSearchKey);
|
|
466
|
-
|
|
476
|
+
activeSearchKeyRef.current = null;
|
|
467
477
|
setOpenKeys((prev) => {
|
|
468
478
|
const next = new Set(prev);
|
|
469
479
|
next.delete(activeSearchKey);
|
|
470
480
|
return next;
|
|
471
481
|
});
|
|
472
482
|
},
|
|
473
|
-
[
|
|
483
|
+
[clearSearchValue]
|
|
474
484
|
);
|
|
475
485
|
const handleMenuOpenChange = (0, import_react.useCallback)(
|
|
476
486
|
(nextOpenKeys) => {
|
|
477
487
|
var _a, _b;
|
|
478
488
|
let normalized = normalizeOpenKeys(nextOpenKeys);
|
|
479
|
-
|
|
480
|
-
|
|
489
|
+
const activeSearchKey = activeSearchKeyRef.current;
|
|
490
|
+
if (activeSearchKey && !normalized.includes(activeSearchKey)) {
|
|
491
|
+
if (skipPreserveActiveSearchRef.current) {
|
|
481
492
|
clearSearchValue(activeSearchKey);
|
|
482
|
-
|
|
493
|
+
activeSearchKeyRef.current = null;
|
|
483
494
|
} else {
|
|
484
|
-
normalized =
|
|
495
|
+
normalized = Array.from(openKeys);
|
|
485
496
|
}
|
|
486
497
|
}
|
|
487
498
|
if (!normalized.length && shouldPreventClose()) {
|
|
@@ -498,13 +509,15 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
498
509
|
(_b = dropdownMenuProps.onOpenChange) == null ? void 0 : _b.call(dropdownMenuProps, normalized);
|
|
499
510
|
skipPreserveActiveSearchRef.current = false;
|
|
500
511
|
},
|
|
501
|
-
[
|
|
512
|
+
[clearSearchValue, dropdownMenuProps, openKeys, shouldPreventClose]
|
|
502
513
|
);
|
|
503
514
|
(0, import_react.useEffect)(() => {
|
|
504
515
|
if (!menuVisible) return;
|
|
505
516
|
const markOutsideClick = /* @__PURE__ */ __name((event) => {
|
|
506
517
|
const target = event.target;
|
|
507
|
-
const
|
|
518
|
+
const isInsidePopup = target == null ? void 0 : target.closest(".ant-dropdown, .ant-dropdown-menu, .ant-dropdown-menu-submenu-popup");
|
|
519
|
+
const isInsideCurrentTrigger = target == null ? void 0 : target.closest(`.${triggerOpenClassName}`);
|
|
520
|
+
const isOutside = !isInsidePopup && !isInsideCurrentTrigger;
|
|
508
521
|
closeByOutsideClickRef.current = isOutside;
|
|
509
522
|
if (isOutside) {
|
|
510
523
|
closeMenu();
|
|
@@ -516,7 +529,7 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
516
529
|
document.removeEventListener("pointerdown", markOutsideClick, true);
|
|
517
530
|
document.removeEventListener("mousedown", markOutsideClick, true);
|
|
518
531
|
};
|
|
519
|
-
}, [closeMenu, menuVisible]);
|
|
532
|
+
}, [closeMenu, menuVisible, triggerOpenClassName]);
|
|
520
533
|
(0, import_react.useEffect)(() => {
|
|
521
534
|
if (!persistKey) return;
|
|
522
535
|
const until = dropdownPersistRegistry.get(persistKey) || 0;
|
|
@@ -734,13 +747,15 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
734
747
|
...props,
|
|
735
748
|
open: menuVisible,
|
|
736
749
|
destroyPopupOnHide: true,
|
|
750
|
+
mouseLeaveDelay: props.mouseLeaveDelay ?? MENU_CLOSE_DELAY,
|
|
751
|
+
openClassName: mergedOpenClassName,
|
|
737
752
|
overlayClassName,
|
|
738
753
|
placement: "bottomLeft",
|
|
739
754
|
menu: {
|
|
740
755
|
...dropdownMenuProps,
|
|
741
756
|
openKeys: Array.from(openKeys),
|
|
742
757
|
items,
|
|
743
|
-
subMenuCloseDelay: dropdownMenuProps.subMenuCloseDelay ??
|
|
758
|
+
subMenuCloseDelay: dropdownMenuProps.subMenuCloseDelay ?? MENU_CLOSE_DELAY,
|
|
744
759
|
motion: dropdownMenuProps.motion ?? SUBMENU_MOTION_DISABLED,
|
|
745
760
|
onClick: /* @__PURE__ */ __name(() => {
|
|
746
761
|
}, "onClick"),
|
|
@@ -752,7 +767,7 @@ const LazyDropdown = /* @__PURE__ */ __name(({ menu, ...props }) => {
|
|
|
752
767
|
}
|
|
753
768
|
},
|
|
754
769
|
onOpenChange: (visible, info) => {
|
|
755
|
-
if (!visible &&
|
|
770
|
+
if (!visible && activeSearchKeyRef.current && (info == null ? void 0 : info.source) === "trigger" && !closeByOutsideClickRef.current) {
|
|
756
771
|
return;
|
|
757
772
|
}
|
|
758
773
|
if (!visible && shouldPreventClose()) {
|
package/lib/flowContext.d.ts
CHANGED
|
@@ -27,6 +27,9 @@ import type { RecordRef } from './utils/serverContextParams';
|
|
|
27
27
|
import { FlowView, FlowViewer } from './views/FlowView';
|
|
28
28
|
import { type RunJSVersion } from './runjs-context/registry';
|
|
29
29
|
type Getter<T = any> = (ctx: FlowContext) => T | Promise<T>;
|
|
30
|
+
export type ResolveJsonTemplateOptions = {
|
|
31
|
+
contractModelUid?: string | number | null;
|
|
32
|
+
};
|
|
30
33
|
export type FlowContextDocRef = string | {
|
|
31
34
|
url: string;
|
|
32
35
|
title?: string;
|
|
@@ -77,6 +80,7 @@ export interface MetaTreeNode {
|
|
|
77
80
|
hidden?: boolean | (() => boolean);
|
|
78
81
|
disabled?: boolean | (() => boolean);
|
|
79
82
|
disabledReason?: string | (() => string | undefined);
|
|
83
|
+
selectable?: boolean;
|
|
80
84
|
children?: MetaTreeNode[] | (() => Promise<MetaTreeNode[]>);
|
|
81
85
|
}
|
|
82
86
|
export interface PropertyMeta {
|
|
@@ -343,7 +347,7 @@ declare class BaseFlowEngineContext extends FlowContext {
|
|
|
343
347
|
* @deprecated use `resolveJsonTemplate` instead
|
|
344
348
|
*/
|
|
345
349
|
renderJson: (template: JSONValue) => Promise<any>;
|
|
346
|
-
resolveJsonTemplate: (template: JSONValue) => Promise<any>;
|
|
350
|
+
resolveJsonTemplate: (template: JSONValue, options?: ResolveJsonTemplateOptions) => Promise<any>;
|
|
347
351
|
getVar: (path: string) => Promise<any>;
|
|
348
352
|
request: (options: RequestOptions) => Promise<any>;
|
|
349
353
|
runjs: (code: string, variables?: Record<string, any>, options?: JSRunnerOptions) => Promise<any>;
|
package/lib/flowContext.js
CHANGED
|
@@ -2383,8 +2383,8 @@ const _FlowEngineContext = class _FlowEngineContext extends BaseFlowEngineContex
|
|
|
2383
2383
|
this.defineMethod("renderJson", function(template) {
|
|
2384
2384
|
return this.resolveJsonTemplate(template);
|
|
2385
2385
|
});
|
|
2386
|
-
|
|
2387
|
-
var _a, _b;
|
|
2386
|
+
const resolveJsonTemplate = /* @__PURE__ */ __name(async function(template, options) {
|
|
2387
|
+
var _a, _b, _c;
|
|
2388
2388
|
const used = (0, import_utils.extractUsedVariablePaths)(template);
|
|
2389
2389
|
const usedVarNames = Object.keys(used || {});
|
|
2390
2390
|
if (!usedVarNames.length) {
|
|
@@ -2412,13 +2412,13 @@ const _FlowEngineContext = class _FlowEngineContext extends BaseFlowEngineContex
|
|
|
2412
2412
|
let serverResolved = template;
|
|
2413
2413
|
if (needServer) {
|
|
2414
2414
|
const inferRecordRefWithMeta = /* @__PURE__ */ __name((ctx) => {
|
|
2415
|
-
var _a2, _b2,
|
|
2415
|
+
var _a2, _b2, _c2, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
2416
2416
|
const ref = (0, import_variablesParams.inferRecordRef)(ctx);
|
|
2417
2417
|
if (ref) return ref;
|
|
2418
2418
|
try {
|
|
2419
2419
|
const tk = (_b2 = (_a2 = ctx == null ? void 0 : ctx.resource) == null ? void 0 : _a2.getMeta) == null ? void 0 : _b2.call(_a2, "currentFilterByTk");
|
|
2420
2420
|
if (typeof tk === "undefined" || tk === null) return void 0;
|
|
2421
|
-
const collection = ((
|
|
2421
|
+
const collection = ((_c2 = ctx == null ? void 0 : ctx.collection) == null ? void 0 : _c2.name) || ((_j = (_i = (_h = (_g = (_f = (_e = (_d = ctx == null ? void 0 : ctx.resource) == null ? void 0 : _d.getResourceName) == null ? void 0 : _e.call(_d)) == null ? void 0 : _f.split) == null ? void 0 : _g.call(_f, ".")) == null ? void 0 : _h.slice) == null ? void 0 : _i.call(_h, -1)) == null ? void 0 : _j[0]);
|
|
2422
2422
|
if (!collection) return void 0;
|
|
2423
2423
|
const dataSourceKey = ((_k = ctx == null ? void 0 : ctx.collection) == null ? void 0 : _k.dataSourceKey) || ((_m = (_l = ctx == null ? void 0 : ctx.resource) == null ? void 0 : _l.getDataSourceKey) == null ? void 0 : _m.call(_l));
|
|
2424
2424
|
return { collection, dataSourceKey, filterByTk: tk };
|
|
@@ -2461,6 +2461,11 @@ const _FlowEngineContext = class _FlowEngineContext extends BaseFlowEngineContex
|
|
|
2461
2461
|
}, "collectFromMeta");
|
|
2462
2462
|
const inputFromMeta = await collectFromMeta();
|
|
2463
2463
|
const autoInput = { ...inputFromMeta };
|
|
2464
|
+
const viewPaths = serverVarPaths.view || [];
|
|
2465
|
+
if (!autoInput.view && viewPaths.some((path) => path === "record" || path.startsWith("record.") || path.startsWith("record["))) {
|
|
2466
|
+
const recordRef = (0, import_variablesParams.inferViewRecordRef)(this);
|
|
2467
|
+
if (recordRef) autoInput.view = { record: recordRef };
|
|
2468
|
+
}
|
|
2464
2469
|
try {
|
|
2465
2470
|
const varName = "formValues";
|
|
2466
2471
|
const neededPaths = serverVarPaths[varName] || [];
|
|
@@ -2511,18 +2516,25 @@ const _FlowEngineContext = class _FlowEngineContext extends BaseFlowEngineContex
|
|
|
2511
2516
|
}
|
|
2512
2517
|
if (this.api) {
|
|
2513
2518
|
try {
|
|
2519
|
+
const contractRd = (0, import_params_resolvers.buildFlowModelResolveDescriptor)(
|
|
2520
|
+
this,
|
|
2521
|
+
options == null ? void 0 : options.contractModelUid
|
|
2522
|
+
);
|
|
2514
2523
|
serverResolved = await (0, import_params_resolvers.enqueueVariablesResolve)(this, {
|
|
2524
|
+
...contractRd ? { contractRd } : {},
|
|
2525
|
+
rd: (0, import_params_resolvers.buildFlowModelResolveDescriptor)(this, (_a = this.model) == null ? void 0 : _a.uid),
|
|
2515
2526
|
template,
|
|
2516
2527
|
contextParams: autoContextParams || {}
|
|
2517
2528
|
});
|
|
2518
2529
|
} catch (e) {
|
|
2519
|
-
(
|
|
2530
|
+
(_c = (_b = this.logger) == null ? void 0 : _b.warn) == null ? void 0 : _c.call(_b, { err: e }, "variables:resolve failed, fallback to client-only");
|
|
2520
2531
|
serverResolved = template;
|
|
2521
2532
|
}
|
|
2522
2533
|
}
|
|
2523
2534
|
}
|
|
2524
2535
|
return (0, import_utils.resolveExpressions)(serverResolved, this);
|
|
2525
|
-
});
|
|
2536
|
+
}, "resolveJsonTemplate");
|
|
2537
|
+
this.defineMethod("resolveJsonTemplate", resolveJsonTemplate);
|
|
2526
2538
|
this.defineMethod(
|
|
2527
2539
|
"getVar",
|
|
2528
2540
|
async function(varPath) {
|
package/lib/locale/en-US.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"Add": "Add",
|
|
3
|
+
"Are you sure you want to perform the action?": "Are you sure you want to perform the action?",
|
|
3
4
|
"Are you sure you want to delete this item? This action cannot be undone.": "Are you sure you want to delete this item? This action cannot be undone.",
|
|
4
5
|
"Are you sure to convert this template block to copy mode?": "Are you sure you want to convert this template block to copy mode?",
|
|
5
6
|
"Array index out of bounds": "Array index {{index}} out of bounds for '{{subKey}}'",
|
|
@@ -53,6 +54,7 @@
|
|
|
53
54
|
"Other blocks": "Other blocks",
|
|
54
55
|
"Parent not found, cannot replace block": "Parent not found, cannot replace block",
|
|
55
56
|
"Previous step": "Previous step",
|
|
57
|
+
"Please Confirm": "Please Confirm",
|
|
56
58
|
"Replace current block with template?": "Replace current block with template?",
|
|
57
59
|
"Replaced with template block": "Replaced with template block",
|
|
58
60
|
"Render failed": "Render failed",
|
package/lib/locale/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
export declare const locales: {
|
|
10
10
|
'en-US': {
|
|
11
11
|
Add: string;
|
|
12
|
+
"Are you sure you want to perform the action?": string;
|
|
12
13
|
"Are you sure you want to delete this item? This action cannot be undone.": string;
|
|
13
14
|
"Are you sure to convert this template block to copy mode?": string;
|
|
14
15
|
"Array index out of bounds": string;
|
|
@@ -62,6 +63,7 @@ export declare const locales: {
|
|
|
62
63
|
"Other blocks": string;
|
|
63
64
|
"Parent not found, cannot replace block": string;
|
|
64
65
|
"Previous step": string;
|
|
66
|
+
"Please Confirm": string;
|
|
65
67
|
"Replace current block with template?": string;
|
|
66
68
|
"Replaced with template block": string;
|
|
67
69
|
"Render failed": string;
|
|
@@ -91,6 +93,7 @@ export declare const locales: {
|
|
|
91
93
|
};
|
|
92
94
|
'zh-CN': {
|
|
93
95
|
Add: string;
|
|
96
|
+
"Are you sure you want to perform the action?": string;
|
|
94
97
|
"Are you sure you want to delete this item? This action cannot be undone.": string;
|
|
95
98
|
"Are you sure to convert this template block to copy mode?": string;
|
|
96
99
|
"Array index out of bounds": string;
|
|
@@ -150,6 +153,7 @@ export declare const locales: {
|
|
|
150
153
|
OK: string;
|
|
151
154
|
"Other blocks": string;
|
|
152
155
|
"Previous step": string;
|
|
156
|
+
"Please Confirm": string;
|
|
153
157
|
"Render failed": string;
|
|
154
158
|
"Response record": string;
|
|
155
159
|
"Step configuration": string;
|
package/lib/locale/zh-CN.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"Add": "添加",
|
|
3
|
+
"Are you sure you want to perform the action?": "确定要执行此操作吗?",
|
|
3
4
|
"Are you sure you want to delete this item? This action cannot be undone.": "确定要删除此项吗?此操作不可撤销。",
|
|
4
5
|
"Are you sure to convert this template block to copy mode?": "确定将该模板区块转换为复制模式吗?",
|
|
5
6
|
"Array index out of bounds": "数组索引 {{index}} 超出 '{{subKey}}' 的边界",
|
|
@@ -59,6 +60,7 @@
|
|
|
59
60
|
"OK": "确定",
|
|
60
61
|
"Other blocks": "其他区块",
|
|
61
62
|
"Previous step": "上一步",
|
|
63
|
+
"Please Confirm": "请确认",
|
|
62
64
|
"Render failed": "渲染失败",
|
|
63
65
|
"Response record": "响应结果记录",
|
|
64
66
|
"Step configuration": "步骤配置",
|
|
@@ -8,6 +8,16 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { Collection } from '../data-source';
|
|
10
10
|
import type { FlowContext, PropertyMetaFactory } from '../flowContext';
|
|
11
|
+
/**
|
|
12
|
+
* 从值中提取主键:
|
|
13
|
+
* - 支持主键原始值(string/number)
|
|
14
|
+
* - 支持对象(按主键名取值)
|
|
15
|
+
*
|
|
16
|
+
* @param value 字段当前值
|
|
17
|
+
* @param primaryKey 主键字段名
|
|
18
|
+
* @returns 解析出的主键值,无法解析时返回 undefined
|
|
19
|
+
*/
|
|
20
|
+
export declare function getAssociationFilterByTk(value: unknown, primaryKey: string | string[]): unknown;
|
|
11
21
|
/**
|
|
12
22
|
* 创建一个用于“对象类变量”(如 formValues / item)的 `resolveOnServer` 判定函数。
|
|
13
23
|
* 仅当访问路径以“关联字段名”开头(且继续访问其子属性)时,返回 true 交由服务端解析;
|
|
@@ -38,7 +38,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
38
38
|
var associationObjectVariable_exports = {};
|
|
39
39
|
__export(associationObjectVariable_exports, {
|
|
40
40
|
createAssociationAwareObjectMetaFactory: () => createAssociationAwareObjectMetaFactory,
|
|
41
|
-
createAssociationSubpathResolver: () => createAssociationSubpathResolver
|
|
41
|
+
createAssociationSubpathResolver: () => createAssociationSubpathResolver,
|
|
42
|
+
getAssociationFilterByTk: () => getAssociationFilterByTk
|
|
42
43
|
});
|
|
43
44
|
module.exports = __toCommonJS(associationObjectVariable_exports);
|
|
44
45
|
var import_lodash = __toESM(require("lodash"));
|
|
@@ -58,13 +59,14 @@ function findFieldByName(collection, name) {
|
|
|
58
59
|
return fields.find((f) => f.name === name);
|
|
59
60
|
}
|
|
60
61
|
__name(findFieldByName, "findFieldByName");
|
|
61
|
-
function
|
|
62
|
+
function getAssociationFilterByTk(value, primaryKey) {
|
|
62
63
|
if (value == null) return void 0;
|
|
63
64
|
if (Array.isArray(primaryKey)) {
|
|
64
65
|
if (typeof value !== "object" || !value) return void 0;
|
|
66
|
+
const record = value;
|
|
65
67
|
const out = {};
|
|
66
68
|
for (const k of primaryKey) {
|
|
67
|
-
const v =
|
|
69
|
+
const v = record[k];
|
|
68
70
|
if (typeof v === "undefined" || v === null) return void 0;
|
|
69
71
|
out[k] = v;
|
|
70
72
|
}
|
|
@@ -76,7 +78,7 @@ function toFilterByTk(value, primaryKey) {
|
|
|
76
78
|
}
|
|
77
79
|
return void 0;
|
|
78
80
|
}
|
|
79
|
-
__name(
|
|
81
|
+
__name(getAssociationFilterByTk, "getAssociationFilterByTk");
|
|
80
82
|
function createAssociationSubpathResolver(collectionAccessor, valueAccessor) {
|
|
81
83
|
return (p) => {
|
|
82
84
|
if (!p || !p.includes(".")) return false;
|
|
@@ -122,7 +124,7 @@ function createAssociationAwareObjectMetaFactory(collectionAccessor, title, valu
|
|
|
122
124
|
const associationValue = obj[name];
|
|
123
125
|
if (associationValue == null) continue;
|
|
124
126
|
if (Array.isArray(associationValue)) {
|
|
125
|
-
const ids = associationValue.map((item) =>
|
|
127
|
+
const ids = associationValue.map((item) => getAssociationFilterByTk(item, primaryKey)).filter((v) => v != null);
|
|
126
128
|
if (ids.length) {
|
|
127
129
|
params[name] = {
|
|
128
130
|
collection: target,
|
|
@@ -131,7 +133,7 @@ function createAssociationAwareObjectMetaFactory(collectionAccessor, title, valu
|
|
|
131
133
|
};
|
|
132
134
|
}
|
|
133
135
|
} else {
|
|
134
|
-
const id =
|
|
136
|
+
const id = getAssociationFilterByTk(associationValue, primaryKey);
|
|
135
137
|
if (id != null) {
|
|
136
138
|
params[name] = {
|
|
137
139
|
collection: target,
|
|
@@ -153,5 +155,6 @@ __name(createAssociationAwareObjectMetaFactory, "createAssociationAwareObjectMet
|
|
|
153
155
|
// Annotate the CommonJS export names for ESM import in node:
|
|
154
156
|
0 && (module.exports = {
|
|
155
157
|
createAssociationAwareObjectMetaFactory,
|
|
156
|
-
createAssociationSubpathResolver
|
|
158
|
+
createAssociationSubpathResolver,
|
|
159
|
+
getAssociationFilterByTk
|
|
157
160
|
});
|
|
@@ -6,11 +6,33 @@
|
|
|
6
6
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
|
+
declare const PRESET_KEY_LIST: readonly ["today", "now", "yesterday", "tomorrow", "thisWeek", "lastWeek", "nextWeek", "thisMonth", "lastMonth", "nextMonth", "thisQuarter", "lastQuarter", "nextQuarter", "thisYear", "lastYear", "nextYear"];
|
|
10
|
+
export type CtxDatePreset = (typeof PRESET_KEY_LIST)[number];
|
|
11
|
+
export type CtxDateRelativeDirection = 'next' | 'past';
|
|
12
|
+
export type CtxDateRelativeUnit = 'day' | 'week' | 'month' | 'year';
|
|
13
|
+
export type CtxDateExpressionConfig = {
|
|
14
|
+
kind: 'exact';
|
|
15
|
+
value: string | [string, string];
|
|
16
|
+
format?: string;
|
|
17
|
+
} | {
|
|
18
|
+
kind: 'relative';
|
|
19
|
+
direction: CtxDateRelativeDirection;
|
|
20
|
+
amount: number;
|
|
21
|
+
unit: CtxDateRelativeUnit;
|
|
22
|
+
format?: string;
|
|
23
|
+
} | {
|
|
24
|
+
kind: 'preset';
|
|
25
|
+
preset: CtxDatePreset;
|
|
26
|
+
format?: string;
|
|
27
|
+
};
|
|
9
28
|
export declare function isCtxDatePathPrefix(pathSegments: string[]): boolean;
|
|
10
29
|
export declare function encodeBase64Url(input: string): string;
|
|
11
30
|
export declare function decodeBase64Url(input: string): string | undefined;
|
|
12
31
|
export declare function isCtxDateExpression(value: unknown): value is string;
|
|
13
32
|
export declare function isCompleteCtxDatePath(pathSegments: string[]): boolean;
|
|
14
33
|
export declare function parseCtxDateExpression(value: unknown): any;
|
|
34
|
+
export declare function parseCtxDateExpressionConfig(value: unknown): CtxDateExpressionConfig | undefined;
|
|
35
|
+
export declare function serializeCtxDateExpressionConfig(config: CtxDateExpressionConfig): string | undefined;
|
|
15
36
|
export declare function serializeCtxDateValue(value: unknown): string | undefined;
|
|
16
37
|
export declare function resolveCtxDatePath(pathSegments: string[]): any;
|
|
38
|
+
export {};
|