@gadmin2n/schematics 0.0.147 → 0.0.149
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/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/workflow.prisma +2 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/permission.seed.ts +67 -15
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +83 -53
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/config/index.ts +5 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/structured-all-exceptions.filter.spec.ts +24 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/lib/structured-all-exceptions.filter.ts +15 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agenda/agenda.controller.ts +4 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agenda/agenda.service.ts +31 -4
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/codeRuntime/code-runtime.controller.spec.ts +2 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/approval-subject-source.util.spec.ts +28 -29
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/approval-subject-source.util.ts +16 -34
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.dto.ts +3 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/human-approval.service.spec.ts +61 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/humanApproval/runtime/human-approval-runtime.controller.spec.ts +2 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/index.ts +2 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/integrationConnection/integration-connection.controller.ts +11 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/integrationConnection/integration-connection.service.spec.ts +126 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/integrationConnection/integration-connection.service.ts +136 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/staffDirectory/staff-directory.controller.ts +24 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/staffDirectory/staff-directory.dto.ts +8 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/staffDirectory/staff-directory.service.spec.ts +62 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/staffDirectory/staff-directory.service.ts +94 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/staffDirectory/staff-directory.types.ts +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/staffDirectory/staffDirectory.module.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/providers/user.provider.ts +6 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/temporalTenantConfig.controller.spec.ts +20 -12
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/temporalTenantConfig/temporalTenantConfig.controller.ts +21 -19
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.controller.ts +11 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.service.spec.ts +97 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.service.ts +150 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.controller.ts +13 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.dto.ts +13 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.errors.ts +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.service.spec.ts +125 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/userGroup/user-group.service.ts +118 -5
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-cancellation.spec.ts +20 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.spec.ts +6 -9
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-event-catalog.spec.ts +166 -17
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-event-trigger.pg.spec.ts +82 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-runtime.facade.ts +2 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.spec.ts +4 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +5 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.dto.ts +7 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.spec.ts +44 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +106 -37
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/node-config-validator.service.spec.ts +59 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/subject-config-validator.service.spec.ts +33 -22
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/subject-config-validator.service.ts +10 -23
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/StaffSelect/StaffSelect.test.tsx +69 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/StaffSelect/StaffSelect.tsx +145 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/StaffSelect/index.ts +2 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/StaffSelect/staff-directory.api.ts +16 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/index.ts +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.test.ts +10 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/routeRegistry.tsx +20 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useDynamicResources.tsx +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/workflow.json +60 -14
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/workflow.json +60 -14
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agenda/index.tsx +41 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/people-groups/PeopleGroupModal.tsx +181 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/people-groups/index.tsx +203 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/people-groups/people-groups.api.ts +145 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/person-directory/PersonModal.tsx +112 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/person-directory/index.tsx +214 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/person-directory/person-directory.api.ts +79 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/components/form-modal.tsx +7 -11
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/list.tsx +7 -7
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/RegisterBusinessEventModal.test.tsx +46 -5
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/RegisterBusinessEventModal.tsx +14 -10
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/detail.test.tsx +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/detail.tsx +2 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/index.test.tsx +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/business-events/index.tsx +2 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodeOutputSummary.test.tsx +20 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodePropertyPanel.test.tsx +97 -28
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodePropertyPanel.tsx +87 -119
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/WorkflowCodeEditor.test.tsx +22 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/WorkflowCodeEditor.tsx +8 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/connections.css +8 -46
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/connectionsApi.ts +7 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/index.test.tsx +32 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/connections/index.tsx +163 -106
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/index.tsx +8 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.test.tsx +26 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.tsx +34 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/node-config.css +13 -16
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/node-instance-show.css +185 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/show.tsx +224 -105
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/CodeConfigEditor.test.tsx +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/DbExecuteConfigEditor.tsx +21 -12
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/EventTriggerConfigEditor.test.tsx +140 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/EventTriggerConfigEditor.tsx +124 -62
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/HumanApprovalConfigEditor.test.tsx +160 -8
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/HumanApprovalConfigEditor.tsx +125 -55
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SimplifiedNodeEditors.test.tsx +78 -17
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SubjectPicker.test.tsx +33 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SubjectPicker.tsx +28 -46
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/event-catalog.api.ts +4 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/human-approval-config.css +16 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/subject-provider.api.ts +0 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.test.tsx +24 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/temporal-tenant/index.tsx +14 -17
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/types.ts +1 -0
- package/package.json +1 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/CreateNodeInstanceDrawer.tsx +0 -162
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/UserGroupCreateModal.tsx +0 -72
|
@@ -4,7 +4,33 @@ import i18n from '../../../../i18n';
|
|
|
4
4
|
import { HumanApprovalConfigEditor } from './HumanApprovalConfigEditor';
|
|
5
5
|
|
|
6
6
|
vi.mock('./SubjectPicker', () => ({
|
|
7
|
-
SubjectPicker: () =>
|
|
7
|
+
SubjectPicker: (props: any) => {
|
|
8
|
+
const provider = props.allowedProviders?.[0];
|
|
9
|
+
const isGroup = provider === 'USER_GROUP';
|
|
10
|
+
return (
|
|
11
|
+
<div data-testid={`subject-picker-${provider}`}>
|
|
12
|
+
<button
|
|
13
|
+
aria-label={isGroup ? '选择固定人员组' : '选择固定人员'}
|
|
14
|
+
onClick={() =>
|
|
15
|
+
props.onChange([
|
|
16
|
+
{
|
|
17
|
+
provider,
|
|
18
|
+
refs: [isGroup ? 'finance' : 'alice'],
|
|
19
|
+
},
|
|
20
|
+
])
|
|
21
|
+
}
|
|
22
|
+
/>
|
|
23
|
+
{!isGroup ? (
|
|
24
|
+
<button
|
|
25
|
+
aria-label="继续添加固定人员"
|
|
26
|
+
onClick={() =>
|
|
27
|
+
props.onChange([{ provider: 'USER', refs: ['alice', 'bob'] }])
|
|
28
|
+
}
|
|
29
|
+
/>
|
|
30
|
+
) : null}
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
},
|
|
8
34
|
}));
|
|
9
35
|
|
|
10
36
|
Object.defineProperty(window, 'matchMedia', {
|
|
@@ -116,9 +142,9 @@ describe('HumanApprovalConfigEditor', () => {
|
|
|
116
142
|
);
|
|
117
143
|
});
|
|
118
144
|
|
|
119
|
-
it('
|
|
145
|
+
it('can add multiple HTTP and Code lookups inside the approval node', () => {
|
|
120
146
|
const onChange = vi.fn();
|
|
121
|
-
const { container } = render(
|
|
147
|
+
const { container, rerender } = render(
|
|
122
148
|
<HumanApprovalConfigEditor
|
|
123
149
|
value={config}
|
|
124
150
|
onChange={onChange}
|
|
@@ -126,30 +152,157 @@ describe('HumanApprovalConfigEditor', () => {
|
|
|
126
152
|
/>,
|
|
127
153
|
);
|
|
128
154
|
|
|
129
|
-
fireEvent.click(screen.getByRole('button', { name: '
|
|
155
|
+
fireEvent.click(screen.getByRole('button', { name: '添加动态审批人' }));
|
|
130
156
|
|
|
131
157
|
expect(onChange).toHaveBeenLastCalledWith(
|
|
132
158
|
expect.objectContaining({
|
|
133
159
|
subjects: [
|
|
134
160
|
{
|
|
135
161
|
source: 'DYNAMIC',
|
|
136
|
-
provider: 'USER',
|
|
137
162
|
inputKey: 'dynamic_users',
|
|
138
163
|
label: '查询审批人',
|
|
139
164
|
lookup: {
|
|
140
165
|
request: expect.objectContaining({ method: 'GET', path: '/' }),
|
|
141
166
|
result: expect.objectContaining({
|
|
142
167
|
mode: 'runOnceForAll',
|
|
143
|
-
script: expect.stringContaining(
|
|
168
|
+
script: expect.stringContaining('return approverIds;'),
|
|
144
169
|
}),
|
|
145
170
|
},
|
|
146
171
|
},
|
|
147
172
|
],
|
|
148
173
|
}),
|
|
149
174
|
);
|
|
175
|
+
const dynamicConfig = onChange.mock.lastCall?.[0];
|
|
176
|
+
rerender(
|
|
177
|
+
<HumanApprovalConfigEditor
|
|
178
|
+
value={dynamicConfig}
|
|
179
|
+
onChange={onChange}
|
|
180
|
+
schema={{ type: 'object' }}
|
|
181
|
+
/>,
|
|
182
|
+
);
|
|
183
|
+
fireEvent.click(screen.getByRole('button', { name: '添加动态审批人' }));
|
|
184
|
+
expect(onChange.mock.lastCall?.[0].subjects).toEqual([
|
|
185
|
+
dynamicConfig.subjects[0],
|
|
186
|
+
expect.objectContaining({
|
|
187
|
+
source: 'DYNAMIC',
|
|
188
|
+
inputKey: 'dynamic_users_2',
|
|
189
|
+
}),
|
|
190
|
+
]);
|
|
150
191
|
expect(container.querySelector('.human-approval-config__rail')).toBeNull();
|
|
151
192
|
});
|
|
152
193
|
|
|
194
|
+
it('can add repeated fixed sources and a separate fixed group source', () => {
|
|
195
|
+
const onChange = vi.fn();
|
|
196
|
+
const { rerender } = render(
|
|
197
|
+
<HumanApprovalConfigEditor
|
|
198
|
+
value={config}
|
|
199
|
+
onChange={onChange}
|
|
200
|
+
schema={{ type: 'object' }}
|
|
201
|
+
/>,
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
expect(screen.queryByTestId('subject-picker-USER')).not.toBeInTheDocument();
|
|
205
|
+
expect(screen.getByText('暂未添加固定人员或人员组')).toBeInTheDocument();
|
|
206
|
+
|
|
207
|
+
fireEvent.click(screen.getByRole('button', { name: '添加人员' }));
|
|
208
|
+
const peopleConfig = onChange.mock.lastCall?.[0];
|
|
209
|
+
expect(peopleConfig).toEqual(
|
|
210
|
+
expect.objectContaining({
|
|
211
|
+
subjects: [{ source: 'STATIC', provider: 'USER', refs: [] }],
|
|
212
|
+
}),
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
rerender(
|
|
216
|
+
<HumanApprovalConfigEditor
|
|
217
|
+
value={peopleConfig}
|
|
218
|
+
onChange={onChange}
|
|
219
|
+
schema={{ type: 'object' }}
|
|
220
|
+
/>,
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
expect(screen.getByTestId('subject-picker-USER')).toBeInTheDocument();
|
|
224
|
+
expect(
|
|
225
|
+
screen.getByRole('button', { name: '添加人员组' }),
|
|
226
|
+
).toBeInTheDocument();
|
|
227
|
+
|
|
228
|
+
fireEvent.click(screen.getByRole('button', { name: '选择固定人员' }));
|
|
229
|
+
const selectedPeopleConfig = onChange.mock.lastCall?.[0];
|
|
230
|
+
expect(selectedPeopleConfig).toEqual(
|
|
231
|
+
expect.objectContaining({
|
|
232
|
+
subjects: [{ source: 'STATIC', provider: 'USER', refs: ['alice'] }],
|
|
233
|
+
}),
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
rerender(
|
|
237
|
+
<HumanApprovalConfigEditor
|
|
238
|
+
value={selectedPeopleConfig}
|
|
239
|
+
onChange={onChange}
|
|
240
|
+
schema={{ type: 'object' }}
|
|
241
|
+
/>,
|
|
242
|
+
);
|
|
243
|
+
fireEvent.click(screen.getByRole('button', { name: '继续添加固定人员' }));
|
|
244
|
+
const morePeopleConfig = onChange.mock.lastCall?.[0];
|
|
245
|
+
expect(morePeopleConfig.subjects[0].refs).toEqual(['alice', 'bob']);
|
|
246
|
+
|
|
247
|
+
rerender(
|
|
248
|
+
<HumanApprovalConfigEditor
|
|
249
|
+
value={morePeopleConfig}
|
|
250
|
+
onChange={onChange}
|
|
251
|
+
schema={{ type: 'object' }}
|
|
252
|
+
/>,
|
|
253
|
+
);
|
|
254
|
+
expect(
|
|
255
|
+
screen.getByRole('button', { name: '添加人员' }),
|
|
256
|
+
).toBeInTheDocument();
|
|
257
|
+
fireEvent.click(screen.getByRole('button', { name: '添加人员' }));
|
|
258
|
+
const repeatedPeopleConfig = onChange.mock.lastCall?.[0];
|
|
259
|
+
expect(repeatedPeopleConfig.subjects).toEqual([
|
|
260
|
+
{ source: 'STATIC', provider: 'USER', refs: ['alice', 'bob'] },
|
|
261
|
+
{ source: 'STATIC', provider: 'USER', refs: [] },
|
|
262
|
+
]);
|
|
263
|
+
|
|
264
|
+
rerender(
|
|
265
|
+
<HumanApprovalConfigEditor
|
|
266
|
+
value={repeatedPeopleConfig}
|
|
267
|
+
onChange={onChange}
|
|
268
|
+
schema={{ type: 'object' }}
|
|
269
|
+
/>,
|
|
270
|
+
);
|
|
271
|
+
expect(screen.getAllByTestId('subject-picker-USER')).toHaveLength(2);
|
|
272
|
+
fireEvent.click(screen.getAllByRole('button', { name: '选择固定人员' })[1]);
|
|
273
|
+
const selectedRepeatedPeopleConfig = onChange.mock.lastCall?.[0];
|
|
274
|
+
|
|
275
|
+
rerender(
|
|
276
|
+
<HumanApprovalConfigEditor
|
|
277
|
+
value={selectedRepeatedPeopleConfig}
|
|
278
|
+
onChange={onChange}
|
|
279
|
+
schema={{ type: 'object' }}
|
|
280
|
+
/>,
|
|
281
|
+
);
|
|
282
|
+
fireEvent.click(screen.getByRole('button', { name: '添加人员组' }));
|
|
283
|
+
const bothSourcesConfig = onChange.mock.lastCall?.[0];
|
|
284
|
+
rerender(
|
|
285
|
+
<HumanApprovalConfigEditor
|
|
286
|
+
value={bothSourcesConfig}
|
|
287
|
+
onChange={onChange}
|
|
288
|
+
schema={{ type: 'object' }}
|
|
289
|
+
/>,
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
expect(screen.getAllByTestId('subject-picker-USER')).toHaveLength(2);
|
|
293
|
+
expect(screen.getByTestId('subject-picker-USER_GROUP')).toBeInTheDocument();
|
|
294
|
+
fireEvent.click(screen.getByRole('button', { name: '选择固定人员组' }));
|
|
295
|
+
expect(onChange).toHaveBeenLastCalledWith(
|
|
296
|
+
expect.objectContaining({
|
|
297
|
+
subjects: expect.arrayContaining([
|
|
298
|
+
{ source: 'STATIC', provider: 'USER', refs: ['alice', 'bob'] },
|
|
299
|
+
{ source: 'STATIC', provider: 'USER', refs: ['alice'] },
|
|
300
|
+
{ source: 'STATIC', provider: 'USER_GROUP', refs: ['finance'] },
|
|
301
|
+
]),
|
|
302
|
+
}),
|
|
303
|
+
);
|
|
304
|
+
});
|
|
305
|
+
|
|
153
306
|
it('uses the collapsible sections without repeating a lookup flow summary', () => {
|
|
154
307
|
const { container } = render(
|
|
155
308
|
<HumanApprovalConfigEditor
|
|
@@ -158,7 +311,6 @@ describe('HumanApprovalConfigEditor', () => {
|
|
|
158
311
|
subjects: [
|
|
159
312
|
{
|
|
160
313
|
source: 'DYNAMIC',
|
|
161
|
-
provider: 'USER',
|
|
162
314
|
inputKey: 'direct_manager',
|
|
163
315
|
label: '查询直属主管',
|
|
164
316
|
lookup: {
|
|
@@ -174,7 +326,7 @@ describe('HumanApprovalConfigEditor', () => {
|
|
|
174
326
|
result: {
|
|
175
327
|
mode: 'runOnceForAll',
|
|
176
328
|
timeout: 5000,
|
|
177
|
-
script:
|
|
329
|
+
script: "return ['leader-a'];",
|
|
178
330
|
},
|
|
179
331
|
},
|
|
180
332
|
},
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
Switch,
|
|
16
16
|
Typography,
|
|
17
17
|
} from 'antd';
|
|
18
|
-
import {
|
|
18
|
+
import { useState } from 'react';
|
|
19
19
|
import { useTranslation } from 'react-i18next';
|
|
20
20
|
import { ParameterBindingEditor } from '../../components/ParameterBindingEditor';
|
|
21
21
|
import type { ParameterBinding } from '../../types/parameter-binding';
|
|
@@ -26,17 +26,19 @@ import { HttpRequestConfigEditor } from './HttpRequestConfigEditor';
|
|
|
26
26
|
import { SubjectPicker, type SubjectSelection } from './SubjectPicker';
|
|
27
27
|
import './human-approval-config.css';
|
|
28
28
|
|
|
29
|
-
type Provider = 'USER' | 'USER_GROUP';
|
|
30
29
|
type EmbeddedCodeConfig = {
|
|
31
30
|
script: string;
|
|
32
31
|
mode: 'runOnceForAll';
|
|
33
32
|
timeout: number;
|
|
34
33
|
};
|
|
35
34
|
type SubjectSource =
|
|
36
|
-
| {
|
|
35
|
+
| {
|
|
36
|
+
source: 'STATIC';
|
|
37
|
+
provider?: 'USER' | 'USER_GROUP';
|
|
38
|
+
refs: string[];
|
|
39
|
+
}
|
|
37
40
|
| {
|
|
38
41
|
source: 'DYNAMIC';
|
|
39
|
-
provider: Provider;
|
|
40
42
|
inputKey: string;
|
|
41
43
|
label: string;
|
|
42
44
|
lookup: {
|
|
@@ -49,6 +51,7 @@ type Rule = {
|
|
|
49
51
|
count?: number | null;
|
|
50
52
|
percentage?: number | null;
|
|
51
53
|
};
|
|
54
|
+
|
|
52
55
|
type TaskDetail = {
|
|
53
56
|
key: string;
|
|
54
57
|
label: string;
|
|
@@ -126,7 +129,7 @@ return {
|
|
|
126
129
|
},
|
|
127
130
|
};
|
|
128
131
|
|
|
129
|
-
function defaultDynamicLookup(
|
|
132
|
+
function defaultDynamicLookup(): {
|
|
130
133
|
request: Record<string, unknown>;
|
|
131
134
|
result: EmbeddedCodeConfig;
|
|
132
135
|
} {
|
|
@@ -145,13 +148,11 @@ function defaultDynamicLookup(provider: Provider): {
|
|
|
145
148
|
timeout: 5000,
|
|
146
149
|
script: `// response 是查询接口返回的 { status, data, headers }。
|
|
147
150
|
// 从响应中提取审批人 ID,并返回统一格式。
|
|
148
|
-
const
|
|
149
|
-
if (!Array.isArray(
|
|
151
|
+
const approverIds = response?.data?.approverIds;
|
|
152
|
+
if (!Array.isArray(approverIds) || approverIds.length === 0) {
|
|
150
153
|
throw new Error('未查询到有效的审批人');
|
|
151
154
|
}
|
|
152
|
-
return
|
|
153
|
-
subjects: [{ provider: '${provider}', refs }],
|
|
154
|
-
};`,
|
|
155
|
+
return approverIds;`,
|
|
155
156
|
},
|
|
156
157
|
};
|
|
157
158
|
}
|
|
@@ -176,16 +177,19 @@ export function HumanApprovalConfigEditor({
|
|
|
176
177
|
blockCancellationOnResolved: value?.blockCancellationOnResolved ?? false,
|
|
177
178
|
timeout: value?.timeout ?? { durationSeconds: 0, action: 'REJECTED' },
|
|
178
179
|
};
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
180
|
+
const staticSources = config.subjects.flatMap((subject, subjectIndex) =>
|
|
181
|
+
subject.source === 'STATIC'
|
|
182
|
+
? [
|
|
183
|
+
{
|
|
184
|
+
subjectIndex,
|
|
185
|
+
selection: {
|
|
186
|
+
provider: subject.provider ?? 'USER',
|
|
187
|
+
refs: subject.refs,
|
|
188
|
+
} satisfies SubjectSelection,
|
|
189
|
+
},
|
|
190
|
+
]
|
|
191
|
+
: [],
|
|
192
|
+
);
|
|
189
193
|
const dynamicSources = config.subjects.filter(
|
|
190
194
|
(subject): subject is Extract<SubjectSource, { source: 'DYNAMIC' }> =>
|
|
191
195
|
subject.source === 'DYNAMIC',
|
|
@@ -219,19 +223,38 @@ export function HumanApprovalConfigEditor({
|
|
|
219
223
|
onChange(gadminOnlyConfig);
|
|
220
224
|
}
|
|
221
225
|
|
|
222
|
-
function updateStatic(
|
|
226
|
+
function updateStatic(
|
|
227
|
+
subjectIndex: number,
|
|
228
|
+
provider: 'USER' | 'USER_GROUP',
|
|
229
|
+
next: SubjectSelection[],
|
|
230
|
+
) {
|
|
231
|
+
const refs =
|
|
232
|
+
next.find((subject) => subject.provider === provider)?.refs ?? [];
|
|
233
|
+
update({
|
|
234
|
+
subjects: config.subjects.map((subject, index) =>
|
|
235
|
+
index === subjectIndex
|
|
236
|
+
? { source: 'STATIC' as const, provider, refs }
|
|
237
|
+
: subject,
|
|
238
|
+
),
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function addStatic(provider: 'USER' | 'USER_GROUP' = 'USER') {
|
|
223
243
|
update({
|
|
224
244
|
subjects: [
|
|
225
|
-
...
|
|
226
|
-
|
|
227
|
-
provider: subject.provider as Provider,
|
|
228
|
-
refs: subject.refs,
|
|
229
|
-
})),
|
|
245
|
+
...config.subjects.filter((subject) => subject.source === 'STATIC'),
|
|
246
|
+
{ source: 'STATIC', provider, refs: [] },
|
|
230
247
|
...dynamicSources,
|
|
231
248
|
],
|
|
232
249
|
});
|
|
233
250
|
}
|
|
234
251
|
|
|
252
|
+
function removeStatic(subjectIndex: number) {
|
|
253
|
+
update({
|
|
254
|
+
subjects: config.subjects.filter((_, index) => index !== subjectIndex),
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
235
258
|
function updateDetail(index: number, patch: Partial<TaskDetail>) {
|
|
236
259
|
updateTask({
|
|
237
260
|
details: config.task.details.map((detail, current) =>
|
|
@@ -253,8 +276,8 @@ export function HumanApprovalConfigEditor({
|
|
|
253
276
|
});
|
|
254
277
|
}
|
|
255
278
|
|
|
256
|
-
function addDynamic(
|
|
257
|
-
const base =
|
|
279
|
+
function addDynamic() {
|
|
280
|
+
const base = 'dynamic_users';
|
|
258
281
|
const existing = new Set(dynamicSources.map((source) => source.inputKey));
|
|
259
282
|
let inputKey = base;
|
|
260
283
|
let suffix = 2;
|
|
@@ -264,13 +287,9 @@ export function HumanApprovalConfigEditor({
|
|
|
264
287
|
...config.subjects,
|
|
265
288
|
{
|
|
266
289
|
source: 'DYNAMIC',
|
|
267
|
-
provider,
|
|
268
290
|
inputKey,
|
|
269
|
-
label:
|
|
270
|
-
|
|
271
|
-
? t('approval.dynamicUserDefaultName')
|
|
272
|
-
: t('approval.dynamicGroupDefaultName'),
|
|
273
|
-
lookup: defaultDynamicLookup(provider),
|
|
291
|
+
label: t('approval.dynamicUserDefaultName'),
|
|
292
|
+
lookup: defaultDynamicLookup(),
|
|
274
293
|
},
|
|
275
294
|
],
|
|
276
295
|
});
|
|
@@ -554,13 +573,76 @@ export function HumanApprovalConfigEditor({
|
|
|
554
573
|
>
|
|
555
574
|
{t('approval.description')}
|
|
556
575
|
</Typography.Paragraph>
|
|
557
|
-
<
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
576
|
+
<div className="human-approval-config__fixed-heading">
|
|
577
|
+
<Typography.Text strong>
|
|
578
|
+
{t('approval.fixedApprovers')}
|
|
579
|
+
</Typography.Text>
|
|
580
|
+
<Space wrap size={8}>
|
|
581
|
+
<Button
|
|
582
|
+
size="small"
|
|
583
|
+
className="workflow-panel-action"
|
|
584
|
+
icon={<UserAddOutlined />}
|
|
585
|
+
aria-label={t('approval.addFixedUsers')}
|
|
586
|
+
disabled={disabled}
|
|
587
|
+
onClick={() => addStatic('USER')}
|
|
588
|
+
>
|
|
589
|
+
{t('approval.addFixedUsers')}
|
|
590
|
+
</Button>
|
|
591
|
+
<Button
|
|
592
|
+
size="small"
|
|
593
|
+
className="workflow-panel-action"
|
|
594
|
+
icon={<UsergroupAddOutlined />}
|
|
595
|
+
aria-label={t('approval.addFixedUserGroups')}
|
|
596
|
+
disabled={disabled}
|
|
597
|
+
onClick={() => addStatic('USER_GROUP')}
|
|
598
|
+
>
|
|
599
|
+
{t('approval.addFixedUserGroups')}
|
|
600
|
+
</Button>
|
|
601
|
+
</Space>
|
|
602
|
+
</div>
|
|
603
|
+
{staticSources.length === 0 ? (
|
|
604
|
+
<div className="human-approval-config__empty">
|
|
605
|
+
{t('approval.noFixedApprovers')}
|
|
606
|
+
</div>
|
|
607
|
+
) : (
|
|
608
|
+
staticSources.map(({ subjectIndex, selection }) => {
|
|
609
|
+
const name =
|
|
610
|
+
selection.provider === 'USER_GROUP'
|
|
611
|
+
? t('approval.fixedUserGroups')
|
|
612
|
+
: t('approval.fixedUsers');
|
|
613
|
+
return (
|
|
614
|
+
<div
|
|
615
|
+
className="human-approval-config__static-source"
|
|
616
|
+
key={`${selection.provider}-${subjectIndex}`}
|
|
617
|
+
>
|
|
618
|
+
<div className="human-approval-config__static-source-header">
|
|
619
|
+
<Typography.Text strong>{name}</Typography.Text>
|
|
620
|
+
<Button
|
|
621
|
+
type="text"
|
|
622
|
+
danger
|
|
623
|
+
icon={<DeleteOutlined />}
|
|
624
|
+
disabled={disabled}
|
|
625
|
+
aria-label={t('approval.removeFixedSource', { name })}
|
|
626
|
+
onClick={() => removeStatic(subjectIndex)}
|
|
627
|
+
/>
|
|
628
|
+
</div>
|
|
629
|
+
<SubjectPicker
|
|
630
|
+
value={[selection]}
|
|
631
|
+
onChange={(next) =>
|
|
632
|
+
updateStatic(
|
|
633
|
+
subjectIndex,
|
|
634
|
+
selection.provider as 'USER' | 'USER_GROUP',
|
|
635
|
+
next,
|
|
636
|
+
)
|
|
637
|
+
}
|
|
638
|
+
disabled={disabled}
|
|
639
|
+
schema={schema}
|
|
640
|
+
allowedProviders={[selection.provider]}
|
|
641
|
+
/>
|
|
642
|
+
</div>
|
|
643
|
+
);
|
|
644
|
+
})
|
|
645
|
+
)}
|
|
564
646
|
<div className="human-approval-config__dynamic-heading">
|
|
565
647
|
<div>
|
|
566
648
|
<Typography.Text strong>
|
|
@@ -574,20 +656,10 @@ export function HumanApprovalConfigEditor({
|
|
|
574
656
|
icon={<UserAddOutlined />}
|
|
575
657
|
aria-label={t('approval.addUsers')}
|
|
576
658
|
disabled={disabled}
|
|
577
|
-
onClick={
|
|
659
|
+
onClick={addDynamic}
|
|
578
660
|
>
|
|
579
661
|
{t('approval.addUsers')}
|
|
580
662
|
</Button>
|
|
581
|
-
<Button
|
|
582
|
-
size="small"
|
|
583
|
-
className="workflow-panel-action"
|
|
584
|
-
icon={<UsergroupAddOutlined />}
|
|
585
|
-
aria-label={t('approval.addGroups')}
|
|
586
|
-
disabled={disabled}
|
|
587
|
-
onClick={() => addDynamic('USER_GROUP')}
|
|
588
|
-
>
|
|
589
|
-
{t('approval.addGroups')}
|
|
590
|
-
</Button>
|
|
591
663
|
</Space>
|
|
592
664
|
</div>
|
|
593
665
|
{dynamicSources.length === 0 ? (
|
|
@@ -603,9 +675,7 @@ export function HumanApprovalConfigEditor({
|
|
|
603
675
|
<div className="human-approval-config__dynamic-source-header">
|
|
604
676
|
<div>
|
|
605
677
|
<Typography.Text strong>
|
|
606
|
-
{
|
|
607
|
-
? t('approval.dynamicUsers')
|
|
608
|
-
: t('approval.dynamicGroups')}
|
|
678
|
+
{t('approval.dynamicUsers')}
|
|
609
679
|
</Typography.Text>
|
|
610
680
|
<Typography.Text
|
|
611
681
|
type="secondary"
|
|
@@ -9,6 +9,28 @@ import {
|
|
|
9
9
|
import { EventTriggerConfigEditor } from './EventTriggerConfigEditor';
|
|
10
10
|
import { ParallelConfigEditor } from './ParallelConfigEditor';
|
|
11
11
|
|
|
12
|
+
vi.mock('@monaco-editor/react', () => ({
|
|
13
|
+
default: ({
|
|
14
|
+
language,
|
|
15
|
+
value,
|
|
16
|
+
onChange,
|
|
17
|
+
options,
|
|
18
|
+
}: {
|
|
19
|
+
language?: string;
|
|
20
|
+
value?: string;
|
|
21
|
+
onChange?(value: string): void;
|
|
22
|
+
options?: { ariaLabel?: string; readOnly?: boolean };
|
|
23
|
+
}) => (
|
|
24
|
+
<textarea
|
|
25
|
+
aria-label={options?.ariaLabel}
|
|
26
|
+
data-language={language}
|
|
27
|
+
readOnly={options?.readOnly}
|
|
28
|
+
value={value}
|
|
29
|
+
onChange={(event) => onChange?.(event.target.value)}
|
|
30
|
+
/>
|
|
31
|
+
),
|
|
32
|
+
}));
|
|
33
|
+
|
|
12
34
|
vi.mock('./event-catalog.api', () => ({
|
|
13
35
|
eventCatalogApi: {
|
|
14
36
|
list: vi.fn().mockResolvedValue([
|
|
@@ -17,7 +39,7 @@ vi.mock('./event-catalog.api', () => ({
|
|
|
17
39
|
label: '用户创建',
|
|
18
40
|
schemaName: 'public',
|
|
19
41
|
tableName: 't_user',
|
|
20
|
-
|
|
42
|
+
operations: ['INSERT'],
|
|
21
43
|
watchedFields: ['id'],
|
|
22
44
|
primaryKeyFields: ['id'],
|
|
23
45
|
isEnabled: true,
|
|
@@ -46,7 +68,7 @@ vi.mock('./event-catalog.api', () => ({
|
|
|
46
68
|
label: '硬件资产报废申请',
|
|
47
69
|
schemaName: 'public',
|
|
48
70
|
tableName: 't_hardware_asset',
|
|
49
|
-
|
|
71
|
+
operations: ['UPDATE'],
|
|
50
72
|
watchedFields: ['status'],
|
|
51
73
|
primaryKeyFields: ['id'],
|
|
52
74
|
isEnabled: true,
|
|
@@ -390,7 +412,11 @@ describe('simplified workflow node editors', () => {
|
|
|
390
412
|
const onChange = vi.fn();
|
|
391
413
|
render(
|
|
392
414
|
<EventTriggerConfigEditor
|
|
393
|
-
value={{
|
|
415
|
+
value={{
|
|
416
|
+
registrationCode: '',
|
|
417
|
+
operationRules: [],
|
|
418
|
+
processingMode: 'PARALLEL',
|
|
419
|
+
}}
|
|
394
420
|
onChange={onChange}
|
|
395
421
|
schema={schema}
|
|
396
422
|
/>,
|
|
@@ -403,6 +429,7 @@ describe('simplified workflow node editors', () => {
|
|
|
403
429
|
|
|
404
430
|
expect(onChange).toHaveBeenCalledWith({
|
|
405
431
|
registrationCode: 'user-account-created',
|
|
432
|
+
operationRules: [{ operation: 'INSERT' }],
|
|
406
433
|
processingMode: 'PARALLEL',
|
|
407
434
|
});
|
|
408
435
|
});
|
|
@@ -412,7 +439,11 @@ describe('simplified workflow node editors', () => {
|
|
|
412
439
|
const onChange = vi.fn();
|
|
413
440
|
render(
|
|
414
441
|
<EventTriggerConfigEditor
|
|
415
|
-
value={{
|
|
442
|
+
value={{
|
|
443
|
+
registrationCode: '',
|
|
444
|
+
operationRules: [],
|
|
445
|
+
processingMode: 'PARALLEL',
|
|
446
|
+
}}
|
|
416
447
|
onChange={onChange}
|
|
417
448
|
schema={schema}
|
|
418
449
|
/>,
|
|
@@ -425,10 +456,33 @@ describe('simplified workflow node editors', () => {
|
|
|
425
456
|
|
|
426
457
|
expect(onChange).toHaveBeenCalledWith({
|
|
427
458
|
registrationCode: 'asset-disposal-submitted',
|
|
459
|
+
operationRules: [{ operation: 'UPDATE' }],
|
|
428
460
|
processingMode: 'PARALLEL',
|
|
429
461
|
});
|
|
430
462
|
});
|
|
431
463
|
|
|
464
|
+
it('shows only the source table in the selected business event summary', async () => {
|
|
465
|
+
await i18n.changeLanguage('zh_CN');
|
|
466
|
+
render(
|
|
467
|
+
<EventTriggerConfigEditor
|
|
468
|
+
value={{
|
|
469
|
+
registrationCode: 'asset-disposal-submitted',
|
|
470
|
+
operationRules: [{ operation: 'UPDATE' }],
|
|
471
|
+
processingMode: 'PARALLEL',
|
|
472
|
+
}}
|
|
473
|
+
onChange={vi.fn()}
|
|
474
|
+
schema={schema}
|
|
475
|
+
/>,
|
|
476
|
+
);
|
|
477
|
+
|
|
478
|
+
expect(
|
|
479
|
+
await screen.findByText('public.t_hardware_asset'),
|
|
480
|
+
).toBeInTheDocument();
|
|
481
|
+
expect(
|
|
482
|
+
screen.queryByText('public.t_hardware_asset · status'),
|
|
483
|
+
).not.toBeInTheDocument();
|
|
484
|
+
});
|
|
485
|
+
|
|
432
486
|
it('adds a typed start condition for the selected event', async () => {
|
|
433
487
|
await i18n.changeLanguage('zh_CN');
|
|
434
488
|
const onChange = vi.fn();
|
|
@@ -436,6 +490,7 @@ describe('simplified workflow node editors', () => {
|
|
|
436
490
|
<EventTriggerConfigEditor
|
|
437
491
|
value={{
|
|
438
492
|
registrationCode: 'asset-disposal-submitted',
|
|
493
|
+
operationRules: [{ operation: 'UPDATE' }],
|
|
439
494
|
processingMode: 'PARALLEL',
|
|
440
495
|
}}
|
|
441
496
|
onChange={onChange}
|
|
@@ -448,17 +503,22 @@ describe('simplified workflow node editors', () => {
|
|
|
448
503
|
expect(onChange).toHaveBeenLastCalledWith({
|
|
449
504
|
registrationCode: 'asset-disposal-submitted',
|
|
450
505
|
processingMode: 'PARALLEL',
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
{
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
506
|
+
operationRules: [
|
|
507
|
+
{
|
|
508
|
+
operation: 'UPDATE',
|
|
509
|
+
match: {
|
|
510
|
+
combineOperation: 'AND',
|
|
511
|
+
conditions: [
|
|
512
|
+
{
|
|
513
|
+
id: expect.stringMatching(/^condition-/),
|
|
514
|
+
left: { source: 'WORKFLOW', path: 'after.status' },
|
|
515
|
+
operator: 'EQ',
|
|
516
|
+
right: { source: 'FIXED', valueType: 'STRING', value: '' },
|
|
517
|
+
},
|
|
518
|
+
],
|
|
459
519
|
},
|
|
460
|
-
|
|
461
|
-
|
|
520
|
+
},
|
|
521
|
+
],
|
|
462
522
|
});
|
|
463
523
|
});
|
|
464
524
|
|
|
@@ -477,9 +537,10 @@ describe('simplified workflow node editors', () => {
|
|
|
477
537
|
/>,
|
|
478
538
|
);
|
|
479
539
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
);
|
|
540
|
+
const sqlEditor = screen.getByRole('textbox', { name: 'SQL 语句' });
|
|
541
|
+
expect(sqlEditor).toHaveValue('SELECT * FROM users WHERE id = $1');
|
|
542
|
+
expect(sqlEditor).toHaveAttribute('data-language', 'sql');
|
|
543
|
+
expect(screen.getByText('只读 SQL')).toBeInTheDocument();
|
|
483
544
|
expect(screen.getByRole('textbox', { name: '参数 $1' })).toBeDisabled();
|
|
484
545
|
expect(screen.getByRole('textbox', { name: '参数 $1' })).toHaveValue('$1');
|
|
485
546
|
expect(screen.getByText('参数')).toBeInTheDocument();
|