@gadmin2n/schematics 0.0.147 → 0.0.148
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 +1 -0
- 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 +17 -4
- 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/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-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.service.spec.ts +44 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +15 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/node-config-validator.service.spec.ts +2 -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 +49 -11
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/workflow.json +49 -11
- 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/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.tsx +1 -2
- 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 +47 -3
- 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/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
package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/person-directory/index.tsx
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DeleteOutlined,
|
|
3
|
+
EditOutlined,
|
|
4
|
+
PlusOutlined,
|
|
5
|
+
ReloadOutlined,
|
|
6
|
+
} from '@ant-design/icons';
|
|
7
|
+
import {
|
|
8
|
+
Button,
|
|
9
|
+
Card,
|
|
10
|
+
Input,
|
|
11
|
+
Popconfirm,
|
|
12
|
+
Select,
|
|
13
|
+
Space,
|
|
14
|
+
Table,
|
|
15
|
+
Tag,
|
|
16
|
+
Tooltip,
|
|
17
|
+
Typography,
|
|
18
|
+
message,
|
|
19
|
+
type TableColumnsType,
|
|
20
|
+
} from 'antd';
|
|
21
|
+
import { PageHeader } from 'components';
|
|
22
|
+
import { PAGE_WRAPPER_STYLE, SEARCH_CARD_PROPS } from 'constants/layout';
|
|
23
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
24
|
+
import { PersonModal } from './PersonModal';
|
|
25
|
+
import {
|
|
26
|
+
deletePerson,
|
|
27
|
+
fetchPeople,
|
|
28
|
+
type PersonRecord,
|
|
29
|
+
} from './person-directory.api';
|
|
30
|
+
|
|
31
|
+
const PAGE_SIZE = 20;
|
|
32
|
+
|
|
33
|
+
export default function PersonDirectoryPage() {
|
|
34
|
+
const [people, setPeople] = useState<PersonRecord[]>([]);
|
|
35
|
+
const [total, setTotal] = useState(0);
|
|
36
|
+
const [page, setPage] = useState(1);
|
|
37
|
+
const [query, setQuery] = useState('');
|
|
38
|
+
const [active, setActive] = useState<boolean>();
|
|
39
|
+
const [loading, setLoading] = useState(false);
|
|
40
|
+
const [deletingId, setDeletingId] = useState<number>();
|
|
41
|
+
const [editing, setEditing] = useState<PersonRecord | null | undefined>();
|
|
42
|
+
|
|
43
|
+
const load = useCallback(async () => {
|
|
44
|
+
setLoading(true);
|
|
45
|
+
try {
|
|
46
|
+
const result = await fetchPeople({
|
|
47
|
+
query,
|
|
48
|
+
active,
|
|
49
|
+
page,
|
|
50
|
+
pageSize: PAGE_SIZE,
|
|
51
|
+
});
|
|
52
|
+
setPeople(result.data);
|
|
53
|
+
setTotal(result.meta.itemCount);
|
|
54
|
+
} catch (error: any) {
|
|
55
|
+
message.error(error?.message ?? '加载人员失败');
|
|
56
|
+
} finally {
|
|
57
|
+
setLoading(false);
|
|
58
|
+
}
|
|
59
|
+
}, [active, page, query]);
|
|
60
|
+
|
|
61
|
+
useEffect(() => void load(), [load]);
|
|
62
|
+
|
|
63
|
+
async function remove(person: PersonRecord) {
|
|
64
|
+
setDeletingId(person.id);
|
|
65
|
+
try {
|
|
66
|
+
await deletePerson(person.id);
|
|
67
|
+
message.success('人员已删除');
|
|
68
|
+
if (people.length === 1 && page > 1) setPage(page - 1);
|
|
69
|
+
else await load();
|
|
70
|
+
} catch (error: any) {
|
|
71
|
+
message.error(error?.message ?? '删除人员失败');
|
|
72
|
+
} finally {
|
|
73
|
+
setDeletingId(undefined);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const columns: TableColumnsType<PersonRecord> = [
|
|
78
|
+
{
|
|
79
|
+
title: '人员',
|
|
80
|
+
key: 'person',
|
|
81
|
+
render: (_, person) => (
|
|
82
|
+
<div>
|
|
83
|
+
<Typography.Text strong>
|
|
84
|
+
{person.username || person.userid}
|
|
85
|
+
</Typography.Text>
|
|
86
|
+
<br />
|
|
87
|
+
<Typography.Text type="secondary">{person.userid}</Typography.Text>
|
|
88
|
+
</div>
|
|
89
|
+
),
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
title: '备注',
|
|
93
|
+
dataIndex: 'description',
|
|
94
|
+
render: (value?: string | null) => value || '—',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
title: '状态',
|
|
98
|
+
dataIndex: 'isActive',
|
|
99
|
+
width: 100,
|
|
100
|
+
render: (value: boolean) => (
|
|
101
|
+
<Tag color={value ? 'green' : 'default'}>
|
|
102
|
+
{value ? '已启用' : '已停用'}
|
|
103
|
+
</Tag>
|
|
104
|
+
),
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
title: '操作',
|
|
108
|
+
width: 110,
|
|
109
|
+
render: (_, person) => (
|
|
110
|
+
<Space size={4}>
|
|
111
|
+
<Tooltip title="编辑">
|
|
112
|
+
<Button
|
|
113
|
+
size="small"
|
|
114
|
+
icon={<EditOutlined />}
|
|
115
|
+
aria-label={`编辑 ${person.username || person.userid}`}
|
|
116
|
+
onClick={() => setEditing(person)}
|
|
117
|
+
/>
|
|
118
|
+
</Tooltip>
|
|
119
|
+
<Popconfirm
|
|
120
|
+
title="删除该人员?"
|
|
121
|
+
description="只有未授予角色且未被人员组或 Workflow 引用的人员才能删除。"
|
|
122
|
+
okText="删除"
|
|
123
|
+
cancelText="取消"
|
|
124
|
+
okButtonProps={{ danger: true }}
|
|
125
|
+
onConfirm={() => remove(person)}
|
|
126
|
+
>
|
|
127
|
+
<Tooltip title="删除">
|
|
128
|
+
<Button
|
|
129
|
+
danger
|
|
130
|
+
size="small"
|
|
131
|
+
icon={<DeleteOutlined />}
|
|
132
|
+
loading={deletingId === person.id}
|
|
133
|
+
aria-label={`删除 ${person.username || person.userid}`}
|
|
134
|
+
/>
|
|
135
|
+
</Tooltip>
|
|
136
|
+
</Popconfirm>
|
|
137
|
+
</Space>
|
|
138
|
+
),
|
|
139
|
+
},
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<div style={PAGE_WRAPPER_STYLE}>
|
|
144
|
+
<PageHeader
|
|
145
|
+
title="人员目录"
|
|
146
|
+
subtitle="维护可被审批、人员组等功能引用的人员;未授予角色的人员不能访问系统。"
|
|
147
|
+
extra={
|
|
148
|
+
<Space>
|
|
149
|
+
<Button icon={<ReloadOutlined />} onClick={load} />
|
|
150
|
+
<Button
|
|
151
|
+
type="primary"
|
|
152
|
+
icon={<PlusOutlined />}
|
|
153
|
+
onClick={() => setEditing(null)}
|
|
154
|
+
>
|
|
155
|
+
添加人员
|
|
156
|
+
</Button>
|
|
157
|
+
</Space>
|
|
158
|
+
}
|
|
159
|
+
/>
|
|
160
|
+
<Card {...SEARCH_CARD_PROPS}>
|
|
161
|
+
<Space wrap>
|
|
162
|
+
<Input.Search
|
|
163
|
+
allowClear
|
|
164
|
+
placeholder="搜索姓名或账号"
|
|
165
|
+
style={{ width: 260 }}
|
|
166
|
+
onSearch={(value) => {
|
|
167
|
+
setPage(1);
|
|
168
|
+
setQuery(value);
|
|
169
|
+
}}
|
|
170
|
+
/>
|
|
171
|
+
<Select
|
|
172
|
+
allowClear
|
|
173
|
+
placeholder="全部状态"
|
|
174
|
+
style={{ width: 140 }}
|
|
175
|
+
options={[
|
|
176
|
+
{ label: '已启用', value: true },
|
|
177
|
+
{ label: '已停用', value: false },
|
|
178
|
+
]}
|
|
179
|
+
onChange={(value) => {
|
|
180
|
+
setPage(1);
|
|
181
|
+
setActive(value);
|
|
182
|
+
}}
|
|
183
|
+
/>
|
|
184
|
+
</Space>
|
|
185
|
+
</Card>
|
|
186
|
+
<Card className="card-table">
|
|
187
|
+
<Table
|
|
188
|
+
rowKey="id"
|
|
189
|
+
loading={loading}
|
|
190
|
+
dataSource={people}
|
|
191
|
+
columns={columns}
|
|
192
|
+
pagination={{
|
|
193
|
+
current: page,
|
|
194
|
+
pageSize: PAGE_SIZE,
|
|
195
|
+
total,
|
|
196
|
+
showTotal: (count) => `共 ${count} 人`,
|
|
197
|
+
onChange: setPage,
|
|
198
|
+
}}
|
|
199
|
+
/>
|
|
200
|
+
</Card>
|
|
201
|
+
{editing !== undefined && (
|
|
202
|
+
<PersonModal
|
|
203
|
+
open
|
|
204
|
+
person={editing}
|
|
205
|
+
onCancel={() => setEditing(undefined)}
|
|
206
|
+
onSaved={() => {
|
|
207
|
+
setEditing(undefined);
|
|
208
|
+
void load();
|
|
209
|
+
}}
|
|
210
|
+
/>
|
|
211
|
+
)}
|
|
212
|
+
</div>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { customRequest } from 'helpers/http';
|
|
2
|
+
|
|
3
|
+
export interface PersonRecord {
|
|
4
|
+
id: number;
|
|
5
|
+
userid: string;
|
|
6
|
+
username: string | null;
|
|
7
|
+
description: string | null;
|
|
8
|
+
isActive: boolean;
|
|
9
|
+
createdAt: string;
|
|
10
|
+
updatedAt: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface PersonPage {
|
|
14
|
+
data: PersonRecord[];
|
|
15
|
+
meta: { itemCount: number };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const personSelect = {
|
|
19
|
+
id: true,
|
|
20
|
+
userid: true,
|
|
21
|
+
username: true,
|
|
22
|
+
description: true,
|
|
23
|
+
isActive: true,
|
|
24
|
+
createdAt: true,
|
|
25
|
+
updatedAt: true,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export async function fetchPeople(input: {
|
|
29
|
+
query: string;
|
|
30
|
+
active?: boolean;
|
|
31
|
+
page: number;
|
|
32
|
+
pageSize: number;
|
|
33
|
+
}) {
|
|
34
|
+
const query = input.query.trim();
|
|
35
|
+
return customRequest<PersonPage>('user/findMany', 'POST', {
|
|
36
|
+
where: {
|
|
37
|
+
isApiKey: false,
|
|
38
|
+
...(input.active !== undefined && { isActive: input.active }),
|
|
39
|
+
...(query && {
|
|
40
|
+
OR: [
|
|
41
|
+
{ userid: { contains: query, mode: 'insensitive' } },
|
|
42
|
+
{ username: { contains: query, mode: 'insensitive' } },
|
|
43
|
+
],
|
|
44
|
+
}),
|
|
45
|
+
},
|
|
46
|
+
select: personSelect,
|
|
47
|
+
orderBy: { userid: 'asc' },
|
|
48
|
+
skip: (input.page - 1) * input.pageSize,
|
|
49
|
+
take: input.pageSize,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function createPerson(input: {
|
|
54
|
+
userid: string;
|
|
55
|
+
username?: string;
|
|
56
|
+
description?: string;
|
|
57
|
+
isActive: boolean;
|
|
58
|
+
}) {
|
|
59
|
+
return customRequest<PersonRecord>('user/createOne', 'POST', {
|
|
60
|
+
...input,
|
|
61
|
+
roles: '[]',
|
|
62
|
+
isApiKey: false,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function updatePerson(
|
|
67
|
+
id: number,
|
|
68
|
+
input: {
|
|
69
|
+
username?: string;
|
|
70
|
+
description?: string | null;
|
|
71
|
+
isActive: boolean;
|
|
72
|
+
},
|
|
73
|
+
) {
|
|
74
|
+
return customRequest<PersonRecord>(`user/updateUnique/${id}`, 'PATCH', input);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function deletePerson(id: number) {
|
|
78
|
+
return customRequest<{ count: number }>(`user/deletePerson/${id}`, 'DELETE');
|
|
79
|
+
}
|
|
@@ -98,16 +98,16 @@ export const UserFormModal: React.FC<Props> = ({
|
|
|
98
98
|
//TODO: modalProps.onCancel expect an event so, I used close. Actually both of them are same.
|
|
99
99
|
close();
|
|
100
100
|
}}
|
|
101
|
-
title={action === 'create' ? '
|
|
101
|
+
title={action === 'create' ? '新增访问主体' : '编辑访问授权'}
|
|
102
102
|
>
|
|
103
103
|
<Form {...formProps} layout="vertical">
|
|
104
104
|
<Form.Item
|
|
105
105
|
rules={[{ required: true }]}
|
|
106
106
|
name="userid"
|
|
107
|
-
label="
|
|
107
|
+
label="账号(企业微信 ID 或太湖账号)"
|
|
108
108
|
>
|
|
109
109
|
<Input
|
|
110
|
-
placeholder="
|
|
110
|
+
placeholder="请输入账号"
|
|
111
111
|
onChange={(e) => {
|
|
112
112
|
if (action === 'create' && !usernameTouched) {
|
|
113
113
|
form.setFieldsValue({ username: e.target.value });
|
|
@@ -116,23 +116,19 @@ export const UserFormModal: React.FC<Props> = ({
|
|
|
116
116
|
/>
|
|
117
117
|
</Form.Item>
|
|
118
118
|
|
|
119
|
-
<Form.Item
|
|
120
|
-
rules={[{ required: true }]}
|
|
121
|
-
name="username"
|
|
122
|
-
label="Username(一般设置为和上面的 User ID 一样)"
|
|
123
|
-
>
|
|
119
|
+
<Form.Item rules={[{ required: true }]} name="username" label="名称">
|
|
124
120
|
<Input
|
|
125
|
-
placeholder="
|
|
121
|
+
placeholder="请输入名称"
|
|
126
122
|
onChange={() => {
|
|
127
123
|
if (!usernameTouched) setUsernameTouched(true);
|
|
128
124
|
}}
|
|
129
125
|
/>
|
|
130
126
|
</Form.Item>
|
|
131
127
|
|
|
132
|
-
<Form.Item rules={[{ required: true }]} name="roles" label="
|
|
128
|
+
<Form.Item rules={[{ required: true }]} name="roles" label="角色">
|
|
133
129
|
<Select
|
|
134
130
|
mode="multiple"
|
|
135
|
-
placeholder="
|
|
131
|
+
placeholder="请选择角色"
|
|
136
132
|
options={roleOptions}
|
|
137
133
|
loading={rolesLoading}
|
|
138
134
|
/>
|
|
@@ -137,7 +137,7 @@ export const UserListPage: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
137
137
|
icon={<PlusOutlined />}
|
|
138
138
|
onClick={() => setShowModal({ type: 'create', id: null })}
|
|
139
139
|
>
|
|
140
|
-
|
|
140
|
+
新增访问主体
|
|
141
141
|
</Button>
|
|
142
142
|
);
|
|
143
143
|
}}
|
|
@@ -155,7 +155,7 @@ export const UserListPage: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
155
155
|
>
|
|
156
156
|
<Table.Column
|
|
157
157
|
dataIndex="userid"
|
|
158
|
-
title="
|
|
158
|
+
title="账号"
|
|
159
159
|
render={(_, record) => {
|
|
160
160
|
return (
|
|
161
161
|
<Space>
|
|
@@ -173,7 +173,7 @@ export const UserListPage: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
173
173
|
/>
|
|
174
174
|
<Table.Column
|
|
175
175
|
dataIndex="username"
|
|
176
|
-
title="
|
|
176
|
+
title="名称"
|
|
177
177
|
filterDropdown={(props) => (
|
|
178
178
|
<FilterDropdown
|
|
179
179
|
{...props}
|
|
@@ -194,7 +194,7 @@ export const UserListPage: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
194
194
|
/>
|
|
195
195
|
<Table.Column
|
|
196
196
|
dataIndex="roles"
|
|
197
|
-
title="
|
|
197
|
+
title="角色"
|
|
198
198
|
defaultFilteredValue={getDefaultFilter(
|
|
199
199
|
'roles',
|
|
200
200
|
filters,
|
|
@@ -224,7 +224,7 @@ export const UserListPage: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
224
224
|
/>
|
|
225
225
|
<Table.Column
|
|
226
226
|
dataIndex="isApiKey"
|
|
227
|
-
title="API Key"
|
|
227
|
+
title="API Key 主体"
|
|
228
228
|
defaultFilteredValue={getDefaultFilter('isApiKey', filters, 'eq')}
|
|
229
229
|
filterDropdown={(props) => (
|
|
230
230
|
<FilterDropdown
|
|
@@ -258,7 +258,7 @@ export const UserListPage: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
258
258
|
/>
|
|
259
259
|
<Table.Column
|
|
260
260
|
dataIndex="isActive"
|
|
261
|
-
title="
|
|
261
|
+
title="状态"
|
|
262
262
|
defaultFilteredValue={getDefaultFilter('isActive', filters, 'eq')}
|
|
263
263
|
filterDropdown={(props) => (
|
|
264
264
|
<FilterDropdown
|
|
@@ -292,7 +292,7 @@ export const UserListPage: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
292
292
|
/>
|
|
293
293
|
<Table.Column
|
|
294
294
|
fixed="right"
|
|
295
|
-
title="
|
|
295
|
+
title="操作"
|
|
296
296
|
dataIndex="actions"
|
|
297
297
|
render={(_, record) => {
|
|
298
298
|
return (
|
|
@@ -83,6 +83,13 @@ describe('NodeOutputSummary', () => {
|
|
|
83
83
|
type: 'object',
|
|
84
84
|
properties: {
|
|
85
85
|
operation: { type: 'string' },
|
|
86
|
+
table: {
|
|
87
|
+
type: 'object',
|
|
88
|
+
properties: {
|
|
89
|
+
schema: { type: 'string' },
|
|
90
|
+
name: { type: 'string' },
|
|
91
|
+
},
|
|
92
|
+
},
|
|
86
93
|
primaryKey: { type: 'object' },
|
|
87
94
|
before: { type: ['object', 'null'] },
|
|
88
95
|
after: { type: ['object', 'null'] },
|
|
@@ -90,6 +97,7 @@ describe('NodeOutputSummary', () => {
|
|
|
90
97
|
},
|
|
91
98
|
required: [
|
|
92
99
|
'operation',
|
|
100
|
+
'table',
|
|
93
101
|
'primaryKey',
|
|
94
102
|
'before',
|
|
95
103
|
'after',
|
|
@@ -102,8 +110,20 @@ describe('NodeOutputSummary', () => {
|
|
|
102
110
|
render(<NodeOutputSummary node={event} nodeType={nodeType} />);
|
|
103
111
|
|
|
104
112
|
expect(screen.getByText('input.operation')).toBeInTheDocument();
|
|
113
|
+
expect(screen.getByText('操作类型')).toBeInTheDocument();
|
|
114
|
+
expect(
|
|
115
|
+
screen.getByText(
|
|
116
|
+
'数据库操作类型:INSERT(新增)、UPDATE(更新)或 DELETE(删除)',
|
|
117
|
+
),
|
|
118
|
+
).toBeInTheDocument();
|
|
119
|
+
expect(screen.getByText('input.table')).toBeInTheDocument();
|
|
120
|
+
expect(screen.getByText('数据表')).toBeInTheDocument();
|
|
105
121
|
expect(screen.getByText('input.primaryKey')).toBeInTheDocument();
|
|
122
|
+
expect(screen.getByText('主键')).toBeInTheDocument();
|
|
123
|
+
expect(screen.getByText('变更前的数据')).toBeInTheDocument();
|
|
124
|
+
expect(screen.getByText('变更后的数据')).toBeInTheDocument();
|
|
106
125
|
expect(screen.getByText('input.changedFields')).toBeInTheDocument();
|
|
126
|
+
expect(screen.getByText('发生变化的字段')).toBeInTheDocument();
|
|
107
127
|
expect(
|
|
108
128
|
screen.queryByText('触发本次运行的事件数据'),
|
|
109
129
|
).not.toBeInTheDocument();
|
|
@@ -188,11 +188,10 @@ describe('NodePropertyPanel instance references', () => {
|
|
|
188
188
|
expect(
|
|
189
189
|
screen.queryByText('Connection is required'),
|
|
190
190
|
).not.toBeInTheDocument();
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
);
|
|
191
|
+
expect(
|
|
192
|
+
screen.queryByRole('button', { name: 'Save changes' }),
|
|
193
|
+
).not.toBeInTheDocument();
|
|
194
|
+
expect(onSave).not.toHaveBeenCalled();
|
|
196
195
|
expect(
|
|
197
196
|
screen.queryByText('Connection is required'),
|
|
198
197
|
).not.toBeInTheDocument();
|
|
@@ -307,7 +306,6 @@ describe('NodePropertyPanel instance references', () => {
|
|
|
307
306
|
/>,
|
|
308
307
|
);
|
|
309
308
|
|
|
310
|
-
await waitFor(() => expect(customRequest).toHaveBeenCalled());
|
|
311
309
|
await act(async () => {
|
|
312
310
|
await Promise.resolve();
|
|
313
311
|
await Promise.resolve();
|
|
@@ -346,8 +344,8 @@ describe('NodePropertyPanel instance references', () => {
|
|
|
346
344
|
screen.getByText('Includes the approver and completion time'),
|
|
347
345
|
).toBeInTheDocument();
|
|
348
346
|
expect(
|
|
349
|
-
screen.
|
|
350
|
-
).toBeInTheDocument();
|
|
347
|
+
screen.queryByRole('button', { name: 'Save changes' }),
|
|
348
|
+
).not.toBeInTheDocument();
|
|
351
349
|
expect(customRequest).not.toHaveBeenCalledWith(
|
|
352
350
|
'workflowNodeInstance/findMany',
|
|
353
351
|
expect.anything(),
|
|
@@ -386,13 +384,14 @@ describe('NodePropertyPanel instance references', () => {
|
|
|
386
384
|
/>,
|
|
387
385
|
);
|
|
388
386
|
|
|
389
|
-
expect(screen.getByRole('button', { name: 'Save changes' })).toBeEnabled();
|
|
390
387
|
expect(
|
|
391
388
|
screen.getByText('Use a template or configure this node directly'),
|
|
392
389
|
).toBeInTheDocument();
|
|
393
|
-
expect(
|
|
394
|
-
'
|
|
395
|
-
|
|
390
|
+
expect(
|
|
391
|
+
screen.getByRole('button', {
|
|
392
|
+
name: /Save current configuration as template/,
|
|
393
|
+
}),
|
|
394
|
+
).toHaveClass('workflow-panel-action');
|
|
396
395
|
expect(screen.getByText('Who can approve')).toBeInTheDocument();
|
|
397
396
|
expect(screen.queryByText('Parameters')).not.toBeInTheDocument();
|
|
398
397
|
expect(
|
|
@@ -419,7 +418,83 @@ describe('NodePropertyPanel instance references', () => {
|
|
|
419
418
|
);
|
|
420
419
|
});
|
|
421
420
|
|
|
422
|
-
it('
|
|
421
|
+
it('saves the current configuration as a template and uses it immediately', async () => {
|
|
422
|
+
const user = userEvent.setup();
|
|
423
|
+
const onSave = vi.fn();
|
|
424
|
+
const inlineNode = {
|
|
425
|
+
...node,
|
|
426
|
+
instanceRef: undefined,
|
|
427
|
+
config: { subjects: [{ refs: ['alice'] }] },
|
|
428
|
+
};
|
|
429
|
+
vi.mocked(customRequest).mockImplementation((path) => {
|
|
430
|
+
if (path === 'workflowNodeInstance/findMany') {
|
|
431
|
+
return Promise.resolve({ data: [] }) as never;
|
|
432
|
+
}
|
|
433
|
+
if (path === 'workflowNodeInstance/createOne') {
|
|
434
|
+
return Promise.resolve({
|
|
435
|
+
id: '21',
|
|
436
|
+
code: 'FINANCE_APPROVAL_V2',
|
|
437
|
+
name: 'Finance approval',
|
|
438
|
+
nodeTypeId: '7',
|
|
439
|
+
isEnabled: true,
|
|
440
|
+
config: inlineNode.config,
|
|
441
|
+
}) as never;
|
|
442
|
+
}
|
|
443
|
+
return Promise.resolve([]) as never;
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
render(
|
|
447
|
+
<NodePropertyPanel
|
|
448
|
+
node={inlineNode}
|
|
449
|
+
nodeTypes={[nodeType]}
|
|
450
|
+
onSave={onSave}
|
|
451
|
+
onLabelChange={vi.fn()}
|
|
452
|
+
onAiEdit={vi.fn()}
|
|
453
|
+
aiLoading={false}
|
|
454
|
+
/>,
|
|
455
|
+
);
|
|
456
|
+
|
|
457
|
+
await user.click(
|
|
458
|
+
screen.getByRole('button', {
|
|
459
|
+
name: /Save current configuration as template/,
|
|
460
|
+
}),
|
|
461
|
+
);
|
|
462
|
+
expect(
|
|
463
|
+
screen.getByText(/Create a template from the current node configuration/),
|
|
464
|
+
).toBeInTheDocument();
|
|
465
|
+
const templateName = screen.getByRole('textbox', {
|
|
466
|
+
name: 'Template name',
|
|
467
|
+
});
|
|
468
|
+
await user.clear(templateName);
|
|
469
|
+
await user.type(templateName, 'Reusable finance approval');
|
|
470
|
+
await user.click(
|
|
471
|
+
screen.getByRole('button', { name: 'Save and use template' }),
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
await waitFor(() =>
|
|
475
|
+
expect(customRequest).toHaveBeenCalledWith(
|
|
476
|
+
'workflowNodeInstance/createOne',
|
|
477
|
+
'POST',
|
|
478
|
+
expect.objectContaining({
|
|
479
|
+
name: 'Reusable finance approval',
|
|
480
|
+
config: inlineNode.config,
|
|
481
|
+
}),
|
|
482
|
+
),
|
|
483
|
+
);
|
|
484
|
+
expect(onSave).toHaveBeenCalledWith(
|
|
485
|
+
'node-1',
|
|
486
|
+
expect.objectContaining({
|
|
487
|
+
config: inlineNode.config,
|
|
488
|
+
configOverride: null,
|
|
489
|
+
instanceRef: {
|
|
490
|
+
instanceId: '21',
|
|
491
|
+
instanceCode: 'FINANCE_APPROVAL_V2',
|
|
492
|
+
},
|
|
493
|
+
}),
|
|
494
|
+
);
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
it('keeps an inline approval configuration in the Workflow draft', async () => {
|
|
423
498
|
const user = userEvent.setup();
|
|
424
499
|
const onSave = vi.fn();
|
|
425
500
|
const onConfigDraftChange = vi.fn();
|
|
@@ -531,10 +606,8 @@ describe('NodePropertyPanel instance references', () => {
|
|
|
531
606
|
},
|
|
532
607
|
});
|
|
533
608
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
const savedConfig = onSave.mock.lastCall?.[1].config;
|
|
537
|
-
expect(savedConfig).toMatchObject({
|
|
609
|
+
const pendingConfig = panelRef.current?.getPendingUpdate()?.updates.config;
|
|
610
|
+
expect(pendingConfig).toMatchObject({
|
|
538
611
|
external: {
|
|
539
612
|
request: {
|
|
540
613
|
connectionRef: 'ticketing',
|
|
@@ -549,6 +622,7 @@ describe('NodePropertyPanel instance references', () => {
|
|
|
549
622
|
rejectionRule: { mode: 'ANY' },
|
|
550
623
|
timeout: { durationSeconds: 0, action: 'REJECTED' },
|
|
551
624
|
});
|
|
625
|
+
expect(onSave).not.toHaveBeenCalled();
|
|
552
626
|
|
|
553
627
|
await waitFor(() =>
|
|
554
628
|
expect(screen.getByLabelText('Task title field path')).toHaveValue(
|
|
@@ -616,7 +690,7 @@ describe('NodePropertyPanel instance references', () => {
|
|
|
616
690
|
|
|
617
691
|
it('uses nested UI Schema widgets for Callable Trigger parameters', async () => {
|
|
618
692
|
const user = userEvent.setup();
|
|
619
|
-
const
|
|
693
|
+
const onConfigDraftChange = vi.fn();
|
|
620
694
|
const callableNodeType = {
|
|
621
695
|
id: '9',
|
|
622
696
|
type: 'callable_trigger',
|
|
@@ -655,7 +729,8 @@ describe('NodePropertyPanel instance references', () => {
|
|
|
655
729
|
<NodePropertyPanel
|
|
656
730
|
node={callableNode}
|
|
657
731
|
nodeTypes={[callableNodeType]}
|
|
658
|
-
onSave={
|
|
732
|
+
onSave={vi.fn()}
|
|
733
|
+
onConfigDraftChange={onConfigDraftChange}
|
|
659
734
|
onLabelChange={vi.fn()}
|
|
660
735
|
onAiEdit={vi.fn()}
|
|
661
736
|
aiLoading={false}
|
|
@@ -672,16 +747,10 @@ describe('NodePropertyPanel instance references', () => {
|
|
|
672
747
|
screen.getByRole('textbox', { name: 'Parameter name' }),
|
|
673
748
|
'departmentId',
|
|
674
749
|
);
|
|
675
|
-
await user.click(screen.getByRole('button', { name: 'Save changes' }));
|
|
676
750
|
|
|
677
|
-
expect(
|
|
678
|
-
'
|
|
679
|
-
|
|
680
|
-
config: {
|
|
681
|
-
inputParameters: [{ key: 'departmentId', label: '', required: true }],
|
|
682
|
-
},
|
|
683
|
-
}),
|
|
684
|
-
);
|
|
751
|
+
expect(onConfigDraftChange).toHaveBeenCalledWith('callable-1', {
|
|
752
|
+
inputParameters: [{ key: 'departmentId', label: '', required: true }],
|
|
753
|
+
});
|
|
685
754
|
});
|
|
686
755
|
|
|
687
756
|
it('filters the node palette by translated name and technical type', async () => {
|