@nocobase/client-v2 2.2.0-beta.3 → 2.2.0-beta.6
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/es/RouteRepository.d.ts +21 -6
- package/es/components/form/JsonTextArea.d.ts +2 -1
- package/es/components/form/VariableJsonTextArea.d.ts +19 -0
- package/es/components/form/index.d.ts +1 -0
- package/es/components/index.d.ts +1 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +2 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.d.ts +2 -1
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +2 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.d.ts +3 -2
- package/es/flow/admin-shell/admin-layout/AdminLayoutSlotModels.d.ts +4 -0
- package/es/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.d.ts +6 -0
- package/es/flow/components/FieldAssignRulesEditor.d.ts +3 -1
- package/es/flow/models/base/GridModel.d.ts +1 -1
- package/es/flow/models/blocks/filter-form/FilterFormBlockModel.d.ts +5 -0
- package/es/flow/models/blocks/form/FormBlockModel.d.ts +4 -0
- package/es/flow/models/blocks/form/value-runtime/rules.d.ts +21 -0
- package/es/flow/models/blocks/form/value-runtime/runtime.d.ts +21 -0
- package/es/flow/models/blocks/form/value-runtime/types.d.ts +2 -2
- package/es/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.d.ts +2 -0
- package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.d.ts +1 -0
- package/es/flow/utils/dateTimeDisplayProps.d.ts +49 -0
- package/es/flow-compat/FieldValidation.d.ts +2 -1
- package/es/index.d.ts +2 -1
- package/es/index.mjs +167 -121
- package/lib/index.js +167 -121
- package/package.json +7 -7
- package/src/RouteRepository.ts +126 -24
- package/src/__tests__/RouteRepository.test.ts +216 -0
- package/src/__tests__/exports.test.ts +16 -0
- package/src/__tests__/settings-center.test.tsx +33 -0
- package/src/components/form/JsonTextArea.tsx +21 -11
- package/src/components/form/VariableJsonTextArea.tsx +175 -0
- package/src/components/form/__tests__/JsonTextArea.test.tsx +43 -0
- package/src/components/form/__tests__/VariableJsonTextArea.test.tsx +86 -0
- package/src/components/form/index.tsx +1 -0
- package/src/components/index.ts +1 -0
- package/src/flow/__tests__/FlowRoute.test.tsx +443 -5
- package/src/flow/actions/__tests__/fieldLinkageRules.scopeDepth.test.ts +150 -1
- package/src/flow/actions/__tests__/linkageAssignField.legacy.test.ts +135 -0
- package/src/flow/actions/__tests__/linkageRules.hiddenSync.restore.test.ts +19 -5
- package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +92 -0
- package/src/flow/actions/customVariable.tsx +1 -2
- package/src/flow/actions/dateTimeFormat.tsx +42 -28
- package/src/flow/actions/linkageRules.tsx +122 -34
- package/src/flow/actions/runjs.tsx +2 -14
- package/src/flow/actions/validation.tsx +62 -30
- package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +33 -10
- package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.test.tsx +310 -0
- package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +44 -8
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.ts +5 -3
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +61 -9
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +71 -8
- package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +1 -1
- package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +21 -8
- package/src/flow/admin-shell/admin-layout/HelpLite.tsx +1 -3
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +188 -0
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +99 -0
- package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +166 -2
- package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.test.ts +20 -0
- package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.ts +33 -5
- package/src/flow/components/DefaultValue.tsx +1 -2
- package/src/flow/components/DynamicFlowsIcon.tsx +447 -126
- package/src/flow/components/FieldAssignRulesEditor.tsx +62 -12
- package/src/flow/components/FlowRoute.tsx +56 -11
- package/src/flow/components/__tests__/DynamicFlowsIcon.test.tsx +148 -2
- package/src/flow/components/__tests__/FieldAssignRulesEditor.test.tsx +81 -4
- package/src/flow/components/code-editor/__tests__/useCodeRunner.test.tsx +2 -17
- package/src/flow/components/code-editor/hooks/useCodeRunner.ts +2 -14
- package/src/flow/components/code-editor/runjsDiagnostics.ts +8 -45
- package/src/flow/flows/editMarkdownFlow.tsx +1 -1
- package/src/flow/index.ts +1 -1
- package/src/flow/internal/utils/operatorSchemaHelper.ts +15 -1
- package/src/flow/models/actions/JSActionModel.tsx +2 -7
- package/src/flow/models/actions/JSCollectionActionModel.tsx +2 -7
- package/src/flow/models/actions/JSItemActionModel.tsx +2 -7
- package/src/flow/models/actions/JSRecordActionModel.tsx +2 -7
- package/src/flow/models/base/ActionModel.tsx +12 -1
- package/src/flow/models/base/GridModel.tsx +38 -7
- package/src/flow/models/base/PageModel/PageModel.tsx +4 -1
- package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +28 -4
- package/src/flow/models/base/__tests__/ActionModel.test.ts +83 -0
- package/src/flow/models/base/__tests__/GridModel.dragSnapshotContainer.test.ts +239 -1
- package/src/flow/models/base/__tests__/transformRowsToSingleColumn.test.ts +48 -0
- package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +31 -3
- package/src/flow/models/blocks/filter-form/FilterFormJSActionModel.tsx +2 -7
- package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +77 -0
- package/src/flow/models/blocks/filter-manager/flow-actions/__tests__/customizeFilterRender.test.tsx +56 -1
- package/src/flow/models/blocks/form/EditFormModel.tsx +1 -0
- package/src/flow/models/blocks/form/FormBlockModel.tsx +7 -0
- package/src/flow/models/blocks/form/JSFormActionModel.tsx +2 -7
- package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +17 -0
- package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +880 -102
- package/src/flow/models/blocks/form/value-runtime/rules.ts +445 -13
- package/src/flow/models/blocks/form/value-runtime/runtime.ts +257 -13
- package/src/flow/models/blocks/form/value-runtime/types.ts +2 -2
- package/src/flow/models/blocks/js-block/JSBlock.tsx +2 -7
- package/src/flow/models/blocks/table/JSColumnModel.tsx +1 -9
- package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +1 -1
- package/src/flow/models/blocks/table/TableBlockModel.tsx +13 -3
- package/src/flow/models/blocks/table/TableColumnModel.tsx +36 -3
- package/src/flow/models/blocks/table/__tests__/TableActionsColumnModel.test.tsx +54 -0
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.mobileSettingsButtons.test.tsx +78 -0
- package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +159 -1
- package/src/flow/models/fields/AssociationFieldModel/AssociationFieldModel.tsx +1 -1
- package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +29 -6
- package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +16 -4
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.tsx +24 -1
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +13 -1
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableColumnModel.rowRecord.test.ts +12 -0
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableFieldModel.reset.test.ts +180 -0
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +6 -4
- package/src/flow/models/fields/AssociationFieldModel/__tests__/AssociationFieldModel.updateAssociation.test.ts +72 -0
- package/src/flow/models/fields/AssociationFieldModel/__tests__/RecordPickerFieldModel.itemContext.test.ts +23 -0
- package/src/flow/models/fields/ClickableFieldModel.tsx +5 -0
- package/src/flow/models/fields/DisplayDateTimeFieldModel.tsx +29 -1
- package/src/flow/models/fields/InputFieldModel.tsx +14 -22
- package/src/flow/models/fields/JSEditableFieldModel.tsx +2 -11
- package/src/flow/models/fields/JSFieldModel.tsx +2 -7
- package/src/flow/models/fields/JSItemModel.tsx +2 -14
- package/src/flow/models/fields/SelectFieldModel.tsx +6 -4
- package/src/flow/models/fields/__tests__/DisplayDateTimeFieldModel.test.tsx +96 -0
- package/src/flow/models/fields/__tests__/InputFieldModel.test.tsx +17 -0
- package/src/flow/models/fields/__tests__/SelectFieldModel.test.tsx +43 -0
- package/src/flow/models/topbar/TopbarActionModel.tsx +93 -10
- package/src/flow/utils/__tests__/dateTimeFormat.test.ts +258 -0
- package/src/flow/utils/dateTimeDisplayProps.ts +135 -0
- package/src/flow-compat/FieldValidation.tsx +122 -60
- package/src/flow-compat/Popover.tsx +43 -4
- package/src/flow-compat/__tests__/Popover.test.tsx +34 -0
- package/src/index.ts +8 -1
- package/src/layout-manager/__tests__/LayoutRoute.test.tsx +41 -1
- package/src/settings-center/AdminSettingsLayout.tsx +3 -0
- package/src/settings-center/plugin-manager/index.tsx +3 -0
|
@@ -16,13 +16,9 @@ import {
|
|
|
16
16
|
FlowRuntimeContext,
|
|
17
17
|
useFlowContext,
|
|
18
18
|
useFlowEngine,
|
|
19
|
-
createSafeWindow,
|
|
20
|
-
createSafeDocument,
|
|
21
|
-
createSafeNavigator,
|
|
22
19
|
observer,
|
|
23
20
|
isRunJSValue,
|
|
24
21
|
normalizeRunJSValue,
|
|
25
|
-
runjsWithSafeGlobals,
|
|
26
22
|
} from '@nocobase/flow-engine';
|
|
27
23
|
import { evaluateConditions, FilterGroupType, removeInvalidFilterItems } from '@nocobase/utils/client';
|
|
28
24
|
import React from 'react';
|
|
@@ -40,7 +36,7 @@ import { FilterGroup } from '../components/filter/FilterGroup';
|
|
|
40
36
|
import { LinkageFilterItem } from '../components/filter';
|
|
41
37
|
import { CodeEditor } from '../components/code-editor';
|
|
42
38
|
import { FieldAssignRulesEditor } from '../components/FieldAssignRulesEditor';
|
|
43
|
-
import type { FieldAssignRuleItem } from '../components/FieldAssignRulesEditor';
|
|
39
|
+
import type { AssignMode, FieldAssignRuleItem } from '../components/FieldAssignRulesEditor';
|
|
44
40
|
import { collectFieldAssignCascaderOptions } from '../components/fieldAssignOptions';
|
|
45
41
|
import { useAssociationTitleFieldSync } from '../components/useAssociationTitleFieldSync';
|
|
46
42
|
import _ from 'lodash';
|
|
@@ -492,7 +488,7 @@ async function resolveLinkageAssignRuntimeValue(ctx: FlowContext, rawValue: any)
|
|
|
492
488
|
|
|
493
489
|
try {
|
|
494
490
|
const { code, version } = normalizeRunJSValue(rawValue);
|
|
495
|
-
const ret = await
|
|
491
|
+
const ret = await ctx.runjs(code, undefined, { version });
|
|
496
492
|
if (!ret?.success) {
|
|
497
493
|
return SKIP_RUNJS_ASSIGN_VALUE;
|
|
498
494
|
}
|
|
@@ -888,6 +884,20 @@ type ArrayFieldComponentProps = {
|
|
|
888
884
|
|
|
889
885
|
const LEGACY_ASSIGN_RULE = { mode: 'assign', valueKey: 'assignValue' } as const;
|
|
890
886
|
const LEGACY_DEFAULT_RULE = { mode: 'default', valueKey: 'initialValue' } as const;
|
|
887
|
+
const LINKAGE_ASSIGN_MODE_PROP = '__linkageAssignMode';
|
|
888
|
+
|
|
889
|
+
function normalizeLinkageAssignMode(mode: unknown): AssignMode {
|
|
890
|
+
if (mode === 'default') return 'default';
|
|
891
|
+
if (mode === 'override') return 'override';
|
|
892
|
+
return 'assign';
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
type LinkageValuePatch = {
|
|
896
|
+
path: Array<string | number>;
|
|
897
|
+
value: unknown;
|
|
898
|
+
whenEmpty?: boolean;
|
|
899
|
+
mode?: AssignMode;
|
|
900
|
+
};
|
|
891
901
|
|
|
892
902
|
const FieldAssignRulesActionComponent: React.FC<
|
|
893
903
|
ArrayFieldComponentProps & {
|
|
@@ -1002,16 +1012,24 @@ export const linkageAssignField = defineAction({
|
|
|
1002
1012
|
continue;
|
|
1003
1013
|
}
|
|
1004
1014
|
|
|
1005
|
-
const mode = it?.mode
|
|
1015
|
+
const mode = normalizeLinkageAssignMode(it?.mode);
|
|
1006
1016
|
if (fieldModel) {
|
|
1007
1017
|
if (mode === 'default') {
|
|
1008
1018
|
setProps(fieldModel as FlowModel, { initialValue: finalValue });
|
|
1009
1019
|
} else {
|
|
1010
|
-
setProps(fieldModel as FlowModel, {
|
|
1020
|
+
setProps(fieldModel as FlowModel, {
|
|
1021
|
+
value: finalValue,
|
|
1022
|
+
...(mode === 'override' ? { [LINKAGE_ASSIGN_MODE_PROP]: mode } : {}),
|
|
1023
|
+
});
|
|
1011
1024
|
}
|
|
1012
1025
|
} else if (typeof addFormValuePatch === 'function') {
|
|
1013
1026
|
// 对关联字段子属性(如 user.name)等没有独立 FormItemModel 的目标,直接写入表单值
|
|
1014
|
-
addFormValuePatch({
|
|
1027
|
+
addFormValuePatch({
|
|
1028
|
+
path: targetPath,
|
|
1029
|
+
value: finalValue,
|
|
1030
|
+
whenEmpty: mode === 'default',
|
|
1031
|
+
...(mode === 'override' ? { mode } : {}),
|
|
1032
|
+
});
|
|
1015
1033
|
}
|
|
1016
1034
|
}
|
|
1017
1035
|
} catch (error) {
|
|
@@ -1177,7 +1195,7 @@ export const subFormLinkageAssignField = defineAction({
|
|
|
1177
1195
|
continue;
|
|
1178
1196
|
}
|
|
1179
1197
|
|
|
1180
|
-
const mode = it?.mode
|
|
1198
|
+
const mode = normalizeLinkageAssignMode(it?.mode);
|
|
1181
1199
|
const actionName = (ctx.model as any)?.getAclActionName?.() ?? (ctx.model as any)?.context?.actionName;
|
|
1182
1200
|
const isEditForm = actionName === 'update';
|
|
1183
1201
|
const isNewItem = (ctx as any)?.item?.__is_new__ === true;
|
|
@@ -1207,12 +1225,21 @@ export const subFormLinkageAssignField = defineAction({
|
|
|
1207
1225
|
continue;
|
|
1208
1226
|
}
|
|
1209
1227
|
|
|
1228
|
+
if (mode === 'override' && hasExplicitPathHit(targetPath)) {
|
|
1229
|
+
continue;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1210
1232
|
if (!fieldUid) {
|
|
1211
1233
|
if (mode === 'default' && hasExplicitPathHit(targetPath)) {
|
|
1212
1234
|
continue;
|
|
1213
1235
|
}
|
|
1214
1236
|
if (typeof addFormValuePatch === 'function') {
|
|
1215
|
-
addFormValuePatch({
|
|
1237
|
+
addFormValuePatch({
|
|
1238
|
+
path: targetPath,
|
|
1239
|
+
value: finalValue,
|
|
1240
|
+
whenEmpty: mode === 'default',
|
|
1241
|
+
...(mode === 'override' ? { mode } : {}),
|
|
1242
|
+
});
|
|
1216
1243
|
}
|
|
1217
1244
|
continue;
|
|
1218
1245
|
}
|
|
@@ -1223,7 +1250,10 @@ export const subFormLinkageAssignField = defineAction({
|
|
|
1223
1250
|
if (mode === 'default') {
|
|
1224
1251
|
setProps(model, { initialValue: finalValue });
|
|
1225
1252
|
} else {
|
|
1226
|
-
setProps(model, {
|
|
1253
|
+
setProps(model, {
|
|
1254
|
+
value: finalValue,
|
|
1255
|
+
...(mode === 'override' ? { [LINKAGE_ASSIGN_MODE_PROP]: mode } : {}),
|
|
1256
|
+
});
|
|
1227
1257
|
}
|
|
1228
1258
|
}
|
|
1229
1259
|
} catch (error) {
|
|
@@ -1363,8 +1393,7 @@ export const linkageRunjs = defineAction({
|
|
|
1363
1393
|
}
|
|
1364
1394
|
|
|
1365
1395
|
try {
|
|
1366
|
-
|
|
1367
|
-
await ctx.runjs(script, { window: createSafeWindow({ navigator }), document: createSafeDocument(), navigator });
|
|
1396
|
+
await ctx.runjs(script);
|
|
1368
1397
|
} catch (error) {
|
|
1369
1398
|
console.error('Script execution error:', error);
|
|
1370
1399
|
// 可以选择显示错误信息给用户
|
|
@@ -1781,7 +1810,7 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
1781
1810
|
const modelsToApply = new Set<FlowModel>(allModels);
|
|
1782
1811
|
const patchPropsByModel = new Map<FlowModel, any>();
|
|
1783
1812
|
const clearValueOnHiddenModelUids = new Set<string>();
|
|
1784
|
-
const directValuePatches:
|
|
1813
|
+
const directValuePatches: LinkageValuePatch[] = [];
|
|
1785
1814
|
const rootCollection = getCollectionFromModel((ctx.model as any)?.context?.blockModel ?? ctx.model);
|
|
1786
1815
|
const isSafeToWriteAssociationSubpath = (namePath: any): boolean => {
|
|
1787
1816
|
if (!Array.isArray(namePath) || !namePath.length) return true;
|
|
@@ -1836,13 +1865,18 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
1836
1865
|
}
|
|
1837
1866
|
|
|
1838
1867
|
for (const patch of lastPatchByPathKey.values()) {
|
|
1839
|
-
if (!patch.whenEmpty) continue;
|
|
1868
|
+
if (!patch.whenEmpty && patch.mode !== 'default') continue;
|
|
1840
1869
|
runtime.recordDefaultValuePatch(patch.path, patch.value);
|
|
1841
1870
|
}
|
|
1842
1871
|
};
|
|
1843
|
-
const
|
|
1872
|
+
const getPatchMode = (patch: { mode?: unknown; whenEmpty?: boolean }): AssignMode => {
|
|
1873
|
+
if (patch?.mode === 'default') return 'default';
|
|
1874
|
+
if (patch?.mode === 'override') return 'override';
|
|
1875
|
+
return patch?.whenEmpty ? 'default' : 'assign';
|
|
1876
|
+
};
|
|
1877
|
+
const addFormValuePatch = (patch: { path: unknown; value: unknown; whenEmpty?: boolean; mode?: AssignMode }) => {
|
|
1844
1878
|
if (!patch) return;
|
|
1845
|
-
const path =
|
|
1879
|
+
const path = patch.path;
|
|
1846
1880
|
if (!path) return;
|
|
1847
1881
|
const resolvedPath = resolveNamePathForPatch(path);
|
|
1848
1882
|
if (!resolvedPath) {
|
|
@@ -1862,8 +1896,9 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
1862
1896
|
});
|
|
1863
1897
|
return;
|
|
1864
1898
|
}
|
|
1865
|
-
const
|
|
1866
|
-
const
|
|
1899
|
+
const mode = getPatchMode(patch);
|
|
1900
|
+
const whenEmpty = mode === 'default';
|
|
1901
|
+
const value = patch.value;
|
|
1867
1902
|
try {
|
|
1868
1903
|
const form = ctx.model?.context?.form;
|
|
1869
1904
|
const current = form?.getFieldValue?.(resolvedPath);
|
|
@@ -1878,6 +1913,15 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
1878
1913
|
return;
|
|
1879
1914
|
}
|
|
1880
1915
|
}
|
|
1916
|
+
if (mode === 'override') {
|
|
1917
|
+
const runtime = getDefaultPatchRuntime();
|
|
1918
|
+
if (
|
|
1919
|
+
typeof runtime?.canApplyOverrideValuePatch === 'function' &&
|
|
1920
|
+
!runtime.canApplyOverrideValuePatch(resolvedPath)
|
|
1921
|
+
) {
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1881
1925
|
if (_.isEqual(current, value)) {
|
|
1882
1926
|
return;
|
|
1883
1927
|
}
|
|
@@ -1889,6 +1933,7 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
1889
1933
|
path: resolvedPath,
|
|
1890
1934
|
value,
|
|
1891
1935
|
...(whenEmpty ? { whenEmpty: true } : {}),
|
|
1936
|
+
...(mode === 'override' ? { mode } : {}),
|
|
1892
1937
|
});
|
|
1893
1938
|
};
|
|
1894
1939
|
const removePendingFormValuePatches = (path: any) => {
|
|
@@ -1948,6 +1993,21 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
1948
1993
|
>;
|
|
1949
1994
|
return normalized.length ? normalized : null;
|
|
1950
1995
|
};
|
|
1996
|
+
const pathKeysEqual = (
|
|
1997
|
+
a: Array<string | number> | null | undefined,
|
|
1998
|
+
b: Array<string | number> | null | undefined,
|
|
1999
|
+
) => {
|
|
2000
|
+
if (!a || !b) return false;
|
|
2001
|
+
return namePathToPathKey(a) === namePathToPathKey(b);
|
|
2002
|
+
};
|
|
2003
|
+
const namePathEndsWith = (
|
|
2004
|
+
namePath: Array<string | number> | null | undefined,
|
|
2005
|
+
suffix: Array<string | number> | null | undefined,
|
|
2006
|
+
) => {
|
|
2007
|
+
if (!namePath || !suffix || suffix.length > namePath.length) return false;
|
|
2008
|
+
const offset = namePath.length - suffix.length;
|
|
2009
|
+
return suffix.every((seg, index) => namePath[offset + index] === seg);
|
|
2010
|
+
};
|
|
1951
2011
|
const getFieldIndexEntries = (fieldIndex: any): Array<{ name: string; index: number }> => {
|
|
1952
2012
|
if (!Array.isArray(fieldIndex)) return [];
|
|
1953
2013
|
return fieldIndex
|
|
@@ -1996,17 +2056,35 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
1996
2056
|
|
|
1997
2057
|
return out;
|
|
1998
2058
|
};
|
|
2059
|
+
const getTrustedFieldPathArray = (
|
|
2060
|
+
fieldPathArray: Array<string | number> | null,
|
|
2061
|
+
targetPath: string | null,
|
|
2062
|
+
fieldIndex: unknown,
|
|
2063
|
+
): Array<string | number> | null => {
|
|
2064
|
+
if (!fieldPathArray || !targetPath) return null;
|
|
2065
|
+
|
|
2066
|
+
const targetNamePath = normalizeNamePathForKey(
|
|
2067
|
+
parsePathString(targetPath).filter((seg) => typeof seg === 'string' || typeof seg === 'number'),
|
|
2068
|
+
);
|
|
2069
|
+
const resolvedTargetPath = normalizeNamePathForKey(resolveDynamicNamePath(targetPath, fieldIndex));
|
|
2070
|
+
const indexedRelativePath = resolveIndexedRelativePath(targetPath, fieldIndex);
|
|
2071
|
+
|
|
2072
|
+
if (
|
|
2073
|
+
pathKeysEqual(fieldPathArray, resolvedTargetPath) ||
|
|
2074
|
+
pathKeysEqual(fieldPathArray, indexedRelativePath) ||
|
|
2075
|
+
namePathEndsWith(fieldPathArray, targetNamePath)
|
|
2076
|
+
) {
|
|
2077
|
+
return fieldPathArray;
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
return null;
|
|
2081
|
+
};
|
|
1999
2082
|
const getModelTargetPathForHiddenClear = (model: any): string | Array<string | number> | null => {
|
|
2000
2083
|
const fieldPathArray = normalizeNamePathForKey(model?.context?.fieldPathArray);
|
|
2001
2084
|
const targetPath = getModelTargetPathForPatch(model);
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
: undefined;
|
|
2006
|
-
const fieldPathArrayLastString = [...fieldPathArray].reverse().find((seg) => typeof seg === 'string');
|
|
2007
|
-
if (!targetPathLastString || targetPathLastString === fieldPathArrayLastString) {
|
|
2008
|
-
return fieldPathArray;
|
|
2009
|
-
}
|
|
2085
|
+
const trustedFieldPathArray = getTrustedFieldPathArray(fieldPathArray, targetPath, model?.context?.fieldIndex);
|
|
2086
|
+
if (trustedFieldPathArray) {
|
|
2087
|
+
return trustedFieldPathArray;
|
|
2010
2088
|
}
|
|
2011
2089
|
|
|
2012
2090
|
if (!targetPath) return null;
|
|
@@ -2016,12 +2094,13 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
2016
2094
|
const getModelTargetPathKeys = (model: any): Set<string> => {
|
|
2017
2095
|
const keys = new Set<string>();
|
|
2018
2096
|
const fieldPathArray = normalizeNamePathForKey(model?.context?.fieldPathArray);
|
|
2019
|
-
|
|
2020
|
-
|
|
2097
|
+
const targetPath = getModelTargetPathForPatch(model);
|
|
2098
|
+
const trustedFieldPathArray = getTrustedFieldPathArray(fieldPathArray, targetPath, model?.context?.fieldIndex);
|
|
2099
|
+
if (trustedFieldPathArray) {
|
|
2100
|
+
keys.add(namePathToPathKey(trustedFieldPathArray));
|
|
2021
2101
|
return keys;
|
|
2022
2102
|
}
|
|
2023
2103
|
|
|
2024
|
-
const targetPath = getModelTargetPathForPatch(model);
|
|
2025
2104
|
if (targetPath) {
|
|
2026
2105
|
const fieldIndexEntries = getFieldIndexEntries(model?.context?.fieldIndex);
|
|
2027
2106
|
if (!fieldIndexEntries.length) {
|
|
@@ -2082,6 +2161,14 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
2082
2161
|
|
|
2083
2162
|
for (const action of actions) {
|
|
2084
2163
|
const setProps = (model: FlowModel & { __originalProps?: any; __shouldReset?: boolean }, props: any) => {
|
|
2164
|
+
const normalizedProps =
|
|
2165
|
+
props && typeof props === 'object' && Object.prototype.hasOwnProperty.call(props, 'value')
|
|
2166
|
+
? {
|
|
2167
|
+
...props,
|
|
2168
|
+
[LINKAGE_ASSIGN_MODE_PROP]: normalizeLinkageAssignMode(props?.[LINKAGE_ASSIGN_MODE_PROP]),
|
|
2169
|
+
}
|
|
2170
|
+
: props;
|
|
2171
|
+
|
|
2085
2172
|
// 存储原始值,用于恢复
|
|
2086
2173
|
if (!model.__originalProps) {
|
|
2087
2174
|
model.__originalProps = {
|
|
@@ -2097,7 +2184,7 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
2097
2184
|
// 临时存起来,遍历完所有规则后,再统一处理
|
|
2098
2185
|
patchPropsByModel.set(model, {
|
|
2099
2186
|
...(patchPropsByModel.get(model) || {}),
|
|
2100
|
-
...
|
|
2187
|
+
...normalizedProps,
|
|
2101
2188
|
});
|
|
2102
2189
|
|
|
2103
2190
|
if (
|
|
@@ -2151,7 +2238,7 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
2151
2238
|
const prevHidden = !!model.hidden;
|
|
2152
2239
|
const nextHidden = !!newProps.hiddenModel;
|
|
2153
2240
|
|
|
2154
|
-
model.setProps(_.omit(newProps, ['hiddenModel', 'value', 'hiddenText']));
|
|
2241
|
+
model.setProps(_.omit(newProps, ['hiddenModel', 'value', 'hiddenText', LINKAGE_ASSIGN_MODE_PROP]));
|
|
2155
2242
|
syncFieldOptionsToForks(model, patchProps);
|
|
2156
2243
|
if (typeof model.setHidden === 'function') {
|
|
2157
2244
|
model.setHidden(nextHidden);
|
|
@@ -2190,7 +2277,8 @@ const commonLinkageRulesHandler = async (ctx: FlowContext, params: any) => {
|
|
|
2190
2277
|
targetUid: model?.uid,
|
|
2191
2278
|
});
|
|
2192
2279
|
} else {
|
|
2193
|
-
|
|
2280
|
+
const mode = normalizeLinkageAssignMode(patchProps?.[LINKAGE_ASSIGN_MODE_PROP]);
|
|
2281
|
+
addFormValuePatch({ path: targetPath, value: newProps.value, ...(mode === 'override' ? { mode } : {}) });
|
|
2194
2282
|
}
|
|
2195
2283
|
}
|
|
2196
2284
|
|
|
@@ -7,14 +7,7 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
ActionScene,
|
|
12
|
-
defineAction,
|
|
13
|
-
tExpr,
|
|
14
|
-
createSafeWindow,
|
|
15
|
-
createSafeDocument,
|
|
16
|
-
createSafeNavigator,
|
|
17
|
-
} from '@nocobase/flow-engine';
|
|
10
|
+
import { ActionScene, defineAction, tExpr } from '@nocobase/flow-engine';
|
|
18
11
|
import { CodeEditor } from '../components/code-editor';
|
|
19
12
|
|
|
20
13
|
export const runjs = defineAction({
|
|
@@ -38,11 +31,6 @@ export const runjs = defineAction({
|
|
|
38
31
|
// 如果是 URL 触发的,则不执行代码
|
|
39
32
|
if (ctx.inputArgs?.navigation) return;
|
|
40
33
|
|
|
41
|
-
|
|
42
|
-
return ctx.runjs(params.code, {
|
|
43
|
-
window: createSafeWindow({ navigator }),
|
|
44
|
-
document: createSafeDocument(),
|
|
45
|
-
navigator,
|
|
46
|
-
});
|
|
34
|
+
return ctx.runjs(params.code);
|
|
47
35
|
},
|
|
48
36
|
});
|
|
@@ -10,6 +10,56 @@
|
|
|
10
10
|
import { defineAction, jioToJoiSchema, tExpr } from '@nocobase/flow-engine';
|
|
11
11
|
import { FieldValidation } from '../../flow-compat';
|
|
12
12
|
|
|
13
|
+
type ValidationType = 'string' | 'number' | 'array' | 'boolean' | 'any';
|
|
14
|
+
|
|
15
|
+
interface ValidationRule {
|
|
16
|
+
name: string;
|
|
17
|
+
args?: any;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface ValidationData {
|
|
21
|
+
type: ValidationType;
|
|
22
|
+
rules?: ValidationRule[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface ValidationContext {
|
|
26
|
+
model: {
|
|
27
|
+
props: {
|
|
28
|
+
label?: string;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
t: (key: string, options?: Record<string, unknown>) => string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function buildValidationRules(ctx: ValidationContext, validation: ValidationData) {
|
|
35
|
+
const rules = [];
|
|
36
|
+
const schema = jioToJoiSchema(validation);
|
|
37
|
+
const label = ctx.model.props.label;
|
|
38
|
+
rules.push({
|
|
39
|
+
validator: (_: unknown, value: unknown) => {
|
|
40
|
+
const { error } = schema.validate(value, {
|
|
41
|
+
abortEarly: false,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (error) {
|
|
45
|
+
const messages = error.details.map((d: { type: string; context?: Record<string, unknown> }) => {
|
|
46
|
+
return ctx.t(`${d.type}`, {
|
|
47
|
+
...d.context,
|
|
48
|
+
ns: 'data-source-main',
|
|
49
|
+
label,
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
const div = document.createElement('div');
|
|
53
|
+
div.innerHTML = messages.join('; ');
|
|
54
|
+
return Promise.reject(div.textContent);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return Promise.resolve();
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
return rules;
|
|
61
|
+
}
|
|
62
|
+
|
|
13
63
|
export const validation = defineAction({
|
|
14
64
|
title: tExpr('Validation'),
|
|
15
65
|
name: 'validation',
|
|
@@ -29,6 +79,7 @@ export const validation = defineAction({
|
|
|
29
79
|
type: targetInterface.validationType,
|
|
30
80
|
availableValidationOptions: [...new Set(targetInterface.availableValidationOptions)],
|
|
31
81
|
excludeValidationOptions: [...new Set(targetInterface.excludeValidationOptions)],
|
|
82
|
+
inheritedValue: ctx.model.collectionField.validation,
|
|
32
83
|
isAssociation: targetInterface.isAssociation,
|
|
33
84
|
},
|
|
34
85
|
},
|
|
@@ -36,40 +87,21 @@ export const validation = defineAction({
|
|
|
36
87
|
},
|
|
37
88
|
handler(ctx, params) {
|
|
38
89
|
if (params.validation) {
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (error) {
|
|
49
|
-
const messages = error.details.map((d) => {
|
|
50
|
-
return ctx.t(`${d.type}`, {
|
|
51
|
-
...d.context,
|
|
52
|
-
ns: 'data-source-main',
|
|
53
|
-
label,
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
const div = document.createElement('div');
|
|
57
|
-
div.innerHTML = messages.join('; ');
|
|
58
|
-
return Promise.reject(div.textContent);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return Promise.resolve();
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
const hasRequiredInCollection = params.validation.rules.some((rule) => rule.name === 'required');
|
|
65
|
-
if (hasRequiredInCollection) {
|
|
90
|
+
const collectionValidation = ctx.model.collectionField?.validation;
|
|
91
|
+
const collectionRules = ctx.model.collectionField?.getComponentProps?.().rules || [];
|
|
92
|
+
const uiRules = params.validation.rules?.length
|
|
93
|
+
? buildValidationRules(ctx as unknown as ValidationContext, params.validation)
|
|
94
|
+
: [];
|
|
95
|
+
const hasRequiredInValidation = [collectionValidation, params.validation].some(
|
|
96
|
+
(validation) => validation?.rules?.some((rule) => rule.name === 'required'),
|
|
97
|
+
);
|
|
98
|
+
if (hasRequiredInValidation) {
|
|
66
99
|
ctx.model.setProps({
|
|
67
|
-
required:
|
|
100
|
+
required: hasRequiredInValidation,
|
|
68
101
|
});
|
|
69
102
|
}
|
|
70
|
-
console.log(rules);
|
|
71
103
|
ctx.model.setProps({
|
|
72
|
-
rules,
|
|
104
|
+
rules: [...collectionRules, ...uiRules],
|
|
73
105
|
validation: params.validation,
|
|
74
106
|
});
|
|
75
107
|
}
|
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
FLOW_SETTINGS_PREFERENCE_STORAGE_KEY,
|
|
43
43
|
readFlowSettingsPreference,
|
|
44
44
|
} from './flowSettingsPreference';
|
|
45
|
+
import { joinAdminLayoutRoutePath, type AdminLayoutRoutePathLike } from './resolveAdminRouteRuntimeTarget';
|
|
45
46
|
import { useAppListRender } from './AppListRender';
|
|
46
47
|
|
|
47
48
|
const className1 = css`
|
|
@@ -314,25 +315,37 @@ const DesignerButtonMenuItem: FC<{ item: AdminLayoutMenuNode; fallbackParentRout
|
|
|
314
315
|
);
|
|
315
316
|
};
|
|
316
317
|
|
|
317
|
-
const matchesRoutePath = (
|
|
318
|
+
const matchesRoutePath = (
|
|
319
|
+
route: NocoBaseDesktopRoute | undefined,
|
|
320
|
+
pathname: string,
|
|
321
|
+
layout?: AdminLayoutRoutePathLike | null,
|
|
322
|
+
): boolean => {
|
|
318
323
|
if (!route) {
|
|
319
324
|
return false;
|
|
320
325
|
}
|
|
321
326
|
|
|
322
327
|
const candidates = [
|
|
323
|
-
route.id != null ?
|
|
324
|
-
route.schemaUid ?
|
|
328
|
+
route.id != null ? joinAdminLayoutRoutePath(layout, route.id) : null,
|
|
329
|
+
route.schemaUid ? joinAdminLayoutRoutePath(layout, route.schemaUid) : null,
|
|
325
330
|
].filter(Boolean) as string[];
|
|
326
331
|
|
|
327
332
|
if (candidates.some((candidate) => pathname === candidate || pathname.startsWith(`${candidate}/`))) {
|
|
328
333
|
return true;
|
|
329
334
|
}
|
|
330
335
|
|
|
331
|
-
return Array.isArray(route.children)
|
|
336
|
+
return Array.isArray(route.children)
|
|
337
|
+
? route.children.some((child) => matchesRoutePath(child, pathname, layout))
|
|
338
|
+
: false;
|
|
332
339
|
};
|
|
333
340
|
|
|
334
|
-
const findSelectedTopGroupRoute = (
|
|
335
|
-
|
|
341
|
+
const findSelectedTopGroupRoute = (
|
|
342
|
+
routes: NocoBaseDesktopRoute[],
|
|
343
|
+
pathname: string,
|
|
344
|
+
layout?: AdminLayoutRoutePathLike | null,
|
|
345
|
+
) => {
|
|
346
|
+
return routes.find(
|
|
347
|
+
(route) => route.type === NocoBaseDesktopRouteType.group && matchesRoutePath(route, pathname, layout),
|
|
348
|
+
);
|
|
336
349
|
};
|
|
337
350
|
|
|
338
351
|
const renderMenuNodeWithModel = (
|
|
@@ -376,6 +389,11 @@ export const AdminLayoutComponent = observer((props: any) => {
|
|
|
376
389
|
const { token } = antdTheme.useToken();
|
|
377
390
|
const customToken = token as CustomToken;
|
|
378
391
|
const isMobileLayout = !!adminLayoutModel?.isMobileLayout;
|
|
392
|
+
const adminLayoutRoutePath = adminLayoutModel?.layout?.routePath;
|
|
393
|
+
const adminLayoutRoutePathLike = useMemo<AdminLayoutRoutePathLike | undefined>(
|
|
394
|
+
() => (adminLayoutRoutePath ? { routePath: adminLayoutRoutePath } : undefined),
|
|
395
|
+
[adminLayoutRoutePath],
|
|
396
|
+
);
|
|
379
397
|
const menuRouteRefreshVersion = adminLayoutModel?.menuRouteRefreshVersion || 0;
|
|
380
398
|
const isMobileSider = isMobileLayout;
|
|
381
399
|
const [collapsed, setCollapsed] = useState(isMobileSider);
|
|
@@ -403,8 +421,8 @@ export const AdminLayoutComponent = observer((props: any) => {
|
|
|
403
421
|
[adminLayoutModel],
|
|
404
422
|
);
|
|
405
423
|
const selectedTopGroupRoute = useMemo(
|
|
406
|
-
() => findSelectedTopGroupRoute(allAccessRoutes, location.pathname),
|
|
407
|
-
[allAccessRoutes, location.pathname],
|
|
424
|
+
() => findSelectedTopGroupRoute(allAccessRoutes, location.pathname, adminLayoutRoutePathLike),
|
|
425
|
+
[adminLayoutRoutePathLike, allAccessRoutes, location.pathname],
|
|
408
426
|
);
|
|
409
427
|
|
|
410
428
|
const handleMenuDragEnd = useCallback(
|
|
@@ -458,6 +476,7 @@ export const AdminLayoutComponent = observer((props: any) => {
|
|
|
458
476
|
|
|
459
477
|
useEffect(() => {
|
|
460
478
|
const routeRepository = flowEngine.context.routeRepository;
|
|
479
|
+
const deactivateLayout = routeRepository?.activateLayout?.(adminLayoutModel?.layout);
|
|
461
480
|
const subscriber = () => {
|
|
462
481
|
const updatedRoutes = routeRepository?.listAccessible() || [];
|
|
463
482
|
setAllAccessRoutes(updatedRoutes);
|
|
@@ -470,8 +489,9 @@ export const AdminLayoutComponent = observer((props: any) => {
|
|
|
470
489
|
|
|
471
490
|
return () => {
|
|
472
491
|
routeRepository?.unsubscribe(subscriber);
|
|
492
|
+
deactivateLayout?.();
|
|
473
493
|
};
|
|
474
|
-
}, [flowEngine]);
|
|
494
|
+
}, [adminLayoutModel, adminLayoutModel?.layout?.uid, flowEngine]);
|
|
475
495
|
|
|
476
496
|
useEffect(() => {
|
|
477
497
|
if (typeof window === 'undefined') {
|
|
@@ -697,7 +717,10 @@ export const AdminLayoutComponent = observer((props: any) => {
|
|
|
697
717
|
<SetIsMobileLayout isMobile={isMobile} model={adminLayoutModel}>
|
|
698
718
|
<ConfigProvider theme={isMobile ? mobileTheme : theme}>
|
|
699
719
|
<GlobalStyle />
|
|
700
|
-
<AdminLayoutContent
|
|
720
|
+
<AdminLayoutContent
|
|
721
|
+
layout={adminLayoutModel?.layout}
|
|
722
|
+
onContentElementChange={handleLayoutContentElementChange}
|
|
723
|
+
/>
|
|
701
724
|
</ConfigProvider>
|
|
702
725
|
</SetIsMobileLayout>
|
|
703
726
|
);
|