@gadmin2n/schematics 0.0.107 → 0.0.109
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/.types.d.ts +8 -9
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/AgendaJob.ts +17 -15
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Audit.ts +13 -17
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Event.ts +48 -17
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Game.ts +1 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/ITActivityDay.ts +14 -18
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Log.ts +0 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Page.ts +42 -18
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/PageResource.ts +28 -18
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Resource.ts +42 -18
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/Role.ts +1 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/RolePages.ts +14 -18
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/RoleResource.ts +28 -18
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/SavedQuery.ts +13 -17
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/User.ts +14 -18
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/WorkflowEventOutbox.ts +17 -17
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/WorkflowNodeInstance.ts +9 -14
- package/dist/lib/application/files/gadmin2-game-angle-demo/config/ui/WorkflowNodeType.ts +9 -14
- package/dist/lib/application/files/gadmin2-game-angle-demo/gitignore +1 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +2 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/temporal.service.ts +7 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/App.tsx +75 -71
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/BulkActions.tsx +36 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/ListPageHeader.tsx +41 -14
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/RowActions.tsx +153 -144
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/agentPanel/inspectorActions.ts +3 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/agentAllowed.tsx +35 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/config/env.ts +2 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/dev-shell/DevShell.tsx +8 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/http.ts +20 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/helpers/list.tsx +48 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/plugins/devShellPlugin.ts +40 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agenda/index.tsx +3 -2
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/list.tsx +6 -6
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasCell.tsx +4 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasListPage.tsx +4 -3
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasPage.tsx +99 -5
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/CanvasToolbar.tsx +28 -30
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/canvas/components/CanvasAiModal.tsx +80 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/game/list.tsx +6 -0
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/editor.tsx +2 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/components/NodeInstanceForm.tsx +2 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/show.tsx +2 -1
- package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflowEventOutbox/list.tsx +6 -6
- package/package.json +1 -1
package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/RowActions.tsx
CHANGED
|
@@ -9,19 +9,18 @@ import {
|
|
|
9
9
|
} from '@refinedev/antd';
|
|
10
10
|
import { RowAction } from '../hooks/types';
|
|
11
11
|
import { whenIsTrue } from '@gadmin2n/react-common';
|
|
12
|
+
import {
|
|
13
|
+
actionsToShowFlags,
|
|
14
|
+
interpolateActionUrl,
|
|
15
|
+
splitVisibleActions,
|
|
16
|
+
} from '../helpers/list';
|
|
12
17
|
|
|
13
18
|
export interface RowActionsRenderContext<T = any> {
|
|
14
|
-
/** Current record */
|
|
15
19
|
record: T;
|
|
16
|
-
/** Record ID */
|
|
17
20
|
recordId: number | string;
|
|
18
|
-
/** Translation function */
|
|
19
21
|
t: (key: string, defaultValue?: string) => string;
|
|
20
|
-
/** Resource name */
|
|
21
22
|
resourceName: string;
|
|
22
|
-
/** Update handler */
|
|
23
23
|
onUpdate: (values: any) => void;
|
|
24
|
-
/** Is update loading */
|
|
25
24
|
updateLoading: boolean;
|
|
26
25
|
}
|
|
27
26
|
|
|
@@ -30,8 +29,13 @@ export interface RowActionsProps<T = any> {
|
|
|
30
29
|
record: T;
|
|
31
30
|
/** Record ID field name */
|
|
32
31
|
idField?: keyof T;
|
|
33
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* 行操作的 actions 数组(来自 config 的 rowActions.actions)。
|
|
34
|
+
* actions 是按钮显示与否、按钮顺序的唯一真相源。
|
|
35
|
+
*/
|
|
34
36
|
actions?: RowAction[];
|
|
37
|
+
/** 主按钮可见数量;超出部分塞入 More dropdown */
|
|
38
|
+
visibleNum?: number;
|
|
35
39
|
/** Handler for row update */
|
|
36
40
|
onUpdate?: (id: number, values: any) => void;
|
|
37
41
|
/** Is update loading */
|
|
@@ -40,15 +44,12 @@ export interface RowActionsProps<T = any> {
|
|
|
40
44
|
t?: (key: string, defaultValue?: string) => string;
|
|
41
45
|
/** Resource name for i18n keys */
|
|
42
46
|
resourceName?: string;
|
|
43
|
-
/**
|
|
47
|
+
/** 强制显式覆盖:传 boolean 时优先于 actions 推导值 */
|
|
44
48
|
showEdit?: boolean;
|
|
45
|
-
/** Show detail/show button */
|
|
46
49
|
showDetail?: boolean;
|
|
47
|
-
/** Show clone button */
|
|
48
50
|
showClone?: boolean;
|
|
49
|
-
/** Show delete button */
|
|
50
51
|
showDelete?: boolean;
|
|
51
|
-
/**
|
|
52
|
+
/** History/Log button —— actions 数组里没有,需要通过 LogButton 或显式 showHistory 开启 */
|
|
52
53
|
showHistory?: boolean;
|
|
53
54
|
/** Custom children - if provided, replaces entire default UI */
|
|
54
55
|
children?:
|
|
@@ -56,17 +57,11 @@ export interface RowActionsProps<T = any> {
|
|
|
56
57
|
| ((context: RowActionsRenderContext<T>) => React.ReactNode);
|
|
57
58
|
|
|
58
59
|
// Render props for fine-grained customization
|
|
59
|
-
/** Custom render for edit button */
|
|
60
60
|
renderEditButton?: (recordId: number | string) => React.ReactNode;
|
|
61
|
-
/** Custom render for detail/show button */
|
|
62
61
|
renderDetailButton?: (recordId: number | string) => React.ReactNode;
|
|
63
|
-
/** Custom render for clone button */
|
|
64
62
|
renderCloneButton?: (recordId: number | string) => React.ReactNode;
|
|
65
|
-
/** Custom render for delete button */
|
|
66
63
|
renderDeleteButton?: (recordId: number | string) => React.ReactNode;
|
|
67
|
-
/** Custom render for history/log button */
|
|
68
64
|
renderHistoryButton?: (recordId: number | string) => React.ReactNode;
|
|
69
|
-
/** Custom render for status action buttons */
|
|
70
65
|
renderStatusAction?: (
|
|
71
66
|
action: RowAction,
|
|
72
67
|
record: T,
|
|
@@ -74,60 +69,32 @@ export interface RowActionsProps<T = any> {
|
|
|
74
69
|
loading: boolean,
|
|
75
70
|
disabled: boolean,
|
|
76
71
|
) => React.ReactNode;
|
|
77
|
-
/** Custom render for more dropdown trigger */
|
|
78
72
|
renderMoreTrigger?: () => React.ReactNode;
|
|
79
|
-
/**
|
|
73
|
+
/** 额外塞入 More dropdown 的菜单项 */
|
|
80
74
|
extraDropdownItems?: Array<{ key: string; label: React.ReactNode }>;
|
|
81
|
-
/**
|
|
75
|
+
/** 主按钮区追加的内容(在 More dropdown 之前) */
|
|
82
76
|
extraPrimaryActions?: React.ReactNode;
|
|
83
|
-
/** Log button component
|
|
77
|
+
/** Log button component */
|
|
84
78
|
LogButton?: React.ComponentType<any>;
|
|
85
79
|
}
|
|
86
80
|
|
|
87
81
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* // Basic usage
|
|
92
|
-
* <RowActions
|
|
93
|
-
* record={record}
|
|
94
|
-
* actions={tableConfig.rowActions.actions}
|
|
95
|
-
* t={t}
|
|
96
|
-
* resourceName="audit"
|
|
97
|
-
* />
|
|
98
|
-
*
|
|
99
|
-
* @example
|
|
100
|
-
* // With custom render props
|
|
101
|
-
* <RowActions
|
|
102
|
-
* {...props}
|
|
103
|
-
* renderEditButton={(id) => <MyEditButton id={id} />}
|
|
104
|
-
* renderDeleteButton={(id) => <MyDeleteButton id={id} />}
|
|
105
|
-
* />
|
|
106
|
-
*
|
|
107
|
-
* @example
|
|
108
|
-
* // Completely custom UI using children
|
|
109
|
-
* <RowActions record={record}>
|
|
110
|
-
* {({ record, recordId, onUpdate }) => (
|
|
111
|
-
* <MyCustomActionsUI
|
|
112
|
-
* record={record}
|
|
113
|
-
* onEdit={() => navigate(`/edit/${recordId}`)}
|
|
114
|
-
* onUpdate={onUpdate}
|
|
115
|
-
* />
|
|
116
|
-
* )}
|
|
117
|
-
* </RowActions>
|
|
82
|
+
* 行操作组件:按 config.rowActions.actions 数组顺序渲染按钮,
|
|
83
|
+
* 前 visibleNum 个为主按钮,其余进 More dropdown。
|
|
118
84
|
*/
|
|
119
85
|
export function RowActions<T extends { id: number | string }>({
|
|
120
86
|
record,
|
|
121
87
|
idField = 'id' as keyof T,
|
|
122
88
|
actions = [],
|
|
89
|
+
visibleNum,
|
|
123
90
|
onUpdate,
|
|
124
91
|
updateLoading = false,
|
|
125
92
|
t = (key, defaultValue) => defaultValue || key,
|
|
126
93
|
resourceName = '',
|
|
127
|
-
showEdit
|
|
128
|
-
showDetail
|
|
129
|
-
showClone
|
|
130
|
-
showDelete
|
|
94
|
+
showEdit,
|
|
95
|
+
showDetail,
|
|
96
|
+
showClone,
|
|
97
|
+
showDelete,
|
|
131
98
|
showHistory = false,
|
|
132
99
|
children,
|
|
133
100
|
renderEditButton,
|
|
@@ -152,40 +119,35 @@ export function RowActions<T extends { id: number | string }>({
|
|
|
152
119
|
updateLoading,
|
|
153
120
|
};
|
|
154
121
|
|
|
155
|
-
// If children is a function, call it with context
|
|
156
122
|
if (typeof children === 'function') {
|
|
157
123
|
return <>{children(context)}</>;
|
|
158
124
|
}
|
|
159
|
-
|
|
160
|
-
// If children is provided as ReactNode, render it directly
|
|
161
125
|
if (children) {
|
|
162
126
|
return <>{children}</>;
|
|
163
127
|
}
|
|
164
128
|
|
|
165
|
-
//
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
129
|
+
// actions 是显示与否的唯一真相源;显式 show* 可强制覆盖
|
|
130
|
+
const flags = actionsToShowFlags(actions);
|
|
131
|
+
const resolvedShowEdit = showEdit ?? flags.showEdit;
|
|
132
|
+
const resolvedShowDetail = showDetail ?? flags.showDetail;
|
|
133
|
+
const resolvedShowClone = showClone ?? flags.showClone;
|
|
134
|
+
const resolvedShowDelete = showDelete ?? flags.showDelete;
|
|
169
135
|
|
|
170
|
-
//
|
|
136
|
+
// ---------- 默认按钮渲染器 ----------
|
|
171
137
|
const defaultEditButton = (id: number | string) => (
|
|
172
138
|
<EditButton icon={false} type="text" size="small" recordItemId={id} />
|
|
173
139
|
);
|
|
174
|
-
|
|
175
140
|
const defaultDetailButton = (id: number | string) => (
|
|
176
141
|
<ShowButton icon={false} type="text" size="small" recordItemId={id}>
|
|
177
142
|
{t('table.detail', 'Detail')}
|
|
178
143
|
</ShowButton>
|
|
179
144
|
);
|
|
180
|
-
|
|
181
145
|
const defaultCloneButton = (id: number | string) => (
|
|
182
146
|
<CloneButton icon={false} type="text" size="small" recordItemId={id} />
|
|
183
147
|
);
|
|
184
|
-
|
|
185
148
|
const defaultDeleteButton = (id: number | string) => (
|
|
186
149
|
<DeleteButton icon={false} type="text" size="small" recordItemId={id} />
|
|
187
150
|
);
|
|
188
|
-
|
|
189
151
|
const defaultHistoryButton = (id: number | string) =>
|
|
190
152
|
LogButton ? (
|
|
191
153
|
<LogButton
|
|
@@ -196,7 +158,6 @@ export function RowActions<T extends { id: number | string }>({
|
|
|
196
158
|
recordItemId={id}
|
|
197
159
|
/>
|
|
198
160
|
) : null;
|
|
199
|
-
|
|
200
161
|
const defaultStatusAction = (
|
|
201
162
|
action: RowAction,
|
|
202
163
|
_record: T,
|
|
@@ -217,7 +178,6 @@ export function RowActions<T extends { id: number | string }>({
|
|
|
217
178
|
</Button>
|
|
218
179
|
</Popconfirm>
|
|
219
180
|
);
|
|
220
|
-
|
|
221
181
|
const defaultMoreTrigger = () => (
|
|
222
182
|
<Button type="text" size="small">
|
|
223
183
|
{t('table.more', 'More')}
|
|
@@ -225,87 +185,136 @@ export function RowActions<T extends { id: number | string }>({
|
|
|
225
185
|
</Button>
|
|
226
186
|
);
|
|
227
187
|
|
|
228
|
-
//
|
|
229
|
-
|
|
230
|
-
// Status change actions
|
|
231
|
-
...statusActions.map((action) => {
|
|
232
|
-
const disabled = !whenIsTrue(action.when, record);
|
|
233
|
-
const onConfirm = () => onUpdate?.(Number(recordId), action.values);
|
|
188
|
+
// ---------- 按 actions 顺序构造可渲染项 ----------
|
|
189
|
+
type Renderable = { key: string; node: React.ReactNode };
|
|
234
190
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
191
|
+
const renderAction = (action: RowAction, idx: number): Renderable | null => {
|
|
192
|
+
const whenOk = action.when == null ? true : whenIsTrue(action.when, record);
|
|
193
|
+
if (!whenOk) return null;
|
|
194
|
+
|
|
195
|
+
const key = `${action.action}-${action.desc}-${idx}`;
|
|
196
|
+
switch (action.action) {
|
|
197
|
+
case 'EDIT':
|
|
198
|
+
return resolvedShowEdit
|
|
199
|
+
? {
|
|
200
|
+
key,
|
|
201
|
+
node: renderEditButton
|
|
202
|
+
? renderEditButton(recordId)
|
|
203
|
+
: defaultEditButton(recordId),
|
|
204
|
+
}
|
|
205
|
+
: null;
|
|
206
|
+
case 'DETAIL':
|
|
207
|
+
return resolvedShowDetail
|
|
208
|
+
? {
|
|
209
|
+
key,
|
|
210
|
+
node: renderDetailButton
|
|
211
|
+
? renderDetailButton(recordId)
|
|
212
|
+
: defaultDetailButton(recordId),
|
|
213
|
+
}
|
|
214
|
+
: null;
|
|
215
|
+
case 'DELETE':
|
|
216
|
+
return resolvedShowDelete
|
|
217
|
+
? {
|
|
218
|
+
key,
|
|
219
|
+
node: renderDeleteButton
|
|
220
|
+
? renderDeleteButton(recordId)
|
|
221
|
+
: defaultDeleteButton(recordId),
|
|
222
|
+
}
|
|
223
|
+
: null;
|
|
224
|
+
case 'CHANGE_STATUS': {
|
|
225
|
+
const disabled = !whenIsTrue(action.when, record);
|
|
226
|
+
const onConfirm = () => onUpdate?.(Number(recordId), action.values);
|
|
227
|
+
return {
|
|
228
|
+
key,
|
|
229
|
+
node: renderStatusAction
|
|
230
|
+
? renderStatusAction(
|
|
231
|
+
action,
|
|
232
|
+
record,
|
|
233
|
+
onConfirm,
|
|
234
|
+
updateLoading,
|
|
235
|
+
disabled,
|
|
236
|
+
)
|
|
237
|
+
: defaultStatusAction(
|
|
238
|
+
action,
|
|
239
|
+
record,
|
|
240
|
+
onConfirm,
|
|
241
|
+
updateLoading,
|
|
242
|
+
disabled,
|
|
243
|
+
),
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
case 'JUMP': {
|
|
247
|
+
const url = interpolateActionUrl(
|
|
248
|
+
(action as any).url || '',
|
|
249
|
+
record as any,
|
|
250
|
+
);
|
|
251
|
+
const isExternal = /^https?:\/\//.test(url);
|
|
252
|
+
return {
|
|
253
|
+
key,
|
|
254
|
+
node: (
|
|
255
|
+
<Button
|
|
256
|
+
type="link"
|
|
257
|
+
size="small"
|
|
258
|
+
onClick={() => {
|
|
259
|
+
if (!url) return;
|
|
260
|
+
if (isExternal) {
|
|
261
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
262
|
+
} else {
|
|
263
|
+
window.location.href = url;
|
|
264
|
+
}
|
|
265
|
+
}}
|
|
266
|
+
>
|
|
267
|
+
{t(
|
|
268
|
+
`resources.${resourceName}.actions.${action.desc}`,
|
|
269
|
+
action.desc,
|
|
270
|
+
)}
|
|
271
|
+
</Button>
|
|
272
|
+
),
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
default:
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
const renderables = actions
|
|
281
|
+
.map((a, i) => renderAction(a, i))
|
|
282
|
+
.filter((x): x is Renderable => x != null);
|
|
283
|
+
|
|
284
|
+
// 隐式按钮(actions 数组里通常没有):CLONE 由显式 showClone 控制;HISTORY 由 showHistory 或 LogButton 触发
|
|
285
|
+
if (resolvedShowClone) {
|
|
286
|
+
renderables.push({
|
|
287
|
+
key: 'clone-implicit',
|
|
288
|
+
node: renderCloneButton
|
|
289
|
+
? renderCloneButton(recordId)
|
|
290
|
+
: defaultCloneButton(recordId),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
if (showHistory || LogButton) {
|
|
294
|
+
const node = renderHistoryButton
|
|
295
|
+
? renderHistoryButton(recordId)
|
|
296
|
+
: defaultHistoryButton(recordId);
|
|
297
|
+
if (node != null) {
|
|
298
|
+
renderables.push({ key: 'history-implicit', node });
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const { visible, overflow } = splitVisibleActions(
|
|
303
|
+
renderables,
|
|
304
|
+
visibleNum ?? renderables.length,
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
const dropdownItems = [
|
|
308
|
+
...overflow.map((r) => ({ key: r.key, label: r.node })),
|
|
288
309
|
...extraDropdownItems,
|
|
289
310
|
].filter((item) => item.label != null);
|
|
290
311
|
|
|
291
312
|
return (
|
|
292
313
|
<Space>
|
|
293
|
-
{
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
? renderEditButton(recordId)
|
|
297
|
-
: defaultEditButton(recordId))}
|
|
298
|
-
|
|
299
|
-
{/* Show/Detail button */}
|
|
300
|
-
{showDetail &&
|
|
301
|
-
(renderDetailButton
|
|
302
|
-
? renderDetailButton(recordId)
|
|
303
|
-
: defaultDetailButton(recordId))}
|
|
304
|
-
|
|
305
|
-
{/* Extra primary actions */}
|
|
314
|
+
{visible.map((r) => (
|
|
315
|
+
<React.Fragment key={r.key}>{r.node}</React.Fragment>
|
|
316
|
+
))}
|
|
306
317
|
{extraPrimaryActions}
|
|
307
|
-
|
|
308
|
-
{/* More dropdown */}
|
|
309
318
|
{dropdownItems.length > 0 && (
|
|
310
319
|
<Dropdown menu={{ items: dropdownItems as any }} trigger={['click']}>
|
|
311
320
|
{renderMoreTrigger ? renderMoreTrigger() : defaultMoreTrigger()}
|
|
@@ -288,14 +288,14 @@ export function getInspectorActions(
|
|
|
288
288
|
{
|
|
289
289
|
label: '添加表头搜索',
|
|
290
290
|
skill: 'ui-config-update',
|
|
291
|
-
promptTemplate: `为 ${col(r, f)}
|
|
291
|
+
promptTemplate: `为 ${col(r, f)} 添加搜索过滤(修改 ui配置中的 header.filter)`,
|
|
292
292
|
requiresConfirm: true,
|
|
293
293
|
confirmDescription: `将为 ${r} 列表的 "${f}" 列添加表头搜索过滤。`,
|
|
294
294
|
},
|
|
295
295
|
{
|
|
296
296
|
label: '移除表头搜索',
|
|
297
297
|
skill: 'ui-config-update',
|
|
298
|
-
promptTemplate: `移除 ${col(r, f)}
|
|
298
|
+
promptTemplate: `移除 ${col(r, f)} 的搜索过滤(修改 ui配置中的 header.filter)`,
|
|
299
299
|
requiresConfirm: true,
|
|
300
300
|
confirmDescription: `将移除 ${r} 列表 "${f}" 列的表头搜索过滤。`,
|
|
301
301
|
},
|
|
@@ -528,7 +528,7 @@ export function getInspectorActions(
|
|
|
528
528
|
{
|
|
529
529
|
label: '增加 模糊匹配 过滤',
|
|
530
530
|
skill: 'list-search-filter',
|
|
531
|
-
promptTemplate: `为 ${r}
|
|
531
|
+
promptTemplate: `为 ${r} 搜索栏添加模糊匹配过滤(修改 ui配置中的 toolbar.searchBar)`,
|
|
532
532
|
promptPrefix: '要添加的字段:',
|
|
533
533
|
requiresInput: true,
|
|
534
534
|
inputPlaceholder: '请填写要添加模糊搜索的字段名,例如:name、title',
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React, { createContext, useContext, useEffect, useState } from 'react';
|
|
2
|
+
import { authProvider } from '../authProvider';
|
|
3
|
+
import { isAgentEnabled } from './env';
|
|
4
|
+
|
|
5
|
+
// Shared singleton promise — resolves to true only if the logged-in userid
|
|
6
|
+
// matches VITE_BRANCH_OWNER. Both the React context and DevShell (separate root)
|
|
7
|
+
// use this same promise so the fetch only happens once.
|
|
8
|
+
const branchOwner = import.meta.env.VITE_BRANCH_OWNER;
|
|
9
|
+
export const agentAllowedPromise: Promise<boolean> = isAgentEnabled
|
|
10
|
+
? branchOwner
|
|
11
|
+
? authProvider.getIdentity!().then(
|
|
12
|
+
(identity: any) => identity?.id === branchOwner,
|
|
13
|
+
)
|
|
14
|
+
: Promise.resolve(true)
|
|
15
|
+
: Promise.resolve(false);
|
|
16
|
+
|
|
17
|
+
const AgentAllowedContext = createContext(false);
|
|
18
|
+
|
|
19
|
+
export const AgentAllowedProvider: React.FC<{ children: React.ReactNode }> = ({
|
|
20
|
+
children,
|
|
21
|
+
}) => {
|
|
22
|
+
const [allowed, setAllowed] = useState(false);
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
agentAllowedPromise.then(setAllowed);
|
|
26
|
+
}, []);
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<AgentAllowedContext.Provider value={allowed}>
|
|
30
|
+
{children}
|
|
31
|
+
</AgentAllowedContext.Provider>
|
|
32
|
+
);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const useIsAgentAllowed = () => useContext(AgentAllowedContext);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Agent panel is enabled in dev mode unless
|
|
2
|
-
//
|
|
1
|
+
// Agent panel is enabled in dev mode unless VITE_ENABLE_AGENT=false.
|
|
2
|
+
// If VITE_BRANCH_OWNER is set, only the matching user can use it.
|
|
3
3
|
export const isAgentEnabled =
|
|
4
4
|
import.meta.env.DEV && import.meta.env.VITE_ENABLE_AGENT !== 'false';
|
|
@@ -12,7 +12,7 @@ import DeleteDataConfirm from './DeleteDataConfirm';
|
|
|
12
12
|
import EditDataModal from './EditDataModal';
|
|
13
13
|
import { resolvePagePaths } from '../components/agentPanel/pagePathUtils';
|
|
14
14
|
import SkillMenu from './SkillMenu';
|
|
15
|
-
import {
|
|
15
|
+
import { agentAllowedPromise } from '../config/agentAllowed';
|
|
16
16
|
import './style.css';
|
|
17
17
|
import UndoConfirm from './UndoConfirm';
|
|
18
18
|
|
|
@@ -61,7 +61,13 @@ export type ModalType =
|
|
|
61
61
|
| null;
|
|
62
62
|
|
|
63
63
|
export default function DevShell() {
|
|
64
|
-
|
|
64
|
+
const [allowed, setAllowed] = useState(false);
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
agentAllowedPromise.then(setAllowed);
|
|
68
|
+
}, []);
|
|
69
|
+
|
|
70
|
+
if (!allowed) return null;
|
|
65
71
|
return <DevShellInner />;
|
|
66
72
|
}
|
|
67
73
|
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
+
import { Modal } from 'antd';
|
|
2
3
|
import {
|
|
3
4
|
GadminCrud as gadminDataProvider,
|
|
4
5
|
applyErrorInterceptor,
|
|
5
6
|
customRequest as baseCustomRequest,
|
|
6
7
|
} from '@gadmin2n/react-common';
|
|
7
|
-
import { login, requestHeaders } from './login';
|
|
8
|
+
import { login, requestHeaders, isWoaDomain } from './login';
|
|
8
9
|
import { getApiUrl } from 'config/http';
|
|
9
10
|
// Re-export Prisma helpers from react-common for backward compatibility
|
|
10
11
|
export { convertPrismaDecimal, isPrismaDecimal } from '@gadmin2n/react-common';
|
|
@@ -13,6 +14,8 @@ const apiUrl = getApiUrl();
|
|
|
13
14
|
|
|
14
15
|
const axiosInstance = axios.create();
|
|
15
16
|
|
|
17
|
+
let woaSessionExpiredModalShown = false;
|
|
18
|
+
|
|
16
19
|
// 注入 auth header
|
|
17
20
|
axiosInstance.interceptors.request.use(
|
|
18
21
|
(config) => {
|
|
@@ -29,6 +32,22 @@ axiosInstance.interceptors.response.use(
|
|
|
29
32
|
(response) => response,
|
|
30
33
|
(error) => {
|
|
31
34
|
if (error?.response?.status === 401) {
|
|
35
|
+
if (isWoaDomain()) {
|
|
36
|
+
if (!woaSessionExpiredModalShown) {
|
|
37
|
+
woaSessionExpiredModalShown = true;
|
|
38
|
+
Modal.confirm({
|
|
39
|
+
title: '登录态已过期',
|
|
40
|
+
content: '您的登录状态已失效,请刷新页面重新登录。',
|
|
41
|
+
okText: '刷新',
|
|
42
|
+
cancelText: '取消',
|
|
43
|
+
onOk: () => window.location.reload(),
|
|
44
|
+
onCancel: () => {
|
|
45
|
+
woaSessionExpiredModalShown = false;
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return new Promise(() => {});
|
|
50
|
+
}
|
|
32
51
|
console.log('login expired');
|
|
33
52
|
login();
|
|
34
53
|
// 返回pending状态的promise,以免后续代码出错
|
|
@@ -35,6 +35,54 @@ import { ModelConfig } from 'generated/types/config.types';
|
|
|
35
35
|
import { agentAttrs } from '../components/agentPanel/agentAttributes';
|
|
36
36
|
import dayjs from 'dayjs';
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* 把 actions 数组解析成 show* 布尔标志集合。
|
|
40
|
+
* 用于让 config 中的 actions 数组成为「显示与否」的唯一真相源。
|
|
41
|
+
*/
|
|
42
|
+
export function actionsToShowFlags(
|
|
43
|
+
actions: ReadonlyArray<{ action: string }> = [],
|
|
44
|
+
) {
|
|
45
|
+
const set = new Set(actions.map((a) => a.action));
|
|
46
|
+
return {
|
|
47
|
+
showInsert: set.has('INSERT'),
|
|
48
|
+
showEdit: set.has('EDIT'),
|
|
49
|
+
showDelete: set.has('DELETE'),
|
|
50
|
+
showExport: set.has('EXPORT'),
|
|
51
|
+
showDetail: set.has('DETAIL'),
|
|
52
|
+
showClone: set.has('CLONE'),
|
|
53
|
+
showImport: set.has('IMPORT'),
|
|
54
|
+
showRefresh: set.has('REFRESH'),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 按 visibleNum 把 actions 切成「主区直接显示」和「溢出 More dropdown」两组。
|
|
60
|
+
*/
|
|
61
|
+
export function splitVisibleActions<T>(
|
|
62
|
+
actions: ReadonlyArray<T> = [],
|
|
63
|
+
visibleNum = Number.POSITIVE_INFINITY,
|
|
64
|
+
): { visible: T[]; overflow: T[] } {
|
|
65
|
+
const n = Number.isFinite(visibleNum)
|
|
66
|
+
? Math.max(0, visibleNum)
|
|
67
|
+
: actions.length;
|
|
68
|
+
return {
|
|
69
|
+
visible: actions.slice(0, n),
|
|
70
|
+
overflow: actions.slice(n),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 用 record 字段值替换 `{{fieldName}}` 占位符(JUMP action 的 url)。
|
|
76
|
+
*/
|
|
77
|
+
export function interpolateActionUrl(
|
|
78
|
+
template: string,
|
|
79
|
+
record: Record<string, any>,
|
|
80
|
+
): string {
|
|
81
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_, key) =>
|
|
82
|
+
String(record?.[key] ?? ''),
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
38
86
|
export function getTableParams(filters: Record<string, string> = {}) {
|
|
39
87
|
return stringifyTableParams({
|
|
40
88
|
pagination: { pageSize: 10, current: 1 },
|
package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/plugins/devShellPlugin.ts
CHANGED
|
@@ -8,8 +8,9 @@ import type { Plugin } from 'vite';
|
|
|
8
8
|
*
|
|
9
9
|
* Features:
|
|
10
10
|
* 1. POST /canvas-patch — agent pushes new component code to canvas via HMR
|
|
11
|
-
* 2.
|
|
12
|
-
* 3.
|
|
11
|
+
* 2. POST /canvas-add-item — agent adds a new component to canvas via HMR
|
|
12
|
+
* 3. "/" serves dev-shell-entry.html (agent panel outer shell)
|
|
13
|
+
* 4. All other HTML navigation falls back to index.html (SPA router)
|
|
13
14
|
*/
|
|
14
15
|
export function devShellPlugin(): Plugin {
|
|
15
16
|
return {
|
|
@@ -49,6 +50,43 @@ export function devShellPlugin(): Plugin {
|
|
|
49
50
|
return;
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
// POST /canvas-add-item — agent 新增一个组件到画布(item + layout)
|
|
54
|
+
if (req.method === 'POST' && url === '/canvas-add-item') {
|
|
55
|
+
let body = '';
|
|
56
|
+
req.on('data', (chunk: Buffer) => {
|
|
57
|
+
body += chunk.toString();
|
|
58
|
+
});
|
|
59
|
+
req.on('end', () => {
|
|
60
|
+
try {
|
|
61
|
+
const { item, layout } = JSON.parse(body) as {
|
|
62
|
+
item: { id: string; componentType: string; code: string };
|
|
63
|
+
layout: { x: number; y: number; w: number; h: number };
|
|
64
|
+
};
|
|
65
|
+
if (
|
|
66
|
+
item?.id &&
|
|
67
|
+
item?.componentType &&
|
|
68
|
+
item?.code !== undefined &&
|
|
69
|
+
layout
|
|
70
|
+
) {
|
|
71
|
+
server.hot.send('canvas:add-item', { item, layout });
|
|
72
|
+
res.setHeader('Content-Type', 'application/json');
|
|
73
|
+
res.end(JSON.stringify({ ok: true }));
|
|
74
|
+
} else {
|
|
75
|
+
res.statusCode = 400;
|
|
76
|
+
res.end(
|
|
77
|
+
JSON.stringify({
|
|
78
|
+
error: 'item (id, componentType, code) and layout required',
|
|
79
|
+
}),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
res.statusCode = 400;
|
|
84
|
+
res.end(JSON.stringify({ error: 'invalid JSON' }));
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
52
90
|
// "/" → dev-shell-entry.html(Agent 外壳),除非 agent 被禁用
|
|
53
91
|
if (url === '/') {
|
|
54
92
|
const agentDisabled = process.env.VITE_ENABLE_AGENT === 'false';
|