@nocobase/client-v2 2.1.0-beta.41 → 2.1.0-beta.42
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/index.mjs +2 -2
- package/lib/index.js +22 -22
- package/package.json +7 -7
- package/src/flow/models/blocks/assign-form/AssignFormGridModel.tsx +1 -3
- package/src/flow/models/blocks/assign-form/AssignFormItemModel.tsx +0 -1
- package/src/flow/models/blocks/assign-form/__tests__/assignFieldValuesFlow.editor.test.tsx +124 -0
- package/src/flow/models/blocks/assign-form/assignFieldValuesFlow.tsx +80 -32
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/client-v2",
|
|
3
|
-
"version": "2.1.0-beta.
|
|
3
|
+
"version": "2.1.0-beta.42",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "es/index.mjs",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"@formily/antd-v5": "1.2.3",
|
|
28
28
|
"@formily/react": "^2.2.27",
|
|
29
29
|
"@formily/shared": "^2.2.27",
|
|
30
|
-
"@nocobase/evaluators": "2.1.0-beta.
|
|
31
|
-
"@nocobase/flow-engine": "2.1.0-beta.
|
|
32
|
-
"@nocobase/sdk": "2.1.0-beta.
|
|
33
|
-
"@nocobase/shared": "2.1.0-beta.
|
|
34
|
-
"@nocobase/utils": "2.1.0-beta.
|
|
30
|
+
"@nocobase/evaluators": "2.1.0-beta.42",
|
|
31
|
+
"@nocobase/flow-engine": "2.1.0-beta.42",
|
|
32
|
+
"@nocobase/sdk": "2.1.0-beta.42",
|
|
33
|
+
"@nocobase/shared": "2.1.0-beta.42",
|
|
34
|
+
"@nocobase/utils": "2.1.0-beta.42",
|
|
35
35
|
"ahooks": "^3.7.2",
|
|
36
36
|
"antd": "5.24.2",
|
|
37
37
|
"antd-style": "3.7.1",
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
"react-i18next": "^11.15.1",
|
|
46
46
|
"react-router-dom": "^6.30.1"
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "619b4d06c934bf3266ba7f388438db018217c87d"
|
|
49
49
|
}
|
|
@@ -111,7 +111,7 @@ export class AssignFormGridModel extends FormGridModel {
|
|
|
111
111
|
return;
|
|
112
112
|
}
|
|
113
113
|
const fieldModel = binding.modelName;
|
|
114
|
-
const created = this.
|
|
114
|
+
const created = this.addSubModel('items', {
|
|
115
115
|
use: 'AssignFormItemModel',
|
|
116
116
|
stepParams: {
|
|
117
117
|
fieldSettings: {
|
|
@@ -137,8 +137,6 @@ export class AssignFormGridModel extends FormGridModel {
|
|
|
137
137
|
},
|
|
138
138
|
},
|
|
139
139
|
},
|
|
140
|
-
parentId: this.uid,
|
|
141
|
-
subKey: 'items',
|
|
142
140
|
});
|
|
143
141
|
created['assignValue'] = value;
|
|
144
142
|
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import React from 'react';
|
|
11
|
+
import { App, ConfigProvider } from 'antd';
|
|
12
|
+
import { describe, expect, it } from 'vitest';
|
|
13
|
+
import { render, screen, waitFor } from '@nocobase/test/client';
|
|
14
|
+
import {
|
|
15
|
+
FlowEngine,
|
|
16
|
+
FlowEngineProvider,
|
|
17
|
+
FlowRuntimeContext,
|
|
18
|
+
FlowSettingsContextProvider,
|
|
19
|
+
type FlowModel,
|
|
20
|
+
type IFlowModelRepository,
|
|
21
|
+
} from '@nocobase/flow-engine';
|
|
22
|
+
import { AssignFormGridModel } from '../AssignFormGridModel';
|
|
23
|
+
import { AssignFormItemModel } from '../AssignFormItemModel';
|
|
24
|
+
import { AssignFormModel } from '../AssignFormModel';
|
|
25
|
+
import { createAssignFieldValuesStep } from '../assignFieldValuesFlow';
|
|
26
|
+
import { InputFieldModel } from '../../../fields/InputFieldModel';
|
|
27
|
+
|
|
28
|
+
class MockFlowModelRepository implements IFlowModelRepository {
|
|
29
|
+
async findOne(): Promise<Record<string, any> | null> {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async save(model: FlowModel): Promise<Record<string, any>> {
|
|
34
|
+
return { uid: model.uid };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async destroy(): Promise<boolean> {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async move(): Promise<void> {}
|
|
42
|
+
|
|
43
|
+
async duplicate(): Promise<Record<string, any> | null> {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
describe('assignFieldValuesFlow (editor)', () => {
|
|
49
|
+
it('repairs AssignFormModel resource init and clears cached collection', async () => {
|
|
50
|
+
const engine = new FlowEngine();
|
|
51
|
+
engine.setModelRepository(new MockFlowModelRepository());
|
|
52
|
+
engine.registerModels({ AssignFormModel, AssignFormGridModel, AssignFormItemModel, InputFieldModel });
|
|
53
|
+
engine.context.defineProperty('location', { value: { search: '' } });
|
|
54
|
+
engine.context.defineProperty('themeToken', { value: { marginLG: 24 } });
|
|
55
|
+
|
|
56
|
+
const dsm = engine.context.dataSourceManager;
|
|
57
|
+
const main = dsm.getDataSource('main');
|
|
58
|
+
main.addCollection({
|
|
59
|
+
name: 'users',
|
|
60
|
+
fields: [{ name: 'nickname', type: 'string', interface: 'input' }],
|
|
61
|
+
});
|
|
62
|
+
const users = dsm.getCollection('main', 'users');
|
|
63
|
+
expect(users?.name).toBe('users');
|
|
64
|
+
|
|
65
|
+
const action = engine.createModel({
|
|
66
|
+
use: 'FlowModel',
|
|
67
|
+
uid: 'act-assign',
|
|
68
|
+
});
|
|
69
|
+
action.setStepParams('assignSettings', 'assignFieldValues', { assignedValues: { nickname: '1111' } });
|
|
70
|
+
|
|
71
|
+
const existing = engine.createModel<AssignFormModel>({
|
|
72
|
+
use: 'AssignFormModel',
|
|
73
|
+
uid: 'form-assign',
|
|
74
|
+
parentId: action.uid,
|
|
75
|
+
subKey: 'assignForm',
|
|
76
|
+
stepParams: {
|
|
77
|
+
resourceSettings: {
|
|
78
|
+
init: {
|
|
79
|
+
dataSourceKey: 'main',
|
|
80
|
+
collectionName: 'missing',
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
expect(existing.context.collection).toBeUndefined();
|
|
86
|
+
|
|
87
|
+
action.context.defineProperty('blockModel', { value: { collection: users } });
|
|
88
|
+
|
|
89
|
+
const step = createAssignFieldValuesStep({ settingsFlowKey: 'assignSettings' });
|
|
90
|
+
const schema = step.uiSchema();
|
|
91
|
+
const Editor = schema.editor?.['x-component'] as React.ComponentType;
|
|
92
|
+
expect(Editor).toBeTypeOf('function');
|
|
93
|
+
|
|
94
|
+
const flowSettingsCtx = new FlowRuntimeContext(action as any, 'assignSettings', 'settings');
|
|
95
|
+
|
|
96
|
+
render(
|
|
97
|
+
<FlowEngineProvider engine={engine}>
|
|
98
|
+
<ConfigProvider>
|
|
99
|
+
<App>
|
|
100
|
+
<FlowSettingsContextProvider value={flowSettingsCtx}>
|
|
101
|
+
<Editor />
|
|
102
|
+
</FlowSettingsContextProvider>
|
|
103
|
+
</App>
|
|
104
|
+
</ConfigProvider>
|
|
105
|
+
</FlowEngineProvider>,
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
await waitFor(() => {
|
|
109
|
+
const form = engine.findModelByParentId<AssignFormModel>(action.uid, 'assignForm');
|
|
110
|
+
expect(form?.getStepParams('resourceSettings', 'init')).toEqual({
|
|
111
|
+
dataSourceKey: 'main',
|
|
112
|
+
collectionName: 'users',
|
|
113
|
+
});
|
|
114
|
+
expect(form?.context.collection?.name).toBe('users');
|
|
115
|
+
expect(form?.context.collection?.getFields?.().length).toBeGreaterThan(0);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
await waitFor(() => {
|
|
119
|
+
expect(screen.getByText('nickname')).toBeInTheDocument();
|
|
120
|
+
const form = engine.findModelByParentId<AssignFormModel>(action.uid, 'assignForm');
|
|
121
|
+
expect(form?.subModels.grid.subModels.items.length).toBe(1);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
});
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
useFlowEngine,
|
|
16
16
|
useFlowSettingsContext,
|
|
17
17
|
} from '@nocobase/flow-engine';
|
|
18
|
-
import React, { useEffect
|
|
18
|
+
import React, { useEffect } from 'react';
|
|
19
19
|
import { CollectionActionModel } from '../../base/CollectionActionModel';
|
|
20
20
|
import { RecordActionModel } from '../../base/RecordActionModel';
|
|
21
21
|
import { AssignFormModel } from './AssignFormModel';
|
|
@@ -62,6 +62,39 @@ function getResourceInit(ctx: AssignFieldValuesContext): { dataSourceKey: string
|
|
|
62
62
|
return dsKey && collName ? { dataSourceKey: dsKey, collectionName: collName } : undefined;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
function resolveInitFromFlowSettings(
|
|
66
|
+
blockModel: unknown,
|
|
67
|
+
actionContext: AssignFieldValuesContext | undefined,
|
|
68
|
+
): { dataSourceKey: string; collectionName: string } | undefined {
|
|
69
|
+
const maybeBlockCollection = (blockModel as { collection?: { dataSourceKey?: string; name?: string } } | undefined)
|
|
70
|
+
?.collection;
|
|
71
|
+
const collection = maybeBlockCollection || getContextCollection(actionContext);
|
|
72
|
+
const dsKey = collection?.dataSourceKey;
|
|
73
|
+
const collName = collection?.name;
|
|
74
|
+
return dsKey && collName ? { dataSourceKey: dsKey, collectionName: collName } : undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function isValidResourceInit(init: unknown): init is {
|
|
78
|
+
dataSourceKey: string;
|
|
79
|
+
collectionName: string;
|
|
80
|
+
} {
|
|
81
|
+
if (!init || typeof init !== 'object') return false;
|
|
82
|
+
const obj = init as { dataSourceKey?: unknown; collectionName?: unknown };
|
|
83
|
+
return (
|
|
84
|
+
typeof obj.dataSourceKey === 'string' &&
|
|
85
|
+
!!obj.dataSourceKey &&
|
|
86
|
+
typeof obj.collectionName === 'string' &&
|
|
87
|
+
!!obj.collectionName
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function clearResourceContextCache(model: { context?: { removeCache?: (key: string) => void } } | undefined) {
|
|
92
|
+
model?.context?.removeCache?.('dataSource');
|
|
93
|
+
model?.context?.removeCache?.('collection');
|
|
94
|
+
model?.context?.removeCache?.('resource');
|
|
95
|
+
model?.context?.removeCache?.('association');
|
|
96
|
+
}
|
|
97
|
+
|
|
65
98
|
export function createAssignFormSubModelOptions(ctx: AssignFieldValuesContext) {
|
|
66
99
|
return {
|
|
67
100
|
async: true,
|
|
@@ -114,21 +147,52 @@ function AssignFieldsEditor(props: { settingsFlowKey: string; clearRecordContext
|
|
|
114
147
|
const { model, blockModel } = useFlowSettingsContext();
|
|
115
148
|
const action = model as AssignFieldValuesModel;
|
|
116
149
|
const engine = useFlowEngine();
|
|
117
|
-
const initializedRef = useRef(false);
|
|
118
150
|
const [formModel, setFormModel] = React.useState<AssignFormModel | null>(null);
|
|
119
151
|
|
|
120
152
|
useEffect(() => {
|
|
121
153
|
let cancelled = false;
|
|
122
154
|
const loadAssignForm = async () => {
|
|
155
|
+
const init = resolveInitFromFlowSettings(blockModel, action?.context);
|
|
123
156
|
const loaded = (await engine.loadOrCreateModel(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
157
|
+
init
|
|
158
|
+
? {
|
|
159
|
+
parentId: action.uid,
|
|
160
|
+
subKey: 'assignForm',
|
|
161
|
+
use: 'AssignFormModel',
|
|
162
|
+
stepParams: { resourceSettings: { init } },
|
|
163
|
+
}
|
|
164
|
+
: {
|
|
165
|
+
parentId: action.uid,
|
|
166
|
+
subKey: 'assignForm',
|
|
167
|
+
use: 'AssignFormModel',
|
|
168
|
+
},
|
|
129
169
|
{ skipSave: !model.context.flowSettingsEnabled },
|
|
130
170
|
)) as AssignFormModel;
|
|
131
171
|
if (cancelled) return;
|
|
172
|
+
|
|
173
|
+
if (isValidResourceInit(init)) {
|
|
174
|
+
const existingInit = loaded.getStepParams?.('resourceSettings', 'init');
|
|
175
|
+
// Ensure `resourceSettings.init` is available before first render to avoid cached undefined `collection`
|
|
176
|
+
if (
|
|
177
|
+
!isValidResourceInit(existingInit) ||
|
|
178
|
+
existingInit.dataSourceKey !== init.dataSourceKey ||
|
|
179
|
+
existingInit.collectionName !== init.collectionName
|
|
180
|
+
) {
|
|
181
|
+
loaded.setStepParams?.('resourceSettings', 'init', init);
|
|
182
|
+
}
|
|
183
|
+
clearResourceContextCache(loaded);
|
|
184
|
+
clearResourceContextCache(loaded.subModels?.grid);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const prev = action.getStepParams?.(props.settingsFlowKey, ASSIGN_FIELD_VALUES_STEP_KEY) || {};
|
|
188
|
+
loaded.setInitialAssignedValues(prev?.assignedValues || {});
|
|
189
|
+
|
|
190
|
+
const isBulkCollectionAction = model instanceof CollectionActionModel && !(model instanceof RecordActionModel);
|
|
191
|
+
const isBulk = props.clearRecordContext || isBulkCollectionAction;
|
|
192
|
+
if (isBulk && loaded.context?.defineProperty) {
|
|
193
|
+
loaded.context.defineProperty('record', { get: () => undefined, cache: false });
|
|
194
|
+
}
|
|
195
|
+
|
|
132
196
|
setFormModel(loaded);
|
|
133
197
|
action.assignFormUid = loaded?.uid || action.assignFormUid;
|
|
134
198
|
};
|
|
@@ -136,31 +200,15 @@ function AssignFieldsEditor(props: { settingsFlowKey: string; clearRecordContext
|
|
|
136
200
|
return () => {
|
|
137
201
|
cancelled = true;
|
|
138
202
|
};
|
|
139
|
-
}, [
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const collName = coll?.name;
|
|
149
|
-
if (dsKey && collName) {
|
|
150
|
-
formModel.setStepParams('resourceSettings', 'init', {
|
|
151
|
-
dataSourceKey: dsKey,
|
|
152
|
-
collectionName: collName,
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
formModel.setInitialAssignedValues(prev?.assignedValues || {});
|
|
156
|
-
|
|
157
|
-
const isBulk =
|
|
158
|
-
props.clearRecordContext || (action instanceof CollectionActionModel && !(action instanceof RecordActionModel));
|
|
159
|
-
if (isBulk && formModel.context?.defineProperty) {
|
|
160
|
-
formModel.context.defineProperty('record', { get: () => undefined, cache: false });
|
|
161
|
-
}
|
|
162
|
-
initializedRef.current = true;
|
|
163
|
-
}, [action, blockModel?.collection, formModel, props.clearRecordContext, props.settingsFlowKey]);
|
|
203
|
+
}, [
|
|
204
|
+
action,
|
|
205
|
+
blockModel,
|
|
206
|
+
engine,
|
|
207
|
+
model,
|
|
208
|
+
model.context.flowSettingsEnabled,
|
|
209
|
+
props.clearRecordContext,
|
|
210
|
+
props.settingsFlowKey,
|
|
211
|
+
]);
|
|
164
212
|
|
|
165
213
|
return formModel ? <FlowModelRenderer model={formModel} showFlowSettings={false} /> : null;
|
|
166
214
|
}
|