@jiangood/open-admin 2.5.2 → 2.5.6

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 (108) hide show
  1. package/package.json +3 -2
  2. package/src/.umi-production/appData.json +78 -77
  3. package/src/.umi-production/core/route.tsx +6 -6
  4. package/src/framework/GlobalData.d.ts +8 -0
  5. package/src/framework/GlobalData.js +33 -0
  6. package/src/framework/{components/system → biz}/HasPerm.tsx +1 -1
  7. package/src/framework/{components/OrgTree/index.tsx → biz/OrgTree.tsx} +12 -2
  8. package/src/framework/{components/system/ButtonList.tsx → biz/PermActions.tsx} +6 -5
  9. package/src/framework/{components/RoleTree/index.tsx → biz/RoleTree.tsx} +13 -3
  10. package/src/framework/{components/system → biz}/index.tsx +4 -2
  11. package/src/framework/components/ContextMenu/index.tsx +1 -1
  12. package/src/framework/components/DownloadModal/index.tsx +332 -0
  13. package/src/framework/components/ErrorBoundary/index.tsx +18 -0
  14. package/src/framework/components/NamedIcon/{index.jsx → index.tsx} +8 -3
  15. package/src/framework/components/Page/index.tsx +2 -2
  16. package/src/framework/components/ProTable/index.tsx +3 -10
  17. package/src/framework/components/index.ts +2 -6
  18. package/src/framework/fields/BaseRemoteSelect/index.tsx +2 -3
  19. package/src/framework/fields/FieldEditor/index.tsx +35 -44
  20. package/src/framework/fields/FieldNumberRange/index.tsx +2 -2
  21. package/src/framework/fields/FieldRemoteTree/index.tsx +1 -1
  22. package/src/framework/fields/FieldTable/index.tsx +20 -22
  23. package/src/framework/fields/FieldUploadFile/index.tsx +3 -3
  24. package/src/framework/fields/index.ts +1 -3
  25. package/src/framework/index.ts +2 -0
  26. package/src/framework/utils/ArrayUtils.d.ts +6 -0
  27. package/src/framework/utils/ArrayUtils.js +30 -0
  28. package/src/framework/utils/DateUtils.d.ts +20 -0
  29. package/src/framework/utils/DateUtils.js +81 -0
  30. package/src/framework/utils/DeviceUtils.d.ts +3 -0
  31. package/src/framework/utils/DeviceUtils.js +10 -0
  32. package/src/framework/utils/EventBus.d.ts +6 -0
  33. package/src/framework/utils/EventBus.js +51 -0
  34. package/src/framework/utils/MessageUtils.d.ts +10 -0
  35. package/src/framework/utils/MessageUtils.js +43 -0
  36. package/src/framework/utils/ObjectUtils.d.ts +14 -0
  37. package/src/framework/utils/ObjectUtils.js +12 -0
  38. package/src/framework/utils/StorageUtils.d.ts +4 -0
  39. package/src/framework/utils/{StorageUtils.ts → StorageUtils.js} +7 -11
  40. package/src/framework/utils/StringUtils.d.ts +17 -0
  41. package/src/framework/utils/StringUtils.js +107 -0
  42. package/src/framework/utils/TreeUtils.d.ts +13 -0
  43. package/src/framework/utils/TreeUtils.js +63 -0
  44. package/src/framework/utils/UrlUtils.d.ts +6 -0
  45. package/src/framework/utils/UrlUtils.js +71 -0
  46. package/src/framework/utils/index.ts +1 -5
  47. package/src/framework/utils/system/DictUtils.d.ts +20 -0
  48. package/src/framework/utils/system/DictUtils.js +31 -0
  49. package/src/framework/utils/system/FormRegistry.d.ts +8 -0
  50. package/src/framework/utils/system/FormRegistry.js +31 -0
  51. package/src/framework/utils/system/HttpUtils.d.ts +14 -0
  52. package/src/framework/utils/system/HttpUtils.js +87 -0
  53. package/src/framework/utils/system/PageUtils.d.ts +14 -0
  54. package/src/framework/utils/system/PageUtils.js +59 -0
  55. package/src/framework/utils/system/PermUtils.d.ts +5 -0
  56. package/src/framework/utils/system/PermUtils.js +27 -0
  57. package/src/framework/utils/system/ThemeUtils.d.ts +6 -0
  58. package/src/framework/utils/system/{ThemeUtils.ts → ThemeUtils.js} +19 -19
  59. package/src/framework/utils/system/index.ts +1 -2
  60. package/src/framework/views/ViewFile/index.tsx +1 -2
  61. package/src/framework/views/ViewImage/index.tsx +3 -3
  62. package/src/framework/views/{ViewBooleanEnableDisable → ViewSwitch}/index.tsx +1 -1
  63. package/src/framework/views/index.ts +3 -4
  64. package/src/layouts/admin/HeaderRight.jsx +32 -44
  65. package/src/layouts/admin/index.jsx +5 -29
  66. package/src/layouts/index.jsx +26 -17
  67. package/src/pages/about.jsx +24 -4
  68. package/src/pages/{userCenter/ChangePassword.jsx → account/changePassword.jsx} +1 -2
  69. package/src/pages/{userCenter → account}/index.jsx +2 -6
  70. package/src/pages/forceUpdatePwd.jsx +89 -0
  71. package/src/pages/login.jsx +5 -5
  72. package/src/pages/system/dict/index.jsx +6 -6
  73. package/src/pages/system/file/index.jsx +4 -4
  74. package/src/pages/system/job/index.jsx +7 -3
  75. package/src/pages/system/org/index.jsx +2 -2
  76. package/src/pages/system/role/index.jsx +4 -4
  77. package/src/pages/system/role/{perm.jsx → rolePerm.jsx} +2 -2
  78. package/src/pages/system/user/index.jsx +4 -4
  79. package/src/framework/components/DownloadFileButton/index.tsx +0 -33
  80. package/src/framework/components/NamedIcon/index.d.ts +0 -7
  81. package/src/framework/components/ProTable/utils/index.ts +0 -62
  82. package/src/framework/fields/FieldRemoteSelectMultipleInline/index.tsx +0 -33
  83. package/src/framework/fields/FieldRemoteTreeSelectMultiple/index.tsx +0 -43
  84. package/src/framework/utils/ArrayUtils.ts +0 -186
  85. package/src/framework/utils/ColorsUtils.ts +0 -378
  86. package/src/framework/utils/DateUtils.ts +0 -187
  87. package/src/framework/utils/DeviceUtils.ts +0 -46
  88. package/src/framework/utils/DomUtils.ts +0 -50
  89. package/src/framework/utils/EventBusUtils.ts +0 -143
  90. package/src/framework/utils/Logger.ts +0 -40
  91. package/src/framework/utils/MessageUtils.tsx +0 -90
  92. package/src/framework/utils/ObjectUtils.ts +0 -118
  93. package/src/framework/utils/StringUtils.ts +0 -436
  94. package/src/framework/utils/TreeUtils.ts +0 -251
  95. package/src/framework/utils/UrlUtils.ts +0 -152
  96. package/src/framework/utils/UuidUtils.ts +0 -88
  97. package/src/framework/utils/ValidateUtils.ts +0 -28
  98. package/src/framework/utils/system/DictUtils.ts +0 -96
  99. package/src/framework/utils/system/FormRegistryUtils.ts +0 -76
  100. package/src/framework/utils/system/HttpUtils.ts +0 -251
  101. package/src/framework/utils/system/PageUtils.ts +0 -163
  102. package/src/framework/utils/system/PermUtils.ts +0 -79
  103. package/src/framework/utils/system/SysUtils.ts +0 -105
  104. package/src/framework/views/ViewProcessInstanceProgress/index.tsx +0 -98
  105. package/src/framework/views/ViewProcessInstanceProgressButton/index.tsx +0 -23
  106. package/src/pages/userCenter/message.jsx +0 -105
  107. /package/src/framework/{components → views}/ViewRange/index.tsx +0 -0
  108. /package/src/pages/system/user/{UserPerm.jsx → userPerm.jsx} +0 -0
@@ -2,6 +2,7 @@
2
2
  "cwd": "/home/runner/work/open-admin/open-admin/web",
3
3
  "pkg": {
4
4
  "devDependencies": {
5
+ "@types/qs": "^6.15.1",
5
6
  "@types/react": "^19.2.2",
6
7
  "@types/react-dom": "^19.0.0",
7
8
  "babel-plugin-react-compiler": "^1.0.0",
@@ -37,7 +38,7 @@
37
38
  "dev": "umi dev",
38
39
  "build": "umi build"
39
40
  },
40
- "version": "2.5.2"
41
+ "version": "2.5.6"
41
42
  },
42
43
  "pkgPath": "/home/runner/work/open-admin/open-admin/web/package.json",
43
44
  "plugins": {
@@ -50,7 +51,7 @@
50
51
  "config": {},
51
52
  "time": {
52
53
  "hooks": {},
53
- "register": 0
54
+ "register": 1
54
55
  },
55
56
  "enableBy": "register"
56
57
  },
@@ -80,7 +81,7 @@
80
81
  0
81
82
  ]
82
83
  },
83
- "register": 3
84
+ "register": 2
84
85
  },
85
86
  "enableBy": "register"
86
87
  },
@@ -97,7 +98,7 @@
97
98
  1
98
99
  ]
99
100
  },
100
- "register": 1
101
+ "register": 2
101
102
  },
102
103
  "enableBy": "register"
103
104
  },
@@ -114,7 +115,7 @@
114
115
  0
115
116
  ]
116
117
  },
117
- "register": 1
118
+ "register": 0
118
119
  },
119
120
  "enableBy": "register"
120
121
  },
@@ -141,10 +142,10 @@
141
142
  "time": {
142
143
  "hooks": {
143
144
  "modifyAppData": [
144
- 71
145
+ 67
145
146
  ]
146
147
  },
147
- "register": 21
148
+ "register": 22
148
149
  },
149
150
  "enableBy": "register"
150
151
  },
@@ -174,7 +175,7 @@
174
175
  0
175
176
  ],
176
177
  "onCheck": [
177
- 0
178
+ 1
178
179
  ]
179
180
  },
180
181
  "register": 1
@@ -191,10 +192,10 @@
191
192
  "time": {
192
193
  "hooks": {
193
194
  "onCheck": [
194
- 1
195
+ 0
195
196
  ]
196
197
  },
197
- "register": 1
198
+ "register": 0
198
199
  },
199
200
  "enableBy": "register"
200
201
  },
@@ -207,7 +208,7 @@
207
208
  "config": {},
208
209
  "time": {
209
210
  "hooks": {},
210
- "register": 0
211
+ "register": 1
211
212
  },
212
213
  "enableBy": "config"
213
214
  },
@@ -1076,7 +1077,7 @@
1076
1077
  "config": {},
1077
1078
  "time": {
1078
1079
  "hooks": {},
1079
- "register": 0
1080
+ "register": 1
1080
1081
  },
1081
1082
  "enableBy": "config"
1082
1083
  },
@@ -1093,7 +1094,7 @@
1093
1094
  0
1094
1095
  ]
1095
1096
  },
1096
- "register": 1
1097
+ "register": 0
1097
1098
  },
1098
1099
  "enableBy": "register"
1099
1100
  },
@@ -1119,7 +1120,7 @@
1119
1120
  "config": {},
1120
1121
  "time": {
1121
1122
  "hooks": {},
1122
- "register": 98
1123
+ "register": 94
1123
1124
  },
1124
1125
  "enableBy": "register"
1125
1126
  },
@@ -1132,7 +1133,7 @@
1132
1133
  "config": {},
1133
1134
  "time": {
1134
1135
  "hooks": {},
1135
- "register": 26
1136
+ "register": 27
1136
1137
  },
1137
1138
  "enableBy": "config"
1138
1139
  },
@@ -1188,7 +1189,7 @@
1188
1189
  "config": {},
1189
1190
  "time": {
1190
1191
  "hooks": {},
1191
- "register": 2
1192
+ "register": 1
1192
1193
  },
1193
1194
  "enableBy": "config"
1194
1195
  },
@@ -1213,7 +1214,7 @@
1213
1214
  "config": {},
1214
1215
  "time": {
1215
1216
  "hooks": {},
1216
- "register": 0
1217
+ "register": 1
1217
1218
  },
1218
1219
  "enableBy": "config"
1219
1220
  },
@@ -1226,7 +1227,7 @@
1226
1227
  "config": {},
1227
1228
  "time": {
1228
1229
  "hooks": {},
1229
- "register": 1
1230
+ "register": 0
1230
1231
  }
1231
1232
  },
1232
1233
  "./node_modules/@umijs/preset-umi/dist/features/overrides/overrides": {
@@ -1238,7 +1239,7 @@
1238
1239
  "config": {},
1239
1240
  "time": {
1240
1241
  "hooks": {},
1241
- "register": 0
1242
+ "register": 1
1242
1243
  },
1243
1244
  "enableBy": "register"
1244
1245
  },
@@ -1251,7 +1252,7 @@
1251
1252
  "config": {},
1252
1253
  "time": {
1253
1254
  "hooks": {},
1254
- "register": 1
1255
+ "register": 0
1255
1256
  },
1256
1257
  "enableBy": "config"
1257
1258
  },
@@ -1268,7 +1269,7 @@
1268
1269
  0
1269
1270
  ]
1270
1271
  },
1271
- "register": 1
1272
+ "register": 2
1272
1273
  }
1273
1274
  },
1274
1275
  "./node_modules/@umijs/preset-umi/dist/features/polyfill/publicPathPolyfill": {
@@ -1306,7 +1307,7 @@
1306
1307
  "config": {},
1307
1308
  "time": {
1308
1309
  "hooks": {},
1309
- "register": 1
1310
+ "register": 0
1310
1311
  },
1311
1312
  "enableBy": "config"
1312
1313
  },
@@ -1319,7 +1320,7 @@
1319
1320
  "config": {},
1320
1321
  "time": {
1321
1322
  "hooks": {},
1322
- "register": 0
1323
+ "register": 1
1323
1324
  },
1324
1325
  "enableBy": "config"
1325
1326
  },
@@ -1332,7 +1333,7 @@
1332
1333
  "config": {},
1333
1334
  "time": {
1334
1335
  "hooks": {},
1335
- "register": 1
1336
+ "register": 0
1336
1337
  },
1337
1338
  "enableBy": "register"
1338
1339
  },
@@ -1371,7 +1372,7 @@
1371
1372
  "config": {},
1372
1373
  "time": {
1373
1374
  "hooks": {},
1374
- "register": 0
1375
+ "register": 1
1375
1376
  }
1376
1377
  },
1377
1378
  "./node_modules/@umijs/preset-umi/dist/features/ssr/ssr": {
@@ -1396,7 +1397,7 @@
1396
1397
  "config": {},
1397
1398
  "time": {
1398
1399
  "hooks": {},
1399
- "register": 2
1400
+ "register": 1
1400
1401
  },
1401
1402
  "enableBy": "register"
1402
1403
  },
@@ -1422,7 +1423,7 @@
1422
1423
  "config": {},
1423
1424
  "time": {
1424
1425
  "hooks": {},
1425
- "register": 2
1426
+ "register": 3
1426
1427
  },
1427
1428
  "enableBy": "config"
1428
1429
  },
@@ -1460,7 +1461,7 @@
1460
1461
  "config": {},
1461
1462
  "time": {
1462
1463
  "hooks": {},
1463
- "register": 21
1464
+ "register": 22
1464
1465
  },
1465
1466
  "enableBy": "config"
1466
1467
  },
@@ -1552,7 +1553,7 @@
1552
1553
  "config": {},
1553
1554
  "time": {
1554
1555
  "hooks": {},
1555
- "register": 3
1556
+ "register": 2
1556
1557
  }
1557
1558
  },
1558
1559
  "./node_modules/@umijs/preset-umi/dist/features/mako/mako": {
@@ -1589,7 +1590,7 @@
1589
1590
  "config": {},
1590
1591
  "time": {
1591
1592
  "hooks": {},
1592
- "register": 2
1593
+ "register": 3
1593
1594
  }
1594
1595
  },
1595
1596
  "./node_modules/@umijs/preset-umi/dist/features/routePreloadOnLoad/routePreloadOnLoad": {
@@ -1601,7 +1602,7 @@
1601
1602
  "config": {},
1602
1603
  "time": {
1603
1604
  "hooks": {},
1604
- "register": 28
1605
+ "register": 27
1605
1606
  }
1606
1607
  },
1607
1608
  "./node_modules/@umijs/preset-umi/dist/features/forget/forget": {
@@ -1652,7 +1653,7 @@
1652
1653
  "config": {},
1653
1654
  "time": {
1654
1655
  "hooks": {},
1655
- "register": 45
1656
+ "register": 41
1656
1657
  },
1657
1658
  "enableBy": "register"
1658
1659
  },
@@ -1833,7 +1834,7 @@
1833
1834
  "config": {},
1834
1835
  "time": {
1835
1836
  "hooks": {},
1836
- "register": 0
1837
+ "register": 1
1837
1838
  },
1838
1839
  "enableBy": "register"
1839
1840
  },
@@ -1846,7 +1847,7 @@
1846
1847
  "config": {},
1847
1848
  "time": {
1848
1849
  "hooks": {},
1849
- "register": 1
1850
+ "register": 0
1850
1851
  },
1851
1852
  "enableBy": "register"
1852
1853
  },
@@ -2038,35 +2039,35 @@
2038
2039
  "__isJSFile": true,
2039
2040
  "__absFile": "/home/runner/work/open-admin/open-admin/web/src/pages/404.jsx"
2040
2041
  },
