@gadmin2n/schematics 0.0.149 → 0.0.151

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.
Files changed (81) hide show
  1. package/dist/lib/application/files/gadmin2-game-angle-demo/CODEBUDDY.md +125 -36
  2. package/dist/lib/application/files/gadmin2-game-angle-demo/config/prisma/workflow.prisma +42 -0
  3. package/dist/lib/application/files/gadmin2-game-angle-demo/gitignore +2 -0
  4. package/dist/lib/application/files/gadmin2-game-angle-demo/server/.seed-timestamp +0 -0
  5. package/dist/lib/application/files/gadmin2-game-angle-demo/server/package.json +3 -1
  6. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/check-dev-env.js +218 -0
  7. package/dist/lib/application/files/gadmin2-game-angle-demo/server/scripts/page-manage.ts +78 -12
  8. package/dist/lib/application/files/gadmin2-game-angle-demo/server/seed/workflow-node-types.ts +37 -67
  9. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/config/index.ts +0 -17
  10. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/agenda/agenda.controller.ts +46 -0
  11. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/canvas/canvas.controller.ts +60 -1
  12. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/external-callback.controller.ts +3 -0
  13. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/externalInteraction/workflow-callback-url.controller.ts +9 -1
  14. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.controller.ts +55 -0
  15. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/page/page.module.ts +2 -0
  16. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/permTransfer/page-transfer.service.ts +491 -0
  17. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/permTransfer/perm-transfer.dto.ts +213 -0
  18. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/permTransfer/perm-transfer.module.ts +28 -0
  19. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/permTransfer/resource-transfer.service.ts +137 -0
  20. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/permTransfer/role-transfer.service.ts +248 -0
  21. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/permTransfer/transfer-token.service.ts +65 -0
  22. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/permTransfer/user-transfer.service.ts +146 -0
  23. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.controller.ts +55 -0
  24. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/resource/resource.module.ts +2 -0
  25. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.controller.ts +55 -0
  26. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/role/role.module.ts +2 -0
  27. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/providers/user-group.provider.spec.ts +46 -0
  28. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/subjectProvider/providers/user-group.provider.ts +3 -2
  29. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.controller.ts +55 -0
  30. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/user/user.module.ts +2 -0
  31. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-cancellation.spec.ts +1 -0
  32. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.spec.ts +96 -14
  33. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-compiler.service.ts +41 -41
  34. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-database-function.spec.ts +36 -0
  35. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-database-function.ts +90 -0
  36. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-event-catalog.spec.ts +4 -0
  37. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-event-trigger.pg.spec.ts +1 -0
  38. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-execution-idempotency.spec.ts +115 -63
  39. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-lifecycle-outbox.service.spec.ts +166 -0
  40. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-lifecycle-outbox.service.ts +217 -0
  41. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-lifecycle-runtime.controller.spec.ts +33 -0
  42. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-lifecycle-runtime.controller.ts +57 -0
  43. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-lifecycle.service.spec.ts +196 -0
  44. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-lifecycle.service.ts +125 -0
  45. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow-node-type.controller.ts +9 -3
  46. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.controller.ts +148 -4
  47. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.module.ts +12 -1
  48. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.spec.ts +14 -0
  49. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflow/workflow.service.ts +75 -45
  50. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/modules/workflowConfig/node-config-validator.service.spec.ts +25 -0
  51. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/TransferExportModal.tsx +115 -0
  52. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/TransferImportModal.tsx +257 -0
  53. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/hooks/useDynamicResources.tsx +0 -2
  54. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/common.json +40 -0
  55. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/en/workflow.json +15 -13
  56. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/common.json +40 -0
  57. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/locales/zh_CN/workflow.json +15 -13
  58. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/plugins/devShellPlugin.ts +2 -1
  59. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/agendaJob/list.tsx +89 -65
  60. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/page/list.tsx +51 -127
  61. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/permissionReadme/index.tsx +53 -109
  62. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/resource/list.tsx +45 -41
  63. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/role/list.tsx +51 -72
  64. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/user/list.tsx +49 -40
  65. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/NodePropertyPanel.test.tsx +16 -5
  66. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/components/ReadonlyNodePanel.test.tsx +28 -10
  67. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.test.tsx +60 -1
  68. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/instance-detail.tsx +94 -12
  69. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/node-instance-show.css +13 -0
  70. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/schema-form/default-widgets.tsx +3 -3
  71. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/show.tsx +8 -1
  72. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/DbExecuteConfigEditor.tsx +167 -94
  73. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/EventTriggerConfigEditor.tsx +95 -95
  74. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/EventTriggerLifecycleConfigEditor.test.tsx +81 -0
  75. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/EventTriggerLifecycleConfigEditor.tsx +126 -0
  76. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/node-instances/widgets/SimplifiedNodeEditors.test.tsx +48 -52
  77. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/forEachConnection.ts +2 -2
  78. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/routes/workflow/utils/newNodeConfig.ts +1 -3
  79. package/package.json +1 -1
  80. package/dist/lib/application/files/gadmin2-game-angle-demo/server/src/config/ac.config.ts +0 -24
  81. package/dist/lib/application/files/gadmin2-game-angle-demo/web/src/components/SqlModal.tsx +0 -495
