@nocobase/client-v2 2.2.0-beta.9 → 2.2.1
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/APIClient.d.ts +13 -3
- package/es/BaseApplication.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/category-tabs/SortableCategoryTabs.d.ts +55 -0
- package/es/components/category-tabs/index.d.ts +9 -0
- package/es/components/form/ScanInput/useCodeScanner.d.ts +3 -2
- package/es/components/form/TypedVariableInput.d.ts +31 -5
- package/es/components/form/filter/CollectionFilter.d.ts +2 -0
- package/es/components/form/filter/CollectionFilterItem.d.ts +11 -1
- package/es/components/form/filter/CollectionFilterPanel.d.ts +2 -0
- package/es/components/form/filter/index.d.ts +2 -0
- package/es/components/form/filter/useFilterActionProps.d.ts +2 -0
- package/es/components/index.d.ts +1 -0
- package/es/flow/actions/index.d.ts +1 -1
- package/es/flow/actions/linkageRules.d.ts +27 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +1 -0
- package/es/flow/components/FieldAssignExactDatePicker.d.ts +1 -0
- package/es/flow/components/FieldAssignValueInput.d.ts +7 -24
- package/es/flow/components/FlowRoute.d.ts +2 -2
- package/es/flow/components/RunJSValueEditor.d.ts +1 -0
- package/es/flow/components/field-value-variable/DateVariableEditor.d.ts +24 -0
- package/es/flow/components/field-value-variable/FieldValueVariableInput.d.ts +31 -0
- package/es/flow/components/field-value-variable/dateValue.d.ts +36 -0
- package/es/flow/components/field-value-variable/index.d.ts +11 -0
- package/es/flow/components/filter/FilterGroup.d.ts +1 -0
- package/es/flow/components/filter/VariableFilterItem.d.ts +6 -1
- package/es/flow/components/placeholders/BlockPlaceholder.d.ts +1 -0
- package/es/flow/index.d.ts +1 -0
- package/es/flow/internal/registerDeviceTypeContext.d.ts +10 -0
- package/es/flow/internal/utils/operatorSchemaHelper.d.ts +2 -2
- package/es/flow/models/actions/UpdateRecordActionUtils.d.ts +8 -2
- package/es/flow/models/base/ActionModelCore.d.ts +4 -2
- package/es/flow/models/base/BlockGridModel.d.ts +3 -0
- package/es/flow/models/base/CollectionBlockModel.d.ts +3 -0
- package/es/flow/models/base/PageModel/PageModel.d.ts +12 -0
- package/es/flow/models/base/PageModel/PageModelTabBar.d.ts +22 -0
- package/es/flow/models/base/PageModel/PageTabModel.d.ts +12 -0
- package/es/flow/models/blocks/details/DetailsBlockModel.d.ts +3 -0
- package/es/flow/models/blocks/filter-form/FilterFormGridModel.d.ts +1 -0
- package/es/flow/models/blocks/filter-form/FilterFormSubmitActionModel.d.ts +3 -1
- package/es/flow/models/blocks/form/FormBlockModel.d.ts +7 -0
- package/es/flow/models/blocks/form/FormGridModel.d.ts +6 -0
- package/es/flow/models/blocks/form/QuickEditFormModel.d.ts +17 -2
- package/es/flow/models/blocks/form/value-runtime/rules.d.ts +1 -0
- package/es/flow/models/blocks/table/JSColumnModel.d.ts +2 -0
- package/es/flow/models/blocks/table/TableBlockModel.d.ts +1 -0
- package/es/flow/models/blocks/table/TableColumnModel.d.ts +12 -0
- package/es/flow/models/blocks/table/utils.d.ts +1 -0
- package/es/flow/models/fields/AssociationFieldModel/RecordPickerFieldModel.d.ts +2 -1
- package/es/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.d.ts +0 -17
- package/es/flow/models/fields/AssociationFieldModel/recordSelectShared.d.ts +41 -0
- package/es/flow/models/fields/DateTimeFieldModel/dateLimit.d.ts +15 -7
- package/es/flow-compat/fieldValidationConstants.d.ts +1 -1
- package/es/flow-compat/passwordUtils.d.ts +1 -1
- package/es/flow-compat/routeTypes.d.ts +1 -0
- package/es/index.d.ts +5 -0
- package/es/index.mjs +199 -161
- package/es/ui-operation/index.d.ts +15 -0
- package/es/ui-operation/ui-operation-codec.d.ts +10 -0
- package/es/utils/getRouteRuntimeVersion.d.ts +23 -0
- package/es/utils/index.d.ts +1 -0
- package/lib/index.js +215 -177
- package/lib/locale/languageCodes.js +3 -1
- package/package.json +9 -7
- package/src/APIClient.ts +90 -7
- package/src/Application.tsx +7 -1
- package/src/BaseApplication.tsx +11 -6
- package/src/__tests__/APIClient.test.tsx +58 -0
- package/src/__tests__/app.test.tsx +73 -0
- package/src/__tests__/browserChecker.test.ts +61 -0
- package/src/__tests__/getRouteRuntimeVersion.test.ts +68 -0
- package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +35 -2
- package/src/__tests__/settings-center.test.tsx +270 -6
- package/src/acl/ACLProvider.tsx +2 -0
- package/src/acl/__tests__/ACLProvider.test.tsx +92 -0
- package/src/collection-field-interface/CollectionFieldInterface.ts +1 -0
- package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +1 -0
- package/src/collection-manager/field-validation.ts +1 -1
- package/src/components/README.md +7 -1
- package/src/components/README.zh-CN.md +6 -1
- package/src/components/category-tabs/SortableCategoryTabs.tsx +210 -0
- package/src/components/category-tabs/index.ts +10 -0
- package/src/components/form/JsonTextArea.tsx +4 -0
- package/src/components/form/ScanInput/CodeScanner.tsx +79 -35
- package/src/components/form/ScanInput/__tests__/CodeScanner.test.tsx +101 -0
- package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +284 -11
- package/src/components/form/ScanInput/useCodeScanner.ts +332 -24
- package/src/components/form/TypedVariableInput.tsx +452 -101
- package/src/components/form/__tests__/TypedVariableInput.test.tsx +364 -12
- package/src/components/form/filter/CollectionFilter.tsx +4 -0
- package/src/components/form/filter/CollectionFilterItem.tsx +36 -9
- package/src/components/form/filter/CollectionFilterPanel.tsx +4 -0
- package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +55 -0
- package/src/components/form/filter/__tests__/useFilterActionProps.test.tsx +95 -0
- package/src/components/form/filter/index.ts +2 -0
- package/src/components/form/filter/useFilterActionProps.ts +37 -6
- package/src/components/form/table/Table.tsx +81 -6
- package/src/components/form/table/__tests__/Table.columnWidth.test.tsx +433 -0
- package/src/components/index.ts +1 -0
- package/src/flow/FlowPage.tsx +9 -1
- package/src/flow/__tests__/FlowPage.test.tsx +50 -3
- package/src/flow/__tests__/FlowRoute.test.tsx +128 -10
- package/src/flow/__tests__/PluginFlowEngine.test.ts +58 -0
- package/src/flow/actions/__tests__/actionLinkageRules.forkProps.test.ts +314 -0
- package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +102 -0
- package/src/flow/actions/__tests__/linkageRules.actionStates.test.ts +33 -0
- package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +72 -0
- package/src/flow/actions/__tests__/linkageRules.tab.test.ts +171 -0
- package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +7 -3
- package/src/flow/actions/index.ts +2 -0
- package/src/flow/actions/linkageRules.tsx +144 -26
- package/src/flow/actions/linkageRulesRefresh.tsx +2 -6
- package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +0 -1
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +78 -59
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +253 -6
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +228 -0
- package/src/flow/common/Markdown/style.ts +4 -0
- package/src/flow/components/BlockItemCard.tsx +2 -2
- package/src/flow/components/FieldAssignExactDatePicker.tsx +25 -11
- package/src/flow/components/FieldAssignValueInput.tsx +72 -584
- package/src/flow/components/FlowRoute.tsx +78 -32
- package/src/flow/components/RunJSValueEditor.tsx +9 -1
- package/src/flow/components/__tests__/FieldAssignValueInput.context.test.tsx +216 -0
- package/src/flow/components/field-value-variable/DateVariableEditor.tsx +181 -0
- package/src/flow/components/field-value-variable/FieldValueVariableInput.tsx +326 -0
- package/src/flow/components/field-value-variable/__tests__/FieldValueVariableInput.test.tsx +380 -0
- package/src/flow/components/field-value-variable/dateValue.ts +223 -0
- package/src/flow/components/field-value-variable/index.ts +12 -0
- package/src/flow/components/filter/FilterGroup.tsx +33 -5
- package/src/flow/components/filter/VariableFilterItem.tsx +170 -13
- package/src/flow/components/filter/__tests__/FilterGroup.test.tsx +45 -0
- package/src/flow/components/filter/__tests__/VariableFilterItem.leftMetaTree.test.tsx +9 -0
- package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +195 -1
- package/src/flow/components/placeholders/BlockPlaceholder.tsx +70 -23
- package/src/flow/components/placeholders/__tests__/BlockPlaceholder.test.tsx +57 -7
- package/src/flow/index.ts +3 -0
- package/src/flow/internal/registerDeviceTypeContext.ts +39 -0
- package/src/flow/internal/utils/operatorSchemaHelper.ts +3 -2
- package/src/flow/models/actions/UpdateRecordActionModel.tsx +18 -1
- package/src/flow/models/actions/UpdateRecordActionUtils.ts +18 -6
- package/src/flow/models/actions/__tests__/UpdateRecordActionModel.test.ts +76 -4
- package/src/flow/models/base/ActionModelCore.tsx +11 -2
- package/src/flow/models/base/BlockGridModel.tsx +26 -0
- package/src/flow/models/base/CollectionBlockModel.tsx +38 -3
- package/src/flow/models/base/PageModel/PageModel.tsx +387 -53
- package/src/flow/models/base/PageModel/PageModelTabBar.tsx +114 -0
- package/src/flow/models/base/PageModel/PageTabModel.tsx +249 -18
- package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +790 -10
- package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.module-isolation.test.tsx +130 -0
- package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.test.tsx +118 -0
- package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +1075 -7
- package/src/flow/models/base/__tests__/ActionModelCore.render.test.tsx +49 -0
- package/src/flow/models/base/__tests__/BlockGridModel.selectSceneActivation.test.ts +124 -0
- package/src/flow/models/base/__tests__/CollectionBlockModel.addAppends.test.ts +41 -0
- package/src/flow/models/base/__tests__/CollectionBlockModel.initialBeforeRenderRefresh.test.ts +125 -9
- package/src/flow/models/blocks/assign-form/AssignFormItemModel.tsx +28 -53
- package/src/flow/models/blocks/details/DetailsBlockModel.tsx +8 -3
- package/src/flow/models/blocks/details/__tests__/DetailsBlockModel.initialPaginationChangeRefresh.test.ts +32 -1
- package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +28 -3
- package/src/flow/models/blocks/filter-form/FilterFormGridModel.tsx +36 -5
- package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +155 -25
- package/src/flow/models/blocks/filter-form/FilterFormSubmitActionModel.tsx +57 -4
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormGridModel.onModelCreated.test.ts +174 -1
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +395 -1
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.getFilterValue.test.ts +72 -0
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormSubmitActionModel.lifecycle.test.ts +90 -0
- package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +66 -4
- package/src/flow/models/blocks/filter-manager/FilterManager.ts +5 -0
- package/src/flow/models/blocks/filter-manager/__tests__/FilterManager.test.ts +40 -0
- package/src/flow/models/blocks/form/FormBlockModel.tsx +47 -0
- package/src/flow/models/blocks/form/FormGridModel.tsx +4 -0
- package/src/flow/models/blocks/form/QuickEditFormModel.tsx +189 -36
- package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +8 -2
- package/src/flow/models/blocks/form/__tests__/QuickEditFormModel.quickEdit.test.ts +350 -2
- package/src/flow/models/blocks/form/__tests__/runJsFormSubmit.test.ts +131 -0
- package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +435 -0
- package/src/flow/models/blocks/form/value-runtime/rules.ts +67 -14
- package/src/flow/models/blocks/form/value-runtime/runtime.ts +43 -19
- package/src/flow/models/blocks/js-block/JSBlock.tsx +1 -1
- package/src/flow/models/blocks/table/JSColumnModel.tsx +10 -1
- package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +2 -1
- package/src/flow/models/blocks/table/TableBlockModel.tsx +29 -3
- package/src/flow/models/blocks/table/TableColumnModel.tsx +48 -9
- package/src/flow/models/blocks/table/__tests__/JSColumnModel.test.tsx +52 -5
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.filterReset.test.ts +78 -0
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.quickEditRefresh.test.ts +12 -0
- package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +33 -0
- package/src/flow/models/blocks/table/utils.ts +7 -0
- package/src/flow/models/fields/AssociationFieldModel/CascadeSelectFieldModel.tsx +33 -1
- package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +10 -1
- package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/__tests__/popupContext.test.ts +120 -0
- package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/actions/PopupSubTableEditActionModel.tsx +10 -2
- package/src/flow/models/fields/AssociationFieldModel/RecordPickerFieldModel.tsx +24 -1
- package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +9 -136
- package/src/flow/models/fields/AssociationFieldModel/__tests__/RecordPickerFieldModel.itemContext.test.ts +167 -3
- package/src/flow/models/fields/AssociationFieldModel/recordSelectShared.tsx +156 -0
- package/src/flow/models/fields/DateTimeFieldModel/__tests__/DateTimeNoTzFieldModel.dateLimit.test.tsx +149 -0
- package/src/flow/models/fields/DateTimeFieldModel/dateLimit.ts +149 -113
- package/src/flow/models/fields/DisplayNumberFieldModel.tsx +1 -1
- package/src/flow/models/fields/JsonFieldModel.tsx +31 -8
- package/src/flow/models/fields/NumberFieldModel.tsx +1 -1
- package/src/flow/models/fields/VariableFieldFormModel.tsx +3 -3
- package/src/flow/models/fields/__tests__/DisplayNumberFieldModel.test.ts +33 -0
- package/src/flow/models/fields/__tests__/JsonFieldModel.test.ts +82 -0
- package/src/flow/models/fields/__tests__/NumberFieldModel.test.tsx +47 -0
- package/src/flow/models/fields/__tests__/VariableFieldFormModel.test.tsx +51 -0
- package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +26 -10
- package/src/flow/models/fields/mobile-components/MobileSelect.tsx +51 -14
- package/src/flow/models/fields/mobile-components/__tests__/MobileSelect.test.tsx +139 -13
- package/src/flow/utils/dataScopeFormValueClear.ts +4 -3
- package/src/flow-compat/fieldValidationConstants.ts +1 -1
- package/src/flow-compat/routeTypes.ts +1 -0
- package/src/index.ts +5 -0
- package/src/layout-manager/LayoutContentRoute.tsx +2 -1
- package/src/layout-manager/LayoutRoute.tsx +2 -1
- package/src/layout-manager/__tests__/LayoutRoute.test.tsx +87 -1
- package/src/locale/languageCodes.ts +3 -1
- package/src/nocobase-buildin-plugin/index.tsx +12 -0
- package/src/settings-center/AdminSettingsLayout.tsx +23 -2
- package/src/ui-operation/index.ts +33 -0
- package/src/ui-operation/ui-operation-codec.ts +54 -0
- package/src/utils/getRouteRuntimeVersion.ts +185 -0
- package/src/utils/index.tsx +1 -0
package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.getFilterValue.test.ts
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
import { describe, expect, it, vi } from 'vitest';
|
|
11
11
|
import '../../../../index';
|
|
12
|
+
import { FlowEngine } from '@nocobase/flow-engine';
|
|
13
|
+
import { CascadeSelectFieldModel } from '../../../fields/AssociationFieldModel/CascadeSelectFieldModel';
|
|
12
14
|
import { FilterFormItemModel } from '../FilterFormItemModel';
|
|
13
15
|
|
|
14
16
|
describe('FilterFormItemModel getFilterValue', () => {
|
|
@@ -174,4 +176,74 @@ describe('FilterFormItemModel getFilterValue', () => {
|
|
|
174
176
|
const value = FilterFormItemModel.prototype.getFilterValue.call(model as any);
|
|
175
177
|
expect(value).toBe(25);
|
|
176
178
|
});
|
|
179
|
+
|
|
180
|
+
it('uses the tree Cascader leaf target key with the equality operator', () => {
|
|
181
|
+
const engine = new FlowEngine();
|
|
182
|
+
engine.registerModels({ CascadeSelectFieldModel });
|
|
183
|
+
|
|
184
|
+
const ds = engine.dataSourceManager.getDataSource('main');
|
|
185
|
+
ds?.addCollection({
|
|
186
|
+
name: 'organizations',
|
|
187
|
+
template: 'tree',
|
|
188
|
+
filterTargetKey: 'code',
|
|
189
|
+
fields: [
|
|
190
|
+
{ name: 'code', type: 'string', interface: 'input', filterable: { operators: [] } },
|
|
191
|
+
{ name: 'name', type: 'string', interface: 'input', filterable: { operators: [] } },
|
|
192
|
+
],
|
|
193
|
+
});
|
|
194
|
+
ds?.addCollection({
|
|
195
|
+
name: 'users',
|
|
196
|
+
fields: [
|
|
197
|
+
{
|
|
198
|
+
name: 'organization',
|
|
199
|
+
type: 'belongsTo',
|
|
200
|
+
interface: 'm2o',
|
|
201
|
+
target: 'organizations',
|
|
202
|
+
targetKey: 'code',
|
|
203
|
+
filterable: { operators: [] },
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const fieldModel = engine.createModel<CascadeSelectFieldModel>({
|
|
209
|
+
uid: 'tree-cascader-filter-field',
|
|
210
|
+
use: 'CascadeSelectFieldModel',
|
|
211
|
+
stepParams: {
|
|
212
|
+
fieldSettings: {
|
|
213
|
+
init: {
|
|
214
|
+
dataSourceKey: 'main',
|
|
215
|
+
collectionName: 'users',
|
|
216
|
+
fieldPath: 'organization',
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
props: {
|
|
221
|
+
fieldNames: { label: 'name', value: 'code' },
|
|
222
|
+
value: {
|
|
223
|
+
code: 'leaf',
|
|
224
|
+
name: 'Leaf organization',
|
|
225
|
+
parent: { code: 'root', name: 'Root organization', parent: null },
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
const model = {
|
|
230
|
+
mounted: true,
|
|
231
|
+
props: { name: 'organization' },
|
|
232
|
+
subModels: { field: fieldModel },
|
|
233
|
+
context: {
|
|
234
|
+
form: {
|
|
235
|
+
getFieldValue: vi.fn(() => fieldModel.props.value),
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
getDefaultValue: vi.fn(() => undefined),
|
|
239
|
+
getStepParams: vi.fn(() => undefined),
|
|
240
|
+
getCurrentOperatorMeta: vi.fn(() => null),
|
|
241
|
+
normalizeAssociationFilterValue: FilterFormItemModel.prototype.normalizeAssociationFilterValue,
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const value = FilterFormItemModel.prototype.getFilterValue.call(model as unknown as FilterFormItemModel);
|
|
245
|
+
|
|
246
|
+
expect(fieldModel.operator).toBe('$eq');
|
|
247
|
+
expect(value).toBe('leaf');
|
|
248
|
+
});
|
|
177
249
|
});
|
package/src/flow/models/blocks/filter-form/__tests__/FilterFormSubmitActionModel.lifecycle.test.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
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 { define, observable } from '@formily/reactive';
|
|
11
|
+
import { describe, expect, it } from 'vitest';
|
|
12
|
+
import { FilterFormSubmitActionModel } from '../FilterFormSubmitActionModel';
|
|
13
|
+
|
|
14
|
+
describe('FilterFormSubmitActionModel lifecycle', () => {
|
|
15
|
+
const createAction = (blockModel: { autoTriggerFilter: boolean }, hidden = false, lifecycleAction?: object) => {
|
|
16
|
+
const action = Object.create(FilterFormSubmitActionModel.prototype) as FilterFormSubmitActionModel;
|
|
17
|
+
|
|
18
|
+
Object.defineProperty(action, 'props', {
|
|
19
|
+
value: {},
|
|
20
|
+
});
|
|
21
|
+
Object.defineProperty(action, 'context', {
|
|
22
|
+
value: { blockModel, model: lifecycleAction || action, defineProperty: () => undefined },
|
|
23
|
+
});
|
|
24
|
+
action.hidden = hidden;
|
|
25
|
+
define(action, {
|
|
26
|
+
hidden: observable,
|
|
27
|
+
});
|
|
28
|
+
action.onInit({});
|
|
29
|
+
|
|
30
|
+
return action;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
it('keeps automatic filtering disabled when the submit action remounts', () => {
|
|
34
|
+
const blockModel = { autoTriggerFilter: true };
|
|
35
|
+
const action = createAction(blockModel);
|
|
36
|
+
|
|
37
|
+
action.onMount();
|
|
38
|
+
expect(blockModel.autoTriggerFilter).toBe(false);
|
|
39
|
+
|
|
40
|
+
action.onUnmount();
|
|
41
|
+
expect(blockModel.autoTriggerFilter).toBe(true);
|
|
42
|
+
|
|
43
|
+
action.onMount();
|
|
44
|
+
expect(blockModel.autoTriggerFilter).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('keeps automatic filtering disabled until every mounted submit action unmounts', () => {
|
|
48
|
+
const blockModel = { autoTriggerFilter: true };
|
|
49
|
+
const firstAction = createAction(blockModel);
|
|
50
|
+
const secondAction = createAction(blockModel);
|
|
51
|
+
|
|
52
|
+
firstAction.onMount();
|
|
53
|
+
secondAction.onMount();
|
|
54
|
+
firstAction.onUnmount();
|
|
55
|
+
expect(blockModel.autoTriggerFilter).toBe(false);
|
|
56
|
+
|
|
57
|
+
secondAction.onUnmount();
|
|
58
|
+
expect(blockModel.autoTriggerFilter).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('only disables automatic filtering while a visible submit action is mounted', () => {
|
|
62
|
+
const blockModel = { autoTriggerFilter: true };
|
|
63
|
+
const action = createAction(blockModel, true);
|
|
64
|
+
|
|
65
|
+
action.onMount();
|
|
66
|
+
expect(blockModel.autoTriggerFilter).toBe(true);
|
|
67
|
+
|
|
68
|
+
action.hidden = false;
|
|
69
|
+
expect(blockModel.autoTriggerFilter).toBe(false);
|
|
70
|
+
|
|
71
|
+
action.hidden = true;
|
|
72
|
+
expect(blockModel.autoTriggerFilter).toBe(true);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('tracks fork lifecycles independently when submit actions share the same uid', () => {
|
|
76
|
+
const blockModel = { autoTriggerFilter: true };
|
|
77
|
+
const firstFork = {};
|
|
78
|
+
const secondFork = {};
|
|
79
|
+
const firstAction = createAction(blockModel, false, firstFork);
|
|
80
|
+
const secondAction = createAction(blockModel, false, secondFork);
|
|
81
|
+
|
|
82
|
+
firstAction.onMount();
|
|
83
|
+
secondAction.onMount();
|
|
84
|
+
firstAction.onUnmount();
|
|
85
|
+
expect(blockModel.autoTriggerFilter).toBe(false);
|
|
86
|
+
|
|
87
|
+
secondAction.onUnmount();
|
|
88
|
+
expect(blockModel.autoTriggerFilter).toBe(true);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -17,7 +17,15 @@ import { FilterFormBlockModel } from '../FilterFormBlockModel';
|
|
|
17
17
|
function resolveTemplateValue(raw: any, values: Record<string, any>): any {
|
|
18
18
|
if (typeof raw === 'string') {
|
|
19
19
|
const matched = raw.match(/^\{\{\s*ctx\.formValues\.([^}]+?)\s*\}\}$/);
|
|
20
|
-
|
|
20
|
+
if (matched) {
|
|
21
|
+
return values[matched[1]];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (/^\{\{\s*ctx\.date\.relative\.past\.day\.n7\s*\}\}$/.test(raw)) {
|
|
25
|
+
return '2026-06-15';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return raw;
|
|
21
29
|
}
|
|
22
30
|
if (Array.isArray(raw)) {
|
|
23
31
|
return raw.map((item) => resolveTemplateValue(item, values));
|
|
@@ -30,7 +38,7 @@ function resolveTemplateValue(raw: any, values: Record<string, any>): any {
|
|
|
30
38
|
|
|
31
39
|
function createFilterFormDefaultValuesModel(rules: any[], initialValues: Record<string, any> = {}) {
|
|
32
40
|
const values = { ...initialValues };
|
|
33
|
-
const createItem = (fieldPath: string, uid: string) => ({
|
|
41
|
+
const createItem = (fieldPath: string, uid: string, operator?: string) => ({
|
|
34
42
|
uid,
|
|
35
43
|
fieldPath,
|
|
36
44
|
props: { name: `${fieldPath}_${uid}` },
|
|
@@ -44,7 +52,7 @@ function createFilterFormDefaultValuesModel(rules: any[], initialValues: Record<
|
|
|
44
52
|
return undefined;
|
|
45
53
|
},
|
|
46
54
|
subModels: {
|
|
47
|
-
field: {},
|
|
55
|
+
field: operator ? { operator } : {},
|
|
48
56
|
},
|
|
49
57
|
});
|
|
50
58
|
const model = {
|
|
@@ -76,7 +84,11 @@ function createFilterFormDefaultValuesModel(rules: any[], initialValues: Record<
|
|
|
76
84
|
subModels: {
|
|
77
85
|
grid: {
|
|
78
86
|
subModels: {
|
|
79
|
-
items: [
|
|
87
|
+
items: [
|
|
88
|
+
createItem('nickname', 'nick'),
|
|
89
|
+
createItem('username', 'user'),
|
|
90
|
+
createItem('birthdate_tz', 'birthdate', '$dateOn'),
|
|
91
|
+
],
|
|
80
92
|
},
|
|
81
93
|
},
|
|
82
94
|
},
|
|
@@ -301,6 +313,40 @@ describe('filter-form defaultValues wiring', () => {
|
|
|
301
313
|
expect(values.username_user).toBe('Manual');
|
|
302
314
|
});
|
|
303
315
|
|
|
316
|
+
it('does not reapply a filter form default value after user clears the field', async () => {
|
|
317
|
+
const { model, values } = createFilterFormDefaultValuesModel([
|
|
318
|
+
{
|
|
319
|
+
key: 'username-default',
|
|
320
|
+
enable: true,
|
|
321
|
+
targetPath: 'username',
|
|
322
|
+
mode: 'default',
|
|
323
|
+
value: 'admin',
|
|
324
|
+
},
|
|
325
|
+
]);
|
|
326
|
+
|
|
327
|
+
await FilterFormBlockModel.prototype.applyFormDefaultValues.call(model as any);
|
|
328
|
+
expect(values.username_user).toBe('admin');
|
|
329
|
+
|
|
330
|
+
values.username_user = undefined;
|
|
331
|
+
(model as any).handleFilterFormValuesChange({ username_user: undefined }, { username_user: undefined });
|
|
332
|
+
|
|
333
|
+
await waitFor(() => {
|
|
334
|
+
expect(model.dispatchEvent).toHaveBeenCalledWith(
|
|
335
|
+
'formValuesChange',
|
|
336
|
+
{
|
|
337
|
+
changedValues: {
|
|
338
|
+
username_user: undefined,
|
|
339
|
+
},
|
|
340
|
+
allValues: {
|
|
341
|
+
username_user: undefined,
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
{ debounce: true },
|
|
345
|
+
);
|
|
346
|
+
});
|
|
347
|
+
expect(values.username_user).toBeUndefined();
|
|
348
|
+
});
|
|
349
|
+
|
|
304
350
|
it('applies fixed values even when the target filter field already has a value', async () => {
|
|
305
351
|
const { model, values } = createFilterFormDefaultValuesModel(
|
|
306
352
|
[
|
|
@@ -320,6 +366,22 @@ describe('filter-form defaultValues wiring', () => {
|
|
|
320
366
|
expect(values.username_user).toBe('Bob');
|
|
321
367
|
});
|
|
322
368
|
|
|
369
|
+
it('preserves relative date descriptors for date filter default values', async () => {
|
|
370
|
+
const { model, values } = createFilterFormDefaultValuesModel([
|
|
371
|
+
{
|
|
372
|
+
key: 'birthdate-default',
|
|
373
|
+
enable: true,
|
|
374
|
+
targetPath: 'birthdate_tz',
|
|
375
|
+
mode: 'assign',
|
|
376
|
+
value: '{{ ctx.date.relative.past.day.n7 }}',
|
|
377
|
+
},
|
|
378
|
+
]);
|
|
379
|
+
|
|
380
|
+
await FilterFormBlockModel.prototype.applyFormDefaultValues.call(model as any);
|
|
381
|
+
|
|
382
|
+
expect(values.birthdate_tz_birthdate).toEqual({ type: 'past', unit: 'day', number: 7 });
|
|
383
|
+
});
|
|
384
|
+
|
|
323
385
|
it('applies override values until the target filter field is changed by user', async () => {
|
|
324
386
|
const { model, values } = createFilterFormDefaultValuesModel(
|
|
325
387
|
[
|
|
@@ -569,6 +569,9 @@ export class FilterManager {
|
|
|
569
569
|
|
|
570
570
|
const blockModel = targetModel as CollectionBlockModel;
|
|
571
571
|
const resource = (targetModel as any).resource;
|
|
572
|
+
const filterResettableTarget = targetModel as CollectionBlockModel & {
|
|
573
|
+
resetAfterFilterChange?: () => void;
|
|
574
|
+
};
|
|
572
575
|
|
|
573
576
|
// 4.4 检查数据加载模式
|
|
574
577
|
const loadingMode = blockModel.getDataLoadingMode();
|
|
@@ -581,6 +584,7 @@ export class FilterManager {
|
|
|
581
584
|
resource.setPage(1);
|
|
582
585
|
}
|
|
583
586
|
resource.loading = false;
|
|
587
|
+
filterResettableTarget.resetAfterFilterChange?.();
|
|
584
588
|
return;
|
|
585
589
|
}
|
|
586
590
|
|
|
@@ -590,6 +594,7 @@ export class FilterManager {
|
|
|
590
594
|
resource.setPage(1); // 重置到第一页
|
|
591
595
|
}
|
|
592
596
|
await resource.refresh();
|
|
597
|
+
filterResettableTarget.resetAfterFilterChange?.();
|
|
593
598
|
} catch (error) {
|
|
594
599
|
console.error(`Failed to refresh target model "${targetId}": ${error.message}`);
|
|
595
600
|
return;
|
|
@@ -615,6 +615,46 @@ describe('FilterManager', () => {
|
|
|
615
615
|
expect(mockTargetModel2.resource.refresh).toHaveBeenCalledTimes(1);
|
|
616
616
|
});
|
|
617
617
|
|
|
618
|
+
it('should reset target interaction state after refreshing filtered data', async () => {
|
|
619
|
+
(filterManager as any).filterConfigs = [
|
|
620
|
+
{
|
|
621
|
+
filterId: 'filter-1',
|
|
622
|
+
targetId: 'target-1',
|
|
623
|
+
filterPaths: ['name'],
|
|
624
|
+
operator: '$eq',
|
|
625
|
+
},
|
|
626
|
+
];
|
|
627
|
+
|
|
628
|
+
const refresh = vi.fn().mockResolvedValue(undefined);
|
|
629
|
+
const resetAfterFilterChange = vi.fn(() => {
|
|
630
|
+
expect(refresh).toHaveBeenCalledTimes(1);
|
|
631
|
+
});
|
|
632
|
+
const mockTargetModel = {
|
|
633
|
+
resource: {
|
|
634
|
+
addFilterGroup: vi.fn(),
|
|
635
|
+
removeFilterGroup: vi.fn(),
|
|
636
|
+
refresh,
|
|
637
|
+
},
|
|
638
|
+
setFilterActive: vi.fn(),
|
|
639
|
+
getDataLoadingMode: vi.fn().mockReturnValue('auto'),
|
|
640
|
+
resetAfterFilterChange,
|
|
641
|
+
};
|
|
642
|
+
const mockFilterModel = {
|
|
643
|
+
getFilterValue: vi.fn().mockReturnValue('test-value'),
|
|
644
|
+
};
|
|
645
|
+
|
|
646
|
+
(mockFlowModel.flowEngine.getModel as any).mockImplementation((uid: string) => {
|
|
647
|
+
if (uid === 'target-1') return mockTargetModel;
|
|
648
|
+
if (uid === 'filter-1') return mockFilterModel;
|
|
649
|
+
return null;
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
await filterManager.refreshTargetsByFilter('filter-1');
|
|
653
|
+
|
|
654
|
+
expect(resetAfterFilterChange).toHaveBeenCalledTimes(1);
|
|
655
|
+
expect(refresh).toHaveBeenCalledTimes(1);
|
|
656
|
+
});
|
|
657
|
+
|
|
618
658
|
it('should wait for target resource idle before refreshing', async () => {
|
|
619
659
|
vi.useFakeTimers();
|
|
620
660
|
(filterManager as any).filterConfigs = [
|
|
@@ -72,17 +72,64 @@ const flowKeepMobileHorizontalClassName = css`
|
|
|
72
72
|
function isGridDelegatedStep(flowKey: string, stepKey: string): boolean {
|
|
73
73
|
return !!GRID_DELEGATED_STEP_KEYS[flowKey]?.has(stepKey);
|
|
74
74
|
}
|
|
75
|
+
|
|
76
|
+
interface FormModelWithAvailableData {
|
|
77
|
+
hasAvailableData(): boolean;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface FormModelWithSubmit {
|
|
81
|
+
submit(): Promise<unknown>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function hasAvailableDataCapability(model: object): model is FormModelWithAvailableData {
|
|
85
|
+
return 'hasAvailableData' in model && typeof model.hasAvailableData === 'function';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function hasSubmitCapability(model: object): model is FormModelWithSubmit {
|
|
89
|
+
return 'submit' in model && typeof model.submit === 'function';
|
|
90
|
+
}
|
|
91
|
+
|
|
75
92
|
export class FormBlockModel<
|
|
76
93
|
T extends DefaultCollectionBlockModelStructure = DefaultCollectionBlockModelStructure,
|
|
77
94
|
> extends CollectionBlockModel<T> {
|
|
78
95
|
formValueRuntime?: FormValueRuntime;
|
|
79
96
|
|
|
97
|
+
serialize() {
|
|
98
|
+
return { ...super.serialize(), variableContractType: { type: 'form', use: this.use } };
|
|
99
|
+
}
|
|
100
|
+
|
|
80
101
|
private userModifiedTopLevelFields = new Set<string>();
|
|
81
102
|
|
|
82
103
|
get form() {
|
|
83
104
|
return this.context.form as FormInstance;
|
|
84
105
|
}
|
|
85
106
|
|
|
107
|
+
submitFromRunJs() {
|
|
108
|
+
if (hasAvailableDataCapability(this) && !this.hasAvailableData()) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const submitAction = this.mapSubModels('actions', (action) => action).find((action) =>
|
|
113
|
+
action.getFlow('submitSettings'),
|
|
114
|
+
);
|
|
115
|
+
if (submitAction) {
|
|
116
|
+
return submitAction.onClick(undefined);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const isCoreForm = ['CreateFormModel', 'EditFormModel'].some((modelName) => {
|
|
120
|
+
const ModelClass = this.flowEngine.getModelClass(modelName);
|
|
121
|
+
return ModelClass && this instanceof ModelClass;
|
|
122
|
+
});
|
|
123
|
+
if (this.context.publicFormRuntime || !isCoreForm || !hasSubmitCapability(this)) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
this.submit().catch((error: unknown) => {
|
|
128
|
+
this.context.message.error(this.context.t('Save failed'));
|
|
129
|
+
console.error('Form submission error:', error);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
86
133
|
_defaultCustomModelClasses = {
|
|
87
134
|
FormActionGroupModel: 'FormActionGroupModel',
|
|
88
135
|
FormItemModel: 'FormItemModel',
|
|
@@ -21,6 +21,10 @@ export type DefaultFormGridStructure = {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
export class FormGridModel<T extends DefaultFormGridStructure = DefaultFormGridStructure> extends GridModel<T> {
|
|
24
|
+
serialize() {
|
|
25
|
+
return { ...super.serialize(), variableContractType: { type: 'formGrid', use: this.use } };
|
|
26
|
+
}
|
|
27
|
+
|
|
24
28
|
itemFallback = (<Skeleton.Input block size="small" style={{ marginBottom: '0.5rem' }} />);
|
|
25
29
|
itemSettingsMenuLevel = 2;
|
|
26
30
|
itemFlowSettings = {
|