2041
- "userCenter/ChangePassword": {
2042
- "path": "userCenter/ChangePassword",
2043
- "id": "userCenter/ChangePassword",
2042
+ "account/changePassword": {
2043
+ "path": "account/changePassword",
2044
+ "id": "account/changePassword",
2044
2045
  "parentId": "@@/global-layout",
2045
- "file": "userCenter/ChangePassword.jsx",
2046
- "absPath": "/userCenter/ChangePassword",
2047
- "__content": "import React from \"react\";\nimport {Button, Form, Input, Modal} from \"antd\";\nimport {history} from 'umi'\nimport {HttpUtils, SysUtils} from \"../../framework\";\n\nexport default class extends React.Component {\n\n\n onFinish = (values) => {\n HttpUtils.post('admin/userCenter/update-pwd', values).then(() => {\n Modal.success({\n title: '提示',\n content: '修改密码成功',\n onOk: () => {\n SysUtils.setToken(null)\n history.push('/login')\n }\n })\n })\n }\n\n validator = (rule, value) => {\n return new Promise((resolve, reject) => {\n HttpUtils.get(\"admin/sysUser/pwd-strength\", {password: value}).then(response => {\n const rs = response.data\n if (!rs.success) {\n reject(rs.message)\n }\n resolve()\n })\n })\n\n }\n\n render() {\n return <div>\n\n <Form onFinish={this.onFinish} style={{maxWidth: 400}}>\n\n <Form.Item name='newPassword'\n label='新密码'\n extra={'请输入字母、数字、特殊字符'}\n rules={[\n {required: true},\n {\n validator: this.validator\n }\n ]}\n >\n <Input.Password></Input.Password>\n </Form.Item>\n\n <Form.Item wrapperCol={{offset: 5}} style={{marginTop: 40}}>\n <Button type=\"primary\" htmlType=\"submit\">\n 确定\n </Button>\n </Form.Item>\n </Form>\n\n </div>\n }\n}\n",
2046
+ "file": "account/changePassword.jsx",
2047
+ "absPath": "/account/changePassword",
2048
+ "__content": "import React from \"react\";\nimport {Button, Form, Input, Modal} from \"antd\";\nimport {history} from 'umi'\nimport {HttpUtils} from \"../../framework\";\n\nexport default class extends React.Component {\n\n\n onFinish = (values) => {\n HttpUtils.post('admin/userCenter/update-pwd', values).then(() => {\n Modal.success({\n title: '提示',\n content: '修改密码成功',\n onOk: () => {\n history.push('/login')\n }\n })\n })\n }\n\n validator = (rule, value) => {\n return new Promise((resolve, reject) => {\n HttpUtils.get(\"admin/sysUser/pwd-strength\", {password: value}).then(response => {\n const rs = response.data\n if (!rs.success) {\n reject(rs.message)\n }\n resolve()\n })\n })\n\n }\n\n render() {\n return <div>\n\n <Form onFinish={this.onFinish} style={{maxWidth: 400}}>\n\n <Form.Item name='newPassword'\n label='新密码'\n extra={'请输入字母、数字、特殊字符'}\n rules={[\n {required: true},\n {\n validator: this.validator\n }\n ]}\n >\n <Input.Password></Input.Password>\n </Form.Item>\n\n <Form.Item wrapperCol={{offset: 5}} style={{marginTop: 40}}>\n <Button type=\"primary\" htmlType=\"submit\">\n 确定\n </Button>\n </Form.Item>\n </Form>\n\n </div>\n }\n}\n",
2048
2049
  "__isJSFile": true,
2049
- "__absFile": "/home/runner/work/open-admin/open-admin/web/src/pages/userCenter/ChangePassword.jsx"
2050
+ "__absFile": "/home/runner/work/open-admin/open-admin/web/src/pages/account/changePassword.jsx"
2050
2051
  },
2051
- "system/user/UserPerm": {
2052
- "path": "system/user/UserPerm",
2053
- "id": "system/user/UserPerm",
2052
+ "system/role/rolePerm": {
2053
+ "path": "system/role/rolePerm",
2054
+ "id": "system/role/rolePerm",
2054
2055
  "parentId": "@@/global-layout",
2055
- "file": "system/user/UserPerm.jsx",
2056
- "absPath": "/system/user/UserPerm",
2057
- "__content": "import {Form, Modal} from 'antd';\nimport React from 'react';\nimport {FieldDictSelect, FieldRemoteSelectMultiple, FieldSysOrgTree, HttpUtils} from \"../../../framework\";\n\n\nexport default class UserPerm extends React.Component {\n\n\n state = {\n visible: false,\n\n confirmLoading: false,\n\n formValues: {\n dataPermType: null\n },\n }\n formRef = React.createRef()\n\n show(item) {\n this.setState({visible: true})\n\n HttpUtils.get('admin/sysUser/get-perm-info', {id: item.id}).then(rs => {\n this.setState({formValues: rs})\n this.formRef.current.setFieldsValue(rs)\n })\n }\n\n handleSave = (values) => {\n values.grantOrgIdList = this.state.checked\n\n this.setState({\n confirmLoading: true\n })\n\n\n HttpUtils.post('admin/sysUser/grant-perm', values).then(rs => {\n this.setState({\n visible: false,\n confirmLoading: false\n })\n this.props.onOk()\n }).finally(() => {\n this.setState({\n confirmLoading: false\n })\n\n })\n }\n\n render() {\n const {visible, confirmLoading} = this.state\n\n return <Modal\n title=\"授权\"\n destroyOnHidden\n width={600}\n open={visible}\n confirmLoading={confirmLoading}\n onCancel={() => this.setState({visible: false})}\n onOk={() => this.formRef.current.submit()}\n >\n\n <Form ref={this.formRef}\n onFinish={this.handleSave}\n onValuesChange={(change, values) => {\n this.setState({formValues: values})\n }}\n labelCol={{flex: '100px'}}\n >\n <Form.Item name='id' noStyle></Form.Item>\n <Form.Item label='角色' name='roleIds' rules={[{required: true}]}>\n <FieldRemoteSelectMultiple url='admin/sysRole/options'/>\n </Form.Item>\n <Form.Item label='数据权限' name='dataPermType' rules={[{required: true}]}>\n <FieldDictSelect typeCode='dataPermType'/>\n </Form.Item>\n\n\n {this.state.formValues.dataPermType === 'CUSTOM' && <>\n <Form.Item label='组织机构' name='orgIds'>\n <FieldSysOrgTree/>\n </Form.Item>\n </>}\n\n\n </Form>\n\n\n </Modal>\n }\n\n\n}\n",
2056
+ "file": "system/role/rolePerm.jsx",
2057
+ "absPath": "/system/role/rolePerm",
2058
+ "__content": "import React from \"react\";\nimport {Button, Card, Checkbox, Table, Typography} from \"antd\";\nimport {SaveOutlined} from \"@ant-design/icons\";\nimport {HttpUtils, Page, PageUtils} from \"../../../framework\";\nexport default class extends React.Component {\n\n\n state = {\n loading: false,\n roleInfo: {},\n\n dataSource: [],\n rowSelectedKeys: {},\n }\n\n columns = [\n {\n title: '菜单',\n dataIndex: 'name',\n },\n\n {\n title: '权限',\n dataIndex: 'permCodes',\n render: (permCodes, record) => {\n if (permCodes == null) {\n return\n }\n const {permNames} = record\n const options = [];\n for (let i = 0; i < permCodes.length; i++) {\n const label = permNames[i];\n const value = permCodes[i];\n options.push({label, value});\n }\n\n\n const rowSelectedKey = this.state.rowSelectedKeys[record.id];\n return <Checkbox.Group options={options}\n value={rowSelectedKey}\n onChange={(ks) => {\n const rowSelectedKeys = this.state.rowSelectedKeys;\n rowSelectedKeys[record.id] = ks;\n this.setState({rowSelectedKeys})\n }}/>\n }\n }\n ]\n\n componentDidMount() {\n this.roleId = PageUtils.currentParams().id;\n this.loadData();\n }\n\n\n loadData() {\n this.setState({loading: true})\n Promise.all([\n HttpUtils.get('admin/sysRole/get', {id: this.roleId}).then(rs => {\n this.setState({roleInfo: rs})\n }),\n HttpUtils.get('admin/sysRole/perm-tree-table', {id: this.roleId}).then(rs => {\n this.setState({dataSource: rs})\n }),\n HttpUtils.get('admin/sysRole/own-perms', {id: this.roleId}).then(rs => {\n this.setState({rowSelectedKeys: rs})\n })\n ]).then(rs => {\n this.setState({loading: false})\n })\n }\n\n savePerms = () => {\n const {rowSelectedKeys} = this.state;\n const perms = [];\n const menus = []\n for (let menuId in rowSelectedKeys) {\n const ks = rowSelectedKeys[menuId];\n if (ks == null || ks.length === 0) {\n continue;\n }\n menus.push(menuId)\n perms.push(...ks)\n }\n HttpUtils.post('admin/sysRole/save-perms', {id: this.roleId, perms, menus}).then(rs => {\n // Page.open(PageUtils.currentPathname(), PageUtils.currentLabel())\n })\n };\n\n\n render() {\n return <Page title=\"角色权限设置\" description=\"设置角色的菜单和权限\">\n <Card title='角色权限设置' loading={this.state.loading}\n variant={\"borderless\"}\n extra={<Button type='primary' icon={<SaveOutlined/>} onClick={this.savePerms}>保存权限</Button>}>\n <Typography.Text>角色名称:{this.state.roleInfo.name}, 编码:{this.state.roleInfo.code} </Typography.Text>\n <Table dataSource={this.state.dataSource}\n columns={this.columns}\n size='small' bordered pagination={false} rowKey='id'\n expandable={{defaultExpandAllRows: true}}\n ></Table>\n </Card>\n </Page>\n }\n}\n",
2058
2059
  "__isJSFile": true,
2059
- "__absFile": "/home/runner/work/open-admin/open-admin/web/src/pages/system/user/UserPerm.jsx"
2060
+ "__absFile": "/home/runner/work/open-admin/open-admin/web/src/pages/system/role/rolePerm.jsx"
2060
2061
  },
2061
- "userCenter/message": {
2062
- "path": "userCenter/message",
2063
- "id": "userCenter/message",
2062
+ "system/user/userPerm": {
2063
+ "path": "system/user/userPerm",
2064
+ "id": "system/user/userPerm",
2064
2065
  "parentId": "@@/global-layout",
2065
- "file": "userCenter/message.jsx",
2066
- "absPath": "/userCenter/message",
2067
- "__content": "import React from \"react\";\nimport {Button, Card, Tabs, Tag} from \"antd\";\nimport {HttpUtils, Page, ProTable} from \"../../framework\";\n\nexport default class extends React.Component {\n\n tableRef = React.createRef()\n\n read = (record) => {\n HttpUtils.post(\"admin/user/msg/read\", record).then(rs => {\n this.tableRef.current.reload()\n })\n }\n\n columns = [\n {\n title: '时间',\n dataIndex: 'createTime',\n width: 150\n },\n {\n title: '标题',\n dataIndex: 'title',\n },\n {\n title: '内容',\n dataIndex: 'content',\n render(content, row) {\n return <div dangerouslySetInnerHTML={{__html: content}}/>\n }\n },\n {\n title: '已读时间',\n dataIndex: 'readTime',\n width: 150\n },\n {\n title: '状态',\n dataIndex: 'isRead',\n render: (v, row) => {\n return v == true ? <Tag color='green'>已读</Tag> : <Tag color='red'>未读</Tag>;\n },\n width: 80\n },\n\n {\n title: '-',\n dataIndex: 'option',\n render: (read, row) => {\n if (!row.isRead)\n return <Button size='small' onClick={() => this.read(row)}> 标记已读 </Button>\n }, width: 80\n }\n ]\n\n render() {\n return <Page title=\"消息中心\" description=\"查看系统消息通知\">\n <Card variant={\"borderless\"}>\n\n <Tabs defaultActiveKey=\"0\" destroyOnClose\n >\n <Tabs.TabPane key='0' tab='所有消息'>\n <ProTable\n actionRef={this.tableRef}\n request={(params) => HttpUtils.get(\"admin/user/msg/page\", params)}\n columns={this.columns}\n toolbarOptions={{showSearch: false}}\n size='small'\n />\n </Tabs.TabPane>\n <Tabs.TabPane key='1' tab='未读消息'>\n <ProTable\n actionRef={this.tableRef}\n request={(params) => HttpUtils.get(\"admin/user/msg/page?read=false\", params)}\n columns={this.columns}\n rowSelection={false}\n rowKey='id'\n toolbarOptions={{showSearch: false}}\n size='small'\n />\n\n\n </Tabs.TabPane>\n <Tabs.TabPane key='2' tab='已读消息'>\n\n <ProTable\n actionRef={this.tableRef}\n request={(params) => HttpUtils.get(\"admin/user/msg/page?read=true\", params)}\n rowSelection={false}\n rowKey='id'\n toolbarOptions={{showSearch: false}}\n options={false}\n size='small'\n columns={this.columns}\n />\n\n\n </Tabs.TabPane>\n\n </Tabs>\n\n </Card>\n </Page>\n }\n}\n",
2066
+ "file": "system/user/userPerm.jsx",
2067
+ "absPath": "/system/user/userPerm",
2068
+ "__content": "import {Form, Modal} from 'antd';\nimport React from 'react';\nimport {FieldDictSelect, FieldRemoteSelectMultiple, FieldSysOrgTree, HttpUtils} from \"../../../framework\";\n\n\nexport default class UserPerm extends React.Component {\n\n\n state = {\n visible: false,\n\n confirmLoading: false,\n\n formValues: {\n dataPermType: null\n },\n }\n formRef = React.createRef()\n\n show(item) {\n this.setState({visible: true})\n\n HttpUtils.get('admin/sysUser/get-perm-info', {id: item.id}).then(rs => {\n this.setState({formValues: rs})\n this.formRef.current.setFieldsValue(rs)\n })\n }\n\n handleSave = (values) => {\n values.grantOrgIdList = this.state.checked\n\n this.setState({\n confirmLoading: true\n })\n\n\n HttpUtils.post('admin/sysUser/grant-perm', values).then(rs => {\n this.setState({\n visible: false,\n confirmLoading: false\n })\n this.props.onOk()\n }).finally(() => {\n this.setState({\n confirmLoading: false\n })\n\n })\n }\n\n render() {\n const {visible, confirmLoading} = this.state\n\n return <Modal\n title=\"授权\"\n destroyOnHidden\n width={600}\n open={visible}\n confirmLoading={confirmLoading}\n onCancel={() => this.setState({visible: false})}\n onOk={() => this.formRef.current.submit()}\n >\n\n <Form ref={this.formRef}\n onFinish={this.handleSave}\n onValuesChange={(change, values) => {\n this.setState({formValues: values})\n }}\n labelCol={{flex: '100px'}}\n >\n <Form.Item name='id' noStyle></Form.Item>\n <Form.Item label='角色' name='roleIds' rules={[{required: true}]}>\n <FieldRemoteSelectMultiple url='admin/sysRole/options'/>\n </Form.Item>\n <Form.Item label='数据权限' name='dataPermType' rules={[{required: true}]}>\n <FieldDictSelect typeCode='dataPermType'/>\n </Form.Item>\n\n\n {this.state.formValues.dataPermType === 'CUSTOM' && <>\n <Form.Item label='组织机构' name='orgIds'>\n <FieldSysOrgTree/>\n </Form.Item>\n </>}\n\n\n </Form>\n\n\n </Modal>\n }\n\n\n}\n",
2068
2069
  "__isJSFile": true,
2069
- "__absFile": "/home/runner/work/open-admin/open-admin/web/src/pages/userCenter/message.jsx"
2070
+ "__absFile": "/home/runner/work/open-admin/open-admin/web/src/pages/system/user/userPerm.jsx"
2070
2071
  },
2071
2072
  "system/dict/index": {
2072
2073
  "path": "system/dict",
@@ -2074,7 +2075,7 @@
2074
2075
  "parentId": "@@/global-layout",
2075
2076
  "file": "system/dict/index.jsx",
2076
2077
  "absPath": "/system/dict",
2077
- "__content": "import {DeleteOutlined, EditOutlined, PlusOutlined} from '@ant-design/icons';\nimport {Button, Card, Descriptions, Empty, Form, Input, InputNumber, Modal, Popconfirm, Splitter, Tree, Tag, TreeSelect, Typography} from 'antd';\nimport React from 'react';\nimport {\n ButtonList,\n FieldBoolean,\n FieldDictSelect,\n HttpUtils,\n Page,\n ProTable,\n ViewBooleanEnableDisable,\n} from \"../../../framework\";\n\nexport default class extends React.Component {\n\n state = {\n typeTree: [],\n treeLoading: false,\n selectedType: null,\n selectedTypeCode: null,\n\n formOpen: false,\n formValues: {},\n typeFormOpen: false,\n typeFormValues: {},\n\n }\n\n formRef = React.createRef();\n typeFormRef = React.createRef();\n tableRef = React.createRef();\n\n componentDidMount() {\n this.loadTree()\n }\n\n loadTree = () => {\n this.setState({treeLoading: true})\n HttpUtils.get('admin/dict/type-tree').then(rs => {\n this.setState({typeTree: rs})\n }).finally(() => {\n this.setState({treeLoading: false})\n })\n }\n\n handleTypeAdd = () => {\n this.setState({\n typeFormOpen: true,\n typeFormValues: {pid: this.state.selectedType?.id, enabled: true},\n })\n }\n\n handleTypeEdit = () => {\n const {selectedType} = this.state\n if (!selectedType) return\n this.setState({typeFormOpen: true, typeFormValues: {...selectedType}})\n }\n\n handleTypeDelete = () => {\n const {selectedType} = this.state\n if (!selectedType) return\n HttpUtils.post('admin/dict/type-delete', {id: selectedType.id}).then(() => {\n this.setState({selectedType: null, selectedTypeCode: null})\n this.loadTree()\n })\n }\n\n handleTypeFormFinish = values => {\n const isNew = !values.id\n const url = isNew ? 'admin/dict/type-create' : 'admin/dict/type-update'\n HttpUtils.post(url, values).then(rs => {\n this.setState({typeFormOpen: false})\n this.loadTree()\n })\n }\n\n findNode = (nodes, key) => {\n for (const n of nodes) {\n if (n.id === key) return n\n if (n.children) {\n const found = this.findNode(n.children, key)\n if (found) return found\n }\n }\n return null\n }\n\n getParentLabel = () => {\n const {selectedType, typeTree} = this.state\n if (!selectedType?.pid) return null\n const parent = this.findNode(typeTree, selectedType.pid)\n return parent?.typeLabel || null\n }\n\n onTreeSelect = (selectedKeys) => {\n if (selectedKeys.length === 0) {\n this.setState({selectedType: null, selectedTypeCode: null})\n return\n }\n const node = this.findNode(this.state.typeTree, selectedKeys[0])\n this.setState({\n selectedType: node,\n selectedTypeCode: node?.typeCode || null\n },()=>this.tableRef.current.reload())\n }\n\n handleItemAdd = () => {\n this.setState({formOpen: true, formValues: {typeCode: this.state.selectedTypeCode}})\n }\n\n handleItemEdit = record => {\n this.setState({formOpen: true, formValues: record})\n }\n\n handleItemDelete = row => {\n HttpUtils.post('admin/dict/delete', row).then(rs => {\n this.tableRef.current.reload()\n })\n }\n\n onItemFormFinish = values => {\n const isNew = !values.id\n const url = isNew ? 'admin/dict/create' : 'admin/dict/update'\n HttpUtils.post(url, values).then(rs => {\n this.setState({formOpen: false})\n this.tableRef.current.reload()\n })\n }\n\n columns = [\n {title: '编码', dataIndex: 'code'},\n {title: '标签', dataIndex: 'label'},\n {\n title: '颜色', dataIndex: 'color',\n render(v) {\n if (v) return <Tag color={v.toLowerCase()}>{v}</Tag>\n }\n },\n {\n title: '启用', dataIndex: 'enabled',\n render(v) {\n return <ViewBooleanEnableDisable value={v}/>\n }\n },\n {title: '序号', dataIndex: 'seq'},\n {\n title: '操作', dataIndex: 'option',\n render: (_, record) => {\n return (\n <ButtonList>\n <Button size='small' perm='sys-dict:update'\n onClick={() => this.handleItemEdit(record)}>编辑</Button>\n <Popconfirm perm='sys-dict:delete' title='是否确定删除字典项'\n onConfirm={() => this.handleItemDelete(record)}>\n <Button size='small'>删除</Button>\n </Popconfirm>\n </ButtonList>\n );\n },\n },\n ]\n\n render() {\n const {selectedType, selectedTypeCode} = this.state\n const hasTypeSelected = selectedType != null && selectedTypeCode != null\n\n return <Page title=\"数据字典\" description=\"管理数据字典类型和字典项\"\n actions={<Button perm='sys-dict:create' icon={<PlusOutlined/>} onClick={this.handleTypeAdd}>新增类型</Button>}>\n <Splitter>\n <Splitter.Panel defaultSize={300} style={{paddingRight: 8}}>\n <Card loading={this.state.treeLoading}\n size='small'\n >\n <Typography.Text strong style={{marginBottom: 8, display: 'block'}}>字典类型</Typography.Text>\n <Tree\n treeData={this.state.typeTree}\n onSelect={this.onTreeSelect}\n fieldNames={{title: 'typeLabel', key: 'id'}}\n showLine\n defaultExpandAll\n blockNode\n />\n {this.state.typeTree.length === 0 && <Empty/>}\n </Card>\n </Splitter.Panel>\n\n <Splitter.Panel style={{paddingLeft: 8}}>\n <Card size='small' style={{marginBottom: 8}}>\n <div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8}}>\n <Typography.Text strong>类型信息</Typography.Text>\n {hasTypeSelected && (\n <span>\n <Button size='small' icon={<EditOutlined/>} perm='sys-dict:update'\n onClick={this.handleTypeEdit} style={{marginRight: 4}}>编辑</Button>\n <Popconfirm perm='sys-dict:delete' title='是否确定删除此类型及其所有子类型和字典项?'\n onConfirm={this.handleTypeDelete}>\n <Button size='small' icon={<DeleteOutlined/>}>删除</Button>\n </Popconfirm>\n </span>\n )}\n </div>\n {hasTypeSelected && <Descriptions size='small' column={3}>\n <Descriptions.Item label=\"名称\">{selectedType.typeLabel}</Descriptions.Item>\n <Descriptions.Item label=\"类型编码\">{selectedType.typeCode}</Descriptions.Item>\n <Descriptions.Item label=\"分类\">{this.getParentLabel() || '-'}</Descriptions.Item>\n <Descriptions.Item label=\"序号\">{selectedType.seq}</Descriptions.Item>\n <Descriptions.Item label=\"启用\"><ViewBooleanEnableDisable value={selectedType.enabled}/></Descriptions.Item>\n </Descriptions>}\n </Card>\n <Card size='small'>\n <div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8}}>\n <Typography.Text strong>字典项</Typography.Text>\n {hasTypeSelected && <Button type='primary' icon={<PlusOutlined/>} onClick={this.handleItemAdd}>新增字典项</Button>}\n </div>\n {hasTypeSelected && <ProTable\n rowKey='uid'\n actionRef={this.tableRef}\n request={(params) => {\n params.typeCode = selectedTypeCode\n return HttpUtils.get('admin/dict/page', params)\n }}\n columns={this.columns}\n />\n }\n </Card>\n </Splitter.Panel>\n </Splitter>\n\n <Modal title={this.state.typeFormValues?.id ? '编辑字典类型' : '新增字典类型'}\n open={this.state.typeFormOpen}\n onOk={() => this.typeFormRef.current.submit()}\n onCancel={() => this.setState({typeFormOpen: false})}\n destroyOnClose\n >\n <Form ref={this.typeFormRef} labelCol={{flex: '100px'}}\n initialValues={this.state.typeFormValues}\n onFinish={this.handleTypeFormFinish}\n >\n <Form.Item name='id' noStyle/>\n <Form.Item label='父类型' name='pid'>\n <TreeSelect treeData={this.state.typeTree}\n fieldNames={{label: 'typeLabel', value: 'id'}}\n allowClear\n />\n </Form.Item>\n <Form.Item label='类型编码' name='typeCode' rules={[{required: true}]}>\n <Input placeholder='举例: orderStatus'/>\n </Form.Item>\n <Form.Item label='类型名称' name='typeLabel' rules={[{required: true}]}>\n <Input placeholder='举例: 订单状态'/>\n </Form.Item>\n <Form.Item label='启用' name='enabled' rules={[{required: true}]}>\n <FieldBoolean/>\n </Form.Item>\n <Form.Item label='序号' name='seq'>\n <InputNumber/>\n </Form.Item>\n </Form>\n </Modal>\n\n <Modal title='编辑字典项'\n open={this.state.formOpen}\n onOk={() => this.formRef.current.submit()}\n onCancel={() => this.setState({formOpen: false})}\n destroyOnClose\n >\n <Form ref={this.formRef} labelCol={{flex: '100px'}}\n initialValues={this.state.formValues}\n onFinish={this.onItemFormFinish}\n >\n <Form.Item name='id' noStyle/>\n <Form.Item label='类型编码' name='typeCode'>\n <Input disabled/>\n </Form.Item>\n <Form.Item label='编码' name='code' rules={[{required: true}]}>\n <Input/>\n </Form.Item>\n <Form.Item label='标签' name='label' rules={[{required: true}]} help='显示文本'>\n <Input/>\n </Form.Item>\n <Form.Item label='颜色' name='color'>\n <FieldDictSelect typeCode='statusColor'/>\n </Form.Item>\n <Form.Item label='序号' name='seq'>\n <InputNumber/>\n </Form.Item>\n <Form.Item label='启用' name='enabled' rules={[{required: true}]}>\n <FieldBoolean/>\n </Form.Item>\n </Form>\n </Modal>\n </Page>\n }\n}\n",
2078
+ "__content": "import {DeleteOutlined, EditOutlined, PlusOutlined} from '@ant-design/icons';\nimport {Button, Card, Descriptions, Empty, Form, Input, InputNumber, Modal, Popconfirm, Splitter, Tree, Tag, TreeSelect, Typography} from 'antd';\nimport React from 'react';\nimport {\n PermActions,\n FieldBoolean,\n FieldDictSelect,\n HttpUtils,\n Page,\n ProTable,\n ViewSwitch,\n} from \"../../../framework\";\n\nexport default class extends React.Component {\n\n state = {\n typeTree: [],\n treeLoading: false,\n selectedType: null,\n selectedTypeCode: null,\n\n formOpen: false,\n formValues: {},\n typeFormOpen: false,\n typeFormValues: {},\n\n }\n\n formRef = React.createRef();\n typeFormRef = React.createRef();\n tableRef = React.createRef();\n\n componentDidMount() {\n this.loadTree()\n }\n\n loadTree = () => {\n this.setState({treeLoading: true})\n HttpUtils.get('admin/dict/type-tree').then(rs => {\n this.setState({typeTree: rs})\n }).finally(() => {\n this.setState({treeLoading: false})\n })\n }\n\n handleTypeAdd = () => {\n this.setState({\n typeFormOpen: true,\n typeFormValues: {pid: this.state.selectedType?.id, enabled: true},\n })\n }\n\n handleTypeEdit = () => {\n const {selectedType} = this.state\n if (!selectedType) return\n this.setState({typeFormOpen: true, typeFormValues: {...selectedType}})\n }\n\n handleTypeDelete = () => {\n const {selectedType} = this.state\n if (!selectedType) return\n HttpUtils.post('admin/dict/type-delete', {id: selectedType.id}).then(() => {\n this.setState({selectedType: null, selectedTypeCode: null})\n this.loadTree()\n })\n }\n\n handleTypeFormFinish = values => {\n const isNew = !values.id\n const url = isNew ? 'admin/dict/type-create' : 'admin/dict/type-update'\n HttpUtils.post(url, values).then(rs => {\n this.setState({typeFormOpen: false})\n this.loadTree()\n })\n }\n\n findNode = (nodes, key) => {\n for (const n of nodes) {\n if (n.id === key) return n\n if (n.children) {\n const found = this.findNode(n.children, key)\n if (found) return found\n }\n }\n return null\n }\n\n getParentLabel = () => {\n const {selectedType, typeTree} = this.state\n if (!selectedType?.pid) return null\n const parent = this.findNode(typeTree, selectedType.pid)\n return parent?.typeLabel || null\n }\n\n onTreeSelect = (selectedKeys) => {\n if (selectedKeys.length === 0) {\n this.setState({selectedType: null, selectedTypeCode: null})\n return\n }\n const node = this.findNode(this.state.typeTree, selectedKeys[0])\n this.setState({\n selectedType: node,\n selectedTypeCode: node?.typeCode || null\n },()=>this.tableRef.current.reload())\n }\n\n handleItemAdd = () => {\n this.setState({formOpen: true, formValues: {typeCode: this.state.selectedTypeCode}})\n }\n\n handleItemEdit = record => {\n this.setState({formOpen: true, formValues: record})\n }\n\n handleItemDelete = row => {\n HttpUtils.post('admin/dict/delete', row).then(rs => {\n this.tableRef.current.reload()\n })\n }\n\n onItemFormFinish = values => {\n const isNew = !values.id\n const url = isNew ? 'admin/dict/create' : 'admin/dict/update'\n HttpUtils.post(url, values).then(rs => {\n this.setState({formOpen: false})\n this.tableRef.current.reload()\n })\n }\n\n columns = [\n {title: '编码', dataIndex: 'code'},\n {title: '标签', dataIndex: 'label'},\n {\n title: '颜色', dataIndex: 'color',\n render(v) {\n if (v) return <Tag color={v.toLowerCase()}>{v}</Tag>\n }\n },\n {\n title: '启用', dataIndex: 'enabled',\n render(v) {\n return <ViewSwitch value={v}/>\n }\n },\n {title: '序号', dataIndex: 'seq'},\n {\n title: '操作', dataIndex: 'option',\n render: (_, record) => {\n return (\n <PermActions>\n <Button size='small' perm='sys-dict:update'\n onClick={() => this.handleItemEdit(record)}>编辑</Button>\n <Popconfirm perm='sys-dict:delete' title='是否确定删除字典项'\n onConfirm={() => this.handleItemDelete(record)}>\n <Button size='small'>删除</Button>\n </Popconfirm>\n </PermActions>\n );\n },\n },\n ]\n\n render() {\n const {selectedType, selectedTypeCode} = this.state\n const hasTypeSelected = selectedType != null && selectedTypeCode != null\n\n return <Page title=\"数据字典\" description=\"管理数据字典类型和字典项\"\n actions={<Button perm='sys-dict:create' icon={<PlusOutlined/>} onClick={this.handleTypeAdd}>新增类型</Button>}>\n <Splitter>\n <Splitter.Panel defaultSize={300} style={{paddingRight: 8}}>\n <Card loading={this.state.treeLoading}\n size='small'\n >\n <Typography.Text strong style={{marginBottom: 8, display: 'block'}}>字典类型</Typography.Text>\n <Tree\n treeData={this.state.typeTree}\n onSelect={this.onTreeSelect}\n fieldNames={{title: 'typeLabel', key: 'id'}}\n showLine\n defaultExpandAll\n blockNode\n />\n {this.state.typeTree.length === 0 && <Empty/>}\n </Card>\n </Splitter.Panel>\n\n <Splitter.Panel style={{paddingLeft: 8}}>\n <Card size='small' style={{marginBottom: 8}}>\n <div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8}}>\n <Typography.Text strong>类型信息</Typography.Text>\n {hasTypeSelected && (\n <span>\n <Button size='small' icon={<EditOutlined/>} perm='sys-dict:update'\n onClick={this.handleTypeEdit} style={{marginRight: 4}}>编辑</Button>\n <Popconfirm perm='sys-dict:delete' title='是否确定删除此类型及其所有子类型和字典项?'\n onConfirm={this.handleTypeDelete}>\n <Button size='small' icon={<DeleteOutlined/>}>删除</Button>\n </Popconfirm>\n </span>\n )}\n </div>\n {hasTypeSelected && <Descriptions size='small' column={3}>\n <Descriptions.Item label=\"名称\">{selectedType.typeLabel}</Descriptions.Item>\n <Descriptions.Item label=\"类型编码\">{selectedType.typeCode}</Descriptions.Item>\n <Descriptions.Item label=\"分类\">{this.getParentLabel() || '-'}</Descriptions.Item>\n <Descriptions.Item label=\"序号\">{selectedType.seq}</Descriptions.Item>\n <Descriptions.Item label=\"启用\"><ViewSwitch value={selectedType.enabled}/></Descriptions.Item>\n </Descriptions>}\n </Card>\n <Card size='small'>\n <div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8}}>\n <Typography.Text strong>字典项</Typography.Text>\n {hasTypeSelected && <Button type='primary' icon={<PlusOutlined/>} onClick={this.handleItemAdd}>新增字典项</Button>}\n </div>\n {hasTypeSelected && <ProTable\n rowKey='uid'\n actionRef={this.tableRef}\n request={(params) => {\n params.typeCode = selectedTypeCode\n return HttpUtils.get('admin/dict/page', params)\n }}\n columns={this.columns}\n />\n }\n </Card>\n </Splitter.Panel>\n </Splitter>\n\n <Modal title={this.state.typeFormValues?.id ? '编辑字典类型' : '新增字典类型'}\n open={this.state.typeFormOpen}\n onOk={() => this.typeFormRef.current.submit()}\n onCancel={() => this.setState({typeFormOpen: false})}\n destroyOnClose\n >\n <Form ref={this.typeFormRef} labelCol={{flex: '100px'}}\n initialValues={this.state.typeFormValues}\n onFinish={this.handleTypeFormFinish}\n >\n <Form.Item name='id' noStyle/>\n <Form.Item label='父类型' name='pid'>\n <TreeSelect treeData={this.state.typeTree}\n fieldNames={{label: 'typeLabel', value: 'id'}}\n allowClear\n />\n </Form.Item>\n <Form.Item label='类型编码' name='typeCode' rules={[{required: true}]}>\n <Input placeholder='举例: orderStatus'/>\n </Form.Item>\n <Form.Item label='类型名称' name='typeLabel' rules={[{required: true}]}>\n <Input placeholder='举例: 订单状态'/>\n </Form.Item>\n <Form.Item label='启用' name='enabled' rules={[{required: true}]}>\n <FieldBoolean/>\n </Form.Item>\n <Form.Item label='序号' name='seq'>\n <InputNumber/>\n </Form.Item>\n </Form>\n </Modal>\n\n <Modal title='编辑字典项'\n open={this.state.formOpen}\n onOk={() => this.formRef.current.submit()}\n onCancel={() => this.setState({formOpen: false})}\n destroyOnClose\n >\n <Form ref={this.formRef} labelCol={{flex: '100px'}}\n initialValues={this.state.formValues}\n onFinish={this.onItemFormFinish}\n >\n <Form.Item name='id' noStyle/>\n <Form.Item label='类型编码' name='typeCode'>\n <Input disabled/>\n </Form.Item>\n <Form.Item label='编码' name='code' rules={[{required: true}]}>\n <Input/>\n </Form.Item>\n <Form.Item label='标签' name='label' rules={[{required: true}]} help='显示文本'>\n <Input/>\n </Form.Item>\n <Form.Item label='颜色' name='color'>\n <FieldDictSelect typeCode='statusColor'/>\n </Form.Item>\n <Form.Item label='序号' name='seq'>\n <InputNumber/>\n </Form.Item>\n <Form.Item label='启用' name='enabled' rules={[{required: true}]}>\n <FieldBoolean/>\n </Form.Item>\n </Form>\n </Modal>\n </Page>\n }\n}\n",
2078
2079
  "__isJSFile": true,
2079
2080
  "__absFile": "/home/runner/work/open-admin/open-admin/web/src/pages/system/dict/index.jsx"
2080
2081
  },
@@ -2084,7 +2085,7 @@
2084
2085
  "parentId": "@@/global-layout",
2085
2086
  "file": "system/file/index.jsx",
2086
2087
  "absPath": "/system/file",
2087
- "__content": "import React from 'react'\nimport {Button, Form, Input, Modal, Popconfirm} from 'antd'\nimport {CloudUploadOutlined} from \"@ant-design/icons\";\n\nimport {ButtonList, FieldDateRange, FieldDictSelect, FieldUploadFile, HttpUtils, Page, ProTable, SysUtils} from \"../../../framework\";\n\nexport default class extends React.Component {\n\n state = {\n formOpen: false,\n formValues: {}\n }\n\n tableRef = React.createRef()\n formRef = React.createRef()\n\n handleDelete = row => {\n HttpUtils.post('admin/sysFile/delete', row).then(rs => {\n this.tableRef.current.reload()\n })\n }\n\n columns = [\n {\n title: '原始名称',\n tooltip: '上传时候的文件名',\n dataIndex: 'originName',\n width: 200,\n },\n {\n title: '存储名称',\n dataIndex: 'objectName',\n tooltip: '文件唯一标识id'\n },\n\n {\n title: '文件大小',\n dataIndex: 'sizeInfo',\n },\n\n {\n title: 'mime',\n dataIndex: 'mimeType',\n },\n {\n title: '扩展名',\n dataIndex: 'suffix',\n },\n\n {\n title: '上传时间',\n dataIndex: 'createTime',\n },\n {\n title: '上传者',\n dataIndex: 'createUserLabel',\n },\n {\n title: '预览',\n dataIndex: 'id',\n render(id, record) {\n const nodes = [<a href={SysUtils.contextPath('/admin/sysFile/preview/' + record.id)} target='_blank'>预览</a>]\n\n\n if (record.imageUrls?.length > 0) {\n for (let item of record.imageUrls) {\n const url = item.url;\n const label = item.label;\n nodes.push(<a href={(url)} target='_blank' style={{marginLeft: 8}}>{label}</a>)\n }\n }\n\n return nodes;\n\n }\n },\n {\n title: '操作',\n dataIndex: 'option',\n render: (_, record) => (\n <ButtonList>\n <Popconfirm perm='sys-file:delete' title='是否确定删除文件信息'\n onConfirm={() => this.handleDelete(record)}>\n <a>删除</a>\n </Popconfirm>\n </ButtonList>\n ),\n },\n ]\n\n render() {\n return <Page title=\"文件管理\" description=\"管理系统上传文件\" actions={<Button type='primary' icon={<CloudUploadOutlined/>} onClick={() => this.setState({formOpen: true})}>上传文件</Button>}>\n <ProTable\n actionRef={this.tableRef}\n request={(params) => {\n return HttpUtils.get('admin/sysFile/page', params);\n }}\n\n columns={this.columns}\n\n >\n <Form.Item label='文件名' name='originName'>\n <Input/>\n </Form.Item>\n <Form.Item label='对象名称' name='objectName'>\n <Input/>\n </Form.Item>\n <Form.Item label='类型' name='type'>\n <FieldDictSelect typeCode='materialType'/>\n </Form.Item>\n\n <Form.Item label='上传时间' name='dateRange'>\n <FieldDateRange/>\n </Form.Item>\n </ProTable>\n\n <Modal open={this.state.formOpen} title='上传文件'\n width={800}\n onCancel={() => {\n this.setState({formOpen: false})\n this.tableRef.current.reload()\n }}\n footer={null}\n destroyOnHidden\n >\n <Form ref={this.formRef}\n initialValues={this.state.formValues}\n >\n <Form.Item name='文件'>\n <FieldUploadFile accept=\"*/*\"/>\n </Form.Item>\n\n\n </Form>\n </Modal>\n\n </Page>\n }\n}\n\n\n\n",
2088
+ "__content": "import React from 'react'\nimport {Button, Form, Input, Modal, Popconfirm} from 'antd'\nimport {CloudUploadOutlined} from \"@ant-design/icons\";\n\nimport {PermActions, FieldDateRange, FieldDictSelect, FieldUploadFile, HttpUtils, Page, ProTable, UrlUtils} from \"../../../framework\";\n\nexport default class extends React.Component {\n\n state = {\n formOpen: false,\n formValues: {}\n }\n\n tableRef = React.createRef()\n formRef = React.createRef()\n\n handleDelete = row => {\n HttpUtils.post('admin/sysFile/delete', row).then(rs => {\n this.tableRef.current.reload()\n })\n }\n\n columns = [\n {\n title: '原始名称',\n tooltip: '上传时候的文件名',\n dataIndex: 'originName',\n width: 200,\n },\n {\n title: '存储名称',\n dataIndex: 'objectName',\n tooltip: '文件唯一标识id'\n },\n\n {\n title: '文件大小',\n dataIndex: 'sizeInfo',\n },\n\n {\n title: 'mime',\n dataIndex: 'mimeType',\n },\n {\n title: '扩展名',\n dataIndex: 'suffix',\n },\n\n {\n title: '上传时间',\n dataIndex: 'createTime',\n },\n {\n title: '上传者',\n dataIndex: 'createUserLabel',\n },\n {\n title: '预览',\n dataIndex: 'id',\n render(id, record) {\n const nodes = [<a href={UrlUtils.contextPath('/admin/sysFile/preview/' + record.id)} target='_blank'>预览</a>]\n\n\n if (record.imageUrls?.length > 0) {\n for (let item of record.imageUrls) {\n const url = item.url;\n const label = item.label;\n nodes.push(<a href={(url)} target='_blank' style={{marginLeft: 8}}>{label}</a>)\n }\n }\n\n return nodes;\n\n }\n },\n {\n title: '操作',\n dataIndex: 'option',\n render: (_, record) => (\n <PermActions>\n <Popconfirm perm='sys-file:delete' title='是否确定删除文件信息'\n onConfirm={() => this.handleDelete(record)}>\n <a>删除</a>\n </Popconfirm>\n </PermActions>\n ),\n },\n ]\n\n render() {\n return <Page title=\"文件管理\" description=\"管理系统上传文件\" actions={<Button type='primary' icon={<CloudUploadOutlined/>} onClick={() => this.setState({formOpen: true})}>上传文件</Button>}>\n <ProTable\n actionRef={this.tableRef}\n request={(params) => {\n return HttpUtils.get('admin/sysFile/page', params);\n }}\n\n columns={this.columns}\n\n >\n <Form.Item label='文件名' name='originName'>\n <Input/>\n </Form.Item>\n <Form.Item label='对象名称' name='objectName'>\n <Input/>\n </Form.Item>\n <Form.Item label='类型' name='type'>\n <FieldDictSelect typeCode='materialType'/>\n </Form.Item>\n\n <Form.Item label='上传时间' name='dateRange'>\n <FieldDateRange/>\n </Form.Item>\n </ProTable>\n\n <Modal open={this.state.formOpen} title='上传文件'\n width={800}\n onCancel={() => {\n this.setState({formOpen: false})\n this.tableRef.current.reload()\n }}\n footer={null}\n destroyOnHidden\n >\n <Form ref={this.formRef}\n initialValues={this.state.formValues}\n >\n <Form.Item name='文件'>\n <FieldUploadFile accept=\"*/*\"/>\n </Form.Item>\n\n\n </Form>\n </Modal>\n\n </Page>\n }\n}\n\n\n\n",
2088
2089
  "__isJSFile": true,
2089
2090
  "__absFile": "/home/runner/work/open-admin/open-admin/web/src/pages/system/file/index.jsx"
2090
2091
  },
@@ -2094,7 +2095,7 @@
2094
2095
  "parentId": "@@/global-layout",
2095
2096
  "file": "system/role/index.jsx",
2096
2097
  "absPath": "/system/role",
2097
- "__content": "import {PlusOutlined} from '@ant-design/icons'\nimport {Button, Form, Input, InputNumber, Modal, Popconfirm, Select, Transfer, Tree} from 'antd'\nimport React from 'react'\nimport {ButtonList, FieldBoolean, HttpUtils, Page, PageUtils, ProTable, ViewText} from \"../../../framework\";\n\n\nexport default class extends React.Component {\n\n state = {\n formValues: {},\n formOpen: false,\n\n usersModalOpen: false,\n\n\n userList: [],\n targetKeys: [],\n selectedKeys: [],\n\n menuOpen: false,\n menuTree: [],\n menuTreeLoading: false,\n menuChecked: [],\n menuHalfChecked: []\n }\n\n formRef = React.createRef()\n tableRef = React.createRef()\n\n handleAdd = () => {\n this.setState({formOpen: true, formValues: {}})\n }\n\n handleEdit = record => {\n this.setState({formOpen: true, formValues: record}, () => {\n\n })\n }\n\n handleEditUser = record => {\n this.setState({usersModalOpen: true, formValues: record})\n HttpUtils.get('admin/sysRole/user-list', {id: record.id}).then(rs => {\n this.setState({userList: rs.list, targetKeys: rs.selectedKeys})\n })\n }\n\n onFinish = values => {\n const isNew = !values.id;\n const url = isNew ? 'admin/sysRole/create' : 'admin/sysRole/update';\n HttpUtils.post(url, values).then(rs => {\n this.setState({formOpen: false})\n this.tableRef.current.reload()\n })\n }\n\n handleDelete = record => {\n HttpUtils.post('admin/sysRole/delete', {id: record.id}).then(rs => {\n this.tableRef.current.reload()\n })\n }\n\n columns = [\n\n {\n title: '名称',\n dataIndex: 'name',\n\n\n },\n\n {\n title: '编号',\n dataIndex: 'code',\n\n\n },\n\n {\n title: '排序',\n dataIndex: 'seq',\n\n\n },\n\n {\n title: '备注',\n dataIndex: 'remark',\n\n\n },\n\n {\n title: '启用',\n dataIndex: 'enabled',\n\n\n render(v) {\n return v == null ? null : (v ? '是' : '否')\n },\n\n\n },\n {\n title: '是否内置',\n dataIndex: 'builtin',\n\n\n render(v) {\n return v == null ? null : (v ? '是' : '否')\n },\n\n\n },\n\n {\n title: '权限码',\n dataIndex: 'perms',\n width: 300,\n render(v) {\n if (v) {\n return <ViewText value={v.join(',')} ellipsis={true} />\n }\n }\n\n },\n\n\n {\n title: '操作',\n dataIndex: 'option',\n render: (_, record) => {\n\n return (\n <ButtonList>\n <Button size='small' perm='sys-role:grant-permission'\n onClick={() => this.handleEditUser(record)}>用户设置</Button>\n\n <Button size='small' perm='sys-role:grant-permission' disabled={record.builtin}\n onClick={() => PageUtils.open('/system/role/perm?id=' + record.id, '角色权限设置')}>权限设置</Button>\n\n <Button size='small' perm='sys-role:update' disabled={record.builtin}\n onClick={() => this.handleEdit(record)}>编辑</Button>\n <Popconfirm perm='sys-role:delete' disabled={record.builtin} title='是否确定删除系统角色'\n onConfirm={() => this.handleDelete(record)}>\n <Button size='small' disabled={record.builtin}>删除</Button>\n </Popconfirm>\n </ButtonList>\n );\n },\n },\n ]\n\n handleSaveUsers = () => {\n const params = {\n id: this.state.formValues.id,\n userIdList: this.state.targetKeys\n }\n HttpUtils.post('admin/sysRole/grant-users', params).then(rs => {\n this.setState({usersModalOpen: false})\n })\n }\n\n handleEditMenu = (record) => {\n this.setState({menuOpen: true, formValues: record, menuTreeLoading: true})\n HttpUtils.get('admin/sysRole/ownMenu', {id: record.id}).then(rs => {\n this.setState({menuChecked: rs.checked, menuHalfChecked: rs.halfChecked})\n })\n HttpUtils.get('admin/sysRole/menuTree').then(rs => {\n this.setState({menuTree: rs, menuTreeLoading: false})\n })\n }\n handleGrantMenu = () => {\n const params = {\n id: this.state.formValues.id,\n menuIds: [...this.state.menuChecked, ...this.state.menuHalfChecked]\n }\n HttpUtils.post('admin/sysRole/grantMenu', params).then(rs => {\n this.setState({menuOpen: false})\n })\n }\n\n render() {\n return <Page\n title=\"角色管理\"\n description=\"管理系统角色,包括角色权限分配、用户设置等\"\n actions={<Button perm='sys-role:create' type='primary' icon={<PlusOutlined/>} onClick={this.handleAdd}>新增</Button>}\n >\n <ProTable\n actionRef={this.tableRef}\n request={(params) => HttpUtils.get('admin/sysRole/page', params)}\n columns={this.columns}\n >\n <Form.Item label='角色名称' name='name'>\n <Input/>\n </Form.Item>\n <Form.Item label='角色编码' name='code'>\n <Input/>\n </Form.Item>\n <Form.Item label='是否内置' name='builtin'>\n <Select allowClear placeholder='全部'>\n <Select.Option value={true}>是</Select.Option>\n <Select.Option value={false}>否</Select.Option>\n </Select>\n </Form.Item>\n </ProTable>\n\n <Modal title='系统角色'\n open={this.state.formOpen}\n onOk={() => this.formRef.current.submit()}\n onCancel={() => this.setState({formOpen: false})}\n destroyOnHidden\n mask={{ closable: false }}\n width={600}\n >\n\n <Form ref={this.formRef} labelCol={{flex: '100px'}}\n initialValues={this.state.formValues}\n onFinish={this.onFinish}\n >\n <Form.Item name='id' noStyle></Form.Item>\n\n\n <Form.Item label='名称' name='name' rules={[{required: true}]}>\n <Input/>\n </Form.Item>\n\n <Form.Item label='编码' name='code' rules={[{required: true}]}>\n <Input/>\n </Form.Item>\n\n\n <Form.Item label='排序' name='seq'>\n <InputNumber/>\n </Form.Item>\n\n\n <Form.Item label='备注' name='remark'>\n <Input/>\n </Form.Item>\n\n <Form.Item label='启用' name='enabled' rules={[{required: true}]}>\n <FieldBoolean/>\n </Form.Item>\n\n\n </Form>\n </Modal>\n\n\n <Modal title={'角色用户' + \"【\" + this.state.formValues?.name + '】'}\n open={this.state.usersModalOpen}\n destroyOnHidden\n mask={{ closable: false }}\n width={800}\n onCancel={() => this.setState({usersModalOpen: false})}\n onOk={this.handleSaveUsers}\n >\n\n\n <Transfer\n styles={{ section: { height: '60vh', width: 300 } }}\n\n dataSource={this.state.userList} titles={[\"未选择\", \"已选择\"]}\n targetKeys={this.state.targetKeys}\n selectedKeys={this.state.selectedKeys}\n render={item => item.title}\n onChange={(nextTargetKeys, direction, moveKeys) => {\n this.setState({\n targetKeys: nextTargetKeys\n })\n }}\n onSelectChange={(sourceSelectedKeys, targetSelectedKeys) => {\n this.setState({\n selectedKeys: [...sourceSelectedKeys, ...targetSelectedKeys]\n })\n }}\n showSearch\n />\n\n\n </Modal>\n\n <Modal title={'角色授权菜单权限' + \"【\" + this.state.formValues?.name + '】'}\n open={this.state.menuOpen}\n destroyOnHidden\n mask={{ closable: false }}\n width={800}\n onCancel={() => this.setState({menuOpen: false})}\n onOk={this.handleGrantMenu}\n loading={this.state.menuTreeLoading}\n >\n <Tree\n height={600}\n treeData={this.state.menuTree}\n multiple\n checkable\n checkedKeys={{checked: this.state.menuChecked}}\n onCheck={(keys, e) => {\n this.setState({menuChecked: keys, menuHalfChecked: e.halfCheckedKeys})\n }}\n defaultExpandAll\n titleRender={node => {\n return <span title={node.perm}>{node.title}</span>\n }}\n />\n </Modal>\n </Page>\n\n\n }\n}\n\n",
2098
+ "__content": "import {PlusOutlined} from '@ant-design/icons'\nimport {Button, Form, Input, InputNumber, Modal, Popconfirm, Select, Transfer, Tree} from 'antd'\nimport React from 'react'\nimport {PermActions, FieldBoolean, HttpUtils, Page, PageUtils, ProTable, ViewText} from \"../../../framework\";\n\n\nexport default class extends React.Component {\n\n state = {\n formValues: {},\n formOpen: false,\n\n usersModalOpen: false,\n\n\n userList: [],\n targetKeys: [],\n selectedKeys: [],\n\n menuOpen: false,\n menuTree: [],\n menuTreeLoading: false,\n menuChecked: [],\n menuHalfChecked: []\n }\n\n formRef = React.createRef()\n tableRef = React.createRef()\n\n handleAdd = () => {\n this.setState({formOpen: true, formValues: {}})\n }\n\n handleEdit = record => {\n this.setState({formOpen: true, formValues: record}, () => {\n\n })\n }\n\n handleEditUser = record => {\n this.setState({usersModalOpen: true, formValues: record})\n HttpUtils.get('admin/sysRole/user-list', {id: record.id}).then(rs => {\n this.setState({userList: rs.list, targetKeys: rs.selectedKeys})\n })\n }\n\n onFinish = values => {\n const isNew = !values.id;\n const url = isNew ? 'admin/sysRole/create' : 'admin/sysRole/update';\n HttpUtils.post(url, values).then(rs => {\n this.setState({formOpen: false})\n this.tableRef.current.reload()\n })\n }\n\n handleDelete = record => {\n HttpUtils.post('admin/sysRole/delete', {id: record.id}).then(rs => {\n this.tableRef.current.reload()\n })\n }\n\n columns = [\n\n {\n title: '名称',\n dataIndex: 'name',\n\n\n },\n\n {\n title: '编号',\n dataIndex: 'code',\n\n\n },\n\n {\n title: '排序',\n dataIndex: 'seq',\n\n\n },\n\n {\n title: '备注',\n dataIndex: 'remark',\n\n\n },\n\n {\n title: '启用',\n dataIndex: 'enabled',\n\n\n render(v) {\n return v == null ? null : (v ? '是' : '否')\n },\n\n\n },\n {\n title: '是否内置',\n dataIndex: 'builtin',\n\n\n render(v) {\n return v == null ? null : (v ? '是' : '否')\n },\n\n\n },\n\n {\n title: '权限码',\n dataIndex: 'perms',\n width: 300,\n render(v) {\n if (v) {\n return <ViewText value={v.join(',')} ellipsis={true} />\n }\n }\n\n },\n\n\n {\n title: '操作',\n dataIndex: 'option',\n render: (_, record) => {\n\n return (\n <PermActions>\n <Button size='small' perm='sys-role:grant-permission'\n onClick={() => this.handleEditUser(record)}>用户设置</Button>\n\n <Button size='small' perm='sys-role:grant-permission' disabled={record.builtin}\n onClick={() => PageUtils.open('/system/role/rolePerm?id=' + record.id, '角色权限设置')}>权限设置</Button>\n\n <Button size='small' perm='sys-role:update' disabled={record.builtin}\n onClick={() => this.handleEdit(record)}>编辑</Button>\n <Popconfirm perm='sys-role:delete' disabled={record.builtin} title='是否确定删除系统角色'\n onConfirm={() => this.handleDelete(record)}>\n <Button size='small' disabled={record.builtin}>删除</Button>\n </Popconfirm>\n </PermActions>\n );\n },\n },\n ]\n\n handleSaveUsers = () => {\n const params = {\n id: this.state.formValues.id,\n userIdList: this.state.targetKeys\n }\n HttpUtils.post('admin/sysRole/grant-users', params).then(rs => {\n this.setState({usersModalOpen: false})\n })\n }\n\n handleEditMenu = (record) => {\n this.setState({menuOpen: true, formValues: record, menuTreeLoading: true})\n HttpUtils.get('admin/sysRole/ownMenu', {id: record.id}).then(rs => {\n this.setState({menuChecked: rs.checked, menuHalfChecked: rs.halfChecked})\n })\n HttpUtils.get('admin/sysRole/menuTree').then(rs => {\n this.setState({menuTree: rs, menuTreeLoading: false})\n })\n }\n handleGrantMenu = () => {\n const params = {\n id: this.state.formValues.id,\n menuIds: [...this.state.menuChecked, ...this.state.menuHalfChecked]\n }\n HttpUtils.post('admin/sysRole/grantMenu', params).then(rs => {\n this.setState({menuOpen: false})\n })\n }\n\n render() {\n return <Page\n title=\"角色管理\"\n description=\"管理系统角色,包括角色权限分配、用户设置等\"\n actions={<Button perm='sys-role:create' type='primary' icon={<PlusOutlined/>} onClick={this.handleAdd}>新增</Button>}\n >\n <ProTable\n actionRef={this.tableRef}\n request={(params) => HttpUtils.get('admin/sysRole/page', params)}\n columns={this.columns}\n >\n <Form.Item label='角色名称' name='name'>\n <Input/>\n </Form.Item>\n <Form.Item label='角色编码' name='code'>\n <Input/>\n </Form.Item>\n <Form.Item label='是否内置' name='builtin'>\n <Select allowClear placeholder='全部'>\n <Select.Option value={true}>是</Select.Option>\n <Select.Option value={false}>否</Select.Option>\n </Select>\n </Form.Item>\n </ProTable>\n\n <Modal title='系统角色'\n open={this.state.formOpen}\n onOk={() => this.formRef.current.submit()}\n onCancel={() => this.setState({formOpen: false})}\n destroyOnHidden\n mask={{ closable: false }}\n width={600}\n >\n\n <Form ref={this.formRef} labelCol={{flex: '100px'}}\n initialValues={this.state.formValues}\n onFinish={this.onFinish}\n >\n <Form.Item name='id' noStyle></Form.Item>\n\n\n <Form.Item label='名称' name='name' rules={[{required: true}]}>\n <Input/>\n </Form.Item>\n\n <Form.Item label='编码' name='code' rules={[{required: true}]}>\n <Input/>\n </Form.Item>\n\n\n <Form.Item label='排序' name='seq'>\n <InputNumber/>\n </Form.Item>\n\n\n <Form.Item label='备注' name='remark'>\n <Input/>\n </Form.Item>\n\n <Form.Item label='启用' name='enabled' rules={[{required: true}]}>\n <FieldBoolean/>\n </Form.Item>\n\n\n </Form>\n </Modal>\n\n\n <Modal title={'角色用户' + \"【\" + this.state.formValues?.name + '】'}\n open={this.state.usersModalOpen}\n destroyOnHidden\n mask={{ closable: false }}\n width={800}\n onCancel={() => this.setState({usersModalOpen: false})}\n onOk={this.handleSaveUsers}\n >\n\n\n <Transfer\n styles={{ section: { height: '60vh', width: 300 } }}\n\n dataSource={this.state.userList} titles={[\"未选择\", \"已选择\"]}\n targetKeys={this.state.targetKeys}\n selectedKeys={this.state.selectedKeys}\n render={item => item.title}\n onChange={(nextTargetKeys, direction, moveKeys) => {\n this.setState({\n targetKeys: nextTargetKeys\n })\n }}\n onSelectChange={(sourceSelectedKeys, targetSelectedKeys) => {\n this.setState({\n selectedKeys: [...sourceSelectedKeys, ...targetSelectedKeys]\n })\n }}\n showSearch\n />\n\n\n </Modal>\n\n <Modal title={'角色授权菜单权限' + \"【\" + this.state.formValues?.name + '】'}\n open={this.state.menuOpen}\n destroyOnHidden\n mask={{ closable: false }}\n width={800}\n onCancel={() => this.setState({menuOpen: false})}\n onOk={this.handleGrantMenu}\n loading={this.state.menuTreeLoading}\n >\n <Tree\n height={600}\n treeData={this.state.menuTree}\n multiple\n checkable\n checkedKeys={{checked: this.state.menuChecked}}\n onCheck={(keys, e) => {\n this.setState({menuChecked: keys, menuHalfChecked: e.halfCheckedKeys})\n }}\n defaultExpandAll\n titleRender={node => {\n return <span title={node.perm}>{node.title}</span>\n }}\n />\n </Modal>\n </Page>\n\n\n }\n}\n\n",
2098
2099
  "__isJSFile": true,
2099
2100
  "__absFile": "/home/runner/work/open-admin/open-admin/web/src/pages/system/role/index.jsx"
2100
2101
  },
@@ -2104,7 +2105,7 @@
2104
2105
  "parentId": "@@/global-layout",
2105
2106
  "file": "system/user/index.jsx",
2106
2107
  "absPath": "/system/user",
2107
- "__content": "import {PlusOutlined} from '@ant-design/icons';\nimport {Button, Card, Form, Input, Modal, Popconfirm, Select, Splitter} from 'antd';\nimport React from 'react';\nimport {\n ButtonList,\n DictUtils,\n FieldBoolean,\n FieldRemoteSelect,\n FieldSysOrgTreeSelect,\n HttpUtils,\n OrgTree,\n Page,\n ProTable,\n} from \"../../../framework\";\nimport UserPerm from \"./UserPerm\";\n\nexport default class extends React.Component {\n\n state = {\n showAddForm: false,\n showEditForm: false,\n formValues: {},\n\n currentOrgId: null,\n }\n permRef = React.createRef();\n\n formRef = React.createRef()\n tableRef = React.createRef()\n\n resetPwd(row) {\n HttpUtils.post('admin/sysUser/reset-pwd', {id: row.id}).then(rs => {\n Modal.success({\n title: '重置密码成功',\n content: rs\n })\n })\n }\n\n handleDelete = r => {\n HttpUtils.post('admin/sysUser/delete', {id: r.id}).then(rs => {\n this.tableRef.current.reload();\n })\n }\n\n onSelectOrg = (key) => {\n this.setState({currentOrgId: key}, () => this.tableRef.current.reload())\n }\n\n handleAdd = () => {\n this.setState({formOpen: true, formValues: {}})\n }\n\n handleEdit = record => {\n record.deptId = record.deptId || record.unitId\n this.setState({formOpen: true, formValues: record})\n }\n\n columns = [\n {\n title: '单位',\n dataIndex: 'unitLabel',\n },\n {\n title: '部门',\n dataIndex: 'deptLabel',\n },\n {\n title: '姓名',\n dataIndex: 'name',\n sorter: true\n },\n {\n title: '登录账号',\n dataIndex: 'account',\n sorter: true\n },\n\n\n {\n title: '手机',\n dataIndex: 'phone'\n },\n {\n title: '邮箱',\n dataIndex: 'email'\n },\n\n {\n title: '角色',\n dataIndex: 'roleIds',\n render: (_, row) => {\n if (row.roleNames) {\n return row.roleNames.join(',')\n }\n },\n },\n {\n title: '状态',\n dataIndex: 'enabled',\n render(v) {\n return v == null ? null : (v ? '是' : '否')\n },\n },\n {\n title: '数据权限',\n dataIndex: 'dataPermType',\n render(v) {\n return DictUtils.dictTag('dataPermType', v)\n }\n },\n\n {\n title: '创建时间',\n dataIndex: 'createTime',\n },\n {\n title: '操作',\n dataIndex: 'option',\n fixed: 'right',\n render: (_, record) => {\n return <ButtonList>\n <Button size='small' perm='sys-user:update' onClick={() => this.handleEdit(record)}> 编辑 </Button>\n\n <Button size='small' perm='sys-user:grant-permission'\n onClick={() => this.permRef.current.show(record)}> 授权 </Button>\n\n <Popconfirm perm='sys-user:reset-password' title='确认重置密码?' onConfirm={() => this.resetPwd(record)}>\n <Button size='small'>重置密码</Button>\n </Popconfirm>\n\n <Popconfirm perm='sys-user:delete' title={'是否确定删除用户'}\n onConfirm={() => this.handleDelete(record)}>\n <Button size='small'>删除</Button>\n </Popconfirm>\n </ButtonList>;\n },\n },\n ];\n\n onFinish = values => {\n const isNew = !values.id;\n const url = isNew ? 'admin/sysUser/create' : 'admin/sysUser/update';\n HttpUtils.post(url, values).then(rs => {\n this.setState({formOpen: false})\n this.tableRef.current.reload()\n })\n }\n\n render() {\n\n return <Page title=\"用户管理\" description=\"管理系统用户\" actions={<Button perm='sys-user:create' type=\"primary\" icon={<PlusOutlined/>} onClick={this.handleAdd}>新增</Button>}>\n <Splitter>\n <Splitter.Panel defaultSize={350} style={{paddingRight: 8}}>\n <Card size='small'>\n <OrgTree onChange={this.onSelectOrg}/>\n </Card>\n </Splitter.Panel>\n <Splitter.Panel style={{paddingLeft: 8}}>\n <ProTable\n searchColumns={3}\n actionRef={this.tableRef}\n request={(params) => {\n params.orgId = this.state.currentOrgId\n return HttpUtils.get('admin/sysUser/page', params)\n }}\n columns={this.columns}\n >\n <Form.Item label='姓名' name='name'>\n <Input/>\n </Form.Item>\n <Form.Item label='登录账号' name='account'>\n <Input/>\n </Form.Item>\n <Form.Item label='手机号' name='phone'>\n <Input/>\n </Form.Item>\n <Form.Item label='角色' name='roleId'>\n <FieldRemoteSelect url='admin/sysRole/options' placeholder='请选择角色'/>\n </Form.Item>\n <Form.Item label='状态' name='enabled'>\n <Select allowClear placeholder='全部'>\n <Select.Option value={true}>启用</Select.Option>\n <Select.Option value={false}>停用</Select.Option>\n </Select>\n </Form.Item>\n </ProTable>\n </Splitter.Panel>\n </Splitter>\n\n\n <Modal title='系统用户'\n open={this.state.formOpen}\n onOk={() => this.formRef.current.submit()}\n onCancel={() => this.setState({formOpen: false})}\n destroyOnHidden\n >\n\n <Form ref={this.formRef} labelCol={{flex: '100px'}}\n initialValues={this.state.formValues}\n onFinish={this.onFinish}>\n <Form.Item name='id' noStyle></Form.Item>\n\n <Form.Item label='所属机构' name='deptId' rules={[{required: true}]}>\n <FieldSysOrgTreeSelect/>\n </Form.Item>\n\n <Form.Item label='姓名' name='name' rules={[{required: true}]}>\n <Input/>\n </Form.Item>\n <Form.Item label='账号' name='account' rules={[{required: true}]}>\n <Input/>\n </Form.Item>\n\n\n <Form.Item label='电话' name='phone'>\n <Input/>\n </Form.Item>\n <Form.Item label='邮箱' name='email'>\n <Input/>\n </Form.Item>\n\n <Form.Item label='启用状态' name='enabled' rules={[{required: true}]}>\n <FieldBoolean/>\n </Form.Item>\n\n </Form>\n </Modal>\n\n\n <UserPerm ref={this.permRef} onOk={() => this.tableRef.current.reload()}/>\n\n </Page>\n }\n\n\n}\n\n\n\n",
2108
+ "__content": "import {PlusOutlined} from '@ant-design/icons';\nimport {Button, Card, Form, Input, Modal, Popconfirm, Select, Splitter} from 'antd';\nimport React from 'react';\nimport {\n PermActions,\n DictUtils,\n FieldBoolean,\n FieldRemoteSelect,\n FieldSysOrgTreeSelect,\n HttpUtils,\n OrgTree,\n Page,\n ProTable,\n} from \"../../../framework\";\nimport UserPerm from \"./userPerm\";\n\nexport default class extends React.Component {\n\n state = {\n showAddForm: false,\n showEditForm: false,\n formValues: {},\n\n currentOrgId: null,\n }\n permRef = React.createRef();\n\n formRef = React.createRef()\n tableRef = React.createRef()\n\n resetPwd(row) {\n HttpUtils.post('admin/sysUser/reset-pwd', {id: row.id}).then(rs => {\n Modal.success({\n title: '重置密码成功',\n content: rs\n })\n })\n }\n\n handleDelete = r => {\n HttpUtils.post('admin/sysUser/delete', {id: r.id}).then(rs => {\n this.tableRef.current.reload();\n })\n }\n\n onSelectOrg = (key) => {\n this.setState({currentOrgId: key}, () => this.tableRef.current.reload())\n }\n\n handleAdd = () => {\n this.setState({formOpen: true, formValues: {}})\n }\n\n handleEdit = record => {\n record.deptId = record.deptId || record.unitId\n this.setState({formOpen: true, formValues: record})\n }\n\n columns = [\n {\n title: '单位',\n dataIndex: 'unitLabel',\n },\n {\n title: '部门',\n dataIndex: 'deptLabel',\n },\n {\n title: '姓名',\n dataIndex: 'name',\n sorter: true\n },\n {\n title: '登录账号',\n dataIndex: 'account',\n sorter: true\n },\n\n\n {\n title: '手机',\n dataIndex: 'phone'\n },\n {\n title: '邮箱',\n dataIndex: 'email'\n },\n\n {\n title: '角色',\n dataIndex: 'roleIds',\n render: (_, row) => {\n if (row.roleNames) {\n return row.roleNames.join(',')\n }\n },\n },\n {\n title: '状态',\n dataIndex: 'enabled',\n render(v) {\n return v == null ? null : (v ? '是' : '否')\n },\n },\n {\n title: '数据权限',\n dataIndex: 'dataPermType',\n render(v) {\n return DictUtils.dictTag('dataPermType', v)\n }\n },\n\n {\n title: '创建时间',\n dataIndex: 'createTime',\n },\n {\n title: '操作',\n dataIndex: 'option',\n fixed: 'right',\n render: (_, record) => {\n return <PermActions>\n <Button size='small' perm='sys-user:update' onClick={() => this.handleEdit(record)}> 编辑 </Button>\n\n <Button size='small' perm='sys-user:grant-permission'\n onClick={() => this.permRef.current.show(record)}> 授权 </Button>\n\n <Popconfirm perm='sys-user:reset-password' title='确认重置密码?' onConfirm={() => this.resetPwd(record)}>\n <Button size='small'>重置密码</Button>\n </Popconfirm>\n\n <Popconfirm perm='sys-user:delete' title={'是否确定删除用户'}\n onConfirm={() => this.handleDelete(record)}>\n <Button size='small'>删除</Button>\n </Popconfirm>\n </PermActions>;\n },\n },\n ];\n\n onFinish = values => {\n const isNew = !values.id;\n const url = isNew ? 'admin/sysUser/create' : 'admin/sysUser/update';\n HttpUtils.post(url, values).then(rs => {\n this.setState({formOpen: false})\n this.tableRef.current.reload()\n })\n }\n\n render() {\n\n return <Page title=\"用户管理\" description=\"管理系统用户\" actions={<Button perm='sys-user:create' type=\"primary\" icon={<PlusOutlined/>} onClick={this.handleAdd}>新增</Button>}>\n <Splitter>\n <Splitter.Panel defaultSize={350} style={{paddingRight: 8}}>\n <Card size='small'>\n <OrgTree onChange={this.onSelectOrg}/>\n </Card>\n </Splitter.Panel>\n <Splitter.Panel style={{paddingLeft: 8}}>\n <ProTable\n searchColumns={3}\n actionRef={this.tableRef}\n request={(params) => {\n params.orgId = this.state.currentOrgId\n return HttpUtils.get('admin/sysUser/page', params)\n }}\n columns={this.columns}\n >\n <Form.Item label='姓名' name='name'>\n <Input/>\n </Form.Item>\n <Form.Item label='登录账号' name='account'>\n <Input/>\n </Form.Item>\n <Form.Item label='手机号' name='phone'>\n <Input/>\n </Form.Item>\n <Form.Item label='角色' name='roleId'>\n <FieldRemoteSelect url='admin/sysRole/options' placeholder='请选择角色'/>\n </Form.Item>\n <Form.Item label='状态' name='enabled'>\n <Select allowClear placeholder='全部'>\n <Select.Option value={true}>启用</Select.Option>\n <Select.Option value={false}>停用</Select.Option>\n </Select>\n </Form.Item>\n </ProTable>\n </Splitter.Panel>\n </Splitter>\n\n\n <Modal title='系统用户'\n open={this.state.formOpen}\n onOk={() => this.formRef.current.submit()}\n onCancel={() => this.setState({formOpen: false})}\n destroyOnHidden\n >\n\n <Form ref={this.formRef} labelCol={{flex: '100px'}}\n initialValues={this.state.formValues}\n onFinish={this.onFinish}>\n <Form.Item name='id' noStyle></Form.Item>\n\n <Form.Item label='所属机构' name='deptId' rules={[{required: true}]}>\n <FieldSysOrgTreeSelect/>\n </Form.Item>\n\n <Form.Item label='姓名' name='name' rules={[{required: true}]}>\n <Input/>\n </Form.Item>\n <Form.Item label='账号' name='account' rules={[{required: true}]}>\n <Input/>\n </Form.Item>\n\n\n <Form.Item label='电话' name='phone'>\n <Input/>\n </Form.Item>\n <Form.Item label='邮箱' name='email'>\n <Input/>\n </Form.Item>\n\n <Form.Item label='启用状态' name='enabled' rules={[{required: true}]}>\n <FieldBoolean/>\n </Form.Item>\n\n </Form>\n </Modal>\n\n\n <UserPerm ref={this.permRef} onOk={() => this.tableRef.current.reload()}/>\n\n </Page>\n }\n\n\n}\n\n\n\n",
2108
2109
  "__isJSFile": true,
2109
2110
  "__absFile": "/home/runner/work/open-admin/open-admin/web/src/pages/system/user/index.jsx"
2110
2111
  },
