@nocobase/flow-engine 2.1.0-alpha.1 → 2.1.0-alpha.10
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/LICENSE +201 -661
- package/README.md +79 -10
- package/lib/BlockScopedFlowEngine.js +0 -1
- package/lib/FlowDefinition.d.ts +2 -0
- package/lib/JSRunner.d.ts +15 -0
- package/lib/JSRunner.js +82 -7
- package/lib/ViewScopedFlowEngine.js +8 -1
- package/lib/acl/Acl.js +13 -3
- package/lib/components/FlowContextSelector.js +155 -10
- package/lib/components/MobilePopup.js +6 -5
- package/lib/components/dnd/gridDragPlanner.d.ts +1 -0
- package/lib/components/dnd/gridDragPlanner.js +59 -3
- package/lib/components/settings/wrappers/component/SwitchWithTitle.js +2 -1
- package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +76 -15
- package/lib/components/settings/wrappers/contextual/FlowsContextMenu.js +24 -4
- package/lib/components/settings/wrappers/contextual/StepSettingsDialog.js +21 -3
- package/lib/components/subModel/AddSubModelButton.js +16 -1
- package/lib/components/subModel/utils.js +2 -2
- package/lib/components/variables/VariableInput.js +9 -4
- package/lib/components/variables/VariableTag.js +46 -39
- package/lib/components/variables/utils.d.ts +7 -0
- package/lib/components/variables/utils.js +42 -2
- package/lib/data-source/index.d.ts +7 -27
- package/lib/data-source/index.js +84 -51
- package/lib/executor/FlowExecutor.d.ts +2 -1
- package/lib/executor/FlowExecutor.js +190 -26
- package/lib/flowContext.d.ts +230 -7
- package/lib/flowContext.js +2270 -148
- package/lib/flowEngine.d.ts +160 -1
- package/lib/flowEngine.js +383 -26
- package/lib/flowI18n.js +6 -4
- package/lib/flowSettings.d.ts +14 -6
- package/lib/flowSettings.js +51 -17
- package/lib/index.d.ts +7 -1
- package/lib/index.js +21 -0
- package/lib/lazy-helper.d.ts +14 -0
- package/lib/lazy-helper.js +71 -0
- package/lib/locale/en-US.json +9 -2
- package/lib/locale/index.d.ts +14 -0
- package/lib/locale/zh-CN.json +8 -1
- package/lib/models/CollectionFieldModel.d.ts +1 -0
- package/lib/models/CollectionFieldModel.js +3 -2
- package/lib/models/flowModel.d.ts +7 -0
- package/lib/models/flowModel.js +83 -8
- package/lib/provider.js +7 -6
- package/lib/resources/baseRecordResource.d.ts +5 -0
- package/lib/resources/baseRecordResource.js +24 -0
- package/lib/resources/multiRecordResource.d.ts +1 -0
- package/lib/resources/multiRecordResource.js +11 -4
- package/lib/resources/singleRecordResource.js +2 -0
- package/lib/resources/sqlResource.d.ts +4 -3
- package/lib/resources/sqlResource.js +8 -3
- package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +12 -2
- package/lib/runjs-context/contexts/JSBlockRunJSContext.js +2 -2
- package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.d.ts +16 -0
- package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.js +125 -0
- package/lib/runjs-context/contexts/JSItemRunJSContext.js +12 -2
- package/lib/runjs-context/contexts/base.js +706 -41
- package/lib/runjs-context/contributions.d.ts +33 -0
- package/lib/runjs-context/contributions.js +88 -0
- package/lib/runjs-context/helpers.js +12 -1
- package/lib/runjs-context/registry.d.ts +1 -1
- package/lib/runjs-context/setup.js +22 -9
- package/lib/runjs-context/snippets/global/api-request.snippet.js +3 -3
- package/lib/runjs-context/snippets/global/import-esm.snippet.js +2 -3
- package/lib/runjs-context/snippets/global/query-selector.snippet.js +8 -3
- package/lib/runjs-context/snippets/global/require-amd.snippet.js +1 -1
- package/lib/runjs-context/snippets/index.d.ts +11 -1
- package/lib/runjs-context/snippets/index.js +61 -40
- package/lib/runjs-context/snippets/scene/block/add-event-listener.snippet.js +10 -7
- package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.js +3 -3
- package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/block/echarts-init.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/block/render-react.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/block/resource-example.snippet.js +5 -5
- package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.js +6 -6
- package/lib/runjs-context/snippets/scene/block/vue-component.snippet.js +3 -4
- package/lib/runjs-context/snippets/scene/detail/color-by-value.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.js +20 -3
- package/lib/runjs-context/snippets/scene/detail/format-number.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/detail/innerHTML-value.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.js +3 -3
- package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.js +3 -3
- package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.js +1 -1
- package/lib/runjs-context/snippets/scene/form/render-basic.snippet.js +2 -2
- package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.js +6 -3
- package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.js +3 -1
- package/lib/runjsLibs.d.ts +28 -0
- package/lib/runjsLibs.js +532 -0
- package/lib/scheduler/ModelOperationScheduler.d.ts +7 -1
- package/lib/scheduler/ModelOperationScheduler.js +28 -23
- package/lib/types.d.ts +63 -1
- package/lib/utils/associationObjectVariable.d.ts +2 -2
- package/lib/utils/createCollectionContextMeta.js +1 -0
- package/lib/utils/createEphemeralContext.js +2 -2
- package/lib/utils/dateVariable.d.ts +16 -0
- package/lib/utils/dateVariable.js +380 -0
- package/lib/utils/exceptions.d.ts +7 -0
- package/lib/utils/exceptions.js +10 -0
- package/lib/utils/index.d.ts +8 -3
- package/lib/utils/index.js +49 -0
- package/lib/utils/params-resolvers.js +16 -9
- package/lib/utils/parsePathnameToViewParams.js +1 -1
- package/lib/utils/resolveModuleUrl.d.ts +58 -0
- package/lib/utils/resolveModuleUrl.js +65 -0
- package/lib/utils/resolveRunJSObjectValues.d.ts +16 -0
- package/lib/utils/resolveRunJSObjectValues.js +61 -0
- package/lib/utils/runjsModuleLoader.d.ts +58 -0
- package/lib/utils/runjsModuleLoader.js +422 -0
- package/lib/utils/runjsTemplateCompat.d.ts +35 -0
- package/lib/utils/runjsTemplateCompat.js +743 -0
- package/lib/utils/runjsValue.d.ts +29 -0
- package/lib/utils/runjsValue.js +275 -0
- package/lib/utils/safeGlobals.d.ts +18 -8
- package/lib/utils/safeGlobals.js +164 -17
- package/lib/utils/schema-utils.d.ts +17 -1
- package/lib/utils/schema-utils.js +80 -0
- package/lib/views/FlowView.d.ts +7 -1
- package/lib/views/createViewMeta.d.ts +0 -7
- package/lib/views/createViewMeta.js +19 -70
- package/lib/views/index.d.ts +1 -2
- package/lib/views/index.js +4 -3
- package/lib/views/runViewBeforeClose.d.ts +10 -0
- package/lib/views/runViewBeforeClose.js +45 -0
- package/lib/views/useDialog.d.ts +2 -1
- package/lib/views/useDialog.js +28 -6
- package/lib/views/useDrawer.d.ts +2 -1
- package/lib/views/useDrawer.js +27 -5
- package/lib/views/usePage.d.ts +6 -1
- package/lib/views/usePage.js +53 -9
- package/lib/views/usePopover.js +4 -1
- package/lib/views/viewEvents.d.ts +17 -0
- package/lib/views/viewEvents.js +90 -0
- package/package.json +5 -5
- package/src/BlockScopedFlowEngine.ts +2 -5
- package/src/JSRunner.ts +111 -5
- package/src/ViewScopedFlowEngine.ts +8 -0
- package/src/__tests__/JSRunner.test.ts +91 -1
- package/src/__tests__/createViewMeta.popup.test.ts +62 -1
- package/src/__tests__/flowContext.test.ts +693 -1
- package/src/__tests__/flowEngine.dataSourceDirty.test.ts +63 -0
- package/src/__tests__/flowEngine.modelLoaders.test.ts +245 -0
- package/src/__tests__/flowModel.openView.navigation.test.ts +28 -0
- package/src/__tests__/flowRunJSContextDefine.test.ts +63 -0
- package/src/__tests__/flowRuntimeContext.test.ts +2 -1
- package/src/__tests__/flowSettings.open.test.tsx +123 -19
- package/src/__tests__/flowSettings.test.ts +94 -15
- package/src/__tests__/provider.test.tsx +0 -5
- package/src/__tests__/renderHiddenInConfig.test.tsx +6 -6
- package/src/__tests__/runjsContext.test.ts +23 -7
- package/src/__tests__/runjsContextImplementations.test.ts +34 -3
- package/src/__tests__/runjsContextRuntime.test.ts +3 -3
- package/src/__tests__/runjsContributions.test.ts +89 -0
- package/src/__tests__/runjsExternalLibs.test.ts +242 -0
- package/src/__tests__/runjsLibsLazyLoading.test.ts +44 -0
- package/src/__tests__/runjsLocales.test.ts +4 -1
- package/src/__tests__/runjsPreprocessDefault.test.ts +72 -0
- package/src/__tests__/runjsRuntimeFeatures.test.ts +166 -0
- package/src/__tests__/runjsSnippets.test.ts +40 -3
- package/src/__tests__/viewScopedFlowEngine.test.ts +3 -3
- package/src/acl/Acl.tsx +3 -3
- package/src/components/FlowContextSelector.tsx +208 -12
- package/src/components/MobilePopup.tsx +4 -2
- package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +3 -3
- package/src/components/__tests__/gridDragPlanner.test.ts +229 -1
- package/src/components/dnd/gridDragPlanner.ts +68 -2
- package/src/components/settings/wrappers/component/SwitchWithTitle.tsx +2 -1
- package/src/components/settings/wrappers/component/__tests__/InlineControls.test.tsx +74 -0
- package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +109 -16
- package/src/components/settings/wrappers/contextual/FlowsContextMenu.tsx +41 -7
- package/src/components/settings/wrappers/contextual/StepSettingsDialog.tsx +31 -4
- package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +157 -5
- package/src/components/subModel/AddSubModelButton.tsx +17 -1
- package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +142 -32
- package/src/components/subModel/utils.ts +1 -1
- package/src/components/variables/VariableInput.tsx +12 -4
- package/src/components/variables/VariableTag.tsx +54 -45
- package/src/components/variables/__tests__/FlowContextSelector.test.tsx +260 -3
- package/src/components/variables/__tests__/VariableTag.test.tsx +50 -0
- package/src/components/variables/__tests__/utils.test.ts +81 -3
- package/src/components/variables/utils.ts +67 -6
- package/src/data-source/index.ts +88 -110
- package/src/executor/FlowExecutor.ts +230 -28
- package/src/executor/__tests__/flowExecutor.test.ts +123 -0
- package/src/flowContext.ts +2989 -212
- package/src/flowEngine.ts +427 -22
- package/src/flowI18n.ts +7 -5
- package/src/flowSettings.ts +58 -18
- package/src/index.ts +14 -1
- package/src/lazy-helper.tsx +57 -0
- package/src/locale/en-US.json +9 -2
- package/src/locale/zh-CN.json +8 -1
- package/src/models/CollectionFieldModel.tsx +3 -1
- package/src/models/__tests__/dispatchEvent.when.test.ts +768 -0
- package/src/models/__tests__/flowModel.clone.test.ts +416 -0
- package/src/models/__tests__/flowModel.test.ts +20 -4
- package/src/models/flowModel.tsx +112 -7
- package/src/provider.tsx +9 -7
- package/src/resources/__tests__/multiRecordResource.test.ts +44 -0
- package/src/resources/__tests__/sqlResource.test.ts +60 -0
- package/src/resources/baseRecordResource.ts +31 -0
- package/src/resources/multiRecordResource.ts +11 -4
- package/src/resources/singleRecordResource.ts +3 -0
- package/src/resources/sqlResource.ts +11 -6
- package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +10 -0
- package/src/runjs-context/contexts/JSBlockRunJSContext.ts +6 -2
- package/src/runjs-context/contexts/JSEditableFieldRunJSContext.ts +106 -0
- package/src/runjs-context/contexts/JSItemRunJSContext.ts +10 -0
- package/src/runjs-context/contexts/base.ts +715 -44
- package/src/runjs-context/contributions.ts +88 -0
- package/src/runjs-context/helpers.ts +11 -1
- package/src/runjs-context/registry.ts +1 -1
- package/src/runjs-context/setup.ts +24 -9
- package/src/runjs-context/snippets/global/api-request.snippet.ts +3 -3
- package/src/runjs-context/snippets/global/import-esm.snippet.ts +2 -3
- package/src/runjs-context/snippets/global/query-selector.snippet.ts +8 -3
- package/src/runjs-context/snippets/global/require-amd.snippet.ts +1 -1
- package/src/runjs-context/snippets/index.ts +75 -41
- package/src/runjs-context/snippets/scene/block/add-event-listener.snippet.ts +11 -13
- package/src/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.ts +3 -3
- package/src/runjs-context/snippets/scene/block/chartjs-bar.snippet.ts +2 -2
- package/src/runjs-context/snippets/scene/block/echarts-init.snippet.ts +2 -2
- package/src/runjs-context/snippets/scene/block/render-iframe.snippet.ts +2 -2
- package/src/runjs-context/snippets/scene/block/render-react.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/block/render-statistics.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/block/render-timeline.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/block/resource-example.snippet.ts +6 -11
- package/src/runjs-context/snippets/scene/block/three-users-orbit.snippet.ts +6 -6
- package/src/runjs-context/snippets/scene/block/vue-component.snippet.ts +3 -4
- package/src/runjs-context/snippets/scene/detail/color-by-value.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.ts +20 -3
- package/src/runjs-context/snippets/scene/detail/format-number.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/detail/innerHTML-value.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/detail/percentage-bar.snippet.ts +3 -3
- package/src/runjs-context/snippets/scene/detail/relative-time.snippet.ts +3 -3
- package/src/runjs-context/snippets/scene/detail/status-tag.snippet.ts +2 -2
- package/src/runjs-context/snippets/scene/form/cascade-select.snippet.ts +1 -1
- package/src/runjs-context/snippets/scene/form/render-basic.snippet.ts +3 -8
- package/src/runjs-context/snippets/scene/table/cell-open-dialog.snippet.ts +6 -3
- package/src/runjs-context/snippets/scene/table/concat-fields.snippet.ts +3 -1
- package/src/runjsLibs.ts +622 -0
- package/src/scheduler/ModelOperationScheduler.ts +41 -24
- package/src/types.ts +86 -1
- package/src/utils/__tests__/dateVariable.test.ts +101 -0
- package/src/utils/__tests__/params-resolvers.test.ts +40 -0
- package/src/utils/__tests__/parsePathnameToViewParams.test.ts +7 -0
- package/src/utils/__tests__/runjsRequireAsyncAutoWhitelist.test.ts +38 -0
- package/src/utils/__tests__/runjsTemplateCompat.test.ts +159 -0
- package/src/utils/__tests__/runjsValue.test.ts +44 -0
- package/src/utils/__tests__/safeGlobals.test.ts +57 -2
- package/src/utils/__tests__/utils.test.ts +157 -0
- package/src/utils/associationObjectVariable.ts +2 -2
- package/src/utils/createCollectionContextMeta.ts +1 -0
- package/src/utils/createEphemeralContext.ts +5 -4
- package/src/utils/dateVariable.ts +397 -0
- package/src/utils/exceptions.ts +11 -0
- package/src/utils/index.ts +38 -3
- package/src/utils/params-resolvers.ts +23 -9
- package/src/utils/parsePathnameToViewParams.ts +2 -2
- package/src/utils/resolveModuleUrl.ts +91 -0
- package/src/utils/resolveRunJSObjectValues.ts +46 -0
- package/src/utils/runjsModuleLoader.ts +553 -0
- package/src/utils/runjsTemplateCompat.ts +828 -0
- package/src/utils/runjsValue.ts +287 -0
- package/src/utils/safeGlobals.ts +188 -17
- package/src/utils/schema-utils.ts +109 -1
- package/src/views/FlowView.tsx +11 -1
- package/src/views/__tests__/FlowView.usePage.test.tsx +54 -1
- package/src/views/__tests__/runViewBeforeClose.test.ts +30 -0
- package/src/views/__tests__/useDialog.closeDestroy.test.tsx +44 -16
- package/src/views/__tests__/viewEvents.resolveOpenerEngine.test.ts +28 -0
- package/src/views/createViewMeta.ts +22 -75
- package/src/views/index.tsx +1 -2
- package/src/views/runViewBeforeClose.ts +19 -0
- package/src/views/useDialog.tsx +34 -5
- package/src/views/useDrawer.tsx +33 -4
- package/src/views/usePage.tsx +63 -8
- package/src/views/usePopover.tsx +4 -1
- package/src/views/viewEvents.ts +55 -0
|
@@ -46,6 +46,7 @@ export interface Point {
|
|
|
46
46
|
export interface GridLayoutData {
|
|
47
47
|
rows: Record<string, string[][]>;
|
|
48
48
|
sizes: Record<string, number[]>;
|
|
49
|
+
rowOrder?: string[];
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
export interface ColumnSlot {
|
|
@@ -142,6 +143,49 @@ export interface LayoutSnapshot {
|
|
|
142
143
|
containerRect: Rect;
|
|
143
144
|
}
|
|
144
145
|
|
|
146
|
+
const deriveRowOrder = (rows: Record<string, string[][]>, provided?: string[]) => {
|
|
147
|
+
const order: string[] = [];
|
|
148
|
+
const used = new Set<string>();
|
|
149
|
+
|
|
150
|
+
(provided || Object.keys(rows)).forEach((rowId) => {
|
|
151
|
+
if (rows[rowId] && !used.has(rowId)) {
|
|
152
|
+
order.push(rowId);
|
|
153
|
+
used.add(rowId);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
Object.keys(rows).forEach((rowId) => {
|
|
158
|
+
if (!used.has(rowId)) {
|
|
159
|
+
order.push(rowId);
|
|
160
|
+
used.add(rowId);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
return order;
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const normalizeRowsWithOrder = (rows: Record<string, string[][]>, order: string[]) => {
|
|
168
|
+
const next: Record<string, string[][]> = {};
|
|
169
|
+
order.forEach((rowId) => {
|
|
170
|
+
if (rows[rowId]) {
|
|
171
|
+
next[rowId] = rows[rowId];
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
Object.keys(rows).forEach((rowId) => {
|
|
175
|
+
if (!next[rowId]) {
|
|
176
|
+
next[rowId] = rows[rowId];
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
return next;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const ensureRowOrder = (layout: GridLayoutData) => {
|
|
183
|
+
const order = deriveRowOrder(layout.rows, layout.rowOrder);
|
|
184
|
+
layout.rowOrder = order;
|
|
185
|
+
layout.rows = normalizeRowsWithOrder(layout.rows, order);
|
|
186
|
+
return order;
|
|
187
|
+
};
|
|
188
|
+
|
|
145
189
|
export interface BuildLayoutSnapshotOptions {
|
|
146
190
|
container: HTMLElement | null;
|
|
147
191
|
}
|
|
@@ -289,7 +333,10 @@ export const buildLayoutSnapshot = ({ container }: BuildLayoutSnapshotOptions):
|
|
|
289
333
|
|
|
290
334
|
const columnElements = Array.from(
|
|
291
335
|
container.querySelectorAll(`[data-grid-column-row-id="${rowId}"][data-grid-column-index]`),
|
|
292
|
-
)
|
|
336
|
+
).filter((el) => {
|
|
337
|
+
// 只保留当前 row 下的直接列,避免嵌套 Grid 中相同 rowId 的列混入
|
|
338
|
+
return (el as HTMLElement).closest('[data-grid-row-id]') === rowElement;
|
|
339
|
+
}) as HTMLElement[];
|
|
293
340
|
|
|
294
341
|
const sortedColumns = columnElements.sort((a, b) => {
|
|
295
342
|
const indexA = Number(a.dataset.gridColumnIndex || 0);
|
|
@@ -319,7 +366,10 @@ export const buildLayoutSnapshot = ({ container }: BuildLayoutSnapshotOptions):
|
|
|
319
366
|
|
|
320
367
|
const itemElements = Array.from(
|
|
321
368
|
columnElement.querySelectorAll(`[data-grid-item-row-id="${rowId}"][data-grid-column-index="${columnIndex}"]`),
|
|
322
|
-
)
|
|
369
|
+
).filter((el) => {
|
|
370
|
+
// 只保留当前 column 下的直接 item,避免命中更深层嵌套 column 的 item
|
|
371
|
+
return (el as HTMLElement).closest('[data-grid-column-row-id][data-grid-column-index]') === columnElement;
|
|
372
|
+
}) as HTMLElement[];
|
|
323
373
|
|
|
324
374
|
const sortedItems = itemElements.sort((a, b) => {
|
|
325
375
|
const indexA = Number(a.dataset.gridItemIndex || 0);
|
|
@@ -465,10 +515,12 @@ const removeItemFromLayout = (layout: GridLayoutData, uidValue: string) => {
|
|
|
465
515
|
if (columns.length === 0) {
|
|
466
516
|
delete layout.rows[rowId];
|
|
467
517
|
delete layout.sizes[rowId];
|
|
518
|
+
ensureRowOrder(layout);
|
|
468
519
|
return;
|
|
469
520
|
}
|
|
470
521
|
|
|
471
522
|
normalizeRowSizes(rowId, layout);
|
|
523
|
+
ensureRowOrder(layout);
|
|
472
524
|
};
|
|
473
525
|
|
|
474
526
|
const toIntSizes = (weights: number[], count: number): number[] => {
|
|
@@ -592,8 +644,10 @@ export const simulateLayoutForSlot = ({
|
|
|
592
644
|
const cloned: GridLayoutData = {
|
|
593
645
|
rows: _.cloneDeep(layout.rows),
|
|
594
646
|
sizes: _.cloneDeep(layout.sizes),
|
|
647
|
+
rowOrder: layout.rowOrder ? [...layout.rowOrder] : undefined,
|
|
595
648
|
};
|
|
596
649
|
|
|
650
|
+
ensureRowOrder(cloned);
|
|
597
651
|
removeItemFromLayout(cloned, sourceUid);
|
|
598
652
|
|
|
599
653
|
const createRowId = generateRowId ?? uid;
|
|
@@ -638,8 +692,16 @@ export const simulateLayoutForSlot = ({
|
|
|
638
692
|
case 'row-gap': {
|
|
639
693
|
const newRowId = createRowId();
|
|
640
694
|
const rowPosition: 'before' | 'after' = slot.position === 'above' ? 'before' : 'after';
|
|
695
|
+
const currentOrder = deriveRowOrder(cloned.rows, cloned.rowOrder);
|
|
641
696
|
cloned.rows = insertRow(cloned.rows, slot.targetRowId, newRowId, rowPosition, [[sourceUid]]);
|
|
642
697
|
cloned.sizes[newRowId] = [DEFAULT_GRID_COLUMNS];
|
|
698
|
+
const targetIndex = currentOrder.indexOf(slot.targetRowId);
|
|
699
|
+
const insertIndex =
|
|
700
|
+
targetIndex === -1 ? currentOrder.length : rowPosition === 'before' ? targetIndex : targetIndex + 1;
|
|
701
|
+
const nextOrder = [...currentOrder];
|
|
702
|
+
nextOrder.splice(insertIndex, 0, newRowId);
|
|
703
|
+
cloned.rowOrder = nextOrder;
|
|
704
|
+
cloned.rows = normalizeRowsWithOrder(cloned.rows, nextOrder);
|
|
643
705
|
break;
|
|
644
706
|
}
|
|
645
707
|
case 'empty-row': {
|
|
@@ -649,11 +711,15 @@ export const simulateLayoutForSlot = ({
|
|
|
649
711
|
[newRowId]: [[sourceUid]],
|
|
650
712
|
};
|
|
651
713
|
cloned.sizes[newRowId] = [DEFAULT_GRID_COLUMNS];
|
|
714
|
+
const currentOrder = deriveRowOrder(cloned.rows, cloned.rowOrder);
|
|
715
|
+
cloned.rowOrder = [...currentOrder.filter((id) => id !== newRowId), newRowId];
|
|
716
|
+
cloned.rows = normalizeRowsWithOrder(cloned.rows, cloned.rowOrder);
|
|
652
717
|
break;
|
|
653
718
|
}
|
|
654
719
|
default:
|
|
655
720
|
break;
|
|
656
721
|
}
|
|
657
722
|
|
|
723
|
+
ensureRowOrder(cloned);
|
|
658
724
|
return cloned;
|
|
659
725
|
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import React from 'react';
|
|
11
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
12
|
+
import { render, screen, fireEvent } from '@nocobase/test/client';
|
|
13
|
+
import { FlowEngine, FlowEngineProvider } from '@nocobase/flow-engine';
|
|
14
|
+
|
|
15
|
+
import { SwitchWithTitle } from '../SwitchWithTitle';
|
|
16
|
+
import { SelectWithTitle } from '../SelectWithTitle';
|
|
17
|
+
|
|
18
|
+
vi.mock('antd', async (importOriginal) => {
|
|
19
|
+
const actual = (await importOriginal()) as any;
|
|
20
|
+
return {
|
|
21
|
+
...actual,
|
|
22
|
+
Select: ({
|
|
23
|
+
popupMatchSelectWidth,
|
|
24
|
+
bordered,
|
|
25
|
+
popupClassName,
|
|
26
|
+
fieldNames,
|
|
27
|
+
labelRender,
|
|
28
|
+
optionRender,
|
|
29
|
+
dropdownRender,
|
|
30
|
+
options,
|
|
31
|
+
...props
|
|
32
|
+
}: any) => React.createElement('select', props),
|
|
33
|
+
Switch: ({ checkedChildren, unCheckedChildren, size, ...props }: any) =>
|
|
34
|
+
React.createElement('input', { ...props, type: 'checkbox', readOnly: true }),
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('Inline controls - stopPropagation', () => {
|
|
39
|
+
it('SwitchWithTitle click does not bubble to parent', async () => {
|
|
40
|
+
const engine = new FlowEngine();
|
|
41
|
+
const parentClick = vi.fn();
|
|
42
|
+
const onChange = vi.fn();
|
|
43
|
+
|
|
44
|
+
render(
|
|
45
|
+
<FlowEngineProvider engine={engine}>
|
|
46
|
+
<div onClick={parentClick}>
|
|
47
|
+
<SwitchWithTitle title="Enabled" itemKey="enabled" onChange={onChange} />
|
|
48
|
+
</div>
|
|
49
|
+
</FlowEngineProvider>,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
fireEvent.click(screen.getByText('Enabled'));
|
|
53
|
+
|
|
54
|
+
expect(parentClick).not.toHaveBeenCalled();
|
|
55
|
+
expect(onChange).toHaveBeenCalledWith({ enabled: true });
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('SelectWithTitle click does not bubble to parent', async () => {
|
|
59
|
+
const engine = new FlowEngine();
|
|
60
|
+
const parentClick = vi.fn();
|
|
61
|
+
|
|
62
|
+
render(
|
|
63
|
+
<FlowEngineProvider engine={engine}>
|
|
64
|
+
<div onClick={parentClick}>
|
|
65
|
+
<SelectWithTitle title="Mode" itemKey="mode" options={[{ label: 'A', value: 'a' }]} />
|
|
66
|
+
</div>
|
|
67
|
+
</FlowEngineProvider>,
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
fireEvent.click(screen.getByText('Mode'));
|
|
71
|
+
|
|
72
|
+
expect(parentClick).not.toHaveBeenCalled();
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { ExclamationCircleOutlined, MenuOutlined } from '@ant-design/icons';
|
|
10
|
+
import { ExclamationCircleOutlined, MenuOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
|
11
11
|
import type { DropdownProps, MenuProps } from 'antd';
|
|
12
|
-
import { App, Dropdown, Modal } from 'antd';
|
|
12
|
+
import { App, Dropdown, Modal, Tooltip, theme } from 'antd';
|
|
13
13
|
import React, { startTransition, useCallback, useEffect, useMemo, useState, FC } from 'react';
|
|
14
14
|
import { FlowModel } from '../../../../models';
|
|
15
15
|
import type { FlowModelExtraMenuItem } from '../../../../models';
|
|
@@ -17,6 +17,7 @@ import type { StepDefinition, StepUIMode } from '../../../../types';
|
|
|
17
17
|
import {
|
|
18
18
|
getT,
|
|
19
19
|
resolveStepUiSchema,
|
|
20
|
+
resolveStepDisabledInSettings,
|
|
20
21
|
shouldHideStepInSettings,
|
|
21
22
|
resolveDefaultParams,
|
|
22
23
|
resolveUiMode,
|
|
@@ -33,6 +34,8 @@ interface StepInfo {
|
|
|
33
34
|
title: string;
|
|
34
35
|
modelKey?: string;
|
|
35
36
|
uiMode?: StepUIMode;
|
|
37
|
+
disabled?: boolean;
|
|
38
|
+
disabledReason?: string;
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
interface FlowInfo {
|
|
@@ -150,12 +153,29 @@ const componentMap = {
|
|
|
150
153
|
const MenuLabelItem = ({ title, uiMode, itemProps }) => {
|
|
151
154
|
const type = uiMode?.type || uiMode;
|
|
152
155
|
const Component = type ? componentMap[type] : null;
|
|
156
|
+
const disabled = !!itemProps?.disabled;
|
|
157
|
+
const disabledReason = itemProps?.disabledReason;
|
|
158
|
+
const disabledIconColor = itemProps?.disabledIconColor;
|
|
153
159
|
|
|
154
|
-
|
|
155
|
-
|
|
160
|
+
const content = (() => {
|
|
161
|
+
if (!Component) {
|
|
162
|
+
return <>{title}</>;
|
|
163
|
+
}
|
|
164
|
+
return <Component title={title} {...itemProps} />;
|
|
165
|
+
})();
|
|
166
|
+
|
|
167
|
+
if (!disabled) {
|
|
168
|
+
return content;
|
|
156
169
|
}
|
|
157
170
|
|
|
158
|
-
return
|
|
171
|
+
return (
|
|
172
|
+
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
|
|
173
|
+
{content}
|
|
174
|
+
<Tooltip title={disabledReason} placement="right" destroyTooltipOnHide>
|
|
175
|
+
<QuestionCircleOutlined style={{ color: disabledIconColor }} />
|
|
176
|
+
</Tooltip>
|
|
177
|
+
</span>
|
|
178
|
+
);
|
|
159
179
|
};
|
|
160
180
|
|
|
161
181
|
/**
|
|
@@ -180,10 +200,17 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
180
200
|
}) => {
|
|
181
201
|
const { message } = App.useApp();
|
|
182
202
|
const t = useMemo(() => getT(model), [model]);
|
|
203
|
+
const { token } = theme.useToken();
|
|
204
|
+
const disabledIconColor = token?.colorTextTertiary || token?.colorTextDescription || token?.colorTextSecondary;
|
|
183
205
|
const [visible, setVisible] = useState(false);
|
|
184
206
|
// 当模型发生子模型替换/增删等变化时,强制刷新菜单数据
|
|
185
207
|
const [refreshTick, setRefreshTick] = useState(0);
|
|
186
208
|
const [extraMenuItems, setExtraMenuItems] = useState<FlowModelExtraMenuItem[]>([]);
|
|
209
|
+
const [configurableFlowsAndSteps, setConfigurableFlowsAndSteps] = useState<FlowInfo[]>([]);
|
|
210
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
211
|
+
const closeDropdown = useCallback(() => {
|
|
212
|
+
setVisible(false);
|
|
213
|
+
}, []);
|
|
187
214
|
const handleOpenChange: DropdownProps['onOpenChange'] = useCallback((nextOpen: boolean, info) => {
|
|
188
215
|
if (info.source === 'trigger' || nextOpen) {
|
|
189
216
|
// 当鼠标快速滑过时,终止菜单的渲染,防止卡顿
|
|
@@ -234,7 +261,7 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
234
261
|
return () => {
|
|
235
262
|
mounted = false;
|
|
236
263
|
};
|
|
237
|
-
}, [model, menuLevels, t, refreshTick, visible
|
|
264
|
+
}, [model, menuLevels, t, refreshTick, visible]);
|
|
238
265
|
|
|
239
266
|
// 统一的复制 UID 方法
|
|
240
267
|
const copyUidToClipboard = useCallback(
|
|
@@ -292,6 +319,7 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
292
319
|
);
|
|
293
320
|
|
|
294
321
|
const handleDelete = useCallback(() => {
|
|
322
|
+
closeDropdown();
|
|
295
323
|
Modal.confirm({
|
|
296
324
|
title: t('Confirm delete'),
|
|
297
325
|
icon: <ExclamationCircleOutlined />,
|
|
@@ -312,7 +340,7 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
312
340
|
}
|
|
313
341
|
},
|
|
314
342
|
});
|
|
315
|
-
}, [model]);
|
|
343
|
+
}, [closeDropdown, model, t]);
|
|
316
344
|
|
|
317
345
|
const handleStepConfiguration = useCallback(
|
|
318
346
|
(key: string) => {
|
|
@@ -345,6 +373,7 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
345
373
|
}
|
|
346
374
|
|
|
347
375
|
try {
|
|
376
|
+
closeDropdown();
|
|
348
377
|
targetModel.openFlowSettings({
|
|
349
378
|
flowKey,
|
|
350
379
|
stepKey,
|
|
@@ -353,7 +382,32 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
353
382
|
console.log(t('Configuration popup cancelled or error'), ':', error);
|
|
354
383
|
}
|
|
355
384
|
},
|
|
356
|
-
[model],
|
|
385
|
+
[closeDropdown, model, t],
|
|
386
|
+
);
|
|
387
|
+
|
|
388
|
+
const isStepMenuItemDisabled = useCallback(
|
|
389
|
+
(key: string) => {
|
|
390
|
+
const cleanKey = key.includes('-') && /^(.+)-\d+$/.test(key) ? key.replace(/-\d+$/, '') : key;
|
|
391
|
+
const keys = cleanKey.split(':');
|
|
392
|
+
let modelKey: string | undefined;
|
|
393
|
+
let flowKey: string | undefined;
|
|
394
|
+
let stepKey: string | undefined;
|
|
395
|
+
|
|
396
|
+
if (keys.length === 3) {
|
|
397
|
+
[modelKey, flowKey, stepKey] = keys;
|
|
398
|
+
} else if (keys.length === 2) {
|
|
399
|
+
[flowKey, stepKey] = keys;
|
|
400
|
+
} else {
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return configurableFlowsAndSteps.some(({ flow, steps, modelKey: flowModelKey }: FlowInfo) => {
|
|
405
|
+
const sameModel = (flowModelKey || undefined) === modelKey;
|
|
406
|
+
if (!sameModel || flow.key !== flowKey) return false;
|
|
407
|
+
return steps.some((stepInfo: StepInfo) => stepInfo.stepKey === stepKey && !!stepInfo.disabled);
|
|
408
|
+
});
|
|
409
|
+
},
|
|
410
|
+
[configurableFlowsAndSteps],
|
|
357
411
|
);
|
|
358
412
|
|
|
359
413
|
const handleMenuClick = useCallback(
|
|
@@ -363,18 +417,25 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
363
417
|
const cleanKey = key.includes('-') && /^(.+)-\d+$/.test(key) ? key.replace(/-\d+$/, '') : key;
|
|
364
418
|
|
|
365
419
|
if (cleanKey.startsWith('copy-pop-uid:')) {
|
|
420
|
+
closeDropdown();
|
|
366
421
|
handleCopyPopupUid(cleanKey);
|
|
367
422
|
return;
|
|
368
423
|
}
|
|
369
424
|
|
|
370
425
|
const extra = extraMenuItems.find((it) => it?.key === originalKey || it?.key === cleanKey);
|
|
371
426
|
if (extra?.onClick) {
|
|
427
|
+
closeDropdown();
|
|
372
428
|
extra.onClick();
|
|
373
429
|
return;
|
|
374
430
|
}
|
|
375
431
|
|
|
432
|
+
if (isStepMenuItemDisabled(cleanKey)) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
|
|
376
436
|
switch (cleanKey) {
|
|
377
437
|
case 'copy-uid':
|
|
438
|
+
closeDropdown();
|
|
378
439
|
handleCopyUid();
|
|
379
440
|
break;
|
|
380
441
|
case 'delete':
|
|
@@ -385,7 +446,15 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
385
446
|
break;
|
|
386
447
|
}
|
|
387
448
|
},
|
|
388
|
-
[
|
|
449
|
+
[
|
|
450
|
+
closeDropdown,
|
|
451
|
+
handleCopyUid,
|
|
452
|
+
handleDelete,
|
|
453
|
+
handleStepConfiguration,
|
|
454
|
+
handleCopyPopupUid,
|
|
455
|
+
extraMenuItems,
|
|
456
|
+
isStepMenuItemDisabled,
|
|
457
|
+
],
|
|
389
458
|
);
|
|
390
459
|
|
|
391
460
|
// 获取单个模型的可配置flows和steps
|
|
@@ -409,6 +478,7 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
409
478
|
if (await shouldHideStepInSettings(targetModel, flow, actionStep)) {
|
|
410
479
|
return null;
|
|
411
480
|
}
|
|
481
|
+
const disabledState = await resolveStepDisabledInSettings(targetModel, flow, actionStep as any);
|
|
412
482
|
let uiMode: any = await resolveUiMode(actionStep.uiMode, (targetModel as any).context);
|
|
413
483
|
// 检查是否有uiSchema(静态或动态)
|
|
414
484
|
const hasStepUiSchema = actionStep.uiSchema != null;
|
|
@@ -458,6 +528,8 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
458
528
|
title: t(stepTitle) || stepKey,
|
|
459
529
|
modelKey, // 添加模型标识
|
|
460
530
|
uiMode,
|
|
531
|
+
disabled: disabledState.disabled,
|
|
532
|
+
disabledReason: disabledState.reason,
|
|
461
533
|
};
|
|
462
534
|
}),
|
|
463
535
|
).then((steps) => steps.filter(Boolean));
|
|
@@ -494,9 +566,6 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
494
566
|
return result;
|
|
495
567
|
}, [model, menuLevels, getModelConfigurableFlowsAndSteps]);
|
|
496
568
|
|
|
497
|
-
const [configurableFlowsAndSteps, setConfigurableFlowsAndSteps] = useState<FlowInfo[]>([]);
|
|
498
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
499
|
-
|
|
500
569
|
useEffect(() => {
|
|
501
570
|
const triggerRebuild = () => {
|
|
502
571
|
setRefreshTick((v) => v + 1);
|
|
@@ -603,10 +672,14 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
603
672
|
},
|
|
604
673
|
...((uiMode as any)?.props || {}),
|
|
605
674
|
itemKey: (uiMode as any)?.key,
|
|
675
|
+
disabled: !!stepInfo.disabled,
|
|
676
|
+
disabledReason: stepInfo.disabledReason,
|
|
677
|
+
disabledIconColor,
|
|
606
678
|
};
|
|
607
679
|
items.push({
|
|
608
680
|
key: uniqueKey,
|
|
609
|
-
label: <MenuLabelItem title={
|
|
681
|
+
label: <MenuLabelItem title={stepInfo.title} uiMode={uiMode} itemProps={itemProps} />,
|
|
682
|
+
disabled: !!stepInfo.disabled,
|
|
610
683
|
});
|
|
611
684
|
});
|
|
612
685
|
if (flow.options.divider === 'bottom') {
|
|
@@ -648,7 +721,17 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
648
721
|
|
|
649
722
|
items.push({
|
|
650
723
|
key: uniqueKey,
|
|
651
|
-
label:
|
|
724
|
+
label: stepInfo.disabled ? (
|
|
725
|
+
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
|
|
726
|
+
{stepInfo.title}
|
|
727
|
+
<Tooltip title={stepInfo.disabledReason} placement="right" destroyTooltipOnHide>
|
|
728
|
+
<QuestionCircleOutlined style={{ color: disabledIconColor }} />
|
|
729
|
+
</Tooltip>
|
|
730
|
+
</span>
|
|
731
|
+
) : (
|
|
732
|
+
stepInfo.title
|
|
733
|
+
),
|
|
734
|
+
disabled: !!stepInfo.disabled,
|
|
652
735
|
});
|
|
653
736
|
});
|
|
654
737
|
});
|
|
@@ -663,7 +746,17 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
663
746
|
|
|
664
747
|
subMenuChildren.push({
|
|
665
748
|
key: uniqueKey,
|
|
666
|
-
label:
|
|
749
|
+
label: stepInfo.disabled ? (
|
|
750
|
+
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
|
|
751
|
+
{stepInfo.title}
|
|
752
|
+
<Tooltip title={stepInfo.disabledReason} placement="right" destroyTooltipOnHide>
|
|
753
|
+
<QuestionCircleOutlined style={{ color: disabledIconColor }} />
|
|
754
|
+
</Tooltip>
|
|
755
|
+
</span>
|
|
756
|
+
) : (
|
|
757
|
+
stepInfo.title
|
|
758
|
+
),
|
|
759
|
+
disabled: !!stepInfo.disabled,
|
|
667
760
|
});
|
|
668
761
|
});
|
|
669
762
|
});
|
|
@@ -679,7 +772,7 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
679
772
|
}
|
|
680
773
|
|
|
681
774
|
return items;
|
|
682
|
-
}, [configurableFlowsAndSteps, flattenSubMenus, t]);
|
|
775
|
+
}, [configurableFlowsAndSteps, disabledIconColor, flattenSubMenus, t]);
|
|
683
776
|
|
|
684
777
|
// 向菜单项添加额外按钮
|
|
685
778
|
const finalMenuItems = useMemo((): NonNullable<MenuProps['items']> => {
|
|
@@ -7,14 +7,19 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { DeleteOutlined, ExclamationCircleOutlined, SettingOutlined } from '@ant-design/icons';
|
|
10
|
+
import { DeleteOutlined, ExclamationCircleOutlined, QuestionCircleOutlined, SettingOutlined } from '@ant-design/icons';
|
|
11
11
|
import type { MenuProps } from 'antd';
|
|
12
|
-
import { Alert, Dropdown, Modal } from 'antd';
|
|
12
|
+
import { Alert, Dropdown, Modal, Tooltip, theme } from 'antd';
|
|
13
13
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
14
14
|
import { FlowRuntimeContext } from '../../../../flowContext';
|
|
15
15
|
import { useFlowModelById } from '../../../../hooks';
|
|
16
16
|
import { FlowModel } from '../../../../models';
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
getT,
|
|
19
|
+
resolveStepDisabledInSettings,
|
|
20
|
+
setupRuntimeContextSteps,
|
|
21
|
+
shouldHideStepInSettings,
|
|
22
|
+
} from '../../../../utils';
|
|
18
23
|
import { openStepSettingsDialog } from './StepSettingsDialog';
|
|
19
24
|
import { ActionDefinition } from '../../../../types';
|
|
20
25
|
import { observer } from '../../../../reactive';
|
|
@@ -74,6 +79,21 @@ const FlowsContextMenu: React.FC<FlowsContextMenuProps> = (props) => {
|
|
|
74
79
|
const FlowsContextMenuWithModel: React.FC<ModelProvidedProps> = observer(
|
|
75
80
|
({ model, children, enabled = true, position = 'right', showDeleteButton = true }) => {
|
|
76
81
|
const t = getT(model);
|
|
82
|
+
const { token } = theme.useToken();
|
|
83
|
+
const disabledIconColor = token?.colorTextTertiary || token?.colorTextDescription || token?.colorTextSecondary;
|
|
84
|
+
const [configurableFlowsAndSteps, setConfigurableFlowsAndSteps] = useState<any[]>([]);
|
|
85
|
+
const isStepMenuItemDisabled = useCallback(
|
|
86
|
+
(key: string) => {
|
|
87
|
+
const [flowKey, stepKey] = key.split(':');
|
|
88
|
+
if (!flowKey || !stepKey) return false;
|
|
89
|
+
return configurableFlowsAndSteps.some(({ flow, steps }) => {
|
|
90
|
+
if (flow.key !== flowKey) return false;
|
|
91
|
+
return steps.some((stepInfo) => stepInfo.stepKey === stepKey && !!stepInfo.disabled);
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
[configurableFlowsAndSteps],
|
|
95
|
+
);
|
|
96
|
+
|
|
77
97
|
const handleMenuClick = useCallback(
|
|
78
98
|
({ key }: { key: string }) => {
|
|
79
99
|
if (key === 'delete') {
|
|
@@ -99,6 +119,9 @@ const FlowsContextMenuWithModel: React.FC<ModelProvidedProps> = observer(
|
|
|
99
119
|
},
|
|
100
120
|
});
|
|
101
121
|
} else {
|
|
122
|
+
if (isStepMenuItemDisabled(key)) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
102
125
|
// 处理step配置,key格式为 "flowKey:stepKey"
|
|
103
126
|
const [flowKey, stepKey] = key.split(':');
|
|
104
127
|
try {
|
|
@@ -127,7 +150,7 @@ const FlowsContextMenuWithModel: React.FC<ModelProvidedProps> = observer(
|
|
|
127
150
|
}
|
|
128
151
|
}
|
|
129
152
|
},
|
|
130
|
-
[model],
|
|
153
|
+
[isStepMenuItemDisabled, model],
|
|
131
154
|
);
|
|
132
155
|
|
|
133
156
|
if (!model) {
|
|
@@ -156,6 +179,7 @@ const FlowsContextMenuWithModel: React.FC<ModelProvidedProps> = observer(
|
|
|
156
179
|
if (await shouldHideStepInSettings(model as FlowModel, flow, actionStep)) {
|
|
157
180
|
return null;
|
|
158
181
|
}
|
|
182
|
+
const disabledState = await resolveStepDisabledInSettings(model as FlowModel, flow, actionStep);
|
|
159
183
|
|
|
160
184
|
// 从step获取uiSchema(如果存在)
|
|
161
185
|
const stepUiSchema: ActionDefinition['uiSchema'] = actionStep.uiSchema || {};
|
|
@@ -191,6 +215,8 @@ const FlowsContextMenuWithModel: React.FC<ModelProvidedProps> = observer(
|
|
|
191
215
|
step: actionStep,
|
|
192
216
|
uiSchema: mergedUiSchema,
|
|
193
217
|
title: actionStep.title || stepKey,
|
|
218
|
+
disabled: disabledState.disabled,
|
|
219
|
+
disabledReason: disabledState.reason,
|
|
194
220
|
};
|
|
195
221
|
}),
|
|
196
222
|
).then((steps) => steps.filter(Boolean));
|
|
@@ -206,8 +232,6 @@ const FlowsContextMenuWithModel: React.FC<ModelProvidedProps> = observer(
|
|
|
206
232
|
}
|
|
207
233
|
}, [model]);
|
|
208
234
|
|
|
209
|
-
const [configurableFlowsAndSteps, setConfigurableFlowsAndSteps] = useState<any[]>([]);
|
|
210
|
-
|
|
211
235
|
useEffect(() => {
|
|
212
236
|
let mounted = true;
|
|
213
237
|
(async () => {
|
|
@@ -243,7 +267,17 @@ const FlowsContextMenuWithModel: React.FC<ModelProvidedProps> = observer(
|
|
|
243
267
|
menuItems.push({
|
|
244
268
|
key: `${flow.key}:${stepInfo.stepKey}`,
|
|
245
269
|
icon: <SettingOutlined />,
|
|
246
|
-
label: stepInfo.
|
|
270
|
+
label: stepInfo.disabled ? (
|
|
271
|
+
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
|
|
272
|
+
{stepInfo.title}
|
|
273
|
+
<Tooltip title={stepInfo.disabledReason} placement="right" destroyTooltipOnHide>
|
|
274
|
+
<QuestionCircleOutlined style={{ color: disabledIconColor }} />
|
|
275
|
+
</Tooltip>
|
|
276
|
+
</span>
|
|
277
|
+
) : (
|
|
278
|
+
stepInfo.title
|
|
279
|
+
),
|
|
280
|
+
disabled: !!stepInfo.disabled,
|
|
247
281
|
});
|
|
248
282
|
});
|
|
249
283
|
});
|
|
@@ -14,7 +14,15 @@ import { Button, Space } from 'antd';
|
|
|
14
14
|
import React, { useEffect } from 'react';
|
|
15
15
|
import { FlowSettingsContextProvider, useFlowSettingsContext } from '../../../../hooks/useFlowSettingsContext';
|
|
16
16
|
import { StepSettingsDialogProps } from '../../../../types';
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
compileUiSchema,
|
|
19
|
+
FlowExitException,
|
|
20
|
+
getT,
|
|
21
|
+
resolveDefaultParams,
|
|
22
|
+
resolveStepUiSchema,
|
|
23
|
+
FlowCancelSaveException,
|
|
24
|
+
} from '../../../../utils';
|
|
25
|
+
import { FlowExitAllException } from '../../../../utils/exceptions';
|
|
18
26
|
import { observer } from '../../../../reactive';
|
|
19
27
|
|
|
20
28
|
const SchemaField = createSchemaField();
|
|
@@ -126,6 +134,17 @@ const openStepSettingsDialog = async ({
|
|
|
126
134
|
};
|
|
127
135
|
|
|
128
136
|
const openView = model.context.viewer[mode].bind(model.context.viewer);
|
|
137
|
+
const resolvedUiModeProps = toJS(uiModeProps) || {};
|
|
138
|
+
const { zIndex: uiModeZIndex, ...restUiModeProps } = resolvedUiModeProps;
|
|
139
|
+
const resolveDialogZIndex = (rawZIndex?: number) => {
|
|
140
|
+
const nextZIndex =
|
|
141
|
+
typeof model.context.viewer?.getNextZIndex === 'function'
|
|
142
|
+
? model.context.viewer.getNextZIndex()
|
|
143
|
+
: (model.context.themeToken?.zIndexPopupBase || 1000) + 1;
|
|
144
|
+
const inputZIndex = Number(rawZIndex) || 0;
|
|
145
|
+
return Math.max(nextZIndex, inputZIndex);
|
|
146
|
+
};
|
|
147
|
+
const mergedZIndex = resolveDialogZIndex(uiModeZIndex);
|
|
129
148
|
|
|
130
149
|
const form = createForm({
|
|
131
150
|
initialValues: compileUiSchema(scopes, initialValues),
|
|
@@ -144,7 +163,8 @@ const openStepSettingsDialog = async ({
|
|
|
144
163
|
title: dialogTitle || t(title),
|
|
145
164
|
width: dialogWidth,
|
|
146
165
|
destroyOnClose: true,
|
|
147
|
-
...
|
|
166
|
+
...restUiModeProps,
|
|
167
|
+
zIndex: mergedZIndex,
|
|
148
168
|
// 透传 navigation,便于变量元信息根据真实视图栈推断父级弹窗
|
|
149
169
|
inputArgs,
|
|
150
170
|
onClose: () => {
|
|
@@ -157,7 +177,11 @@ const openStepSettingsDialog = async ({
|
|
|
157
177
|
useEffect(() => {
|
|
158
178
|
return autorun(() => {
|
|
159
179
|
const dynamicProps = toJS(uiModeProps);
|
|
160
|
-
|
|
180
|
+
const { zIndex, ...restDynamicProps } = dynamicProps || {};
|
|
181
|
+
currentDialog.update({
|
|
182
|
+
...restDynamicProps,
|
|
183
|
+
zIndex: resolveDialogZIndex(zIndex),
|
|
184
|
+
});
|
|
161
185
|
});
|
|
162
186
|
}, []);
|
|
163
187
|
|
|
@@ -203,7 +227,10 @@ const openStepSettingsDialog = async ({
|
|
|
203
227
|
await afterParamsSave(flowRuntimeContext, currentValues, previousParams);
|
|
204
228
|
}
|
|
205
229
|
} catch (error) {
|
|
206
|
-
if (error instanceof
|
|
230
|
+
if (error instanceof FlowCancelSaveException) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (error instanceof FlowExitException || error instanceof FlowExitAllException) {
|
|
207
234
|
currentDialog.close();
|
|
208
235
|
return;
|
|
209
236
|
}
|