@nocobase/client-v2 2.2.0-beta.1 → 2.2.0-beta.11
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/BaseApplication.d.ts +3 -0
- package/es/PluginSettingsManager.d.ts +33 -0
- package/es/RouteRepository.d.ts +29 -6
- package/es/RouterManager.d.ts +15 -0
- package/es/authRedirect.d.ts +1 -0
- package/es/collection-field-interface/CollectionFieldInterface.d.ts +1 -0
- package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +1 -0
- 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/entry-actions/EntryActionManager.d.ts +24 -0
- package/es/entry-actions/index.d.ts +9 -0
- package/es/flow/actions/afterSuccess.d.ts +8 -1
- package/es/flow/actions/index.d.ts +1 -1
- package/es/flow/admin-shell/BaseLayoutModel.d.ts +6 -0
- package/es/flow/admin-shell/BaseLayoutRouteCoordinator.d.ts +7 -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 +5 -0
- package/es/flow/admin-shell/admin-layout/AppListRender.d.ts +2 -1
- package/es/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.d.ts +24 -0
- package/es/flow/admin-shell/admin-layout/index.d.ts +1 -0
- package/es/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.d.ts +6 -0
- package/es/flow/admin-shell/admin-layout/useApplications.d.ts +7 -2
- package/es/flow/components/FieldAssignRulesEditor.d.ts +3 -1
- package/es/flow/models/base/ActionModelCore.d.ts +2 -0
- 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/QuickEditFormModel.d.ts +17 -2
- package/es/flow/models/blocks/form/submitHandler.d.ts +1 -1
- package/es/flow/models/blocks/form/value-runtime/rules.d.ts +21 -0
- package/es/flow/models/blocks/form/value-runtime/runtime.d.ts +30 -0
- package/es/flow/models/blocks/form/value-runtime/types.d.ts +2 -2
- package/es/flow/models/blocks/js-block/JSBlock.d.ts +1 -0
- package/es/flow/models/blocks/table/TableActionsColumnModel.d.ts +1 -0
- package/es/flow/models/blocks/table/TableBlockModel.d.ts +1 -0
- package/es/flow/models/blocks/table/dragSort/dragSortHooks.d.ts +1 -1
- package/es/flow/models/blocks/table/dragSort/dragSortSettings.d.ts +2 -1
- package/es/flow/models/blocks/table/dragSort/dragSortUtils.d.ts +6 -4
- package/es/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.d.ts +2 -0
- package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.d.ts +6 -0
- package/es/flow/resolveViewParamsToViewList.d.ts +1 -1
- package/es/flow/routeTransientInputArgs.d.ts +14 -0
- package/es/flow/utils/dataScopeRowSnapshot.d.ts +59 -0
- package/es/flow/utils/dateTimeDisplayProps.d.ts +49 -0
- package/es/flow/utils/formValueDeps.d.ts +32 -0
- package/es/flow-compat/FieldValidation.d.ts +2 -1
- package/es/index.d.ts +4 -1
- package/es/index.mjs +315 -167
- package/es/utils/markdownSanitize.d.ts +11 -0
- package/lib/index.js +315 -167
- package/package.json +8 -7
- package/src/BaseApplication.tsx +12 -5
- package/src/PluginSettingsManager.ts +53 -0
- package/src/RouteRepository.ts +151 -24
- package/src/RouterManager.tsx +142 -1
- package/src/__tests__/PluginSettingsManager.test.ts +13 -0
- package/src/__tests__/RouteRepository.test.ts +239 -0
- package/src/__tests__/RouterManager.test.ts +125 -0
- package/src/__tests__/app.test.tsx +55 -0
- package/src/__tests__/authRedirect.test.ts +17 -0
- package/src/__tests__/exports.test.ts +16 -0
- package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +199 -0
- package/src/__tests__/plugin-manager.test.tsx +44 -2
- package/src/__tests__/settings-center.test.tsx +40 -1
- package/src/authRedirect.ts +38 -0
- package/src/collection-field-interface/CollectionFieldInterface.ts +1 -0
- package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +1 -0
- package/src/collection-manager/field-configure.ts +1 -1
- package/src/collection-manager/interfaces/id.ts +1 -1
- package/src/collection-manager/interfaces/m2m.tsx +2 -2
- package/src/collection-manager/interfaces/m2o.tsx +2 -2
- package/src/collection-manager/interfaces/nanoid.ts +1 -1
- package/src/collection-manager/interfaces/o2m.tsx +2 -2
- package/src/collection-manager/interfaces/obo.tsx +2 -2
- package/src/collection-manager/interfaces/oho.tsx +2 -2
- package/src/collection-manager/interfaces/properties/index.ts +2 -2
- package/src/collection-manager/interfaces/uuid.ts +1 -1
- package/src/components/AppComponents.tsx +19 -3
- package/src/components/form/JsonTextArea.tsx +21 -11
- package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +145 -2
- package/src/components/form/ScanInput/useCodeScanner.ts +154 -2
- 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/entry-actions/EntryActionManager.ts +76 -0
- package/src/entry-actions/index.ts +10 -0
- package/src/flow/FlowPage.tsx +38 -3
- package/src/flow/__tests__/FlowPage.test.tsx +201 -27
- package/src/flow/__tests__/FlowRoute.test.tsx +1088 -105
- package/src/flow/__tests__/getKey.test.ts +7 -0
- package/src/flow/__tests__/resolveViewParamsToViewList.test.ts +34 -0
- package/src/flow/actions/__tests__/afterSuccess.test.ts +73 -0
- package/src/flow/actions/__tests__/dataScopeFilter.test.ts +120 -0
- package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +1022 -0
- 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/__tests__/linkageRulesRefresh.test.ts +7 -3
- package/src/flow/actions/__tests__/openView.defineProps.route.test.tsx +164 -0
- package/src/flow/actions/__tests__/subFormFieldLinkageRules.inputArgs.test.ts +2 -2
- package/src/flow/actions/afterSuccess.tsx +142 -3
- package/src/flow/actions/customVariable.tsx +1 -2
- package/src/flow/actions/dataScopeFilter.ts +20 -1
- package/src/flow/actions/dateTimeFormat.tsx +42 -28
- package/src/flow/actions/index.ts +1 -1
- package/src/flow/actions/linkageRules.tsx +123 -35
- package/src/flow/actions/linkageRulesFormValueRefresh.ts +22 -130
- package/src/flow/actions/linkageRulesRefresh.tsx +2 -6
- package/src/flow/actions/openView.tsx +59 -5
- package/src/flow/actions/runjs.tsx +2 -14
- package/src/flow/actions/validation.tsx +62 -30
- package/src/flow/admin-shell/BaseLayoutModel.tsx +149 -3
- package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +187 -42
- package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +1020 -110
- package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +74 -13
- package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.test.tsx +486 -0
- package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +64 -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 +23 -9
- package/src/flow/admin-shell/admin-layout/AppListRender.tsx +13 -2
- package/src/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.tsx +289 -0
- 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__/AdminLayoutModel.test.tsx +518 -2
- package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +214 -2
- package/src/flow/admin-shell/admin-layout/index.ts +1 -0
- 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/admin-shell/admin-layout/useApplications.tsx +81 -12
- package/src/flow/common/Markdown/Display.tsx +14 -3
- package/src/flow/common/Markdown/Edit.tsx +19 -7
- package/src/flow/components/DefaultValue.tsx +1 -2
- package/src/flow/components/DynamicFlowsIcon.tsx +447 -126
- package/src/flow/components/FieldAssignRulesEditor.tsx +128 -21
- package/src/flow/components/FieldAssignValueInput.tsx +15 -11
- package/src/flow/components/FlowRoute.tsx +186 -25
- package/src/flow/components/__tests__/DynamicFlowsIcon.test.tsx +148 -2
- package/src/flow/components/__tests__/FieldAssignRulesEditor.test.tsx +508 -4
- package/src/flow/components/__tests__/FieldAssignValueInput.context.test.tsx +134 -0
- package/src/flow/components/__tests__/fieldAssignOptions.test.ts +151 -3
- 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/components/fieldAssignOptions.ts +155 -27
- package/src/flow/flows/editMarkdownFlow.tsx +1 -1
- package/src/flow/getViewDiffAndUpdateHidden.tsx +1 -0
- package/src/flow/index.ts +1 -1
- package/src/flow/internal/components/Markdown/util.ts +2 -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 +21 -8
- package/src/flow/models/base/ActionModelCore.tsx +11 -4
- 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__/ActionModelCore.render.test.tsx +37 -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 +32 -3
- package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +27 -12
- package/src/flow/models/blocks/filter-form/FilterFormJSActionModel.tsx +2 -7
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +36 -0
- package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +111 -0
- package/src/flow/models/blocks/filter-form/fields/FilterFormCustomFieldModel.tsx +1 -1
- 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/FormActionModel.tsx +1 -1
- 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/QuickEditFormModel.tsx +189 -36
- package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +17 -0
- package/src/flow/models/blocks/form/__tests__/QuickEditFormModel.quickEdit.test.ts +350 -2
- package/src/flow/models/blocks/form/__tests__/submitHandler.test.ts +54 -1
- package/src/flow/models/blocks/form/submitHandler.ts +17 -3
- package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +940 -75
- package/src/flow/models/blocks/form/value-runtime/rules.ts +445 -13
- package/src/flow/models/blocks/form/value-runtime/runtime.ts +348 -13
- package/src/flow/models/blocks/form/value-runtime/types.ts +2 -2
- package/src/flow/models/blocks/js-block/JSBlock.tsx +225 -9
- package/src/flow/models/blocks/js-block/__tests__/JSBlockModel.test.tsx +150 -0
- package/src/flow/models/blocks/table/JSColumnModel.tsx +1 -9
- package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +38 -14
- package/src/flow/models/blocks/table/TableBlockModel.tsx +38 -17
- package/src/flow/models/blocks/table/TableColumnModel.tsx +42 -5
- package/src/flow/models/blocks/table/__tests__/TableActionsColumnModel.test.tsx +111 -1
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.dragSort.test.ts +127 -0
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.mobileSettingsButtons.test.tsx +78 -0
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.rowSelection.test.tsx +1 -0
- package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +210 -1
- package/src/flow/models/blocks/table/dragSort/dragSortHooks.tsx +28 -17
- package/src/flow/models/blocks/table/dragSort/dragSortSettings.ts +13 -6
- package/src/flow/models/blocks/table/dragSort/dragSortUtils.ts +15 -2
- package/src/flow/models/fields/AssociationFieldModel/AssociationFieldModel.tsx +1 -1
- package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +30 -6
- package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +61 -17
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.tsx +141 -19
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +13 -1
- package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableColumnModel.rowRecord.test.ts +197 -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 +40 -2
- 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/TextareaFieldModel.tsx +72 -3
- package/src/flow/models/fields/__tests__/DisplayDateTimeFieldModel.test.tsx +96 -0
- package/src/flow/models/fields/__tests__/InputFieldModel.test.tsx +117 -1
- package/src/flow/models/fields/__tests__/SelectFieldModel.test.tsx +43 -0
- package/src/flow/models/fields/__tests__/TextareaFieldModel.test.tsx +131 -0
- package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +20 -10
- package/src/flow/models/fields/mobile-components/MobileSelect.tsx +24 -12
- package/src/flow/models/topbar/TopbarActionModel.tsx +168 -10
- package/src/flow/resolveViewParamsToViewList.tsx +11 -3
- package/src/flow/routeTransientInputArgs.ts +27 -0
- package/src/flow/utils/__tests__/dateTimeFormat.test.ts +300 -0
- package/src/flow/utils/dataScopeFormValueClear.ts +218 -81
- package/src/flow/utils/dataScopeRowSnapshot.ts +616 -0
- package/src/flow/utils/dateTimeDisplayProps.ts +135 -0
- package/src/flow/utils/formValueDeps.ts +170 -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 +10 -1
- package/src/layout-manager/__tests__/LayoutRoute.test.tsx +41 -1
- package/src/nocobase-buildin-plugin/index.tsx +51 -15
- package/src/settings-center/AdminSettingsLayout.tsx +10 -2
- package/src/settings-center/SystemSettingsPage.tsx +1 -2
- package/src/settings-center/plugin-manager/PluginCard.tsx +2 -2
- package/src/settings-center/plugin-manager/index.tsx +3 -0
- package/src/settings-center/utils.tsx +0 -6
- package/src/utils/markdownSanitize.ts +88 -0
package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.tsx
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
observer,
|
|
27
27
|
FlowModelProvider,
|
|
28
28
|
FlowErrorFallback,
|
|
29
|
+
extractUsedVariablePaths,
|
|
29
30
|
} from '@nocobase/flow-engine';
|
|
30
31
|
import { TableColumnProps, Tooltip, Input, Space, Divider } from 'antd';
|
|
31
32
|
import { ErrorBoundary } from 'react-error-boundary';
|
|
@@ -37,6 +38,14 @@ import { FieldDeletePlaceholder, CustomWidth, normalizeTableColumnWidth } from '
|
|
|
37
38
|
import { buildDynamicNamePath } from '../../../blocks/form/dynamicNamePath';
|
|
38
39
|
import { getSubTableRowIdentity } from './rowIdentity';
|
|
39
40
|
import { getFieldBindingUse, rebuildFieldSubModel } from '../../../../internal/utils/rebuildFieldSubModel';
|
|
41
|
+
import {
|
|
42
|
+
buildCurrentItemTitle,
|
|
43
|
+
createAssociationItemChainContextPropertyOptions,
|
|
44
|
+
createItemChainGetter,
|
|
45
|
+
createParentItemAccessorsFromContext,
|
|
46
|
+
createRootItemChain,
|
|
47
|
+
type ItemChain,
|
|
48
|
+
} from '../itemChain';
|
|
40
49
|
|
|
41
50
|
export const SUB_TABLE_COLUMN_FIELD_COMPONENT_CONTEXT = 'subTableColumn';
|
|
42
51
|
|
|
@@ -180,6 +189,25 @@ const handleModelName = (modelName) => {
|
|
|
180
189
|
return modelName;
|
|
181
190
|
};
|
|
182
191
|
|
|
192
|
+
const DATA_SCOPE_FLOW_KEYS = ['selectSettings', 'cascadeSelectSettings'];
|
|
193
|
+
|
|
194
|
+
function hasFormValueDrivenDataScope(filter: any) {
|
|
195
|
+
if (!filter) return false;
|
|
196
|
+
try {
|
|
197
|
+
const usage = extractUsedVariablePaths(filter) || {};
|
|
198
|
+
return ['item', 'formValues'].some((name) => Object.prototype.hasOwnProperty.call(usage, name));
|
|
199
|
+
} catch {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function hasFormValueDrivenDataScopeField(fieldModel: any) {
|
|
205
|
+
return DATA_SCOPE_FLOW_KEYS.some((flowKey) => {
|
|
206
|
+
const params = fieldModel?.getStepParams?.(flowKey, 'dataScope');
|
|
207
|
+
return hasFormValueDrivenDataScope(params?.filter);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
183
211
|
const MemoFieldRenderer = React.memo(FieldModelRenderer, (prev, next) => {
|
|
184
212
|
return prev.value === next.value && prev.model === next.model;
|
|
185
213
|
});
|
|
@@ -203,6 +231,20 @@ export function getLatestSubTableRowRecord(form: any, fieldIndex: unknown, fallb
|
|
|
203
231
|
return typeof latestRecord === 'undefined' ? fallbackRecord : latestRecord;
|
|
204
232
|
}
|
|
205
233
|
|
|
234
|
+
export function buildSubTableColumnNamePath(
|
|
235
|
+
fieldPath: Array<string | number>,
|
|
236
|
+
rowIdx: number,
|
|
237
|
+
namePath: string | number,
|
|
238
|
+
rowFieldIndex: unknown,
|
|
239
|
+
): Array<string | number> {
|
|
240
|
+
const currentRowPath = buildRowPathFromFieldIndex(rowFieldIndex);
|
|
241
|
+
if (currentRowPath?.length) {
|
|
242
|
+
return [...currentRowPath, namePath];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return [...fieldPath, rowIdx, namePath];
|
|
246
|
+
}
|
|
247
|
+
|
|
206
248
|
function shouldCommitImmediately(value: any) {
|
|
207
249
|
if (Array.isArray(value)) {
|
|
208
250
|
return true;
|
|
@@ -344,11 +386,23 @@ const MemoCell: React.FC<CellProps> = React.memo(
|
|
|
344
386
|
{parent.mapSubModels('field', (action: FieldModel) => {
|
|
345
387
|
const fieldPath = action.context.fieldPath.split('.');
|
|
346
388
|
const namePath = fieldPath.pop();
|
|
389
|
+
if (typeof namePath === 'undefined') {
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
const fieldNamePath = buildSubTableColumnNamePath(fieldPath, rowIdx, namePath, rowFork?.context?.fieldIndex);
|
|
393
|
+
const formItemName = buildDynamicNamePath([...fieldPath, rowIdx, namePath], parentFieldIndex);
|
|
347
394
|
|
|
348
395
|
const fork: any = action.createFork({}, `${id}`);
|
|
349
396
|
fork.context.defineProperty('currentObject', { get: () => record });
|
|
397
|
+
fork.context.defineProperty('fieldPathArray', {
|
|
398
|
+
get: () => fieldNamePath,
|
|
399
|
+
cache: false,
|
|
400
|
+
});
|
|
350
401
|
if (rowFork) {
|
|
402
|
+
const itemOptions = rowFork.context.getPropertyOptions?.('item');
|
|
403
|
+
const { value: _value, ...itemOptionsWithoutValue } = (itemOptions || {}) as any;
|
|
351
404
|
fork.context.defineProperty('item', {
|
|
405
|
+
...itemOptionsWithoutValue,
|
|
352
406
|
get: () => rowFork.context.item,
|
|
353
407
|
cache: false,
|
|
354
408
|
});
|
|
@@ -384,7 +438,7 @@ const MemoCell: React.FC<CellProps> = React.memo(
|
|
|
384
438
|
<FormItem
|
|
385
439
|
{...parent.props}
|
|
386
440
|
key={id}
|
|
387
|
-
name={
|
|
441
|
+
name={formItemName}
|
|
388
442
|
style={{ marginBottom: 0 }}
|
|
389
443
|
showLabel={false}
|
|
390
444
|
disabled={
|
|
@@ -445,6 +499,57 @@ export class SubTableColumnModel<
|
|
|
445
499
|
static renderMode = ModelRenderMode.RenderFunction;
|
|
446
500
|
static fieldComponentContext = SUB_TABLE_COLUMN_FIELD_COMPONENT_CONTEXT;
|
|
447
501
|
|
|
502
|
+
private getAssociationField() {
|
|
503
|
+
return (this.parent as any)?.context?.collectionField;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
private getParentFormValues() {
|
|
507
|
+
return (this.context?.blockModel as any)?.context?.formValues;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
private createParentItemAccessors(parentItemAccessor?: () => ItemChain | undefined) {
|
|
511
|
+
const baseAccessors = createParentItemAccessorsFromContext({
|
|
512
|
+
parentContextAccessor: () => (this.parent as any)?.context,
|
|
513
|
+
fallbackParentPropertiesAccessor: () => this.getParentFormValues(),
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
if (!parentItemAccessor) {
|
|
517
|
+
return baseAccessors;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
return {
|
|
521
|
+
parentPropertiesAccessor: (ctx?: any) =>
|
|
522
|
+
parentItemAccessor()?.value ?? baseAccessors.parentPropertiesAccessor(ctx),
|
|
523
|
+
parentItemMetaAccessor: baseAccessors.parentItemMetaAccessor,
|
|
524
|
+
parentItemResolverAccessor: baseAccessors.parentItemResolverAccessor,
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
private createRowItemContextPropertyOptions(
|
|
529
|
+
options: {
|
|
530
|
+
resolverPropertiesAccessor?: () => unknown;
|
|
531
|
+
parentItemAccessor?: () => ItemChain | undefined;
|
|
532
|
+
showParentIndex?: boolean;
|
|
533
|
+
} = {},
|
|
534
|
+
) {
|
|
535
|
+
const associationField = this.getAssociationField();
|
|
536
|
+
|
|
537
|
+
return createAssociationItemChainContextPropertyOptions({
|
|
538
|
+
t: this.context.t,
|
|
539
|
+
title: buildCurrentItemTitle(this.context.t, associationField, this.props?.name),
|
|
540
|
+
showIndex: true,
|
|
541
|
+
showParentIndex:
|
|
542
|
+
options.showParentIndex ??
|
|
543
|
+
(Array.isArray((this.parent as any)?.context?.fieldIndex) &&
|
|
544
|
+
(this.parent as any).context.fieldIndex.length > 0),
|
|
545
|
+
collectionAccessor: () => (this.parent as any)?.collection ?? associationField?.targetCollection ?? null,
|
|
546
|
+
propertiesAccessor: (ctx) => ctx?.item?.value,
|
|
547
|
+
resolverPropertiesAccessor: options.resolverPropertiesAccessor ?? (() => undefined),
|
|
548
|
+
parentCollectionAccessor: () => associationField?.collection,
|
|
549
|
+
parentAccessors: this.createParentItemAccessors(options.parentItemAccessor),
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
|
|
448
553
|
renderHiddenInConfig() {
|
|
449
554
|
return <FieldWithoutPermissionPlaceholder targetModel={this} />;
|
|
450
555
|
}
|
|
@@ -505,6 +610,14 @@ export class SubTableColumnModel<
|
|
|
505
610
|
).some(Boolean);
|
|
506
611
|
}
|
|
507
612
|
|
|
613
|
+
get hasFormValueDrivenDataScopeColumn() {
|
|
614
|
+
return (
|
|
615
|
+
this.parent?.mapSubModels('columns', (column: SubTableColumnModel) => {
|
|
616
|
+
return hasFormValueDrivenDataScopeField(column?.subModels?.field);
|
|
617
|
+
}) || []
|
|
618
|
+
).some(Boolean);
|
|
619
|
+
}
|
|
620
|
+
|
|
508
621
|
onInit(options: any): void {
|
|
509
622
|
super.onInit(options);
|
|
510
623
|
this.context.defineProperty('resourceName', {
|
|
@@ -516,6 +629,10 @@ export class SubTableColumnModel<
|
|
|
516
629
|
this.context.defineProperty('actionName', {
|
|
517
630
|
get: () => 'view',
|
|
518
631
|
});
|
|
632
|
+
this.context.defineProperty('item', {
|
|
633
|
+
get: () => undefined,
|
|
634
|
+
...this.createRowItemContextPropertyOptions(),
|
|
635
|
+
});
|
|
519
636
|
this.emitter.on('onSubModelAdded', (subModel: FieldModel) => {
|
|
520
637
|
if (this.collectionField) {
|
|
521
638
|
subModel.setProps(this.collectionField.getComponentProps());
|
|
@@ -673,25 +790,30 @@ export class SubTableColumnModel<
|
|
|
673
790
|
value: [...baseArr, `${associationKey}:${rowIndex}`],
|
|
674
791
|
});
|
|
675
792
|
}
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
793
|
+
const getRowRecord = () => {
|
|
794
|
+
const form = (fork.context as any)?.form || (this.context?.blockModel as any)?.context?.form;
|
|
795
|
+
return getLatestSubTableRowRecord(form, fork.context.fieldIndex, record);
|
|
796
|
+
};
|
|
797
|
+
const getParentItem = () => {
|
|
798
|
+
const parentItemCtx = (parentItem ?? (this.parent as any)?.context?.item) as ItemChain | undefined;
|
|
799
|
+
return parentItemCtx ?? createRootItemChain(this.getParentFormValues());
|
|
800
|
+
};
|
|
801
|
+
const getRowItem = createItemChainGetter({
|
|
802
|
+
valueAccessor: getRowRecord,
|
|
803
|
+
parentItemAccessor: getParentItem,
|
|
804
|
+
indexAccessor: () => (Number.isFinite(rowIndex) ? rowIndex : undefined),
|
|
805
|
+
lengthAccessor: () => {
|
|
683
806
|
const list = (this.parent as any)?.props?.value;
|
|
684
|
-
|
|
685
|
-
return {
|
|
686
|
-
index: Number.isFinite(rowIndex) ? rowIndex : undefined,
|
|
687
|
-
length,
|
|
688
|
-
__is_new__: isNew,
|
|
689
|
-
__is_stored__: isStored,
|
|
690
|
-
value: rowRecord,
|
|
691
|
-
parentItem: parentItemCtx,
|
|
692
|
-
};
|
|
807
|
+
return Array.isArray(list) ? list.length : undefined;
|
|
693
808
|
},
|
|
694
|
-
|
|
809
|
+
});
|
|
810
|
+
fork.context.defineProperty('item', {
|
|
811
|
+
get: getRowItem,
|
|
812
|
+
...this.createRowItemContextPropertyOptions({
|
|
813
|
+
resolverPropertiesAccessor: getRowRecord,
|
|
814
|
+
parentItemAccessor: getParentItem,
|
|
815
|
+
showParentIndex: baseArr.length > 0,
|
|
816
|
+
}),
|
|
695
817
|
});
|
|
696
818
|
return fork;
|
|
697
819
|
})();
|
|
@@ -707,7 +829,7 @@ export class SubTableColumnModel<
|
|
|
707
829
|
rowFork={rowFork}
|
|
708
830
|
memoKey={cellModeKey}
|
|
709
831
|
width={this.props.width}
|
|
710
|
-
commitOnChange={this.hasFormulaColumn}
|
|
832
|
+
commitOnChange={this.hasFormulaColumn || this.hasFormValueDrivenDataScopeColumn}
|
|
711
833
|
/>
|
|
712
834
|
);
|
|
713
835
|
};
|
|
@@ -70,6 +70,7 @@ export function SubTableField(props) {
|
|
|
70
70
|
getCurrentValue,
|
|
71
71
|
fieldPathArray,
|
|
72
72
|
formValuesChangeEmitter,
|
|
73
|
+
onResetFieldValue,
|
|
73
74
|
} = props;
|
|
74
75
|
const [currentPage, setCurrentPage] = useState(1);
|
|
75
76
|
const [currentPageSize, setCurrentPageSize] = useState(pageSize);
|
|
@@ -97,6 +98,17 @@ export function SubTableField(props) {
|
|
|
97
98
|
formValuesChangeEmitter.off('formValuesChange', listener);
|
|
98
99
|
};
|
|
99
100
|
}, [fieldPathArray, formValuesChangeEmitter]);
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (!formValuesChangeEmitter?.on || !formValuesChangeEmitter?.off || !onResetFieldValue) return;
|
|
103
|
+
const listener = () => {
|
|
104
|
+
onResetFieldValue();
|
|
105
|
+
forceRefresh((v) => v + 1);
|
|
106
|
+
};
|
|
107
|
+
formValuesChangeEmitter.on('onFieldReset', listener);
|
|
108
|
+
return () => {
|
|
109
|
+
formValuesChangeEmitter.off('onFieldReset', listener);
|
|
110
|
+
};
|
|
111
|
+
}, [formValuesChangeEmitter, onResetFieldValue]);
|
|
100
112
|
const applyValue = React.useCallback((nextValue: any) => onChange?.(normalizeSubTableRows(nextValue)), [onChange]);
|
|
101
113
|
const getLatestValue = React.useCallback(() => normalizeSubTableRows(getCurrentValue()), [getCurrentValue]);
|
|
102
114
|
useEffect(() => {
|
|
@@ -125,7 +137,7 @@ export function SubTableField(props) {
|
|
|
125
137
|
return t('Total {{count}} items', { count: total });
|
|
126
138
|
},
|
|
127
139
|
} as any;
|
|
128
|
-
}, [currentPage, currentPageSize, currentValue.length]);
|
|
140
|
+
}, [currentPage, currentPageSize, currentValue.length, t]);
|
|
129
141
|
|
|
130
142
|
// 新增一行
|
|
131
143
|
const handleAdd = () => {
|
|
@@ -15,18 +15,50 @@ import {
|
|
|
15
15
|
SubTableColumnModel,
|
|
16
16
|
getLatestSubTableRowRecord,
|
|
17
17
|
buildRowPathFromFieldIndex,
|
|
18
|
+
buildSubTableColumnNamePath,
|
|
18
19
|
isSubTableColumnConfiguredReadPretty,
|
|
19
20
|
getSubTableColumnTitleField,
|
|
20
21
|
getSubTableColumnReadPrettyFieldProps,
|
|
21
22
|
isSubTableColumnReadPretty,
|
|
22
23
|
} from '../SubTableColumnModel';
|
|
23
24
|
|
|
25
|
+
function createMockCollection(name: string, fields: any[] = []) {
|
|
26
|
+
const collection: any = {
|
|
27
|
+
name,
|
|
28
|
+
title: name,
|
|
29
|
+
dataSourceKey: 'main',
|
|
30
|
+
filterTargetKey: 'id',
|
|
31
|
+
fields,
|
|
32
|
+
getFields: () => collection.fields,
|
|
33
|
+
getField: (fieldName: string) => collection.fields.find((field) => field.name === fieldName),
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
collection.fields.forEach((field) => {
|
|
37
|
+
field.collection = field.collection ?? collection;
|
|
38
|
+
field.filterable = field.filterable ?? true;
|
|
39
|
+
field.isAssociationField = field.isAssociationField ?? (() => false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return collection;
|
|
43
|
+
}
|
|
44
|
+
|
|
24
45
|
describe('SubTableColumnModel row record helpers', () => {
|
|
25
46
|
it('builds the row path from fieldIndex entries', () => {
|
|
26
47
|
expect(buildRowPathFromFieldIndex(['roles:0'])).toEqual(['roles', 0]);
|
|
27
48
|
expect(buildRowPathFromFieldIndex(['users:1', 'roles:2'])).toEqual(['users', 1, 'roles', 2]);
|
|
28
49
|
});
|
|
29
50
|
|
|
51
|
+
it('builds absolute form item paths for nested sub-table columns', () => {
|
|
52
|
+
expect(buildSubTableColumnNamePath(['roles'], 0, 'name', ['roles:0'])).toEqual(['roles', 0, 'name']);
|
|
53
|
+
expect(buildSubTableColumnNamePath(['orders', 'lines'], 1, 'product', ['orders:0', 'lines:1'])).toEqual([
|
|
54
|
+
'orders',
|
|
55
|
+
0,
|
|
56
|
+
'lines',
|
|
57
|
+
1,
|
|
58
|
+
'product',
|
|
59
|
+
]);
|
|
60
|
+
});
|
|
61
|
+
|
|
30
62
|
it('prefers the latest row value from form over the fallback record', () => {
|
|
31
63
|
const form = {
|
|
32
64
|
getFieldValue: vi.fn((path: any) => {
|
|
@@ -51,6 +83,171 @@ describe('SubTableColumnModel row record helpers', () => {
|
|
|
51
83
|
expect(getLatestSubTableRowRecord(form, ['roles:0'], fallback)).toBe(fallback);
|
|
52
84
|
});
|
|
53
85
|
|
|
86
|
+
it('provides current item meta for sub-table column data scope variables', async () => {
|
|
87
|
+
const engine = new FlowEngine();
|
|
88
|
+
engine.registerModels({ SubTableColumnModel });
|
|
89
|
+
|
|
90
|
+
const tagsCollection = createMockCollection('tags', [
|
|
91
|
+
{ name: 'id', title: 'ID', type: 'integer', interface: 'integer' },
|
|
92
|
+
{ name: 'name', title: 'Name', type: 'string', interface: 'input' },
|
|
93
|
+
]);
|
|
94
|
+
const rolesCollection = createMockCollection('roles', [
|
|
95
|
+
{ name: 'id', title: 'ID', type: 'integer', interface: 'integer' },
|
|
96
|
+
{ name: 'name', title: 'Name', type: 'string', interface: 'input' },
|
|
97
|
+
{
|
|
98
|
+
name: 'tags',
|
|
99
|
+
title: 'Tags',
|
|
100
|
+
type: 'hasMany',
|
|
101
|
+
interface: 'o2m',
|
|
102
|
+
target: 'tags',
|
|
103
|
+
targetCollection: tagsCollection,
|
|
104
|
+
isAssociationField: () => true,
|
|
105
|
+
},
|
|
106
|
+
]);
|
|
107
|
+
const usersCollection = createMockCollection('users', [
|
|
108
|
+
{ name: 'id', title: 'ID', type: 'integer', interface: 'integer' },
|
|
109
|
+
{ name: 'nickname', title: 'Nickname', type: 'string', interface: 'input' },
|
|
110
|
+
]);
|
|
111
|
+
const rolesField = {
|
|
112
|
+
name: 'roles',
|
|
113
|
+
title: 'Roles',
|
|
114
|
+
type: 'hasMany',
|
|
115
|
+
interface: 'o2m',
|
|
116
|
+
collection: usersCollection,
|
|
117
|
+
target: 'roles',
|
|
118
|
+
targetCollection: rolesCollection,
|
|
119
|
+
isAssociationField: () => true,
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const form = {
|
|
123
|
+
getFieldValue: vi.fn((path: any) => {
|
|
124
|
+
if (JSON.stringify(path) === JSON.stringify(['roles', 0])) {
|
|
125
|
+
return { id: 11, name: 'Admin', tags: [{ id: 7 }] };
|
|
126
|
+
}
|
|
127
|
+
}),
|
|
128
|
+
};
|
|
129
|
+
const blockModel: any = engine.createModel({ use: 'FlowModel', uid: 'form-block', structure: {} as any });
|
|
130
|
+
blockModel.context.defineProperty('form', { value: form });
|
|
131
|
+
blockModel.context.defineProperty('formValues', {
|
|
132
|
+
value: {
|
|
133
|
+
id: 1,
|
|
134
|
+
nickname: 'Alice',
|
|
135
|
+
roles: [{ id: 11, name: 'Admin' }],
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const subTableFieldModel: any = engine.createModel({
|
|
140
|
+
use: 'FlowModel',
|
|
141
|
+
uid: 'users.roles',
|
|
142
|
+
structure: {} as any,
|
|
143
|
+
});
|
|
144
|
+
subTableFieldModel.collection = rolesCollection;
|
|
145
|
+
subTableFieldModel.setProps({ value: [{ id: 11, name: 'Admin' }] });
|
|
146
|
+
subTableFieldModel.context.defineProperty('collectionField', { value: rolesField });
|
|
147
|
+
subTableFieldModel.context.defineProperty('fieldPath', { value: 'roles' });
|
|
148
|
+
subTableFieldModel.context.defineProperty('blockModel', { value: blockModel });
|
|
149
|
+
|
|
150
|
+
const column: any = engine.createModel({
|
|
151
|
+
use: 'SubTableColumnModel',
|
|
152
|
+
uid: 'users.roles.tags',
|
|
153
|
+
stepParams: {
|
|
154
|
+
fieldSettings: {
|
|
155
|
+
init: {
|
|
156
|
+
fieldPath: 'roles.tags',
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
structure: {} as any,
|
|
161
|
+
});
|
|
162
|
+
column.setParent(subTableFieldModel);
|
|
163
|
+
column.context.defineProperty('blockModel', { value: blockModel });
|
|
164
|
+
column.subModels = { field: [] };
|
|
165
|
+
|
|
166
|
+
const designTimeItemOptions = column.context.getPropertyOptions('item');
|
|
167
|
+
expect(typeof designTimeItemOptions.meta).toBe('function');
|
|
168
|
+
const designTimeMeta = await designTimeItemOptions.meta();
|
|
169
|
+
expect(designTimeMeta.properties.value.title).toBe('Attributes');
|
|
170
|
+
expect(designTimeMeta.properties.parentItem.properties.value.title).toBe('Attributes');
|
|
171
|
+
|
|
172
|
+
const renderCell = column.renderItem();
|
|
173
|
+
renderCell({
|
|
174
|
+
id: 'cell-roles-tags-0',
|
|
175
|
+
rowIdx: 0,
|
|
176
|
+
record: { id: 11, name: 'Stale admin', tags: [{ id: 7 }] },
|
|
177
|
+
parentItem: {
|
|
178
|
+
value: { id: 1, nickname: 'Alice' },
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const [rowFork] = Array.from(column.forks ?? []) as any[];
|
|
183
|
+
const rowItemOptions = rowFork.context.getPropertyOptions('item');
|
|
184
|
+
expect(typeof rowItemOptions.meta).toBe('function');
|
|
185
|
+
expect(rowItemOptions.resolveOnServer('value.tags.name')).toBe(true);
|
|
186
|
+
|
|
187
|
+
const rowMeta = await rowItemOptions.meta();
|
|
188
|
+
expect(rowMeta.properties.value.title).toBe('Attributes');
|
|
189
|
+
expect(rowMeta.properties.parentItem.properties.value.title).toBe('Attributes');
|
|
190
|
+
expect(rowFork.context.item).toMatchObject({
|
|
191
|
+
index: 0,
|
|
192
|
+
length: 1,
|
|
193
|
+
value: { id: 11, name: 'Admin', tags: [{ id: 7 }] },
|
|
194
|
+
parentItem: {
|
|
195
|
+
value: { id: 1, nickname: 'Alice' },
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('detects variable data scope columns for immediate row commits', () => {
|
|
201
|
+
const engine = new FlowEngine();
|
|
202
|
+
engine.registerModels({ SubTableColumnModel });
|
|
203
|
+
|
|
204
|
+
const inputColumn: any = engine.createModel({
|
|
205
|
+
use: 'SubTableColumnModel',
|
|
206
|
+
uid: 'users.roles.name',
|
|
207
|
+
structure: {} as any,
|
|
208
|
+
});
|
|
209
|
+
const scopedColumn: any = engine.createModel({
|
|
210
|
+
use: 'SubTableColumnModel',
|
|
211
|
+
uid: 'users.roles.tags',
|
|
212
|
+
structure: {} as any,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
inputColumn.subModels = {
|
|
216
|
+
field: {
|
|
217
|
+
getStepParams: vi.fn(() => undefined),
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
scopedColumn.subModels = {
|
|
221
|
+
field: {
|
|
222
|
+
getStepParams: vi.fn((flowKey: string, stepKey: string) => {
|
|
223
|
+
if (flowKey !== 'selectSettings' || stepKey !== 'dataScope') {
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
filter: {
|
|
228
|
+
logic: '$and',
|
|
229
|
+
items: [{ path: 'name', operator: '$eq', value: '{{ ctx.item.value.name }}' }],
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
}),
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const parent: any = engine.createModel({
|
|
237
|
+
use: 'FlowModel',
|
|
238
|
+
uid: 'users.roles-table',
|
|
239
|
+
structure: {} as any,
|
|
240
|
+
});
|
|
241
|
+
parent.mapSubModels = vi.fn((key: string, iterator: (column: SubTableColumnModel) => unknown) => {
|
|
242
|
+
if (key !== 'columns') return [];
|
|
243
|
+
return [inputColumn, scopedColumn].map(iterator);
|
|
244
|
+
});
|
|
245
|
+
inputColumn.setParent(parent);
|
|
246
|
+
scopedColumn.setParent(parent);
|
|
247
|
+
|
|
248
|
+
expect(inputColumn.hasFormValueDrivenDataScopeColumn).toBe(true);
|
|
249
|
+
});
|
|
250
|
+
|
|
54
251
|
it('treats a display-only column pattern as read-pretty mode', () => {
|
|
55
252
|
expect(isSubTableColumnReadPretty({ props: { pattern: 'readPretty' } })).toBe(true);
|
|
56
253
|
expect(isSubTableColumnReadPretty({ props: { readPretty: true } })).toBe(true);
|
|
@@ -0,0 +1,180 @@
|
|
|
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 { act, render } from '@nocobase/test/client';
|
|
12
|
+
import { FlowEngine, type FlowModel } from '@nocobase/flow-engine';
|
|
13
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
14
|
+
import { PopupSubTableFieldModel } from '../../PopupSubTableFieldModel';
|
|
15
|
+
import { SubTableFieldModel } from '..';
|
|
16
|
+
|
|
17
|
+
vi.mock('react-i18next', async (importOriginal) => ({
|
|
18
|
+
...(await importOriginal<any>()),
|
|
19
|
+
useTranslation: () => ({
|
|
20
|
+
t: (value: string) => value,
|
|
21
|
+
}),
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
vi.mock('antd', async () => {
|
|
25
|
+
const actual = await vi.importActual<any>('antd');
|
|
26
|
+
return {
|
|
27
|
+
...actual,
|
|
28
|
+
Table: ({ dataSource = [], columns = [] }: any) =>
|
|
29
|
+
React.createElement(
|
|
30
|
+
'div',
|
|
31
|
+
{ 'data-testid': 'subtable' },
|
|
32
|
+
dataSource.map((record: any, rowIdx: number) =>
|
|
33
|
+
React.createElement(
|
|
34
|
+
'div',
|
|
35
|
+
{ 'data-testid': `row-${rowIdx}`, key: record.__index__ || rowIdx },
|
|
36
|
+
columns.map((column: any) =>
|
|
37
|
+
React.createElement(
|
|
38
|
+
'div',
|
|
39
|
+
{ 'data-testid': `cell-${rowIdx}-${String(column.dataIndex || column.key)}`, key: column.key },
|
|
40
|
+
column.render?.(record[column.dataIndex], record, rowIdx),
|
|
41
|
+
),
|
|
42
|
+
),
|
|
43
|
+
),
|
|
44
|
+
),
|
|
45
|
+
),
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
function createSubTableFieldModel(props: Record<string, unknown> = {}) {
|
|
50
|
+
const engine = new FlowEngine();
|
|
51
|
+
engine.registerModels({ SubTableFieldModel });
|
|
52
|
+
const setFieldValue = vi.fn();
|
|
53
|
+
|
|
54
|
+
const blockModel = engine.createModel<FlowModel>({
|
|
55
|
+
use: 'FlowModel',
|
|
56
|
+
uid: 'form-block',
|
|
57
|
+
});
|
|
58
|
+
(blockModel as FlowModel & { setFieldValue: typeof setFieldValue }).setFieldValue = setFieldValue;
|
|
59
|
+
const parent = engine.createModel<FlowModel>({
|
|
60
|
+
use: 'FlowModel',
|
|
61
|
+
uid: 'form-item',
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
parent.context.defineProperty('blockModel', { value: blockModel });
|
|
65
|
+
parent.context.defineProperty('collectionField', {
|
|
66
|
+
value: {
|
|
67
|
+
target: 'roles',
|
|
68
|
+
targetCollection: {
|
|
69
|
+
filterTargetKey: 'id',
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
parent.context.defineProperty('fieldPathArray', { value: ['roles'] });
|
|
74
|
+
|
|
75
|
+
const model = engine.createModel<SubTableFieldModel>({
|
|
76
|
+
use: 'SubTableFieldModel',
|
|
77
|
+
uid: 'roles-subtable',
|
|
78
|
+
parentId: parent.uid,
|
|
79
|
+
props,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return { blockModel, model, setFieldValue };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function createPopupSubTableFieldModel(props: Record<string, unknown> = {}) {
|
|
86
|
+
const engine = new FlowEngine();
|
|
87
|
+
engine.registerModels({ PopupSubTableFieldModel });
|
|
88
|
+
const setFieldValue = vi.fn();
|
|
89
|
+
|
|
90
|
+
const blockModel = engine.createModel<FlowModel>({
|
|
91
|
+
use: 'FlowModel',
|
|
92
|
+
uid: 'popup-form-block',
|
|
93
|
+
});
|
|
94
|
+
(blockModel as FlowModel & { setFieldValue: typeof setFieldValue }).setFieldValue = setFieldValue;
|
|
95
|
+
const parent = engine.createModel<FlowModel>({
|
|
96
|
+
use: 'FlowModel',
|
|
97
|
+
uid: 'popup-form-item',
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
parent.context.defineProperty('blockModel', { value: blockModel });
|
|
101
|
+
parent.context.defineProperty('collectionField', {
|
|
102
|
+
value: {
|
|
103
|
+
target: 'roles',
|
|
104
|
+
targetCollection: {
|
|
105
|
+
filterTargetKey: 'id',
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
parent.context.defineProperty('fieldPathArray', { value: ['roles'] });
|
|
110
|
+
|
|
111
|
+
const model = engine.createModel<PopupSubTableFieldModel>({
|
|
112
|
+
use: 'PopupSubTableFieldModel',
|
|
113
|
+
uid: 'roles-popup-subtable',
|
|
114
|
+
parentId: parent.uid,
|
|
115
|
+
props,
|
|
116
|
+
});
|
|
117
|
+
(model.subModels as Record<string, unknown>).subTableColumns = [];
|
|
118
|
+
await model.onDispatchEventStart('beforeRender');
|
|
119
|
+
|
|
120
|
+
return { blockModel, model, setFieldValue };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
describe('SubTable field reset', () => {
|
|
124
|
+
it('resets the rendered fork through its own form field path', () => {
|
|
125
|
+
const onChange = vi.fn();
|
|
126
|
+
const { blockModel, model, setFieldValue } = createSubTableFieldModel({
|
|
127
|
+
onChange,
|
|
128
|
+
value: [{ id: 1 }],
|
|
129
|
+
});
|
|
130
|
+
const fork = model.createFork({ value: [{ id: 1 }] }, 'orders.0.lines');
|
|
131
|
+
fork.context.defineProperty('fieldPathArray', { value: ['orders', 0, 'lines'] });
|
|
132
|
+
|
|
133
|
+
render(React.createElement(React.Fragment, null, fork.render()));
|
|
134
|
+
|
|
135
|
+
act(() => {
|
|
136
|
+
blockModel.emitter.emit('onFieldReset');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
expect(fork.props.value).toEqual([]);
|
|
140
|
+
expect(setFieldValue).toHaveBeenCalledWith(['orders', 0, 'lines'], []);
|
|
141
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('resets the non-forked model through the parent form field path', () => {
|
|
145
|
+
const onChange = vi.fn();
|
|
146
|
+
const { blockModel, model, setFieldValue } = createSubTableFieldModel({
|
|
147
|
+
onChange,
|
|
148
|
+
value: [{ id: 1 }],
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
render(React.createElement(React.Fragment, null, model.render()));
|
|
152
|
+
|
|
153
|
+
act(() => {
|
|
154
|
+
blockModel.emitter.emit('onFieldReset');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
expect(model.props.value).toEqual([]);
|
|
158
|
+
expect(setFieldValue).toHaveBeenCalledWith(['roles'], []);
|
|
159
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('resets popup sub-table form values without relying on onChange', async () => {
|
|
163
|
+
const onChange = vi.fn();
|
|
164
|
+
const { blockModel, model, setFieldValue } = await createPopupSubTableFieldModel({
|
|
165
|
+
onChange,
|
|
166
|
+
pageSize: 10,
|
|
167
|
+
value: [{ id: 1 }],
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
render(React.createElement(React.Fragment, null, model.render()));
|
|
171
|
+
|
|
172
|
+
act(() => {
|
|
173
|
+
blockModel.emitter.emit('onFieldReset');
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
expect(model.props.value).toEqual([]);
|
|
177
|
+
expect(setFieldValue).toHaveBeenCalledWith(['roles'], []);
|
|
178
|
+
expect(onChange).not.toHaveBeenCalled();
|
|
179
|
+
});
|
|
180
|
+
});
|