@@ -2114,7 +2115,7 @@
2114
2115
  "parentId": "@@/global-layout",
2115
2116
  "file": "system/job/index.jsx",
2116
2117
  "absPath": "/system/job",
2117
- "__content": "import React from 'react'\nimport {\n Alert,\n AutoComplete,\n Button,\n Divider,\n Form,\n Input,\n message,\n Modal,\n Popconfirm,\n Select,\n Space,\n Switch,\n Tag\n} from 'antd'\nimport {PlusOutlined} from \"@ant-design/icons\";\nimport {ButtonList, HttpUtils, Page, ProTable, StringUtils, SysUtils, ValueType} from \"../../../framework\";\n\n\nconst cronOptions = [\n {\n label: '*/5 * * * * ? 每隔5秒',\n value: '*/5 * * * * ?'\n },\n {\n label: '0 */5 * * * ? 每隔5分钟',\n value: '0 */5 * * * ?'\n },\n {\n label: '0 0 22 * * ? 每天22点',\n value: '0 0 22 * * ?'\n },\n {\n label: '0 0 1 * * ? 每天1点',\n value: '0 0 1 * * ?'\n },\n {\n label: '0 0 1 1 * ? 每月1号凌晨1点',\n value: '0 0 1 1 * ?'\n }\n]\n\n\nexport default class extends React.Component {\n\n state = {\n formValues: {},\n formOpen: false,\n\n selectedRowKeys: [],\n\n jobClassOptions: [],\n\n paramList: [],\n\n statusOpen: false,\n status: null,\n\n executeRecordOpen: false,\n }\n tableRef = React.createRef()\n formRef = React.createRef()\n\n componentDidMount() {\n HttpUtils.get('admin/job/job-class-options').then(rs => {\n this.setState({jobClassOptions: rs})\n }).catch(e => {\n console.error('[Job] 加载作业类选项失败:', e);\n })\n }\n\n handleAdd = () => {\n this.setState({formOpen: true, formValues: {}, paramList: []})\n }\n\n handleEdit = (record) => {\n this.loadJobParamFields(record.jobClass, record.jobData)\n this.setState({formOpen: true, formValues: record,})\n }\n\n loadJobParamFields(className, jobData) {\n HttpUtils.post(\"admin/job/get-job-param-fields\", jobData || {}, {className}).then(rs => {\n this.setState({paramList: rs})\n }).catch(e => {\n console.error('[Job] 加载作业参数字段失败:', e);\n })\n }\n\n onFinish = (values) => {\n const isNew = !values.id;\n const url = isNew ? 'admin/job/create' : 'admin/job/update';\n HttpUtils.post(url, values).then(rs => {\n this.setState({formOpen: false})\n this.tableRef.current.reload();\n }).catch(e => {\n console.error('[Job] 保存作业失败:', e);\n })\n }\n\n handleDelete = row => {\n const hide = message.loading(\"删除作业中...\")\n HttpUtils.post('admin/job/delete', {id: row.id}).then(rs => {\n this.tableRef.current.reload();\n }).catch(e => {\n console.error('[Job] 删除作业失败:', e);\n hide();\n })\n }\n\n handleTriggerJob = row => {\n HttpUtils.get('admin/job/trigger-job', {id: row.id}).then(rs => {\n this.tableRef.current.reload();\n }).catch(e => {\n console.error('[Job] 触发作业失败:', e);\n })\n }\n\n columns = [\n {\n title: '名称',\n dataIndex: 'name',\n\n },\n {\n title: '执行类',\n dataIndex: 'jobClass',\n\n },\n\n {\n title: 'cron',\n dataIndex: 'cron',\n },\n\n\n {\n title: '参数',\n dataIndex: 'jobData',\n render(list) {\n if (list)\n return JSON.stringify(list)\n }\n },\n\n\n {\n title: '启用状态',\n dataIndex: 'enabled',\n render: (v, record) => {\n return record.enabled ? <Tag color='green'>启用</Tag> : <Tag color='red'>停用</Tag>\n },\n },\n\n {\n title: '操作',\n dataIndex: 'option',\n fixed: 'right',\n render: (_, record) => {\n\n return (\n <Space>\n <Button size='small' onClick={() => this.showExecuteRecord(record)}>执行记录</Button>\n <Button size='small' perm='job:trigger' onClick={() => this.handleTriggerJob(record)}>执行一次</Button>\n <Button size='small' perm='job:update' onClick={() => this.handleEdit(record)}> 编辑 </Button>\n <Popconfirm perm='job:delete' title='是否确定删除?' onConfirm={() => this.handleDelete(record)}>\n <Button size='small'>删除</Button>\n </Popconfirm>\n </Space>\n );\n },\n },\n\n ]\n\n showStatus = () => {\n this.setState({statusOpen: true})\n HttpUtils.get('admin/job/status').then(rs => {\n this.setState({status: rs})\n }).catch(e => {\n console.error('[Job] 加载状态失败:', e);\n })\n };\n\n showExecuteRecord(record) {\n this.setState({executeRecordOpen: true, formValues: record})\n }\n\n\n render() {\n return <Page title=\"作业调度\" description=\"管理定时作业任务\" actions={<><Button type='primary' icon={<PlusOutlined/>} onClick={() => this.handleAdd()}>新增</Button><Button onClick={this.showStatus}>查看状态</Button></>}>\n <ProTable\n actionRef={this.tableRef}\n request={(params) => HttpUtils.get('admin/job/page', params)}\n columns={this.columns}\n >\n <Form.Item label='名称' name='name'>\n <Input/>\n </Form.Item>\n <Form.Item label='执行类' name='jobClass'>\n <Input/>\n </Form.Item>\n </ProTable>\n\n\n <Modal title='作业调度'\n open={this.state.formOpen}\n destroyOnHidden\n width={800}\n onOk={() => this.formRef.current.submit()}\n onCancel={() => this.setState({formOpen: false})}\n >\n\n <Form ref={this.formRef} labelCol={{flex: '100px'}}\n initialValues={this.state.formValues}\n onValuesChange={this.onValuesChange}\n onFinish={this.onFinish}>\n <Form.Item name='id' noStyle>\n </Form.Item>\n <Form.Item label='执行类' name='jobClass' rules={[{required: true}]}\n tooltip='org.quartz.Job接口,参考io.tmgg.job.builtin.DemoJob'>\n <Select options={this.state.jobClassOptions}/>\n </Form.Item>\n <Form.Item label='名称' name='name' rules={[{required: true}]}>\n <Input/>\n </Form.Item>\n\n <Form.Item label='cron表达式' name='cron' help='格式:秒分时日月周,留空表示手动执行'\n rules={[{required: true}]}>\n <AutoComplete placeholder='如 0 */5 * * * ?' options={cronOptions}/>\n </Form.Item>\n\n <Form.Item label='启用' name='enabled' valuePropName='checked' rules={[{required: true}]}>\n <Switch/>\n </Form.Item>\n\n\n {this.state.paramList?.map(p => (\n <div key={p.name}>\n <Divider>作业参数</Divider>\n <Form.Item label={p.label}\n name={['jobData', p.name]}\n key={p.name}\n rules={[{required: p.required}]}>\n {ValueType.renderField(p.componentType, p.componentProps)}\n </Form.Item>\n </div>\n ))}\n </Form>\n </Modal>\n\n <Modal title='作业调度状态'\n open={this.state.statusOpen}\n onCancel={() => this.setState({statusOpen: false})}\n footer={null}\n width={1024}\n >\n <Alert title={<pre>{this.state.status}</pre>}></Alert>\n\n </Modal>\n\n <Modal title='作业调度记录'\n open={this.state.executeRecordOpen}\n onCancel={() => this.setState({executeRecordOpen: false})}\n footer={null}\n width={1024}\n destroyOnHidden\n >\n <ProTable columns={[{\n title: '开始时间',\n dataIndex: 'beginTime',\n },\n {\n title: '结束时间',\n dataIndex: 'endTime',\n },\n {\n title: '耗时',\n dataIndex: 'jobRunTimeLabel',\n\n },\n {\n title: '是否成功',\n dataIndex: 'success',\n width: 200,\n render: v => {\n if (v != null) {\n return v ? '成功' : '异常'\n }\n }\n },\n {\n title: '返回结果',\n dataIndex: 'result',\n width: 300\n },\n\n {\n title: '操作',\n dataIndex: 'option',\n render: (_, record) => {\n const url = SysUtils.contextPath('/admin/sys/log/' + record.id);\n return <a href={url} target='_blank'>日志</a>;\n },\n }\n ]} request={params => {\n params.jobId = this.state.formValues.id\n return HttpUtils.get('admin/job/execute-record', params);\n }}></ProTable>\n\n </Modal>\n </Page>\n }\n\n onValuesChange = (changed, values) => {\n if (changed.jobClass) {\n this.loadJobParamFields(values.jobClass)\n const option = this.state.jobClassOptions.find(o => o.value === changed.jobClass)\n if (option) {\n const {label} = option;\n if (StringUtils.contains(label, \" \")) { // 取中文名部门设置为name\n this.formRef.current.setFieldValue(\"name\", label.split(\" \")[1])\n }\n }\n }\n\n if (changed.jobData) {\n this.loadJobParamFields(values.jobClass, values.jobData)\n }\n\n };\n}\n\n\n\n",
2118
+ "__content": "import React from 'react'\nimport {\n Alert,\n AutoComplete,\n Button,\n Divider,\n Form,\n Input,\n message,\n Modal,\n Popconfirm,\n Select,\n Space,\n Switch,\n Tag\n} from 'antd'\nimport {PlusOutlined} from \"@ant-design/icons\";\nimport {HttpUtils, Page, ProTable, StringUtils, UrlUtils, ValueType} from \"../../../framework\";\n\n\nconst cronOptions = [\n {\n label: '*/5 * * * * ? 每隔5秒',\n value: '*/5 * * * * ?'\n },\n {\n label: '0 */5 * * * ? 每隔5分钟',\n value: '0 */5 * * * ?'\n },\n {\n label: '0 0 22 * * ? 每天22点',\n value: '0 0 22 * * ?'\n },\n {\n label: '0 0 1 * * ? 每天1点',\n value: '0 0 1 * * ?'\n },\n {\n label: '0 0 1 1 * ? 每月1号凌晨1点',\n value: '0 0 1 1 * ?'\n }\n]\n\n\nexport default class extends React.Component {\n\n state = {\n formValues: {},\n formOpen: false,\n\n selectedRowKeys: [],\n\n jobClassOptions: [],\n\n paramList: [],\n\n statusOpen: false,\n status: null,\n\n executeRecordOpen: false,\n }\n tableRef = React.createRef()\n formRef = React.createRef()\n\n componentDidMount() {\n HttpUtils.get('admin/job/job-class-options').then(rs => {\n this.setState({jobClassOptions: rs})\n }).catch(e => {\n console.error('[Job] 加载作业类选项失败:', e);\n })\n }\n\n handleAdd = () => {\n this.setState({formOpen: true, formValues: {}, paramList: []})\n }\n\n handleEdit = (record) => {\n this.loadJobParamFields(record.jobClass, record.jobData)\n this.setState({formOpen: true, formValues: record,})\n }\n\n loadJobParamFields(className, jobData) {\n HttpUtils.post(\"admin/job/get-job-param-fields\", jobData || {}, {className}).then(rs => {\n this.setState({paramList: rs})\n }).catch(e => {\n console.error('[Job] 加载作业参数字段失败:', e);\n })\n }\n\n onFinish = (values) => {\n const isNew = !values.id;\n const url = isNew ? 'admin/job/create' : 'admin/job/update';\n HttpUtils.post(url, values).then(rs => {\n this.setState({formOpen: false})\n this.tableRef.current.reload();\n }).catch(e => {\n console.error('[Job] 保存作业失败:', e);\n })\n }\n\n handleDelete = row => {\n const hide = message.loading(\"删除作业中...\")\n HttpUtils.post('admin/job/delete', {id: row.id}).then(rs => {\n this.tableRef.current.reload();\n }).catch(e => {\n console.error('[Job] 删除作业失败:', e);\n hide();\n })\n }\n\n handleTriggerJob = row => {\n HttpUtils.get('admin/job/trigger-job', {id: row.id}).then(rs => {\n this.tableRef.current.reload();\n }).catch(e => {\n console.error('[Job] 触发作业失败:', e);\n })\n }\n\n columns = [\n {\n title: '名称',\n dataIndex: 'name',\n\n },\n {\n title: '执行类',\n dataIndex: 'jobClass',\n\n },\n\n {\n title: 'cron',\n dataIndex: 'cron',\n },\n\n\n {\n title: '参数',\n dataIndex: 'jobData',\n render(list) {\n if (list)\n return JSON.stringify(list)\n }\n },\n\n\n {\n title: '启用状态',\n dataIndex: 'enabled',\n render: (v, record) => {\n return record.enabled ? <Tag color='green'>启用</Tag> : <Tag color='red'>停用</Tag>\n },\n },\n\n {\n title: '操作',\n dataIndex: 'option',\n fixed: 'right',\n render: (_, record) => {\n\n return (\n <Space>\n <Button size='small' onClick={() => this.showExecuteRecord(record)}>执行记录</Button>\n <Button size='small' perm='job:trigger' onClick={() => this.handleTriggerJob(record)}>执行一次</Button>\n <Button size='small' perm='job:update' onClick={() => this.handleEdit(record)}> 编辑 </Button>\n <Popconfirm perm='job:delete' title='是否确定删除?' onConfirm={() => this.handleDelete(record)}>\n <Button size='small'>删除</Button>\n </Popconfirm>\n </Space>\n );\n },\n },\n\n ]\n\n showStatus = () => {\n this.setState({statusOpen: true})\n HttpUtils.get('admin/job/status').then(rs => {\n this.setState({status: rs})\n }).catch(e => {\n console.error('[Job] 加载状态失败:', e);\n })\n };\n\n showExecuteRecord(record) {\n this.setState({executeRecordOpen: true, formValues: record})\n }\n\n\n render() {\n return <Page title=\"作业调度\" description=\"管理定时作业任务\" actions={<><Button type='primary' icon={<PlusOutlined/>} onClick={() => this.handleAdd()}>新增</Button><Button onClick={this.showStatus}>查看状态</Button></>}>\n <ProTable\n actionRef={this.tableRef}\n request={(params) => HttpUtils.get('admin/job/page', params)}\n columns={this.columns}\n >\n <Form.Item label='名称' name='name'>\n <Input/>\n </Form.Item>\n <Form.Item label='执行类' name='jobClass'>\n <Input/>\n </Form.Item>\n </ProTable>\n\n\n <Modal title='作业调度'\n open={this.state.formOpen}\n destroyOnHidden\n width={800}\n onOk={() => this.formRef.current.submit()}\n onCancel={() => this.setState({formOpen: false})}\n >\n\n <Form ref={this.formRef} labelCol={{flex: '100px'}}\n initialValues={this.state.formValues}\n onValuesChange={this.onValuesChange}\n onFinish={this.onFinish}>\n <Form.Item name='id' noStyle>\n </Form.Item>\n <Form.Item label='执行类' name='jobClass' rules={[{required: true}]}\n tooltip='org.quartz.Job接口,参考io.tmgg.job.builtin.DemoJob'>\n <Select options={this.state.jobClassOptions}/>\n </Form.Item>\n <Form.Item label='名称' name='name' rules={[{required: true}]}>\n <Input/>\n </Form.Item>\n\n <Form.Item label='cron表达式' name='cron' help='格式:秒分时日月周,留空表示手动执行'\n rules={[{required: true}]}>\n <AutoComplete placeholder='如 0 */5 * * * ?' options={cronOptions}/>\n </Form.Item>\n\n <Form.Item label='启用' name='enabled' valuePropName='checked' rules={[{required: true}]}>\n <Switch/>\n </Form.Item>\n\n\n {this.state.paramList?.map(p => (\n <div key={p.name}>\n <Divider>作业参数</Divider>\n <Form.Item label={p.label}\n name={['jobData', p.name]}\n key={p.name}\n initialValue={p.defaultValue}\n rules={[{required: p.required}]}>\n {ValueType.renderField(p.componentType, {\n ...p.componentProps,\n placeholder: p.placeholder || '请输入'\n })}\n </Form.Item>\n </div>\n ))}\n </Form>\n </Modal>\n\n <Modal title='作业调度状态'\n open={this.state.statusOpen}\n onCancel={() => this.setState({statusOpen: false})}\n footer={null}\n width={1024}\n >\n <Alert title={<pre>{this.state.status}</pre>}></Alert>\n\n </Modal>\n\n <Modal title='作业调度记录'\n open={this.state.executeRecordOpen}\n onCancel={() => this.setState({executeRecordOpen: false})}\n footer={null}\n width={1024}\n destroyOnHidden\n >\n <ProTable columns={[{\n title: '开始时间',\n dataIndex: 'beginTime',\n },\n {\n title: '结束时间',\n dataIndex: 'endTime',\n },\n {\n title: '耗时',\n dataIndex: 'jobRunTimeLabel',\n\n },\n {\n title: '是否成功',\n dataIndex: 'success',\n width: 200,\n render: v => {\n if (v != null) {\n return v ? '成功' : '异常'\n }\n }\n },\n {\n title: '返回结果',\n dataIndex: 'result',\n width: 300\n },\n\n {\n title: '操作',\n dataIndex: 'option',\n render: (_, record) => {\n const url = UrlUtils.contextPath('/admin/sys/log/' + record.id);\n return <a href={url} target='_blank'>日志</a>;\n },\n }\n ]} request={params => {\n params.jobId = this.state.formValues.id\n return HttpUtils.get('admin/job/execute-record', params);\n }}></ProTable>\n\n </Modal>\n </Page>\n }\n\n onValuesChange = (changed, values) => {\n if (changed.jobClass) {\n this.loadJobParamFields(values.jobClass)\n const option = this.state.jobClassOptions.find(o => o.value === changed.jobClass)\n if (option) {\n const {label} = option;\n if (StringUtils.contains(label, \" \")) { // 取中文名部门设置为name\n this.formRef.current.setFieldValue(\"name\", label.split(\" \")[1])\n }\n }\n }\n\n if (changed.jobData) {\n this.loadJobParamFields(values.jobClass, values.jobData)\n }\n\n };\n}\n\n\n\n",
2118
2119
  "__isJSFile": true,
