@nocobase/client-v2 2.3.0-alpha.1 → 2.3.0-beta.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/es/APIClient.d.ts +12 -0
- package/es/components/form/ScanInput/useCodeScanner.d.ts +12 -2
- package/es/components/form/ScanInput/zxingWasmDecoder.d.ts +9 -0
- package/es/flow/actions/linkageRules.d.ts +25 -0
- package/es/flow/components/FieldAssignValueInput.d.ts +6 -30
- package/es/flow/components/FlowRoute.d.ts +2 -2
- package/es/flow/components/field-value-variable/DateVariableEditor.d.ts +24 -0
- package/es/flow/components/field-value-variable/FieldValueVariableInput.d.ts +32 -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/VariableFilterItem.d.ts +7 -0
- package/es/flow/components/placeholders/BlockPlaceholder.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/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/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/passwordUtils.d.ts +1 -1
- package/es/index.d.ts +1 -0
- package/es/index.mjs +148 -145
- package/es/ui-operation/index.d.ts +15 -0
- package/es/ui-operation/ui-operation-codec.d.ts +10 -0
- package/lib/index.js +170 -167
- package/lib/locale/languageCodes.js +1 -0
- package/package.json +10 -8
- package/src/APIClient.ts +92 -0
- package/src/Application.tsx +3 -1
- package/src/__tests__/APIClient.test.tsx +58 -0
- package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +7 -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-manager/__tests__/field-configure.test.ts +52 -0
- package/src/collection-manager/field-configure.ts +2 -2
- package/src/components/form/ScanInput/CodeScanner.tsx +64 -35
- package/src/components/form/ScanInput/__tests__/CodeScanner.test.tsx +107 -0
- package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +294 -24
- package/src/components/form/ScanInput/__tests__/zxingWasmDecoder.test.ts +78 -0
- package/src/components/form/ScanInput/useCodeScanner.ts +281 -23
- package/src/components/form/ScanInput/zxingWasmDecoder.ts +64 -0
- package/src/components/form/filter/CollectionFilterItem.tsx +4 -2
- package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +17 -0
- package/src/components/form/table/Table.tsx +81 -6
- package/src/components/form/table/__tests__/Table.columnWidth.test.tsx +433 -0
- package/src/flow/__tests__/FlowRoute.test.tsx +126 -8
- 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/linkageRules.tsx +59 -16
- package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +78 -59
- package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +228 -0
- package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +42 -23
- package/src/flow/common/Markdown/style.ts +4 -0
- package/src/flow/components/BlockItemCard.tsx +2 -2
- package/src/flow/components/FieldAssignValueInput.tsx +42 -621
- package/src/flow/components/FlowRoute.tsx +71 -29
- package/src/flow/components/field-value-variable/DateVariableEditor.tsx +181 -0
- package/src/flow/components/field-value-variable/FieldValueVariableInput.tsx +335 -0
- package/src/flow/components/field-value-variable/__tests__/FieldValueVariableInput.test.tsx +389 -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/VariableFilterItem.tsx +23 -6
- package/src/flow/components/filter/__tests__/VariableFilterItem.rightMetaTree.test.tsx +158 -0
- package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +125 -0
- 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 +2 -0
- package/src/flow/internal/components/Markdown/DisplayMarkdown.tsx +14 -9
- package/src/flow/internal/components/Markdown/__tests__/DisplayMarkdown.test.tsx +46 -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/GridModel.tsx +0 -1
- package/src/flow/models/base/PageModel/PageTabModel.tsx +67 -17
- package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +509 -12
- 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/AssignFormGridModel.tsx +22 -1
- package/src/flow/models/blocks/assign-form/AssignFormItemModel.tsx +36 -50
- package/src/flow/models/blocks/assign-form/__tests__/assignFieldValuesFlow.editor.test.tsx +140 -0
- 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 +27 -3
- package/src/flow/models/blocks/filter-form/FilterFormGridModel.tsx +36 -5
- package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +129 -14
- 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__/FilterFormGridModel.toggleFormFieldsCollapse.test.ts +29 -0
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +359 -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 +32 -4
- package/src/flow/models/blocks/filter-form/fields/FieldComponentProps.tsx +1 -1
- package/src/flow/models/blocks/filter-form/fields/__tests__/FieldComponentProps.options.test.tsx +61 -0
- 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 +73 -5
- package/src/flow/models/blocks/form/FormGridModel.tsx +4 -0
- package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +153 -2
- package/src/flow/models/blocks/form/__tests__/popupLinkage.test.tsx +175 -0
- 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/TableBlockModel.tsx +28 -2
- 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 +8 -0
- 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 +29 -3
- package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +9 -136
- package/src/flow/models/fields/AssociationFieldModel/__tests__/RecordPickerFieldModel.itemContext.test.ts +191 -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/DisplayAssociationField/DisplaySubTableFieldModel.tsx +42 -7
- package/src/flow/models/fields/DisplayAssociationField/__tests__/DisplaySubTableFieldModel.test.tsx +351 -0
- package/src/flow/models/fields/DisplayHtmlFieldModel.tsx +2 -1
- 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/RichTextFieldModel/__tests__/RichTextFieldModel.test.tsx +74 -0
- package/src/flow/models/fields/RichTextFieldModel/index.tsx +27 -6
- 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/mobile-components/MobileLazySelect.tsx +6 -0
- package/src/flow/models/fields/mobile-components/MobileSelect.tsx +27 -2
- package/src/flow/models/fields/mobile-components/__tests__/MobileSelect.test.tsx +139 -13
- package/src/flow/models/topbar/TopbarActionModel.tsx +5 -5
- package/src/flow/utils/dataScopeFormValueClear.ts +4 -3
- package/src/index.ts +1 -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 +1 -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
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
SingleRecordResource,
|
|
21
21
|
createCurrentRecordMetaFactory,
|
|
22
22
|
createRecordResolveOnServerWithLocal,
|
|
23
|
+
observer,
|
|
23
24
|
tExpr,
|
|
24
25
|
} from '@nocobase/flow-engine';
|
|
25
26
|
import { Pagination, Space } from 'antd';
|
|
@@ -154,8 +155,8 @@ export class DetailsBlockModel extends CollectionBlockModel<{
|
|
|
154
155
|
simple
|
|
155
156
|
pageSize={1}
|
|
156
157
|
showSizeChanger={false}
|
|
157
|
-
|
|
158
|
-
total={resource.
|
|
158
|
+
current={resource.getPage()}
|
|
159
|
+
total={resource.getCount()}
|
|
159
160
|
onChange={this.handlePageChange}
|
|
160
161
|
style={{ display: 'inline-block' }}
|
|
161
162
|
/>
|
|
@@ -226,6 +227,8 @@ export class DetailsBlockModel extends CollectionBlockModel<{
|
|
|
226
227
|
}
|
|
227
228
|
}
|
|
228
229
|
|
|
230
|
+
export const DetailsPagination = observer(({ model }: { model: DetailsBlockModel }) => model.renderPagination());
|
|
231
|
+
|
|
229
232
|
const DetailsBlockContent = ({
|
|
230
233
|
model,
|
|
231
234
|
gridModel,
|
|
@@ -297,7 +300,9 @@ const DetailsBlockContent = ({
|
|
|
297
300
|
model={gridModel}
|
|
298
301
|
showFlowSettings={false}
|
|
299
302
|
/>
|
|
300
|
-
<div ref={paginationRef}>
|
|
303
|
+
<div ref={paginationRef}>
|
|
304
|
+
<DetailsPagination model={model} />
|
|
305
|
+
</div>
|
|
301
306
|
</div>
|
|
302
307
|
</FormComponent>
|
|
303
308
|
);
|
|
@@ -7,10 +7,12 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { act, render, screen } from '@testing-library/react';
|
|
10
11
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
11
12
|
import { FlowEngine, MultiRecordResource, SingleRecordResource } from '@nocobase/flow-engine';
|
|
13
|
+
import React from 'react';
|
|
12
14
|
import '../../../../index';
|
|
13
|
-
import { DetailsBlockModel } from '../DetailsBlockModel';
|
|
15
|
+
import { DetailsBlockModel, DetailsPagination } from '../DetailsBlockModel';
|
|
14
16
|
import { DetailsGridModel } from '../DetailsGridModel';
|
|
15
17
|
|
|
16
18
|
function setupDetailsBlockModel(options?: { filterByTk?: string | number }) {
|
|
@@ -93,6 +95,35 @@ describe('DetailsBlockModel initial pagination refresh', () => {
|
|
|
93
95
|
expect(dispatchSpy.mock.calls.filter(([eventName]) => eventName === 'paginationChange')).toHaveLength(1);
|
|
94
96
|
});
|
|
95
97
|
|
|
98
|
+
it('renders pagination from the current count and page after filtering', () => {
|
|
99
|
+
const { model, resource } = setupDetailsBlockModel();
|
|
100
|
+
resource.setData([{ id: 3, name: 'CC', title: 'filtered' }] as any);
|
|
101
|
+
resource.setMeta({ count: 7, page: 3, pageSize: 1, totalPage: 20 });
|
|
102
|
+
|
|
103
|
+
const paginationContainer = model.renderPagination() as any;
|
|
104
|
+
const pagination = paginationContainer.props.children;
|
|
105
|
+
|
|
106
|
+
expect(pagination.props.total).toBe(7);
|
|
107
|
+
expect(pagination.props.current).toBe(3);
|
|
108
|
+
expect(pagination.props.defaultCurrent).toBeUndefined();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('updates the rendered pagination when filtering changes only resource metadata', () => {
|
|
112
|
+
const { model, resource } = setupDetailsBlockModel();
|
|
113
|
+
resource.setData([{ id: 1, name: 'AA', title: 'foo' }] as any);
|
|
114
|
+
resource.setMeta({ count: 7, page: 1, pageSize: 1, totalPage: 7 });
|
|
115
|
+
|
|
116
|
+
render(React.createElement(DetailsPagination, { model }));
|
|
117
|
+
|
|
118
|
+
expect(screen.getByRole('list')).toBeInTheDocument();
|
|
119
|
+
|
|
120
|
+
act(() => {
|
|
121
|
+
resource.setMeta({ count: 1, page: 1, pageSize: 1, totalPage: 1 });
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
expect(screen.queryByRole('list')).not.toBeInTheDocument();
|
|
125
|
+
});
|
|
126
|
+
|
|
96
127
|
it('uses a real single-record details model and does not emit root paginationChange on initial render', async () => {
|
|
97
128
|
const { model, resource } = setupDetailsBlockModel({ filterByTk: 1 });
|
|
98
129
|
expect(resource).toBeInstanceOf(SingleRecordResource);
|
|
@@ -15,8 +15,10 @@ import {
|
|
|
15
15
|
DndProvider,
|
|
16
16
|
DragHandler,
|
|
17
17
|
Droppable,
|
|
18
|
+
isCtxDateExpression,
|
|
18
19
|
isRunJSValue,
|
|
19
20
|
normalizeRunJSValue,
|
|
21
|
+
parseCtxDateExpression,
|
|
20
22
|
tExpr,
|
|
21
23
|
FlowModelRenderer,
|
|
22
24
|
FlowSettingsButton,
|
|
@@ -44,6 +46,15 @@ import { normalizeFilterValueByOperator } from './valueNormalization';
|
|
|
44
46
|
|
|
45
47
|
const RELATION_FIELD_TYPES = ['belongsTo', 'hasOne', 'hasMany', 'belongsToMany', 'belongsToArray'];
|
|
46
48
|
const NUMERIC_FIELD_TYPES = ['integer', 'float', 'double', 'decimal'];
|
|
49
|
+
const DATE_FILTER_OPERATORS = new Set([
|
|
50
|
+
'$dateOn',
|
|
51
|
+
'$dateNotOn',
|
|
52
|
+
'$dateBefore',
|
|
53
|
+
'$dateAfter',
|
|
54
|
+
'$dateNotBefore',
|
|
55
|
+
'$dateNotAfter',
|
|
56
|
+
'$dateBetween',
|
|
57
|
+
]);
|
|
47
58
|
|
|
48
59
|
function getFilterFormFieldMetaType(field: CollectionField) {
|
|
49
60
|
if (RELATION_FIELD_TYPES.includes(field.type)) {
|
|
@@ -66,6 +77,14 @@ function getFilterFormFieldMetaType(field: CollectionField) {
|
|
|
66
77
|
}
|
|
67
78
|
}
|
|
68
79
|
|
|
80
|
+
function parseDateFilterDefaultValue(operator: string | undefined, rawValue: unknown) {
|
|
81
|
+
if (!operator || !DATE_FILTER_OPERATORS.has(operator) || !isCtxDateExpression(rawValue)) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return parseCtxDateExpression(rawValue);
|
|
86
|
+
}
|
|
87
|
+
|
|
69
88
|
function shouldShowFilterFormFieldMeta(field: CollectionField) {
|
|
70
89
|
return Boolean(field?.interface);
|
|
71
90
|
}
|
|
@@ -489,7 +508,7 @@ export class FilterFormBlockModel extends FilterBlockModel<{
|
|
|
489
508
|
const rules = (params?.value || []) as any[];
|
|
490
509
|
if (!Array.isArray(rules) || rules.length === 0) return appliedValues;
|
|
491
510
|
|
|
492
|
-
const resolveValue = async (raw: any) => {
|
|
511
|
+
const resolveValue = async (raw: any, operator?: string) => {
|
|
493
512
|
// RunJS support
|
|
494
513
|
if (isRunJSValue(raw)) {
|
|
495
514
|
const { code, version } = normalizeRunJSValue(raw);
|
|
@@ -497,6 +516,11 @@ export class FilterFormBlockModel extends FilterBlockModel<{
|
|
|
497
516
|
return ret?.success ? ret.value : undefined;
|
|
498
517
|
}
|
|
499
518
|
|
|
519
|
+
const parsedDateFilterValue = parseDateFilterDefaultValue(operator, raw);
|
|
520
|
+
if (typeof parsedDateFilterValue !== 'undefined') {
|
|
521
|
+
return parsedDateFilterValue;
|
|
522
|
+
}
|
|
523
|
+
|
|
500
524
|
return await (this.context as any).resolveJsonTemplate?.(raw);
|
|
501
525
|
};
|
|
502
526
|
|
|
@@ -520,11 +544,11 @@ export class FilterFormBlockModel extends FilterBlockModel<{
|
|
|
520
544
|
|
|
521
545
|
const current = (form as any).getFieldValue?.(name);
|
|
522
546
|
|
|
523
|
-
const
|
|
547
|
+
const operator = getDefaultOperator(itemModel as any);
|
|
548
|
+
const resolved = await resolveValue(rule.value, operator);
|
|
524
549
|
if (options?.refreshSeq && options.refreshSeq !== this.defaultValuesRefreshSeq) return appliedValues;
|
|
525
550
|
if (typeof resolved === 'undefined') continue;
|
|
526
551
|
|
|
527
|
-
const operator = getDefaultOperator(itemModel as any);
|
|
528
552
|
const normalized = normalizeFilterValueByOperator(operator, resolved);
|
|
529
553
|
const mode = this.normalizeFieldValueMode(rule.mode);
|
|
530
554
|
if (mode === 'default' && !this.canApplyFormDefaultValue(String(name), current, force)) continue;
|
|
@@ -59,11 +59,42 @@ export class FilterFormGridModel extends GridModel {
|
|
|
59
59
|
|
|
60
60
|
readonly loading = observable.ref(false);
|
|
61
61
|
|
|
62
|
-
private
|
|
63
|
-
const
|
|
62
|
+
private isRecordSelectFilterField(subModel: FilterFormItemModel) {
|
|
63
|
+
const fieldModel = subModel.subModels?.field;
|
|
64
|
+
if (!fieldModel) {
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const RecordSelectModel = this.flowEngine.getModelClass('FilterFormRecordSelectFieldModel');
|
|
69
|
+
if (!RecordSelectModel) {
|
|
70
|
+
return fieldModel.constructor?.name === 'FilterFormRecordSelectFieldModel';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return fieldModel instanceof RecordSelectModel;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private getAssociationFilterTargetKey(field: any, subModel: FilterFormItemModel) {
|
|
77
|
+
const normalizeKey = (key: any) => {
|
|
78
|
+
if (Array.isArray(key)) {
|
|
79
|
+
return key[0];
|
|
80
|
+
}
|
|
81
|
+
return key;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const fieldModelValueKey = normalizeKey(subModel.subModels?.field?.props?.fieldNames?.value);
|
|
85
|
+
if (fieldModelValueKey) {
|
|
86
|
+
return fieldModelValueKey;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const filterTargetKey = normalizeKey(field?.targetCollection?.filterTargetKey);
|
|
90
|
+
const fieldTargetKey = normalizeKey(field?.targetKey);
|
|
91
|
+
|
|
92
|
+
if (filterTargetKey && filterTargetKey !== 'id') {
|
|
93
|
+
return filterTargetKey;
|
|
94
|
+
}
|
|
64
95
|
|
|
65
|
-
if (
|
|
66
|
-
return
|
|
96
|
+
if (!this.isRecordSelectFilterField(subModel) && fieldTargetKey) {
|
|
97
|
+
return fieldTargetKey;
|
|
67
98
|
}
|
|
68
99
|
|
|
69
100
|
return filterTargetKey || 'id';
|
|
@@ -370,7 +401,7 @@ export class FilterFormGridModel extends GridModel {
|
|
|
370
401
|
if (field?.target) {
|
|
371
402
|
return {
|
|
372
403
|
targetId: model.uid,
|
|
373
|
-
filterPaths: [`${fieldPath}.${this.getAssociationFilterTargetKey(field)}`],
|
|
404
|
+
filterPaths: [`${fieldPath}.${this.getAssociationFilterTargetKey(field, subModel)}`],
|
|
374
405
|
};
|
|
375
406
|
}
|
|
376
407
|
}
|
|
@@ -31,6 +31,12 @@ import {
|
|
|
31
31
|
import { FilterFormFieldModel } from './fields';
|
|
32
32
|
import { normalizeFilterValueByOperator } from './valueNormalization';
|
|
33
33
|
|
|
34
|
+
type CollectionWithManager = Collection & {
|
|
35
|
+
collectionManager?: {
|
|
36
|
+
getCollection?: (name: string) => Collection | undefined;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
|
|
34
40
|
const getAssociationTargetCollection = (field: any, collection?: Collection, model?: CollectionBlockModel) => {
|
|
35
41
|
if (field?.targetCollection) {
|
|
36
42
|
return field.targetCollection;
|
|
@@ -42,6 +48,10 @@ const getAssociationTargetCollection = (field: any, collection?: Collection, mod
|
|
|
42
48
|
if (collection?.dataSource?.getCollection) {
|
|
43
49
|
return collection.dataSource.getCollection(targetName);
|
|
44
50
|
}
|
|
51
|
+
const collectionManager = (collection as CollectionWithManager | undefined)?.collectionManager;
|
|
52
|
+
if (collectionManager?.getCollection) {
|
|
53
|
+
return collectionManager.getCollection(targetName);
|
|
54
|
+
}
|
|
45
55
|
const dataSourceKey = collection?.dataSourceKey;
|
|
46
56
|
if (dataSourceKey && model?.context?.dataSourceManager?.getCollection) {
|
|
47
57
|
return model.context.dataSourceManager.getCollection(dataSourceKey, targetName);
|
|
@@ -168,6 +178,74 @@ const buildVirtualFilterCollectionField = (ctx: FlowModelContext, filterField: a
|
|
|
168
178
|
return result;
|
|
169
179
|
};
|
|
170
180
|
|
|
181
|
+
const getFilterableChildren = (field: any) => {
|
|
182
|
+
const children = field?.filterable?.children;
|
|
183
|
+
return Array.isArray(children) ? children : [];
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const hasAssociationChildren = (field: any, collection?: Collection, model?: CollectionBlockModel) => {
|
|
187
|
+
return (
|
|
188
|
+
isAssociationCollectionField(field) &&
|
|
189
|
+
(getFilterableChildren(field).length > 0 || !!getAssociationTargetCollection(field, collection, model))
|
|
190
|
+
);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const inferFilterableChildInterface = (child: any) => {
|
|
194
|
+
if (child?.interface) {
|
|
195
|
+
return child.interface;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const schema = child?.schema || child?.uiSchema;
|
|
199
|
+
const component = schema?.['x-component'];
|
|
200
|
+
if (component === 'Select') {
|
|
201
|
+
return 'select';
|
|
202
|
+
}
|
|
203
|
+
if (component === 'Checkbox.Group') {
|
|
204
|
+
return 'checkboxGroup';
|
|
205
|
+
}
|
|
206
|
+
if (component === 'Checkbox') {
|
|
207
|
+
return 'checkbox';
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const type = child?.type || schema?.type;
|
|
211
|
+
if (type === 'number' || type === 'integer') {
|
|
212
|
+
return 'number';
|
|
213
|
+
}
|
|
214
|
+
return 'input';
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const translateFilterableChildTitle = (title: unknown, t: (value: string) => string) => {
|
|
218
|
+
return typeof title === 'string' ? t(title) : title;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const buildFilterableChildField = (child: any, t: (value: string) => string) => {
|
|
222
|
+
const name = child?.name || child?.value;
|
|
223
|
+
if (!name) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const schema = child?.schema || child?.uiSchema || {};
|
|
228
|
+
const rawTitle = child?.title || child?.label || schema?.title || name;
|
|
229
|
+
const title = translateFilterableChildTitle(rawTitle, t);
|
|
230
|
+
const filterable = child?.filterable || (child?.operators ? { operators: child.operators } : undefined);
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
...child,
|
|
234
|
+
name,
|
|
235
|
+
title,
|
|
236
|
+
type: child?.type || schema?.type || 'string',
|
|
237
|
+
interface: inferFilterableChildInterface(child),
|
|
238
|
+
uiSchema: {
|
|
239
|
+
...schema,
|
|
240
|
+
title: translateFilterableChildTitle(schema?.title || title, t),
|
|
241
|
+
},
|
|
242
|
+
filterable,
|
|
243
|
+
__filterFormVirtual: true,
|
|
244
|
+
isAssociationField: () => false,
|
|
245
|
+
getComponentProps: () => schema?.['x-component-props'] || {},
|
|
246
|
+
};
|
|
247
|
+
};
|
|
248
|
+
|
|
171
249
|
const buildFilterFormFieldItem = ({
|
|
172
250
|
model,
|
|
173
251
|
collection,
|
|
@@ -187,11 +265,7 @@ const buildFilterFormFieldItem = ({
|
|
|
187
265
|
if (!binding) {
|
|
188
266
|
return;
|
|
189
267
|
}
|
|
190
|
-
const
|
|
191
|
-
const fieldModel =
|
|
192
|
-
isAssociation && ctxWithFlags.engine?.getModelClass?.('FilterFormRecordSelectFieldModel')
|
|
193
|
-
? 'FilterFormRecordSelectFieldModel'
|
|
194
|
-
: binding.modelName;
|
|
268
|
+
const fieldModel = binding.modelName;
|
|
195
269
|
const label = field.title || field.name;
|
|
196
270
|
const displayLabel = labelPrefix ? `${labelPrefix} / ${label}` : label;
|
|
197
271
|
return {
|
|
@@ -211,7 +285,10 @@ const buildFilterFormFieldItem = ({
|
|
|
211
285
|
},
|
|
212
286
|
filterFormItemSettings: {
|
|
213
287
|
init: {
|
|
214
|
-
filterField:
|
|
288
|
+
filterField: {
|
|
289
|
+
..._.pick(field, ['name', 'title', 'interface', 'type', 'filterable', 'uiSchema', '__filterFormVirtual']),
|
|
290
|
+
title: displayLabel,
|
|
291
|
+
},
|
|
215
292
|
defaultTargetUid: model.uid,
|
|
216
293
|
},
|
|
217
294
|
},
|
|
@@ -245,7 +322,8 @@ const buildAssociationFieldMenuItem = ({
|
|
|
245
322
|
depth: number;
|
|
246
323
|
}) => {
|
|
247
324
|
const targetCollection = getAssociationTargetCollection(field, collection, model);
|
|
248
|
-
|
|
325
|
+
const filterableChildren = getFilterableChildren(field);
|
|
326
|
+
if (!targetCollection && !filterableChildren.length) {
|
|
249
327
|
return;
|
|
250
328
|
}
|
|
251
329
|
if (depth > MAX_ASSOCIATION_DEPTH) {
|
|
@@ -262,10 +340,35 @@ const buildAssociationFieldMenuItem = ({
|
|
|
262
340
|
const targetFields = getTargetFilterableFields(field, collection, model);
|
|
263
341
|
const fieldItems: any[] = [];
|
|
264
342
|
const associationItems: any[] = [];
|
|
343
|
+
const addedFieldPaths = new Set<string>();
|
|
344
|
+
|
|
345
|
+
filterableChildren.forEach((child: any) => {
|
|
346
|
+
const virtualChildField = buildFilterableChildField(child, t);
|
|
347
|
+
if (!virtualChildField) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const childFieldPath = `${fieldPath}.${virtualChildField.name}`;
|
|
352
|
+
const childItem = buildFilterFormFieldItem({
|
|
353
|
+
model,
|
|
354
|
+
collection,
|
|
355
|
+
ctxWithFlags,
|
|
356
|
+
field: virtualChildField,
|
|
357
|
+
fieldPath: childFieldPath,
|
|
358
|
+
labelPrefix: label,
|
|
359
|
+
});
|
|
360
|
+
if (childItem) {
|
|
361
|
+
addedFieldPaths.add(childFieldPath);
|
|
362
|
+
fieldItems.push(childItem);
|
|
363
|
+
}
|
|
364
|
+
});
|
|
265
365
|
|
|
266
366
|
targetFields.forEach((targetField: any) => {
|
|
267
367
|
const targetFieldPath = `${fieldPath}.${targetField.name}`;
|
|
268
|
-
if (
|
|
368
|
+
if (addedFieldPaths.has(targetFieldPath)) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if (hasAssociationChildren(targetField, collection, model)) {
|
|
269
372
|
const associationItem = buildAssociationFieldMenuItem({
|
|
270
373
|
model,
|
|
271
374
|
collection,
|
|
@@ -358,7 +461,7 @@ const getModelFieldGroups = async (model: CollectionBlockModel) => {
|
|
|
358
461
|
baseItems.push(baseItem);
|
|
359
462
|
}
|
|
360
463
|
|
|
361
|
-
if (field
|
|
464
|
+
if (hasAssociationChildren(field, collection, model)) {
|
|
362
465
|
const associationItem = buildAssociationFieldMenuItem({
|
|
363
466
|
model,
|
|
364
467
|
collection,
|
|
@@ -489,6 +592,14 @@ export class FilterFormItemModel extends FilterableItemModel<{
|
|
|
489
592
|
title: initFilterField?.title || initFilterField?.name,
|
|
490
593
|
},
|
|
491
594
|
});
|
|
595
|
+
if (initFilterField?.__filterFormVirtual) {
|
|
596
|
+
const virtualField = buildVirtualFilterCollectionField(this.context, initFilterField);
|
|
597
|
+
if (virtualField) {
|
|
598
|
+
this.context.defineProperty('collectionField', {
|
|
599
|
+
value: virtualField,
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
}
|
|
492
603
|
}
|
|
493
604
|
}
|
|
494
605
|
|
|
@@ -692,7 +803,8 @@ FilterFormItemModel.registerFlow({
|
|
|
692
803
|
// @ts-ignore
|
|
693
804
|
(fieldSettingsInitParams?.dataSourceKey && fieldSettingsInitParams?.collectionName)
|
|
694
805
|
);
|
|
695
|
-
|
|
806
|
+
const shouldUseVirtualFilterField = !!normalizedFilterField?.__filterFormVirtual;
|
|
807
|
+
if ((shouldUseVirtualFilterField || (!hasCollectionContext && !collectionField)) && normalizedFilterField) {
|
|
696
808
|
const virtualField = buildVirtualFilterCollectionField(ctx, normalizedFilterField);
|
|
697
809
|
if (virtualField) {
|
|
698
810
|
ctx.model.context.defineProperty('collectionField', {
|
|
@@ -787,10 +899,13 @@ FilterFormItemModel.registerFlow({
|
|
|
787
899
|
type: 'select',
|
|
788
900
|
key: 'use',
|
|
789
901
|
props: {
|
|
790
|
-
options: classes.map((model) =>
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
902
|
+
options: classes.map((model) => {
|
|
903
|
+
const ModelClass = ctx.engine.getModelClass(model.modelName);
|
|
904
|
+
return {
|
|
905
|
+
label: t(ModelClass?.meta?.label || model.modelName),
|
|
906
|
+
value: model.modelName,
|
|
907
|
+
};
|
|
908
|
+
}),
|
|
794
909
|
},
|
|
795
910
|
};
|
|
796
911
|
},
|
|
@@ -8,23 +8,76 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { ButtonProps } from 'antd/es/button';
|
|
11
|
+
import { reaction } from '@formily/reactive';
|
|
11
12
|
import { tExpr } from '@nocobase/flow-engine';
|
|
12
13
|
import { FilterFormActionModel } from './FilterFormActionModel';
|
|
13
14
|
|
|
15
|
+
type FilterFormBlockState = {
|
|
16
|
+
autoTriggerFilter: boolean;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const mountedSubmitActions = new WeakSet<object>();
|
|
20
|
+
const visibleSubmitActionsByBlock = new WeakMap<FilterFormBlockState, Set<object>>();
|
|
21
|
+
const hiddenReactionDisposers = new WeakMap<object, () => void>();
|
|
22
|
+
|
|
23
|
+
const syncAutoTriggerFilter = (blockModel: FilterFormBlockState, lifecycleAction: object & { hidden?: boolean }) => {
|
|
24
|
+
let visibleSubmitActions = visibleSubmitActionsByBlock.get(blockModel);
|
|
25
|
+
|
|
26
|
+
if (mountedSubmitActions.has(lifecycleAction) && !lifecycleAction.hidden) {
|
|
27
|
+
if (!visibleSubmitActions) {
|
|
28
|
+
visibleSubmitActions = new Set<object>();
|
|
29
|
+
visibleSubmitActionsByBlock.set(blockModel, visibleSubmitActions);
|
|
30
|
+
}
|
|
31
|
+
visibleSubmitActions.add(lifecycleAction);
|
|
32
|
+
} else {
|
|
33
|
+
visibleSubmitActions?.delete(lifecycleAction);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
blockModel.autoTriggerFilter = Boolean(!visibleSubmitActions?.size);
|
|
37
|
+
if (!visibleSubmitActions?.size) {
|
|
38
|
+
visibleSubmitActionsByBlock.delete(blockModel);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
14
42
|
export class FilterFormSubmitActionModel extends FilterFormActionModel {
|
|
15
43
|
defaultProps: ButtonProps = {
|
|
16
44
|
title: tExpr('Filter'),
|
|
17
45
|
type: 'primary',
|
|
18
46
|
};
|
|
19
47
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
48
|
+
private getLifecycleAction(): object & { hidden?: boolean } {
|
|
49
|
+
return this.context.model || this;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private syncAutoTriggerFilter(): void {
|
|
53
|
+
const blockModel = this.context.blockModel as FilterFormBlockState;
|
|
54
|
+
const lifecycleAction = this.getLifecycleAction();
|
|
55
|
+
syncAutoTriggerFilter(blockModel, lifecycleAction);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
onMount(): void {
|
|
59
|
+
super.onMount();
|
|
60
|
+
const blockModel = this.context.blockModel as FilterFormBlockState;
|
|
61
|
+
const lifecycleAction = this.getLifecycleAction();
|
|
62
|
+
mountedSubmitActions.add(lifecycleAction);
|
|
63
|
+
hiddenReactionDisposers.get(lifecycleAction)?.();
|
|
64
|
+
hiddenReactionDisposers.set(
|
|
65
|
+
lifecycleAction,
|
|
66
|
+
reaction(
|
|
67
|
+
() => lifecycleAction.hidden,
|
|
68
|
+
() => syncAutoTriggerFilter(blockModel, lifecycleAction),
|
|
69
|
+
),
|
|
70
|
+
);
|
|
71
|
+
this.syncAutoTriggerFilter();
|
|
23
72
|
}
|
|
24
73
|
|
|
25
74
|
onUnmount(): void {
|
|
26
75
|
super.onUnmount();
|
|
27
|
-
|
|
76
|
+
const lifecycleAction = this.getLifecycleAction();
|
|
77
|
+
hiddenReactionDisposers.get(lifecycleAction)?.();
|
|
78
|
+
hiddenReactionDisposers.delete(lifecycleAction);
|
|
79
|
+
mountedSubmitActions.delete(lifecycleAction);
|
|
80
|
+
this.syncAutoTriggerFilter();
|
|
28
81
|
}
|
|
29
82
|
}
|
|
30
83
|
|