@nocobase/client-v2 2.3.0-alpha.1 → 2.3.0-beta.2
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 +2 -2
- package/es/flow/actions/linkageRules.d.ts +25 -0
- package/es/flow/components/FieldAssignValueInput.d.ts +4 -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 +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/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 +147 -144
- 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 +8 -7
- 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/components/form/ScanInput/CodeScanner.tsx +61 -34
- package/src/components/form/ScanInput/__tests__/CodeScanner.test.tsx +101 -0
- package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +114 -18
- package/src/components/form/ScanInput/useCodeScanner.ts +166 -23
- 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/common/Markdown/style.ts +4 -0
- package/src/flow/components/BlockItemCard.tsx +2 -2
- package/src/flow/components/FieldAssignValueInput.tsx +38 -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 +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/VariableFilterItem.tsx +12 -3
- 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/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/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/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 +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__/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-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/__tests__/FormBlockModel.test.tsx +8 -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/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 +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/__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/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
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
11
|
-
import { MultiRecordResource } from '@nocobase/flow-engine';
|
|
11
|
+
import { FlowEngine, MultiRecordResource } from '@nocobase/flow-engine';
|
|
12
12
|
import { applyUpdateRecordAction } from '../UpdateRecordActionUtils';
|
|
13
|
+
import { UpdateRecordActionModel } from '../UpdateRecordActionModel';
|
|
13
14
|
import { dispatchEventDeep } from '../../../utils';
|
|
14
15
|
|
|
15
16
|
vi.mock('../../../utils', () => ({
|
|
@@ -21,7 +22,7 @@ describe('UpdateRecordActionModel apply action', () => {
|
|
|
21
22
|
vi.clearAllMocks();
|
|
22
23
|
});
|
|
23
24
|
|
|
24
|
-
it('dispatches paginationChange
|
|
25
|
+
it('dispatches paginationChange and returns success after updating the record', async () => {
|
|
25
26
|
const resource: any = Object.create(MultiRecordResource.prototype);
|
|
26
27
|
resource.update = vi.fn(async () => ({}));
|
|
27
28
|
resource.refresh = vi.fn(async () => {});
|
|
@@ -52,12 +53,13 @@ describe('UpdateRecordActionModel apply action', () => {
|
|
|
52
53
|
t: (value: string) => value,
|
|
53
54
|
};
|
|
54
55
|
|
|
55
|
-
await applyUpdateRecordAction(ctx, {
|
|
56
|
+
const updated = await applyUpdateRecordAction(ctx, {
|
|
56
57
|
assignedValues: {
|
|
57
58
|
marital_status: '已婚',
|
|
58
59
|
},
|
|
59
60
|
});
|
|
60
61
|
|
|
62
|
+
expect(updated).toBe(true);
|
|
61
63
|
expect(resource.update).toHaveBeenCalledWith(1, { marital_status: '已婚' }, undefined);
|
|
62
64
|
expect(resource.refresh).not.toHaveBeenCalled();
|
|
63
65
|
|
|
@@ -67,6 +69,76 @@ describe('UpdateRecordActionModel apply action', () => {
|
|
|
67
69
|
expect(paginationCalls.length).toBeGreaterThan(0);
|
|
68
70
|
expect(paginationCalls.some(([model]: [any]) => model === ctx.model)).toBe(true);
|
|
69
71
|
expect(paginationCalls.some(([model]: [any]) => model === blockModel)).toBe(true);
|
|
70
|
-
expect(ctx.message.success).
|
|
72
|
+
expect(ctx.message.success).not.toHaveBeenCalled();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('runs the configured after-success action only after a successful update', async () => {
|
|
76
|
+
const engine = new FlowEngine();
|
|
77
|
+
const action = new UpdateRecordActionModel({ uid: 'update-record-action', flowEngine: engine } as any);
|
|
78
|
+
action.setStepParams('assignSettings', 'confirm', { enable: false });
|
|
79
|
+
action.setStepParams('assignSettings', 'afterSuccess', {
|
|
80
|
+
successMessage: 'Record updated',
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const resource: any = Object.create(MultiRecordResource.prototype);
|
|
84
|
+
resource.update = vi.fn(async () => ({}));
|
|
85
|
+
const blockModel: any = { uid: 'details-block' };
|
|
86
|
+
const runAction = vi.fn(async () => {});
|
|
87
|
+
const ctx: any = {
|
|
88
|
+
model: action,
|
|
89
|
+
blockModel,
|
|
90
|
+
runAction,
|
|
91
|
+
collection: {
|
|
92
|
+
name: 'users',
|
|
93
|
+
getFilterByTK: vi.fn(() => 1),
|
|
94
|
+
},
|
|
95
|
+
record: { id: 1 },
|
|
96
|
+
resource,
|
|
97
|
+
message: {
|
|
98
|
+
success: vi.fn(),
|
|
99
|
+
warning: vi.fn(),
|
|
100
|
+
error: vi.fn(),
|
|
101
|
+
},
|
|
102
|
+
t: (value: string) => value,
|
|
103
|
+
};
|
|
104
|
+
const handler = action.getFlow('apply')?.getStep('apply')?.serialize().handler;
|
|
105
|
+
|
|
106
|
+
await handler(ctx, { assignedValues: { status: 'active' } });
|
|
107
|
+
|
|
108
|
+
expect(runAction).toHaveBeenNthCalledWith(1, 'confirm', { enable: false });
|
|
109
|
+
expect(runAction).toHaveBeenNthCalledWith(2, 'afterSuccess', {
|
|
110
|
+
successMessage: 'Record updated',
|
|
111
|
+
manualClose: false,
|
|
112
|
+
actionAfterSuccess: 'stay',
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('does not run the after-success action when no fields are assigned', async () => {
|
|
117
|
+
const engine = new FlowEngine();
|
|
118
|
+
const action = new UpdateRecordActionModel({ uid: 'update-record-action-empty', flowEngine: engine } as any);
|
|
119
|
+
const runAction = vi.fn(async () => {});
|
|
120
|
+
const ctx: any = {
|
|
121
|
+
model: action,
|
|
122
|
+
runAction,
|
|
123
|
+
collection: {
|
|
124
|
+
name: 'users',
|
|
125
|
+
getFilterByTK: vi.fn(() => 1),
|
|
126
|
+
},
|
|
127
|
+
record: { id: 1 },
|
|
128
|
+
resource: Object.create(MultiRecordResource.prototype),
|
|
129
|
+
message: {
|
|
130
|
+
success: vi.fn(),
|
|
131
|
+
warning: vi.fn(),
|
|
132
|
+
error: vi.fn(),
|
|
133
|
+
},
|
|
134
|
+
t: (value: string) => value,
|
|
135
|
+
};
|
|
136
|
+
const handler = action.getFlow('apply')?.getStep('apply')?.serialize().handler;
|
|
137
|
+
|
|
138
|
+
await handler(ctx, { assignedValues: {} });
|
|
139
|
+
|
|
140
|
+
expect(runAction).toHaveBeenCalledTimes(1);
|
|
141
|
+
expect(runAction).toHaveBeenCalledWith('confirm', { enable: false });
|
|
142
|
+
expect(ctx.message.warning).toHaveBeenCalledWith('No assigned fields configured');
|
|
71
143
|
});
|
|
72
144
|
});
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
10
|
+
import { FlowModel, tExpr, useFlowModel } from '@nocobase/flow-engine';
|
|
11
|
+
import type { DefaultStructure, FlowModelOptions } from '@nocobase/flow-engine';
|
|
11
12
|
import { Icon } from '../../../components';
|
|
12
13
|
import { Button, Tooltip } from 'antd';
|
|
13
14
|
import type { ButtonProps } from 'antd/es/button';
|
|
@@ -87,8 +88,10 @@ export class ActionModel<T extends DefaultStructure = DefaultStructure> extends
|
|
|
87
88
|
return null;
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
onInit(options:
|
|
91
|
+
onInit(options: FlowModelOptions<T>): void {
|
|
91
92
|
super.onInit(options);
|
|
93
|
+
// Action disabled state is computed at runtime and must never be restored from persisted props.
|
|
94
|
+
delete this.props.disabled;
|
|
92
95
|
this.context.defineProperty('actionName', {
|
|
93
96
|
get: () => {
|
|
94
97
|
return this.getAclActionName();
|
|
@@ -97,6 +100,12 @@ export class ActionModel<T extends DefaultStructure = DefaultStructure> extends
|
|
|
97
100
|
});
|
|
98
101
|
}
|
|
99
102
|
|
|
103
|
+
serialize() {
|
|
104
|
+
const data = super.serialize();
|
|
105
|
+
delete data.props.disabled;
|
|
106
|
+
return data;
|
|
107
|
+
}
|
|
108
|
+
|
|
100
109
|
getInputArgs() {
|
|
101
110
|
const inputArgs: Record<string, any> = {};
|
|
102
111
|
const defaultKeys: string[] = [];
|
|
@@ -14,8 +14,10 @@ import {
|
|
|
14
14
|
FlowSettingsButton,
|
|
15
15
|
buildSubModelGroups,
|
|
16
16
|
buildSubModelItems,
|
|
17
|
+
type FlowModel,
|
|
17
18
|
type SubModelItem,
|
|
18
19
|
type SubModelItemsType,
|
|
20
|
+
VIEW_ACTIVATED_EVENT,
|
|
19
21
|
} from '@nocobase/flow-engine';
|
|
20
22
|
import React from 'react';
|
|
21
23
|
import { FilterManager } from '../blocks/filter-manager/FilterManager';
|
|
@@ -24,6 +26,8 @@ import { GridModel } from './GridModel';
|
|
|
24
26
|
const SELECT_SCENE_ALLOWED_OTHER_BLOCK_MODELS = ['JSBlockModel', 'IframeBlockModel', 'MarkdownBlockModel'] as const;
|
|
25
27
|
|
|
26
28
|
export class BlockGridModel extends GridModel {
|
|
29
|
+
private viewActivatedListener?: () => void;
|
|
30
|
+
|
|
27
31
|
dragOverlayConfig: DragOverlayConfig = {
|
|
28
32
|
// 列内插入
|
|
29
33
|
columnInsert: {
|
|
@@ -50,6 +54,28 @@ export class BlockGridModel extends GridModel {
|
|
|
50
54
|
});
|
|
51
55
|
}
|
|
52
56
|
|
|
57
|
+
onMount() {
|
|
58
|
+
super.onMount();
|
|
59
|
+
if (this.context.view?.inputArgs?.scene !== 'select' || this.viewActivatedListener) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.viewActivatedListener = () => {
|
|
64
|
+
this.mapSubModels('items', (item) => {
|
|
65
|
+
(item as FlowModel & { onActive?: () => void }).onActive?.();
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
this.flowEngine.emitter.on(VIEW_ACTIVATED_EVENT, this.viewActivatedListener);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
protected onUnmount() {
|
|
72
|
+
if (this.viewActivatedListener) {
|
|
73
|
+
this.flowEngine.emitter.off(VIEW_ACTIVATED_EVENT, this.viewActivatedListener);
|
|
74
|
+
this.viewActivatedListener = undefined;
|
|
75
|
+
}
|
|
76
|
+
super.onUnmount();
|
|
77
|
+
}
|
|
78
|
+
|
|
53
79
|
get subModelBaseClasses() {
|
|
54
80
|
const inputArgs = this.context.view?.inputArgs ?? {};
|
|
55
81
|
if (inputArgs.collectionName && !inputArgs.filterByTk) {
|
|
@@ -16,10 +16,13 @@ import {
|
|
|
16
16
|
tExpr,
|
|
17
17
|
FlowModelContext,
|
|
18
18
|
MultiRecordResource,
|
|
19
|
+
ResourceError,
|
|
19
20
|
SingleRecordResource,
|
|
20
21
|
} from '@nocobase/flow-engine';
|
|
21
22
|
import type { FlowEngine } from '@nocobase/flow-engine';
|
|
22
23
|
import _ from 'lodash';
|
|
24
|
+
import React from 'react';
|
|
25
|
+
import { BlockResourceErrorPlaceholder } from '../../components/placeholders/BlockPlaceholder';
|
|
23
26
|
import { createDefaultCollectionBlockTitle } from '../../utils/blockUtils';
|
|
24
27
|
import { areCapabilitiesSupported, getBlockCapabilityNamesFromModelClass } from '../../utils/actionCapability';
|
|
25
28
|
import { FilterManager } from '../blocks/filter-manager/FilterManager';
|
|
@@ -82,6 +85,15 @@ export class CollectionBlockModel<T = DefaultStructure> extends DataBlockModel<T
|
|
|
82
85
|
const engine = this.context.engine as FlowEngine;
|
|
83
86
|
const currentVersion = this.getDirtyTrackingVersion(engine, dataSourceKey, resource, params);
|
|
84
87
|
|
|
88
|
+
if (resource instanceof MultiRecordResource && this.getDataLoadingMode() === 'manual' && !this.hasActiveFilters()) {
|
|
89
|
+
resource.setData([]);
|
|
90
|
+
resource.setMeta({ count: 0, hasNext: false });
|
|
91
|
+
resource.setPage(1);
|
|
92
|
+
resource.loading = false;
|
|
93
|
+
this.lastSeenDirtyVersion = currentVersion;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
85
97
|
if (forceRefresh) {
|
|
86
98
|
if (this.dirtyRefreshing) return;
|
|
87
99
|
this.dirtyRefreshing = true;
|
|
@@ -539,6 +551,18 @@ export class CollectionBlockModel<T = DefaultStructure> extends DataBlockModel<T
|
|
|
539
551
|
return this.resource.refresh();
|
|
540
552
|
}
|
|
541
553
|
|
|
554
|
+
isExternalDataSource() {
|
|
555
|
+
const dataSourceKey = this.getResourceSettingsInitParams()?.dataSourceKey;
|
|
556
|
+
return Boolean(dataSourceKey && dataSourceKey !== 'main');
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
render() {
|
|
560
|
+
if (this.isExternalDataSource() && this.collection && this.resource?.getError()) {
|
|
561
|
+
return <BlockResourceErrorPlaceholder />;
|
|
562
|
+
}
|
|
563
|
+
return super.render();
|
|
564
|
+
}
|
|
565
|
+
|
|
542
566
|
protected defaultBlockTitle() {
|
|
543
567
|
const blockLabel = this.translate(this.constructor['meta']?.label || this.constructor.name);
|
|
544
568
|
const params = this.getResourceSettingsInitParams();
|
|
@@ -569,9 +593,13 @@ export class CollectionBlockModel<T = DefaultStructure> extends DataBlockModel<T
|
|
|
569
593
|
}
|
|
570
594
|
if (fieldPath.includes('.')) {
|
|
571
595
|
// 关系数据
|
|
596
|
+
const collection = this.collection;
|
|
597
|
+
if (!collection) {
|
|
598
|
+
return;
|
|
599
|
+
}
|
|
572
600
|
const [field1, field2] = fieldPath.split('.');
|
|
573
601
|
const associationField = this.context.dataSourceManager.getCollectionField(
|
|
574
|
-
`${
|
|
602
|
+
`${collection.dataSourceKey}.${collection.name}.${field1}`,
|
|
575
603
|
) as CollectionField;
|
|
576
604
|
if (!associationField) {
|
|
577
605
|
return;
|
|
@@ -583,7 +611,7 @@ export class CollectionBlockModel<T = DefaultStructure> extends DataBlockModel<T
|
|
|
583
611
|
}
|
|
584
612
|
|
|
585
613
|
const collectionField = this.context.dataSourceManager.getCollectionField(
|
|
586
|
-
`${
|
|
614
|
+
`${collection.dataSourceKey}.${targetCollectionName}.${field2}`,
|
|
587
615
|
) as CollectionField;
|
|
588
616
|
|
|
589
617
|
if (collectionField && collectionField.isAssociationField()) {
|
|
@@ -693,7 +721,14 @@ CollectionBlockModel.registerFlow({
|
|
|
693
721
|
if (ctx.model.isManualRefresh) {
|
|
694
722
|
ctx.model.resource.loading = false;
|
|
695
723
|
} else {
|
|
696
|
-
|
|
724
|
+
try {
|
|
725
|
+
await resource.refresh();
|
|
726
|
+
} catch (error) {
|
|
727
|
+
if (blockModel.isExternalDataSource() && error instanceof ResourceError && resource.getError() === error) {
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
throw error;
|
|
731
|
+
}
|
|
697
732
|
}
|
|
698
733
|
},
|
|
699
734
|
},
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { FlowModel, FlowModelRenderer, observable, type ParamObject, tExpr } from '@nocobase/flow-engine';
|
|
11
11
|
import { Icon, type NocoBaseDesktopRoute } from '../../../../flow-compat';
|
|
12
|
+
import type { RouteRepository } from '../../../../RouteRepository';
|
|
12
13
|
import { useRequest } from 'ahooks';
|
|
13
14
|
import _ from 'lodash';
|
|
14
15
|
import React from 'react';
|
|
@@ -71,6 +72,25 @@ function isLinkageRulesStepParams(value: unknown): value is LinkageRulesStepPara
|
|
|
71
72
|
return isRecord(value);
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
type RefreshableRouteRepository = Pick<RouteRepository, 'refreshAccessible'>;
|
|
76
|
+
|
|
77
|
+
const routeRefreshQueues = new WeakMap<RefreshableRouteRepository, Promise<unknown>>();
|
|
78
|
+
|
|
79
|
+
async function refreshRouteRepository(repository: RefreshableRouteRepository) {
|
|
80
|
+
const previous = routeRefreshQueues.get(repository) || Promise.resolve();
|
|
81
|
+
const current = previous.catch(() => undefined).then(() => repository.refreshAccessible());
|
|
82
|
+
|
|
83
|
+
routeRefreshQueues.set(repository, current);
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
await current;
|
|
87
|
+
} finally {
|
|
88
|
+
if (routeRefreshQueues.get(repository) === current) {
|
|
89
|
+
routeRefreshQueues.delete(repository);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
74
94
|
export class BasePageTabModel extends FlowModel<{
|
|
75
95
|
subModels: {
|
|
76
96
|
grid: BlockGridModel;
|
|
@@ -282,23 +302,36 @@ export class RootPageTabModel extends BasePageTabModel {
|
|
|
282
302
|
async save() {
|
|
283
303
|
const json = this.serialize();
|
|
284
304
|
const documentTitle = this.stepParams?.pageTabSettings?.tab?.documentTitle;
|
|
285
|
-
const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
305
|
+
const route = this.props.route || {};
|
|
306
|
+
const persisted = route.id != null;
|
|
307
|
+
const currentRoute =
|
|
308
|
+
persisted && route.schemaUid ? this.context.routeRepository?.getRouteBySchemaUid?.(route.schemaUid) : undefined;
|
|
309
|
+
const data = {
|
|
310
|
+
...(persisted ? { schemaUid: route.schemaUid } : route),
|
|
311
|
+
title: this.getTabTitle(''),
|
|
312
|
+
icon: this.getTabIcon(),
|
|
313
|
+
options: {
|
|
314
|
+
...(currentRoute ? currentRoute.options : route.options),
|
|
315
|
+
flowRegistry: json.flowRegistry,
|
|
316
|
+
documentTitle,
|
|
290
317
|
},
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
318
|
+
};
|
|
319
|
+
const response = await this.context.api.request(
|
|
320
|
+
persisted
|
|
321
|
+
? {
|
|
322
|
+
method: 'post',
|
|
323
|
+
url: `desktopRoutes:update?filter[id]=${route.id}`,
|
|
324
|
+
data,
|
|
325
|
+
}
|
|
326
|
+
: {
|
|
327
|
+
method: 'post',
|
|
328
|
+
url: 'desktopRoutes:updateOrCreate',
|
|
329
|
+
params: {
|
|
330
|
+
filterKeys: ['schemaUid'],
|
|
331
|
+
},
|
|
332
|
+
data,
|
|
333
|
+
},
|
|
334
|
+
);
|
|
302
335
|
const persistedRoute = normalizePersistedRoute(response?.data?.data);
|
|
303
336
|
|
|
304
337
|
// 新建 tab 首次保存后需要立即拿到持久化 route id,拖拽排序会直接依赖它。
|
|
@@ -312,6 +345,20 @@ export class RootPageTabModel extends BasePageTabModel {
|
|
|
312
345
|
},
|
|
313
346
|
});
|
|
314
347
|
}
|
|
348
|
+
|
|
349
|
+
// 后续保存会从 RouteRepository 读取扩展 options,当前写入完成后必须先同步最新路由快照。
|
|
350
|
+
try {
|
|
351
|
+
if (this.context.routeRepository?.refreshAccessible) {
|
|
352
|
+
await refreshRouteRepository(this.context.routeRepository);
|
|
353
|
+
} else {
|
|
354
|
+
await this.context.refreshDesktopRoutes?.();
|
|
355
|
+
}
|
|
356
|
+
} catch (error) {
|
|
357
|
+
this.context.logger?.warn?.(
|
|
358
|
+
{ err: error },
|
|
359
|
+
'[client-v2] Failed to refresh desktop routes after saving a page tab',
|
|
360
|
+
);
|
|
361
|
+
}
|
|
315
362
|
}
|
|
316
363
|
|
|
317
364
|
private buildAnchorPayload(latestAnchor: PersistedFlowModelAnchor | undefined, linkageRules: LinkageRulesStepParams) {
|
|
@@ -359,8 +406,11 @@ export class RootPageTabModel extends BasePageTabModel {
|
|
|
359
406
|
throw new Error('Route repository is unavailable while persisting the page tab FlowModel marker.');
|
|
360
407
|
}
|
|
361
408
|
|
|
409
|
+
const currentRoute = route.schemaUid
|
|
410
|
+
? this.context.routeRepository.getRouteBySchemaUid?.(route.schemaUid)
|
|
411
|
+
: undefined;
|
|
362
412
|
const nextOptions = {
|
|
363
|
-
...(route.options || {}),
|
|
413
|
+
...(currentRoute?.options || route.options || {}),
|
|
364
414
|
};
|
|
365
415
|
if (hasRules) {
|
|
366
416
|
nextOptions.hasPersistedPageTabFlowModel = true;
|