2119
2120
  "__absFile": "/home/runner/work/open-admin/open-admin/web/src/pages/system/job/index.jsx"
2120
2121
  },
@@ -2134,29 +2135,29 @@
2134
2135
  "parentId": "@@/global-layout",
2135
2136
  "file": "system/org/index.jsx",
2136
2137
  "absPath": "/system/org",
2137
- "__content": "import {DeleteOutlined, EditOutlined, PlusOutlined, SettingOutlined, SyncOutlined} from '@ant-design/icons';\nimport {Button, Card, Checkbox, Descriptions, Form, Input, InputNumber, Modal, Popconfirm, Popover, Space, Splitter, Switch, Tree, Typography} from 'antd';\nimport React from 'react';\nimport {\n FieldBoolean,\n FieldDictSelect,\n FieldRemoteTreeSelect,\n FieldUserSelect,\n HttpUtils,\n NamedIcon,\n Page,\n ViewBooleanEnableDisable,\n} from \"../../../framework\";\n\nexport default class extends React.Component {\n\n state = {\n selectedOrg: null,\n params: {\n onlyShowEnabled: true,\n onlyShowUnit: false,\n searchText: null,\n },\n treeData: [],\n treeLoading: false,\n draggable: false,\n\n modalOpen: false,\n modalValues: {},\n modalLoading: false,\n }\n formRef = React.createRef();\n treeRef = React.createRef();\n\n componentDidMount() {\n this.loadTree()\n }\n\n loadTree = () => {\n this.setState({treeLoading: true})\n HttpUtils.get('admin/sysOrg/tree', this.state.params).then(rs => {\n this.setState({treeData: rs})\n }).finally(() => {\n this.setState({treeLoading: false});\n })\n }\n\n handleDelete = () => {\n const {selectedOrg} = this.state\n if (!selectedOrg) return\n HttpUtils.post('admin/sysOrg/delete', {id: selectedOrg.id}).then(() => {\n this.setState({selectedOrg: null})\n this.loadTree()\n })\n }\n\n onSelect = (selectedKeys) => {\n if (selectedKeys.length === 0) {\n this.setState({selectedOrg: null})\n return\n }\n HttpUtils.get(\"admin/sysOrg/detail\", {id: selectedKeys[0]}).then(rs => {\n this.setState({selectedOrg: rs})\n })\n }\n\n handleAdd = () => {\n const {selectedOrg} = this.state\n this.setState({\n modalOpen: true,\n modalValues: {pid: selectedOrg?.id, enabled: true},\n })\n }\n\n handleEdit = () => {\n this.setState({\n modalOpen: true,\n modalValues: {...this.state.selectedOrg},\n })\n }\n\n handleModalFinish = (values) => {\n this.setState({modalLoading: true})\n const isNew = !values.id\n const url = isNew ? 'admin/sysOrg/create' : 'admin/sysOrg/update'\n HttpUtils.post(url, values).then(() => {\n this.setState({modalOpen: false})\n this.loadTree()\n }).finally(() => {\n this.setState({modalLoading: false})\n })\n }\n\n onDraggableChange = e => {\n this.setState({draggable: e})\n };\n\n render() {\n const {selectedOrg} = this.state\n const params = this.state.params\n\n return <Page title=\"组织机构\" description=\"管理组织机构树\"\n actions={<Button type='primary' perm='sys-org:create' icon={<PlusOutlined/>} onClick={this.handleAdd}>新增</Button>}>\n <Splitter>\n <Splitter.Panel defaultSize={400} style={{paddingRight: 8}}>\n <Card loading={this.state.treeLoading}\n size='small'\n >\n <div style={{display: 'flex', alignItems: 'center', gap: 4, marginBottom: 8}}>\n <Input.Search placeholder='搜索' value={params.searchText} onChange={e => {\n params.searchText = e.target.value\n this.setState({params}, this.loadTree)\n }} style={{flex: 1}}/>\n <Popover\n trigger='click'\n placement='bottomRight'\n title='设置'\n content={<Space direction='vertical'>\n <Checkbox checked={params.onlyShowEnabled}\n onChange={e => {\n params.onlyShowEnabled = e.target.checked;\n this.setState({params}, this.loadTree);\n }}>仅显示启用</Checkbox>\n <Checkbox checked={params.onlyShowUnit}\n onChange={e => {\n params.onlyShowUnit = e.target.checked;\n this.setState({params}, this.loadTree);\n }}>仅显示单位</Checkbox>\n <div>\n 拖拽排序&nbsp;<Switch\n value={this.state.draggable}\n onChange={this.onDraggableChange}/>\n </div>\n <Button size='small' shape='round' icon={<SyncOutlined/>} onClick={this.loadTree}>刷新</Button>\n </Space>}\n >\n <Button type='text' size='small' icon={<SettingOutlined/>}/>\n </Popover>\n </div>\n <Tree ref={this.treeRef}\n treeData={this.state.treeData}\n onSelect={this.onSelect}\n showIcon\n blockNode\n icon={item => <NamedIcon name={item.data.iconName}/>}\n draggable={this.state.draggable}\n onDrop={this.onDrop}\n showLine\n defaultExpandAll\n />\n </Card>\n </Splitter.Panel>\n\n <Splitter.Panel style={{paddingLeft: 8}}>\n <Card size='small' style={{marginBottom: 8}}>\n <div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8}}>\n <Typography.Text strong>机构信息</Typography.Text>\n {selectedOrg && (\n <span>\n <Button size='small' icon={<EditOutlined/>} perm='sys-org:update'\n onClick={this.handleEdit} style={{marginRight: 4}}>编辑</Button>\n <Popconfirm perm='sys-org:delete' title='是否确定删除组织机构'\n onConfirm={this.handleDelete}>\n <Button size='small' icon={<DeleteOutlined/>}>删除</Button>\n </Popconfirm>\n </span>\n )}\n </div>\n {selectedOrg && (\n <Descriptions size='small' column={2}>\n <Descriptions.Item label=\"名称\">{selectedOrg.name}</Descriptions.Item>\n <Descriptions.Item label=\"类型\">{selectedOrg.typeLabel}</Descriptions.Item>\n <Descriptions.Item label=\"上级机构\">{selectedOrg.parentName || '-'}</Descriptions.Item>\n <Descriptions.Item label=\"序号\">{selectedOrg.seq ?? '-'}</Descriptions.Item>\n <Descriptions.Item label=\"部门领导\">{selectedOrg.leader?.name || '-'}</Descriptions.Item>\n <Descriptions.Item label=\"启用\"><ViewBooleanEnableDisable value={selectedOrg.enabled}/></Descriptions.Item>\n {selectedOrg.extra1 && <Descriptions.Item label=\"扩展字段1\">{selectedOrg.extra1}</Descriptions.Item>}\n {selectedOrg.extra2 && <Descriptions.Item label=\"扩展字段2\">{selectedOrg.extra2}</Descriptions.Item>}\n {selectedOrg.extra3 && <Descriptions.Item label=\"扩展字段3\">{selectedOrg.extra3}</Descriptions.Item>}\n </Descriptions>\n )}\n </Card>\n </Splitter.Panel>\n </Splitter>\n\n <Modal title={this.state.modalValues?.id ? '编辑组织机构' : '新增组织机构'}\n open={this.state.modalOpen}\n onOk={() => this.formRef.current.submit()}\n onCancel={() => this.setState({modalOpen: false})}\n destroyOnClose\n >\n <Form ref={this.formRef} labelCol={{flex: '120px'}}\n initialValues={this.state.modalValues}\n onFinish={this.handleModalFinish}\n >\n <Form.Item name='id' noStyle/>\n <Form.Item label='父节点' name='pid'>\n <FieldRemoteTreeSelect url='admin/sysOrg/tree'/>\n </Form.Item>\n <Form.Item label='名称' name='name' rules={[{required: true}]}>\n <Input/>\n </Form.Item>\n <Form.Item label='序号' name='seq'>\n <InputNumber/>\n </Form.Item>\n <Form.Item label='类型' name='type' rules={[{required: true}]}>\n <FieldDictSelect typeCode='orgType'/>\n </Form.Item>\n <Form.Item label='部门领导' name={['leader', 'id']}>\n <FieldUserSelect/>\n </Form.Item>\n <Form.Item label='启用' name='enabled' rules={[{required: true}]}>\n <FieldBoolean/>\n </Form.Item>\n <Form.Item label='扩展字段1' name='extra1'>\n <Input/>\n </Form.Item>\n <Form.Item label='扩展字段2' name='extra2'>\n <Input/>\n </Form.Item>\n <Form.Item label='扩展字段3' name='extra3'>\n <Input/>\n </Form.Item>\n </Form>\n </Modal>\n </Page>\n }\n\n onDrop = (e) => {\n const {dragNode, dropToGap, node} = e;\n const dropKey = node.key;\n const dragKey = dragNode.key;\n const dropPos = e.node.pos.split('-');\n const dropPosition = e.dropPosition - Number(dropPos[dropPos.length - 1]);\n HttpUtils.post('admin/sysOrg/sort', {dropPosition, dropToGap, dropKey, dragKey}).then(this.loadTree)\n };\n}\n\n\n\n",
2138
+ "__content": "import {DeleteOutlined, EditOutlined, PlusOutlined, SettingOutlined, SyncOutlined} from '@ant-design/icons';\nimport {Button, Card, Checkbox, Descriptions, Form, Input, InputNumber, Modal, Popconfirm, Popover, Space, Splitter, Switch, Tree, Typography} from 'antd';\nimport React from 'react';\nimport {\n FieldBoolean,\n FieldDictSelect,\n FieldRemoteTreeSelect,\n FieldUserSelect,\n HttpUtils,\n NamedIcon,\n Page,\n ViewSwitch,\n} from \"../../../framework\";\n\nexport default class extends React.Component {\n\n state = {\n selectedOrg: null,\n params: {\n onlyShowEnabled: true,\n onlyShowUnit: false,\n searchText: null,\n },\n treeData: [],\n treeLoading: false,\n draggable: false,\n\n modalOpen: false,\n modalValues: {},\n modalLoading: false,\n }\n formRef = React.createRef();\n treeRef = React.createRef();\n\n componentDidMount() {\n this.loadTree()\n }\n\n loadTree = () => {\n this.setState({treeLoading: true})\n HttpUtils.get('admin/sysOrg/tree', this.state.params).then(rs => {\n this.setState({treeData: rs})\n }).finally(() => {\n this.setState({treeLoading: false});\n })\n }\n\n handleDelete = () => {\n const {selectedOrg} = this.state\n if (!selectedOrg) return\n HttpUtils.post('admin/sysOrg/delete', {id: selectedOrg.id}).then(() => {\n this.setState({selectedOrg: null})\n this.loadTree()\n })\n }\n\n onSelect = (selectedKeys) => {\n if (selectedKeys.length === 0) {\n this.setState({selectedOrg: null})\n return\n }\n HttpUtils.get(\"admin/sysOrg/detail\", {id: selectedKeys[0]}).then(rs => {\n this.setState({selectedOrg: rs})\n })\n }\n\n handleAdd = () => {\n const {selectedOrg} = this.state\n this.setState({\n modalOpen: true,\n modalValues: {pid: selectedOrg?.id, enabled: true},\n })\n }\n\n handleEdit = () => {\n this.setState({\n modalOpen: true,\n modalValues: {...this.state.selectedOrg},\n })\n }\n\n handleModalFinish = (values) => {\n this.setState({modalLoading: true})\n const isNew = !values.id\n const url = isNew ? 'admin/sysOrg/create' : 'admin/sysOrg/update'\n HttpUtils.post(url, values).then(() => {\n this.setState({modalOpen: false})\n this.loadTree()\n }).finally(() => {\n this.setState({modalLoading: false})\n })\n }\n\n onDraggableChange = e => {\n this.setState({draggable: e})\n };\n\n render() {\n const {selectedOrg} = this.state\n const params = this.state.params\n\n return <Page title=\"组织机构\" description=\"管理组织机构树\"\n actions={<Button type='primary' perm='sys-org:create' icon={<PlusOutlined/>} onClick={this.handleAdd}>新增</Button>}>\n <Splitter>\n <Splitter.Panel defaultSize={400} style={{paddingRight: 8}}>\n <Card loading={this.state.treeLoading}\n size='small'\n >\n <div style={{display: 'flex', alignItems: 'center', gap: 4, marginBottom: 8}}>\n <Input.Search placeholder='搜索' value={params.searchText} onChange={e => {\n params.searchText = e.target.value\n this.setState({params}, this.loadTree)\n }} style={{flex: 1}}/>\n <Popover\n trigger='click'\n placement='bottomRight'\n title='设置'\n content={<Space direction='vertical'>\n <Checkbox checked={params.onlyShowEnabled}\n onChange={e => {\n params.onlyShowEnabled = e.target.checked;\n this.setState({params}, this.loadTree);\n }}>仅显示启用</Checkbox>\n <Checkbox checked={params.onlyShowUnit}\n onChange={e => {\n params.onlyShowUnit = e.target.checked;\n this.setState({params}, this.loadTree);\n }}>仅显示单位</Checkbox>\n <div>\n 拖拽排序&nbsp;<Switch\n value={this.state.draggable}\n onChange={this.onDraggableChange}/>\n </div>\n <Button size='small' shape='round' icon={<SyncOutlined/>} onClick={this.loadTree}>刷新</Button>\n </Space>}\n >\n <Button type='text' size='small' icon={<SettingOutlined/>}/>\n </Popover>\n </div>\n <Tree ref={this.treeRef}\n treeData={this.state.treeData}\n onSelect={this.onSelect}\n showIcon\n blockNode\n icon={item => <NamedIcon name={item.data.iconName}/>}\n draggable={this.state.draggable}\n onDrop={this.onDrop}\n showLine\n defaultExpandAll\n />\n </Card>\n </Splitter.Panel>\n\n <Splitter.Panel style={{paddingLeft: 8}}>\n <Card size='small' style={{marginBottom: 8}}>\n <div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8}}>\n <Typography.Text strong>机构信息</Typography.Text>\n {selectedOrg && (\n <span>\n <Button size='small' icon={<EditOutlined/>} perm='sys-org:update'\n onClick={this.handleEdit} style={{marginRight: 4}}>编辑</Button>\n <Popconfirm perm='sys-org:delete' title='是否确定删除组织机构'\n onConfirm={this.handleDelete}>\n <Button size='small' icon={<DeleteOutlined/>}>删除</Button>\n </Popconfirm>\n </span>\n )}\n </div>\n {selectedOrg && (\n <Descriptions size='small' column={2}>\n <Descriptions.Item label=\"名称\">{selectedOrg.name}</Descriptions.Item>\n <Descriptions.Item label=\"类型\">{selectedOrg.typeLabel}</Descriptions.Item>\n <Descriptions.Item label=\"上级机构\">{selectedOrg.parentName || '-'}</Descriptions.Item>\n <Descriptions.Item label=\"序号\">{selectedOrg.seq ?? '-'}</Descriptions.Item>\n <Descriptions.Item label=\"部门领导\">{selectedOrg.leader?.name || '-'}</Descriptions.Item>\n <Descriptions.Item label=\"启用\"><ViewSwitch value={selectedOrg.enabled}/></Descriptions.Item>\n {selectedOrg.extra1 && <Descriptions.Item label=\"扩展字段1\">{selectedOrg.extra1}</Descriptions.Item>}\n {selectedOrg.extra2 && <Descriptions.Item label=\"扩展字段2\">{selectedOrg.extra2}</Descriptions.Item>}\n {selectedOrg.extra3 && <Descriptions.Item label=\"扩展字段3\">{selectedOrg.extra3}</Descriptions.Item>}\n </Descriptions>\n )}\n </Card>\n </Splitter.Panel>\n </Splitter>\n\n <Modal title={this.state.modalValues?.id ? '编辑组织机构' : '新增组织机构'}\n open={this.state.modalOpen}\n onOk={() => this.formRef.current.submit()}\n onCancel={() => this.setState({modalOpen: false})}\n destroyOnClose\n >\n <Form ref={this.formRef} labelCol={{flex: '120px'}}\n initialValues={this.state.modalValues}\n onFinish={this.handleModalFinish}\n >\n <Form.Item name='id' noStyle/>\n <Form.Item label='父节点' name='pid'>\n <FieldRemoteTreeSelect url='admin/sysOrg/tree'/>\n </Form.Item>\n <Form.Item label='名称' name='name' rules={[{required: true}]}>\n <Input/>\n </Form.Item>\n <Form.Item label='序号' name='seq'>\n <InputNumber/>\n </Form.Item>\n <Form.Item label='类型' name='type' rules={[{required: true}]}>\n <FieldDictSelect typeCode='orgType'/>\n </Form.Item>\n <Form.Item label='部门领导' name={['leader', 'id']}>\n <FieldUserSelect/>\n </Form.Item>\n <Form.Item label='启用' name='enabled' rules={[{required: true}]}>\n <FieldBoolean/>\n </Form.Item>\n <Form.Item label='扩展字段1' name='extra1'>\n <Input/>\n </Form.Item>\n <Form.Item label='扩展字段2' name='extra2'>\n <Input/>\n </Form.Item>\n <Form.Item label='扩展字段3' name='extra3'>\n <Input/>\n </Form.Item>\n </Form>\n </Modal>\n </Page>\n }\n\n onDrop = (e) => {\n const {dragNode, dropToGap, node} = e;\n const dropKey = node.key;\n const dragKey = dragNode.key;\n const dropPos = e.node.pos.split('-');\n const dropPosition = e.dropPosition - Number(dropPos[dropPos.length - 1]);\n HttpUtils.post('admin/sysOrg/sort', {dropPosition, dropToGap, dropKey, dragKey}).then(this.loadTree)\n };\n}\n\n\n\n",
2138
2139
  "__isJSFile": true,