@@ -173,6 +173,14 @@ const faqItems = [
173
173
  label: 'iframe 页面加载失败?',
174
174
  children: (
175
175
  <ol>
176
+ <li>
177
+ <Text strong>先确认本项目是否已实现 iframe 能力</Text>:若{' '}
178
+ <Text code>App.tsx</Text> 的兜底路由仍是{' '}
179
+ <Text code>ErrorComponent</Text>(未接入{' '}
180
+ <Text code>WildcardPage</Text>),则 <Text code>iframeUrl</Text>{' '}
181
+ 不会生效,点菜单必然 404 —— 这与目标网站的设置无关。详见「iframe
182
+ 嵌入」标签页顶部的说明。
183
+ </li>
176
184
  <li>
177
185
  检查目标网站是否允许被 iframe 嵌入(X-Frame-Options /
178
186
  Content-Security-Policy 头)
@@ -269,17 +277,22 @@ const comparisonColumns = [
269
277
  { title: '原生页面', dataIndex: 'native', key: 'native' },
270
278
  { title: '外部链接', dataIndex: 'external', key: 'external' },
271
279
  { title: 'iframe 嵌入', dataIndex: 'iframe', key: 'iframe' },
272
- { title: '动态菜单', dataIndex: 'dynamic', key: 'dynamic' },
273
280
  ];
274
281
 
275
282
  const comparisonData = [
283
+ {
284
+ key: 'support',
285
+ item: '本项目是否可用',
286
+ native: '是',
287
+ external: '否(未实现 WildcardPage)',
288
+ iframe: '否(未实现 WildcardPage)',
289
+ },
276
290
  {
277
291
  key: 'scene',
278
292
  item: '适用场景',
279
293
  native: '内部开发的完整功能页面',
280
294
  external: '跳转到外部系统(Wiki、文档等)',
281
295
  iframe: '在应用内嵌入外部页面(报表等)',
282
- dynamic: '同一组件多实例(如工作室列表)',
283
296
  },
284
297
  {
285
298
  key: 'registry',
@@ -287,7 +300,6 @@ const comparisonData = [
287
300
  native: '是(config/routeRegistry.tsx)',
288
301
  external: '否',
289
302
  iframe: '否',
290
- dynamic: '是(config/dynamicMenus.ts 配置)',
291
303
  },
292
304
  {
293
305
  key: 'component',
@@ -295,7 +307,6 @@ const comparisonData = [
295
307
  native: '是',
296
308
  external: '否',
297
309
  iframe: '否',
298
- dynamic: '是(共享组件)',
299
310
  },
300
311
  {
301
312
  key: 'tpage',
@@ -303,7 +314,6 @@ const comparisonData = [
303
314
  native: '是(code = routeRegistry key)',
304
315
  external: '是(填 externalRedirectUrl)',
305
316
  iframe: '是(填 iframeUrl)',
306
- dynamic: '是(父级目录)',
307
317
  },
308
318
  {
309
319
  key: 'url',
@@ -311,7 +321,6 @@ const comparisonData = [
311
321
  native: 'code, path, parentId',
312
322
  external: 'externalRedirectUrl',
313
323
  iframe: 'iframeUrl',
314
- dynamic: 'code(如 invested / native)',
315
324
  },
316
325
  {
317
326
  key: 'behavior',
@@ -319,7 +328,6 @@ const comparisonData = [
319
328
  native: '应用内完整渲染',
320
329
  external: '新窗口打开 + 自动返回',
321
330
  iframe: '应用框架内嵌入',
322
- dynamic: '应用内完整渲染',
323
331
  },
324
332
  {
325
333
  key: 'template',
@@ -327,7 +335,6 @@ const comparisonData = [
327
335
  native: '—',
328
336
  external: '是',
329
337
  iframe: '是',
330
- dynamic: '—',
331
338
  },
332
339
  ];
333
340
 
@@ -654,6 +661,26 @@ staff: {
654
661
  label: '外部链接',
655
662
  children: (
656
663
  <>
664
+ <Alert
665
+ type="warning"
666
+ showIcon
667
+ message="本项目暂不支持外部链接菜单"
668
+ description={
669
+ <>
670
+ <Text code>externalRedirectUrl</Text>{' '}
671
+ 字段在数据库和本页表单里都存在,但本项目未实现渲染它的{' '}
672
+ <Text code>WildcardPage</Text> 通配组件(
673
+ <Text code>App.tsx</Text> 的兜底路由目前直接返回
674
+ 404)。填了该字段能保存成功,
675
+ <Text strong>但点击菜单会 404</Text>。
676
+ <br />
677
+ 以下内容描述的是上游 ERP
678
+ 项目的机制,供将来移植参考。当前若需要外部系统入口,
679
+ 请做一个极薄的自定义页面(页面内放跳转按钮)。
680
+ </>
681
+ }
682
+ style={{ marginBottom: 16 }}
683
+ />
657
684
  <Paragraph>
658
685
  外部链接方式<Text strong>无需编写任何前端代码</Text>
659
686
  ,只需在 Page Management 中创建一条 t_page 记录,并填写{' '}
@@ -718,6 +745,24 @@ staff: {
718
745
  label: 'iframe 嵌入',
719
746
  children: (
720
747
  <>
748
+ <Alert
749
+ type="warning"
750
+ showIcon
751
+ message="本项目暂不支持 iframe 菜单"
752
+ description={
753
+ <>
754
+ <Text code>iframeUrl</Text>{' '}
755
+ 字段在数据库和本页表单里都存在,但本项目未实现渲染它的{' '}
756
+ <Text code>WildcardPage</Text> 通配组件(
757
+ <Text code>App.tsx</Text> 的兜底路由目前直接返回
758
+ 404)。填了该字段能保存成功,
759
+ <Text strong>但点击菜单会 404</Text>。
760
+ <br />
761
+ 以下内容描述的是上游 ERP 项目的机制,供将来移植参考。
762
+ </>
763
+ }
764
+ style={{ marginBottom: 16 }}
765
+ />
721
766
  <Paragraph>
722
767
  iframe 方式同样<Text strong>无需编写前端代码</Text>
723
768
  ,在 Page Management 中填写 <Text code>iframeUrl</Text>{' '}
@@ -771,107 +816,6 @@ staff: {
771
816
  </>
772
817
  ),
773
818
  },
774
- {
775
- key: 'dynamic',
776
- label: '动态菜单',
777
- children: (
778
- <>
779
- <Paragraph>
780
- 动态菜单适用于
781
- <Text strong>同一个页面组件需要生成多个菜单项</Text>
782
- 的场景。通过 <Text code>
783
- src/config/dynamicMenus.ts
784
- </Text>{' '}
785
- 的配置驱动方案,开发者只需添加一条配置即可新增动态菜单组。
786
- </Paragraph>
787
-
788
- <Title level={5}>配置方式</Title>
789
- <Paragraph>
790
- 在 <Text code>src/config/dynamicMenus.ts</Text> 中的{' '}
791
- <Text code>dynamicMenuGroups</Text> 数组中添加一条配置:
792
- </Paragraph>
793
- <pre
794
- style={{
795
- background: 'var(--ant-color-fill-quaternary)',
796
- padding: 16,
797
- borderRadius: 6,
798
- overflow: 'auto',
799
- fontSize: 13,
800
- lineHeight: 1.6,
801
- }}
802
- >{`// src/config/dynamicMenus.ts
803
- interface DynamicMenuGroup {
804
- parentCode: string; // 父级 page code(需在 t_page 注册)
805
- routePrefix: string; // 路由前缀,如 "/rd/invested"
806
- items: string[]; // 子项列表,每项生成一个菜单项和路由
807
- component: LazyExoticComponent<ComponentType<any>>; // 共享组件
808
- }
809
-
810
- // 示例:RD 工作室
811
- {
812
- parentCode: "invested",
813
- routePrefix: "/rd/invested",
814
- items: studioMap.investedStudios,
815
- component: lazy(() => import("@/routes/rd/rdec")),
816
- }`}</pre>
817
-
818
- <Alert
819
- type="info"
820
- showIcon
821
- message="权限控制"
822
- description={
823
- <>
824
- 动态菜单的权限以 <Text code>parentCode</Text>{' '}
825
- 为单位控制。例如为角色分配 <Text code>invested</Text>{' '}
826
- 页面后,该角色可以看到该组下所有子菜单项。无需为每个子项单独配置权限。
827
- </>
828
- }
829
- style={{ marginTop: 16 }}
830
- />
831
-
832
- <Divider />
833
- <Title level={5}>添加新动态菜单组的步骤</Title>
834
- <ol>
835
- <li>
836
- 在 <Text code>t_page</Text> 中注册父级页面(设置{' '}
837
- <Text code>code</Text>、<Text code>parentId</Text>)
838
- </li>
839
- <li>
840
- 在 <Text code>src/config/dynamicMenus.ts</Text> 的{' '}
841
- <Text code>dynamicMenuGroups</Text> 数组中添加一条配置
842
- </li>
843
- <li>
844
- 路由和菜单项会自动生成,无需修改{' '}
845
- <Text code>routeRegistry.tsx</Text>、
846
- <Text code>App.tsx</Text> 或{' '}
847
- <Text code>useDynamicResources.tsx</Text>
848
- </li>
849
- </ol>
850
-
851
- <Divider />
852
- <Title level={5}>现有示例:RD 工作室</Title>
853
- <Paragraph>
854
- 当前系统中 RD 模块的 invested(投资工作室)和
855
- native(自研工作室)使用动态菜单:
856
- </Paragraph>
857
- <ul>
858
- <li>
859
- 工作室列表定义在 <Text code>src/config/studio.ts</Text> 的{' '}
860
- <Text code>studioMap</Text> 中
861
- </li>
862
- <li>
863
- 所有工作室路由共享同一个 <Text code>RDEC</Text>{' '}
864
- 组件,组件内从 URL 提取工作室名称
865
- </li>
866
- <li>
867
- 添加新工作室只需在 <Text code>studio.ts</Text> 的{' '}
868
- <Text code>StudioEnum</Text> 和{' '}
869
- <Text code>studioMap</Text> 中添加条目即可
870
- </li>
871
- </ul>
872
- </>
873
- ),
874
- },
875
819
  ]}
876
820
  />
877
821
  </Card>
@@ -5,18 +5,16 @@ import {
5
5
  EditOutlined,
6
6
  EyeOutlined,
7
7
  SearchOutlined,
8
- CodeOutlined,
8
+ ExportOutlined,
9
+ ImportOutlined,
9
10
  } from '@ant-design/icons';
10
- import {
11
- getDefaultFilter,
12
- type HttpError,
13
- useGetIdentity,
14
- } from '@refinedev/core';
11
+ import { getDefaultFilter, type HttpError } from '@refinedev/core';
15
12
  import type { Resource } from './types';
16
13
  import { EditModal } from './components/EditModal';
17
14
  import { CreateModal } from './components/CreateModal';
18
15
  import { ResourceDetailDrawer } from './components/ResourceDetailDrawer';
19
- import { SqlModal, generateResourceSql } from '../../components/SqlModal';
16
+ import { TransferExportModal } from 'components/TransferExportModal';
17
+ import { TransferImportModal } from 'components/TransferImportModal';
20
18
  import { PageHeader } from 'components';
21
19
  import { PAGE_WRAPPER_STYLE } from 'constants/layout';
22
20
 
@@ -32,7 +30,6 @@ export const PROTECTED_RESOURCES = [
32
30
  ];
33
31
 
34
32
  export const ResourceManagementListPage: React.FC = () => {
35
- const { data: identity } = useGetIdentity<any>();
36
33
  const {
37
34
  tableProps,
38
35
  filters,
@@ -72,20 +69,8 @@ export const ResourceManagementListPage: React.FC = () => {
72
69
 
73
70
  const [drawerResource, setDrawerResource] = useState<Resource | null>(null);
74
71
 
75
- const [sqlModal, setSqlModal] = useState<{
76
- open: boolean;
77
- title: string;
78
- sql: string;
79
- }>({ open: false, title: '', sql: '' });
80
-
81
- const handleShowSql = (record: Resource) => {
82
- const operator = {
83
- authorId: identity?.userid,
84
- authorName: identity?.username,
85
- };
86
- const sql = generateResourceSql(record, operator);
87
- setSqlModal({ open: true, title: `SQL — Resource: ${record.code}`, sql });
88
- };
72
+ const [exportEndpoint, setExportEndpoint] = useState<string | null>(null);
73
+ const [importOpen, setImportOpen] = useState(false);
89
74
 
90
75
  // fix for ant design filter dropdown contains operator
91
76
  filters.map((filter) => {
@@ -122,17 +107,25 @@ export const ResourceManagementListPage: React.FC = () => {
122
107
  <PageHeader
123
108
  title="Resource mgnts"
124
109
  extra={
125
- <Button
126
- type="primary"
127
- onClick={() =>
128
- setShowModal({
129
- type: 'create',
130
- id: null,
131
- })
132
- }
133
- >
134
- Create Resource
135
- </Button>
110
+ <Space>
111
+ <Button
112
+ icon={<ImportOutlined />}
113
+ onClick={() => setImportOpen(true)}
114
+ >
115
+ Import
116
+ </Button>
117
+ <Button
118
+ type="primary"
119
+ onClick={() =>
120
+ setShowModal({
121
+ type: 'create',
122
+ id: null,
123
+ })
124
+ }
125
+ >
126
+ Create Resource
127
+ </Button>
128
+ </Space>
136
129
  }
137
130
  />
138
131
  <Card className="card-table">
@@ -212,12 +205,16 @@ export const ResourceManagementListPage: React.FC = () => {
212
205
  }
213
206
  />
214
207
  </Tooltip>
215
- <Tooltip title="Generate SQL">
208
+ <Tooltip title="Export">
216
209
  <Button
217
210
  type="default"
218
211
  size="small"
219
- icon={<CodeOutlined />}
220
- onClick={() => handleShowSql(record)}
212
+ icon={<ExportOutlined />}
213
+ onClick={() =>
214
+ setExportEndpoint(
215
+ `resource/${encodeURIComponent(record.code)}/export-token`,
216
+ )
217
+ }
221
218
  />
222
219
  </Tooltip>
223
220
  </Space>
@@ -246,11 +243,18 @@ export const ResourceManagementListPage: React.FC = () => {
246
243
  onClose={() => setDrawerResource(null)}
247
244
  />
248
245
  )}
249
- <SqlModal
250
- open={sqlModal.open}
251
- onClose={() => setSqlModal({ open: false, title: '', sql: '' })}
252
- title={sqlModal.title}
253
- sql={sqlModal.sql}
246
+ <TransferExportModal
247
+ open={!!exportEndpoint}
248
+ exportTokenEndpoint={exportEndpoint}
249
+ entity="resource"
250
+ onClose={() => setExportEndpoint(null)}
251
+ />
252
+ <TransferImportModal
253
+ open={importOpen}
254
+ onClose={() => setImportOpen(false)}
255
+ onSuccess={() => tableQueryResult?.refetch()}
256
+ previewEndpoint="resource/import-preview"
257
+ confirmEndpoint="resource/import-confirm"
254
258
  />
255
259
  </div>
256
260
  );
@@ -1,16 +1,21 @@
1
1
  import React, { useCallback, useMemo, useState } from 'react';
2
2
  import { useTable } from '@refinedev/antd';
3
- import { useGetLocale, useGetIdentity } from '@refinedev/core';
4
- import { Table, Button, Card, Space, Tooltip, message } from 'antd';
3
+ import { useGetLocale } from '@refinedev/core';
4
+ import { Table, Button, Card, Space, Tooltip } from 'antd';
5
5
 
6
6
  import { EditModal } from './components/EditModal';
7
7
  import { CreateModal } from './components/CreateModal';
8
8
  import type { RolePageTreeNode } from './types';
9
9
  import { useRolePage } from './hooks/useRolePage';
10
- import { EditOutlined, EyeOutlined, CodeOutlined } from '@ant-design/icons';
10
+ import {
11
+ EditOutlined,
12
+ EyeOutlined,
13
+ ExportOutlined,
14
+ ImportOutlined,
15
+ } from '@ant-design/icons';
11
16
  import { RoleDetailDrawer } from './components/RoleDetailDrawer';
12
- import { SqlModal, generateRoleSql } from '../../components/SqlModal';
13
- import { customRequest } from '@/helpers/http';
17
+ import { TransferExportModal } from '@/components/TransferExportModal';
18
+ import { TransferImportModal } from '@/components/TransferImportModal';
14
19
  import { buildPageTree } from '@/helpers/page-tree';
15
20
  import { PageHeader } from 'components';
16
21
  import { PAGE_WRAPPER_STYLE } from 'constants/layout';
@@ -85,9 +90,8 @@ const extractActionsFromRelation = (
85
90
  export const RoleManagementListPage: React.FC = () => {
86
91
  const getLocale = useGetLocale();
87
92
  const currentLocale = getLocale();
88
- const { data: identity } = useGetIdentity<any>();
89
93
 
90
- const { tableProps } = useTable({
94
+ const { tableProps, tableQuery: tableQueryResult } = useTable({
91
95
  resource: 'role',
92
96
  pagination: {
93
97
  pageSize: 20,
@@ -258,52 +262,8 @@ export const RoleManagementListPage: React.FC = () => {
258
262
  id: null,
259
263
  });
260
264
 
261
- const [sqlModal, setSqlModal] = useState<{
262
- open: boolean;
263
- title: string;
264
- sql: string;
265
- }>({ open: false, title: '', sql: '' });
266
-
267
- const handleShowSql = useCallback(
268
- async (record: any) => {
269
- const summary = rolePermissionSummary[record.id];
270
- const rolePages =
271
- summary?.pageInfos?.map((p) => ({
272
- pageId: p.id,
273
- pageCode: p.code,
274
- pageName: p.name,
275
- })) ?? [];
276
-
277
- let roleResources: Array<{
278
- resourceCode: string;
279
- resourceName: string;
280
- actions: string[];
281
- }> = [];
282
- try {
283
- const resp: any = await customRequest('roleResource/findMany', 'post', {
284
- where: { roleId: record.id },
285
- include: { resource: true },
286
- });
287
- const items: any[] = Array.isArray(resp?.data) ? resp.data : [];
288
- roleResources = items.map((item) => ({
289
- resourceCode: item.resource?.code ?? String(item.resourceId),
290
- resourceName: item.resource?.name ?? String(item.resourceId),
291
- actions: Array.isArray(item.actions) ? item.actions : [],
292
- }));
293
- } catch {
294
- message.warning(
295
- 'Could not fetch role resource details; SQL will omit permissions.',
296
- );
297
- }
298
-
299
- const sql = generateRoleSql(record, rolePages, roleResources, {
300
- authorId: identity?.userid,
301
- authorName: identity?.username,
302
- });
303
- setSqlModal({ open: true, title: `SQL — Role: ${record.name}`, sql });
304
- },
305
- [rolePermissionSummary, identity],
306
- );
265
+ const [exportEndpoint, setExportEndpoint] = useState<string | null>(null);
266
+ const [importOpen, setImportOpen] = useState(false);
307
267
 
308
268
  const refreshRoleRelatedData = useCallback(() => {
309
269
  refetchRolePages(rolePagesQuery ?? {});
@@ -324,17 +284,25 @@ export const RoleManagementListPage: React.FC = () => {
324
284
  <PageHeader
325
285
  title="Role mgnts"
326
286
  extra={
327
- <Button
328
- type="primary"
329
- onClick={() =>
330
- setShowModal({
331
- type: 'create',
332
- id: null,
333
- })
334
- }
335
- >
336
- Create Role
337
- </Button>
287
+ <Space>
288
+ <Button
289
+ icon={<ImportOutlined />}
290
+ onClick={() => setImportOpen(true)}
291
+ >
292
+ Import
293
+ </Button>
294
+ <Button
295
+ type="primary"
296
+ onClick={() =>
297
+ setShowModal({
298
+ type: 'create',
299
+ id: null,
300
+ })
301
+ }
302
+ >
303
+ Create Role
304
+ </Button>
305
+ </Space>
338
306
  }
339
307
  />
340
308
  <Card className="card-table">
@@ -364,12 +332,16 @@ export const RoleManagementListPage: React.FC = () => {
364
332
  }
365
333
  />
366
334
  </Tooltip>
367
- <Tooltip title="Generate SQL">
335
+ <Tooltip title="Export">
368
336
  <Button
369
337
  type="default"
370
338
  size="small"
371
- icon={<CodeOutlined />}
372
- onClick={() => handleShowSql(record)}
339
+ icon={<ExportOutlined />}
340
+ onClick={() =>
341
+ setExportEndpoint(
342
+ `role/${encodeURIComponent(record.name)}/export-token`,
343
+ )
344
+ }
373
345
  />
374
346
  </Tooltip>
375
347
  </Space>
@@ -401,11 +373,18 @@ export const RoleManagementListPage: React.FC = () => {
401
373
  pageTreeData={pageTreeData}
402
374
  />
403
375
  )}
404
- <SqlModal
405
- open={sqlModal.open}
406
- onClose={() => setSqlModal({ open: false, title: '', sql: '' })}
407
- title={sqlModal.title}
408
- sql={sqlModal.sql}
376
+ <TransferExportModal
377
+ open={!!exportEndpoint}
378
+ exportTokenEndpoint={exportEndpoint}
379
+ entity="role"
380
+ onClose={() => setExportEndpoint(null)}
381
+ />
382
+ <TransferImportModal
383
+ open={importOpen}
384
+ onClose={() => setImportOpen(false)}
385
+ onSuccess={() => tableQueryResult?.refetch()}
386
+ previewEndpoint="role/import-preview"
387
+ confirmEndpoint="role/import-confirm"
409
388
  />
410
389
  </div>
411
390
  );