2139
2140
  "__absFile": "/home/runner/work/open-admin/open-admin/web/src/pages/system/org/index.jsx"
2140
2141
  },
2141
- "system/role/perm": {
2142
- "path": "system/role/perm",
2143
- "id": "system/role/perm",
2142
+ "forceUpdatePwd": {
2143
+ "path": "forceUpdatePwd",
2144
+ "id": "forceUpdatePwd",
2144
2145
  "parentId": "@@/global-layout",
2145
- "file": "system/role/perm.jsx",
2146
- "absPath": "/system/role/perm",
2147
- "__content": "import React from \"react\";\nimport {Button, Card, Checkbox, Table, Typography} from \"antd\";\nimport {SaveOutlined} from \"@ant-design/icons\";\nimport {ArrayUtils, HttpUtils, Page, PageUtils} from \"../../../framework\";\nexport default class extends React.Component {\n\n\n state = {\n loading: false,\n roleInfo: {},\n\n dataSource: [],\n rowSelectedKeys: {},\n }\n\n columns = [\n {\n title: '菜单',\n dataIndex: 'name',\n },\n\n {\n title: '权限',\n dataIndex: 'permCodes',\n render: (permCodes, record) => {\n if (permCodes == null) {\n return\n }\n const {permNames} = record\n const options = [];\n for (let i = 0; i < permCodes.length; i++) {\n const label = permNames[i];\n const value = permCodes[i];\n options.push({label, value});\n }\n\n\n const rowSelectedKey = this.state.rowSelectedKeys[record.id];\n return <Checkbox.Group options={options}\n value={rowSelectedKey}\n onChange={(ks) => {\n const rowSelectedKeys = this.state.rowSelectedKeys;\n rowSelectedKeys[record.id] = ks;\n this.setState({rowSelectedKeys})\n }}/>\n }\n }\n ]\n\n componentDidMount() {\n this.roleId = PageUtils.currentParams().id;\n this.loadData();\n }\n\n\n loadData() {\n this.setState({loading: true})\n Promise.all([\n HttpUtils.get('admin/sysRole/get', {id: this.roleId}).then(rs => {\n this.setState({roleInfo: rs})\n }),\n HttpUtils.get('admin/sysRole/perm-tree-table', {id: this.roleId}).then(rs => {\n this.setState({dataSource: rs})\n }),\n HttpUtils.get('admin/sysRole/own-perms', {id: this.roleId}).then(rs => {\n this.setState({rowSelectedKeys: rs})\n })\n ]).then(rs => {\n this.setState({loading: false})\n })\n }\n\n savePerms = () => {\n const {rowSelectedKeys} = this.state;\n const perms = [];\n const menus = []\n for (let menuId in rowSelectedKeys) {\n const ks = rowSelectedKeys[menuId];\n if (ks == null || ks.length === 0) {\n continue;\n }\n menus.push(menuId)\n ArrayUtils.addAll(perms, ks)\n }\n HttpUtils.post('admin/sysRole/save-perms', {id: this.roleId, perms, menus}).then(rs => {\n // Page.open(PageUtils.currentPathname(), PageUtils.currentLabel())\n })\n };\n\n\n render() {\n return <Page title=\"角色权限设置\" description=\"设置角色的菜单和权限\">\n <Card title='角色权限设置' loading={this.state.loading}\n variant={\"borderless\"}\n extra={<Button type='primary' icon={<SaveOutlined/>} onClick={this.savePerms}>保存权限</Button>}>\n <Typography.Text>角色名称:{this.state.roleInfo.name}, 编码:{this.state.roleInfo.code} </Typography.Text>\n <Table dataSource={this.state.dataSource}\n columns={this.columns}\n size='small' bordered pagination={false} rowKey='id'\n expandable={{defaultExpandAllRows: true}}\n ></Table>\n </Card>\n </Page>\n }\n}\n",
2146
+ "file": "forceUpdatePwd.jsx",
2147
+ "absPath": "/forceUpdatePwd",
2148
+ "__content": "import React from \"react\";\nimport {Button, Form, Input, message} from \"antd\";\nimport {history} from 'umi'\nimport {HttpUtils, PageUtils, GlobalData} from \"../framework\";\n\nexport default class extends React.Component {\n\n state = {\n checking: true\n }\n\n async componentDidMount() {\n try {\n const rs = await HttpUtils.get('/admin/public/user-info', null, {showError: false});\n if (!rs.login) {\n PageUtils.redirectToLogin();\n return;\n }\n GlobalData.setLoginInfo(rs.loginInfo);\n this.setState({checking: false});\n } catch (e) {\n PageUtils.redirectToLogin();\n }\n }\n\n onFinish = (values) => {\n HttpUtils.post('admin/userCenter/update-pwd', values).then(() => {\n message.success('修改密码成功,请重新登录');\n history.push('/login');\n })\n }\n\n validator = (rule, value) => {\n return new Promise((resolve, reject) => {\n HttpUtils.get(\"admin/sysUser/pwd-strength\", {password: value}).then(response => {\n const rs = response.data\n if (!rs.success) {\n reject(rs.message)\n }\n resolve()\n })\n })\n }\n\n render() {\n if (this.state.checking) return null;\n\n return (\n <div style={{\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n minHeight: '100vh',\n background: '#f0f2f5'\n }}>\n <div style={{\n background: '#fff',\n padding: '40px',\n borderRadius: '8px',\n boxShadow: '0 2px 8px rgba(0,0,0,0.1)',\n maxWidth: 400,\n width: '100%'\n }}>\n <h2 style={{textAlign: 'center', marginBottom: 24}}>修改密码</h2>\n <p style={{textAlign: 'center', color: '#999', marginBottom: 24}}>\n 首次登录或密码已被重置,请修改密码后重新登录\n </p>\n <Form onFinish={this.onFinish}>\n <Form.Item name='newPassword'\n label='新密码'\n extra={'请输入字母、数字、特殊字符'}\n rules={[\n {required: true, message: '请输入新密码'},\n {validator: this.validator}\n ]}\n >\n <Input.Password/>\n </Form.Item>\n <Form.Item style={{marginTop: 40}}>\n <Button type=\"primary\" htmlType=\"submit\" block size='large'>\n 确定\n </Button>\n </Form.Item>\n </Form>\n </div>\n </div>\n )\n }\n}\n",
2148
2149
  "__isJSFile": true,
2149
- "__absFile": "/home/runner/work/open-admin/open-admin/web/src/pages/system/role/perm.jsx"
2150
+ "__absFile": "/home/runner/work/open-admin/open-admin/web/src/pages/forceUpdatePwd.jsx"
2150
2151
  },
2151
- "userCenter/index": {
2152
- "path": "userCenter",
2153
- "id": "userCenter/index",
2152
+ "account/index": {
2153
+ "path": "account",
2154
+ "id": "account/index",
2154
2155
  "parentId": "@@/global-layout",
2155
- "file": "userCenter/index.jsx",
2156
- "absPath": "/userCenter",
2157
- "__content": "import React from \"react\";\nimport {Avatar, Card, Col, Row, Tabs} from \"antd\";\nimport ChangePassword from \"./ChangePassword\";\nimport {HttpUtils, Page} from \"../../framework\";\n\nexport default class extends React.Component {\n\n state = {\n info: {}\n }\n\n componentDidMount() {\n HttpUtils.get('admin/userCenter/info').then(rs => {\n this.setState({info: rs})\n })\n }\n\n render() {\n const {info} = this.state;\n return <Page backgroundGray title=\"个人中心\" description=\"查看和编辑个人资料\">\n\n <Row gutter={[16, 16]}>\n <Col md={6} sm={24}>\n <Card title='个人信息'>\n\n <div className='flex-center' style={{marginBottom: 32}}>\n <Avatar size={128} title='点击修改头像'/>\n </div>\n\n\n <table className='oa-table'>\n <tbody>\n <tr>\n <td>用户名称</td>\n <td align='right'>{info.name}</td>\n </tr>\n <tr>\n <td>账号</td>\n <td align='right'>{info.account}</td>\n </tr>\n <tr>\n <td>手机号码</td>\n <td>{info.phone}</td>\n </tr>\n <tr>\n <td>用户邮箱</td>\n <td>{info.email}</td>\n </tr>\n <tr>\n <td>所属部门</td>\n <td>{info.unit} {info.dept}</td>\n </tr>\n <tr>\n <td>所属角色</td>\n <td>{info.roles}</td>\n </tr>\n <tr>\n <td>创建日期</td>\n <td>{info.createTime}</td>\n </tr>\n </tbody>\n </table>\n </Card>\n </Col>\n <Col md={18} sm={24}>\n <Card title='个人设置'>\n <Tabs\n items={[\n {\n label: '修改密码', key: 'pwd', children: <div>\n <ChangePassword/>\n </div>\n },\n {\n label: '消息订阅', key: 'topic', children: <div>\n 当账号管理邮箱后,系统消息会发送邮件给您\n </div>\n }\n ]}\n tabPlacement='left'\n >\n </Tabs>\n </Card>\n </Col>\n </Row>\n\n\n </Page>\n\n }\n}\n",
2156
+ "file": "account/index.jsx",
2157
+ "absPath": "/account",
2158
+ "__content": "import React from \"react\";\nimport {Avatar, Card, Col, Row, Tabs} from \"antd\";\nimport ChangePassword from \"./changePassword\";\nimport {HttpUtils, Page} from \"../../framework\";\n\nexport default class extends React.Component {\n\n state = {\n info: {}\n }\n\n componentDidMount() {\n HttpUtils.get('admin/userCenter/info').then(rs => {\n this.setState({info: rs})\n })\n }\n\n render() {\n const {info} = this.state;\n return <Page backgroundGray title=\"个人中心\" description=\"查看和编辑个人资料\">\n\n <Row gutter={[16, 16]}>\n <Col md={6} sm={24}>\n <Card title='个人信息'>\n\n <div className='flex-center' style={{marginBottom: 32}}>\n <Avatar size={128} title='点击修改头像'/>\n </div>\n\n\n <table className='oa-table'>\n <tbody>\n <tr>\n <td>用户名称</td>\n <td align='right'>{info.name}</td>\n </tr>\n <tr>\n <td>账号</td>\n <td align='right'>{info.account}</td>\n </tr>\n <tr>\n <td>手机号码</td>\n <td>{info.phone}</td>\n </tr>\n <tr>\n <td>用户邮箱</td>\n <td>{info.email}</td>\n </tr>\n <tr>\n <td>所属部门</td>\n <td>{info.unit} {info.dept}</td>\n </tr>\n <tr>\n <td>所属角色</td>\n <td>{info.roles}</td>\n </tr>\n <tr>\n <td>创建日期</td>\n <td>{info.createTime}</td>\n </tr>\n </tbody>\n </table>\n </Card>\n </Col>\n <Col md={18} sm={24}>\n <Card title='个人设置'>\n <Tabs\n items={[\n {\n label: '修改密码', key: 'pwd', children: <div>\n <ChangePassword/>\n </div>\n },\n\n ]}\n tabPlacement='left'\n >\n </Tabs>\n </Card>\n </Col>\n </Row>\n\n\n </Page>\n\n }\n}\n",
2158
2159
  "__isJSFile": true,
2159
- "__absFile": "/home/runner/work/open-admin/open-admin/web/src/pages/userCenter/index.jsx"
2160
+ "__absFile": "/home/runner/work/open-admin/open-admin/web/src/pages/account/index.jsx"
2160
2161
  },
2161
2162
  "about": {
2162
2163
  "path": "about",
@@ -2164,7 +2165,7 @@
2164
2165
  "parentId": "@@/global-layout",
2165
2166
  "file": "about.jsx",
2166
2167
  "absPath": "/about",
2167
- "__content": "import React from \"react\";\nimport {Page} from \"../framework\";\n\nexport default class extends React.Component {\n\n\n render() {\n return <Page title=\"关于\" description=\"了解系统版本与相关信息\">\n <h1>关于</h1>\n </Page>\n }\n}\n",
2168
+ "__content": "import React from \"react\";\nimport {HttpUtils, Page} from \"../framework\";\n\nexport default class extends React.Component {\n\n state = {\n buildInfo: null,\n }\n\n componentDidMount() {\n HttpUtils.get('/admin/lib-info').then(data => {\n this.setState({buildInfo: data})\n }).catch(e => {\n console.error('[About] 加载版本信息失败:', e);\n })\n }\n\n render() {\n const {buildInfo} = this.state;\n\n return (\n <Page title=\"关于\" description=\"了解系统版本与相关信息\">\n <h1>关于</h1>\n {buildInfo && (\n <div style={{marginTop: '3em', color: 'rgba(0,0,0,0.45)', fontSize: 12}}>\n 依赖库版本 v{buildInfo.version} / {buildInfo.buildTime}\n </div>\n )}\n </Page>\n );\n }\n}\n",
2168
2169
  "__isJSFile": true,
2169
2170
  "__absFile": "/home/runner/work/open-admin/open-admin/web/src/pages/about.jsx"
2170
2171
  },
@@ -2184,7 +2185,7 @@
2184
2185
  "parentId": "@@/global-layout",
2185
2186
  "file": "login.jsx",
2186
2187
  "absPath": "/login",
2187
- "__content": "import React from 'react';\nimport {Button, Form, Input, message} from 'antd';\nimport {LockOutlined, UserOutlined, WarningOutlined} from '@ant-design/icons';\nimport {history} from 'umi';\nimport {EventBusUtils, HttpUtils, PageUtils, SysUtils} from \"../framework/utils\";\n\nimport \"./login.less\"\n\nfunction getRedirect() {\n let redirect = PageUtils.currentParams()['redirect'];\n if (redirect) {\n redirect = decodeURIComponent(redirect)\n } else {\n redirect = '/'\n }\n return redirect;\n}\n\nfunction postLogin(values) {\n return new Promise((resolve, reject) => {\n HttpUtils.post('/admin/auth/login', values).then(rs => {\n EventBusUtils.emit('loginSuccess')\n history.push(getRedirect())\n resolve(rs)\n }).catch(e => {\n console.error('[Login] 登录失败:', e);\n reject(e)\n })\n })\n}\n\nfunction encodePassword(pwd) {\n const chars = [];\n for (let i = 0; i < pwd.length; i++)\n chars.push(pwd.charCodeAt(i) + i + 2);\n return btoa(String.fromCharCode(...chars));\n}\n\nexport default class extends React.Component {\n\n state = {\n logging: false,\n siteInfo: {}\n }\n\n async componentDidMount() {\n const siteInfo = SysUtils.getSiteInfo()\n if (siteInfo && siteInfo.title) {\n this.setState({siteInfo})\n return\n }\n // localStorage 中无站点信息,从服务端重新加载\n try {\n const rs = await HttpUtils.get('/admin/public/site-info')\n SysUtils.setSiteInfo(rs)\n this.setState({siteInfo: rs})\n } catch (e) {\n console.error('[Login] 加载站点信息失败:', e);\n message.error('加载站点信息失败,请刷新页面重试')\n }\n }\n\n submit = values => {\n this.setState({logging: true})\n values.password = encodePassword(values.password)\n postLogin(values).finally(() => {\n this.setState({logging: false})\n })\n }\n\n render() {\n const {siteInfo} = this.state\n\n const pageStyle = {\n backgroundImage: `url(\"./login_bg.jpg\")`\n }\n\n return (\n <section className='login-page' style={pageStyle}>\n <div className=\"login-content\">\n <h1>{siteInfo.title}</h1>\n {this.getForm(siteInfo)}\n {this.renderFormBottom()}\n </div>\n </section>\n );\n }\n\n getForm = siteInfo => {\n const form = (\n <Form\n name=\"normal_login\"\n className=\"login-form\"\n initialValues={{remember: true}}\n onFinish={this.submit}\n requiredMark={false}\n colon={false}\n >\n <Form.Item name=\"username\" rules={[{required: true, message: '请输入用户名!'}]}>\n <Input size='large' prefix={<UserOutlined/>} placeholder=\"用户名\" autoComplete=\"off\"/>\n </Form.Item>\n <Form.Item name=\"password\" rules={[{required: true, message: '请输入密码!'}]}>\n <Input autoComplete=\"off\" prefix={<LockOutlined/>} type=\"password\" placeholder=\"密码\"\n size='large'\n />\n </Form.Item>\n\n <Form.Item style={{marginTop: 10}}>\n <Button loading={this.state.logging} type=\"primary\" htmlType=\"submit\"\n block size='large'>\n 登录\n </Button>\n </Form.Item>\n </Form>\n );\n\n if (this.props.formRender) {\n return this.props.formRender(form);\n }\n return form;\n };\n\n renderFormBottom() {\n const siteInfo = this.state.siteInfo;\n if (siteInfo.loginBoxBottomTip) {\n return (\n <div style={{color: 'white', marginTop: 50, fontSize: '14px', textAlign: 'center'}}>\n <WarningOutlined/> {siteInfo.loginBoxBottomTip}\n </div>\n )\n }\n }\n}\n",
2188
+ "__content": "import React from 'react';\nimport {Button, Form, Input, message} from 'antd';\nimport {LockOutlined, UserOutlined, WarningOutlined} from '@ant-design/icons';\nimport {history} from 'umi';\nimport {EventBus, HttpUtils, PageUtils, GlobalData} from \"../framework\";\n\nimport \"./login.less\"\n\nfunction getRedirect() {\n let redirect = PageUtils.currentParams()['redirect'];\n if (redirect) {\n redirect = decodeURIComponent(redirect)\n } else {\n redirect = '/'\n }\n return redirect;\n}\n\nfunction postLogin(values) {\n return new Promise((resolve, reject) => {\n HttpUtils.post('/admin/auth/login', values).then(rs => {\n EventBus.emit('loginSuccess')\n history.push(getRedirect())\n resolve(rs)\n }).catch(e => {\n console.error('[Login] 登录失败:', e);\n reject(e)\n })\n })\n}\n\nfunction encodePassword(pwd) {\n const chars = [];\n for (let i = 0; i < pwd.length; i++)\n chars.push(pwd.charCodeAt(i) + i + 2);\n return btoa(String.fromCharCode(...chars));\n}\n\nexport default class extends React.Component {\n\n state = {\n logging: false,\n siteInfo: {}\n }\n\n async componentDidMount() {\n const siteInfo = GlobalData.getSiteInfo()\n if (siteInfo && siteInfo.title) {\n this.setState({siteInfo})\n return\n }\n // localStorage 中无站点信息,从服务端重新加载\n try {\n const rs = await HttpUtils.get('/admin/public/site-info', null, { showError: false })\n GlobalData.setSiteInfo(rs)\n this.setState({siteInfo: rs})\n } catch (e) {\n console.error('[Login] 加载站点信息失败:', e);\n message.error('加载站点信息失败,请刷新页面重试')\n }\n }\n\n submit = values => {\n this.setState({logging: true})\n values.password = encodePassword(values.password)\n postLogin(values).finally(() => {\n this.setState({logging: false})\n })\n }\n\n render() {\n const {siteInfo} = this.state\n\n const pageStyle = {\n backgroundImage: `url(\"./login_bg.jpg\")`\n }\n\n return (\n <section className='login-page' style={pageStyle}>\n <div className=\"login-content\">\n <h1>{siteInfo.title}</h1>\n {this.getForm(siteInfo)}\n {this.renderFormBottom()}\n </div>\n </section>\n );\n }\n\n getForm = siteInfo => {\n const form = (\n <Form\n name=\"normal_login\"\n className=\"login-form\"\n initialValues={{remember: true}}\n onFinish={this.submit}\n requiredMark={false}\n colon={false}\n >\n <Form.Item name=\"username\" rules={[{required: true, message: '请输入用户名!'}]}>\n <Input size='large' prefix={<UserOutlined/>} placeholder=\"用户名\" autoComplete=\"off\"/>\n </Form.Item>\n <Form.Item name=\"password\" rules={[{required: true, message: '请输入密码!'}]}>\n <Input autoComplete=\"off\" prefix={<LockOutlined/>} type=\"password\" placeholder=\"密码\"\n size='large'\n />\n </Form.Item>\n\n <Form.Item style={{marginTop: 10}}>\n <Button loading={this.state.logging} type=\"primary\" htmlType=\"submit\"\n block size='large'>\n 登录\n </Button>\n </Form.Item>\n </Form>\n );\n\n if (this.props.formRender) {\n return this.props.formRender(form);\n }\n return form;\n };\n\n renderFormBottom() {\n const siteInfo = this.state.siteInfo;\n if (siteInfo.loginBoxBottomTip) {\n return (\n <div style={{color: 'white', marginTop: 50, fontSize: '14px', textAlign: 'center'}}>\n <WarningOutlined/> {siteInfo.loginBoxBottomTip}\n </div>\n )\n }\n }\n}\n",
2188
2189
  "__isJSFile": true,
2189
2190
  "__absFile": "/home/runner/work/open-admin/open-admin/web/src/pages/login.jsx"
2190
2191
  },