@jiangood/open-admin 3.1.10 → 3.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/src/framework/GlobalData.ts +4 -0
- package/src/framework/biz/OrgTree.tsx +1 -1
- package/src/framework/biz/RoleTree.tsx +1 -1
- package/src/framework/biz/UserPicker.tsx +4 -4
- package/src/framework/biz/index.tsx +4 -4
- package/src/framework/components/CronEditor/index.tsx +318 -0
- package/src/framework/components/LinkButton/index.tsx +4 -0
- package/src/framework/components/OrgSwitcher/index.tsx +2 -2
- package/src/framework/components/Page/index.tsx +6 -14
- package/src/framework/components/ProTable/index.tsx +3 -1
- package/src/framework/components/ThemeSettings/index.tsx +5 -38
- package/src/framework/components/index.ts +1 -0
- package/src/framework/config/index.ts +12 -0
- package/src/framework/config/layoutConfig.ts +47 -8
- package/src/framework/config/themeConfig.ts +93 -25
- package/src/framework/config/uiDefaults.ts +26 -0
- package/src/framework/fields/FieldEditor/index.tsx +1 -1
- package/src/framework/fields/FieldUploadFile/index.tsx +1 -1
- package/src/framework/fields/FieldUploadImage/index.tsx +42 -10
- package/src/framework/fields/FieldUserPicker/index.tsx +3 -3
- package/src/framework/fields/orgTree.ts +1 -1
- package/src/framework/router/index.ts +1 -0
- package/src/framework/router/matcher.ts +19 -4
- package/src/framework/utils/AvatarUtils.ts +64 -0
- package/src/framework/utils/CronUtils.ts +562 -0
- package/src/framework/utils/HttpClient.ts +5 -5
- package/src/framework/utils/PageUtils.ts +38 -7
- package/src/framework/utils/TitleUtils.ts +38 -0
- package/src/framework/utils/UrlUtils.ts +3 -3
- package/src/framework/utils/index.ts +3 -0
- package/src/layouts/admin/HeaderRight.jsx +62 -16
- package/src/layouts/admin/SinglePageLayout.jsx +13 -2
- package/src/layouts/admin/TabLayout.jsx +38 -19
- package/src/layouts/admin/index.jsx +12 -3
- package/src/layouts/index.tsx +52 -15
- package/src/pages/404.jsx +4 -0
- package/src/pages/article/$code.jsx +5 -1
- package/src/pages/login.jsx +157 -0
- package/src/pages/{public/login.less → login.less} +16 -1
- package/src/pages/public/login.jsx +11 -145
- package/src/pages/standalone/{forceUpdatePwd.jsx → force-update-pwd.jsx} +10 -4
- package/src/pages/system/article/index.jsx +1 -1
- package/src/pages/system/config/index.jsx +335 -0
- package/src/pages/system/file/index.jsx +3 -3
- package/src/pages/system/job/index.jsx +62 -19
- package/src/pages/system/log/index.jsx +1 -1
- package/src/pages/system/online/index.jsx +95 -0
- package/src/pages/system/org/index.jsx +7 -7
- package/src/pages/system/role/index.jsx +8 -8
- package/src/pages/system/role/{rolePerm.jsx → role-perm.jsx} +9 -4
- package/src/pages/system/user/{userPerm.jsx → UserPermModal.jsx} +3 -3
- package/src/pages/system/user/index.jsx +6 -6
- package/src/pages/user-center/AvatarPicker.jsx +76 -0
- package/src/pages/{userCenter/permView.jsx → user-center/PermView.jsx} +48 -25
- package/src/pages/user-center/RecentOps.jsx +36 -0
- package/src/pages/user-center/index.jsx +393 -0
- package/vite-plugin/index.js +64 -6
- package/src/pages/userCenter/index.jsx +0 -93
|
@@ -38,7 +38,7 @@ export default class OrgPage extends React.Component {
|
|
|
38
38
|
|
|
39
39
|
loadTree = () => {
|
|
40
40
|
this.setState({treeLoading: true})
|
|
41
|
-
HttpClient.get('admin/
|
|
41
|
+
HttpClient.get('admin/sys-org/tree', this.state.params, {toastError: false}).then(rs => {
|
|
42
42
|
this.setState({treeData: rs.data})
|
|
43
43
|
this.setState({treeLoading: false});
|
|
44
44
|
}).catch(() => {
|
|
@@ -53,7 +53,7 @@ export default class OrgPage extends React.Component {
|
|
|
53
53
|
handleDelete = () => {
|
|
54
54
|
const {selectedOrg} = this.state
|
|
55
55
|
if (!selectedOrg) return
|
|
56
|
-
HttpClient.post('admin/
|
|
56
|
+
HttpClient.post('admin/sys-org/delete', {id: selectedOrg.id}, null).then(() => {
|
|
57
57
|
this.setState({selectedOrg: null, deleteModalOpen: false})
|
|
58
58
|
this.loadTree()
|
|
59
59
|
})
|
|
@@ -64,7 +64,7 @@ export default class OrgPage extends React.Component {
|
|
|
64
64
|
this.setState({selectedOrg: null})
|
|
65
65
|
return
|
|
66
66
|
}
|
|
67
|
-
HttpClient.get("admin/
|
|
67
|
+
HttpClient.get("admin/sys-org/detail", {id: selectedKeys[0]}).then(rs => {
|
|
68
68
|
this.setState({selectedOrg: rs.data})
|
|
69
69
|
})
|
|
70
70
|
}
|
|
@@ -80,7 +80,7 @@ export default class OrgPage extends React.Component {
|
|
|
80
80
|
|
|
81
81
|
handleModalFinish = async values => {
|
|
82
82
|
const isNew = !values.id
|
|
83
|
-
const url = isNew ? 'admin/
|
|
83
|
+
const url = isNew ? 'admin/sys-org/create' : 'admin/sys-org/update'
|
|
84
84
|
await HttpClient.post(url, values)
|
|
85
85
|
this.loadTree()
|
|
86
86
|
}
|
|
@@ -189,13 +189,13 @@ export default class OrgPage extends React.Component {
|
|
|
189
189
|
onFinish={this.handleModalFinish}
|
|
190
190
|
labelCol={{flex: '120px'}}>
|
|
191
191
|
<Form.Item label='父节点' name='pid'>
|
|
192
|
-
<FieldRemoteTreeSelect url='admin/
|
|
192
|
+
<FieldRemoteTreeSelect url='admin/sys-org/tree'/>
|
|
193
193
|
</Form.Item>
|
|
194
194
|
<Form.Item label='名称' name='name' rules={[{required: true}]}>
|
|
195
195
|
<Input/>
|
|
196
196
|
</Form.Item>
|
|
197
197
|
<Form.Item label='类型' name='type' rules={[{required: true}]}>
|
|
198
|
-
<FieldRemoteSelect url='admin/
|
|
198
|
+
<FieldRemoteSelect url='admin/sys-org/type-options' placeholder='请选择类型'/>
|
|
199
199
|
</Form.Item>
|
|
200
200
|
<Form.Item label='部门领导' name={['leader', 'id']}>
|
|
201
201
|
<FieldUserSelect/>
|
|
@@ -233,7 +233,7 @@ export default class OrgPage extends React.Component {
|
|
|
233
233
|
const dragKey = dragNode.key;
|
|
234
234
|
const dropPos = e.node.pos.split('-');
|
|
235
235
|
const dropPosition = e.dropPosition - Number(dropPos[dropPos.length - 1]);
|
|
236
|
-
HttpClient.post('admin/
|
|
236
|
+
HttpClient.post('admin/sys-org/sort', {dropPosition, dropToGap, dropKey, dragKey}, null).then(() => this.loadTree())
|
|
237
237
|
};
|
|
238
238
|
}
|
|
239
239
|
|
|
@@ -26,20 +26,20 @@ export default class RolePage extends React.Component {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
handleEditUser = record => {
|
|
29
|
-
HttpClient.get('admin/
|
|
29
|
+
HttpClient.get('admin/sys-role/user-list', {id: record.id}).then(rs => {
|
|
30
30
|
this.setState({pickerOpen: true, selectedRecord: record, targetKeys: rs.data?.selectedKeys || []})
|
|
31
31
|
})
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
onFinish = async values => {
|
|
35
35
|
const isNew = !values.id;
|
|
36
|
-
const url = isNew ? 'admin/
|
|
36
|
+
const url = isNew ? 'admin/sys-role/create' : 'admin/sys-role/update';
|
|
37
37
|
await HttpClient.post(url, values)
|
|
38
38
|
this.tableRef.current.reload()
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
handleDelete = record => {
|
|
42
|
-
HttpClient.post('admin/
|
|
42
|
+
HttpClient.post('admin/sys-role/delete', {id: record.id}, null).then(() => {
|
|
43
43
|
this.tableRef.current.reload()
|
|
44
44
|
})
|
|
45
45
|
}
|
|
@@ -109,7 +109,7 @@ export default class RolePage extends React.Component {
|
|
|
109
109
|
size="small"
|
|
110
110
|
actions={[
|
|
111
111
|
{label: '用户设置', perm: 'sys-role:grant-permission', onClick: () => this.handleEditUser(record)},
|
|
112
|
-
{label: '权限设置', perm: 'sys-role:grant-permission', onClick: () => PageUtils.open('/system/role/
|
|
112
|
+
{label: '权限设置', perm: 'sys-role:grant-permission', onClick: () => PageUtils.open('/system/role/role-perm?id=' + record.id)},
|
|
113
113
|
{label: '编辑', perm: 'sys-role:update', onClick: () => this.handleEdit(record)},
|
|
114
114
|
{label: '删除', perm: 'sys-role:delete', confirm: '是否确定删除系统角色', onClick: () => this.handleDelete(record)},
|
|
115
115
|
]}
|
|
@@ -124,7 +124,7 @@ export default class RolePage extends React.Component {
|
|
|
124
124
|
id: this.state.selectedRecord.id,
|
|
125
125
|
userIdList
|
|
126
126
|
}
|
|
127
|
-
HttpClient.post('admin/
|
|
127
|
+
HttpClient.post('admin/sys-role/grant-users', params, null, {toastError: false}).then(() => {
|
|
128
128
|
this.setState({pickerOpen: false, usersModalLoading: false})
|
|
129
129
|
}).catch(() => {
|
|
130
130
|
this.setState({usersModalLoading: false})
|
|
@@ -141,7 +141,7 @@ export default class RolePage extends React.Component {
|
|
|
141
141
|
{label: '新增', type: 'primary', icon: <PlusOutlined/>, perm: 'sys-role:create', onClick: this.handleAdd},
|
|
142
142
|
]}/>
|
|
143
143
|
)}
|
|
144
|
-
request={(params) => HttpClient.get('admin/
|
|
144
|
+
request={(params) => HttpClient.get('admin/sys-role/page', params)}
|
|
145
145
|
columns={this.columns}
|
|
146
146
|
searchInline
|
|
147
147
|
searchFormRender={() => (
|
|
@@ -186,8 +186,8 @@ export default class RolePage extends React.Component {
|
|
|
186
186
|
title={'为「' + (this.state.selectedRecord?.name || '') + '」角色分配用户'}
|
|
187
187
|
value={this.state.targetKeys}
|
|
188
188
|
confirmLoading={this.state.usersModalLoading}
|
|
189
|
-
requestUrl='admin/
|
|
190
|
-
usersUrl='admin/
|
|
189
|
+
requestUrl='admin/sys-role/user-page'
|
|
190
|
+
usersUrl='admin/sys-role/user-by-ids'
|
|
191
191
|
onOk={this.handleSaveUsers}
|
|
192
192
|
onCancel={() => this.setState({pickerOpen: false})}
|
|
193
193
|
/>
|
|
@@ -2,6 +2,11 @@ import React from "react";
|
|
|
2
2
|
import {Button, Card, Checkbox, Table, Typography} from "antd";
|
|
3
3
|
import {SaveOutlined} from "@ant-design/icons";
|
|
4
4
|
import {HttpClient, Page} from "../../../framework";
|
|
5
|
+
|
|
6
|
+
export const pageMeta = {
|
|
7
|
+
title: '角色权限设置',
|
|
8
|
+
};
|
|
9
|
+
|
|
5
10
|
export default class RolePermPage extends React.Component {
|
|
6
11
|
|
|
7
12
|
|
|
@@ -62,15 +67,15 @@ export default class RolePermPage extends React.Component {
|
|
|
62
67
|
this.setState({loading: false})
|
|
63
68
|
}
|
|
64
69
|
};
|
|
65
|
-
HttpClient.get('admin/
|
|
70
|
+
HttpClient.get('admin/sys-role/get', {id: this.roleId}).then(rs => {
|
|
66
71
|
this.setState({roleInfo: rs.data})
|
|
67
72
|
onFinish()
|
|
68
73
|
});
|
|
69
|
-
HttpClient.get('admin/
|
|
74
|
+
HttpClient.get('admin/sys-role/perm-tree-table', {id: this.roleId}).then(rs => {
|
|
70
75
|
this.setState({dataSource: rs.data})
|
|
71
76
|
onFinish()
|
|
72
77
|
});
|
|
73
|
-
HttpClient.get('admin/
|
|
78
|
+
HttpClient.get('admin/sys-role/own-perms', {id: this.roleId}).then(rs => {
|
|
74
79
|
this.setState({rowSelectedKeys: rs.data})
|
|
75
80
|
onFinish()
|
|
76
81
|
});
|
|
@@ -88,7 +93,7 @@ export default class RolePermPage extends React.Component {
|
|
|
88
93
|
menus.push(menuId)
|
|
89
94
|
perms.push(...ks)
|
|
90
95
|
}
|
|
91
|
-
HttpClient.post('admin/
|
|
96
|
+
HttpClient.post('admin/sys-role/save-perms', {id: this.roleId, perms, menus})
|
|
92
97
|
};
|
|
93
98
|
|
|
94
99
|
|
|
@@ -20,7 +20,7 @@ export default class UserPerm extends React.Component {
|
|
|
20
20
|
show(item) {
|
|
21
21
|
this.setState({visible: true})
|
|
22
22
|
|
|
23
|
-
HttpClient.get('admin/
|
|
23
|
+
HttpClient.get('admin/sys-user/get-perm-info', {id: item.id}).then(rs => {
|
|
24
24
|
this.setState({formValues: rs.data})
|
|
25
25
|
this.formRef.current.setFieldsValue(rs.data)
|
|
26
26
|
})
|
|
@@ -34,7 +34,7 @@ export default class UserPerm extends React.Component {
|
|
|
34
34
|
})
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
HttpClient.post('admin/
|
|
37
|
+
HttpClient.post('admin/sys-user/grant-perm', values, null, {toastError: false}).then(() => {
|
|
38
38
|
this.setState({
|
|
39
39
|
visible: false,
|
|
40
40
|
confirmLoading: false
|
|
@@ -70,7 +70,7 @@ export default class UserPerm extends React.Component {
|
|
|
70
70
|
>
|
|
71
71
|
<Form.Item name='id' noStyle></Form.Item>
|
|
72
72
|
<Form.Item label='角色' name='roleIds' rules={[{required: true}]}>
|
|
73
|
-
<FieldRemoteSelect url='admin/
|
|
73
|
+
<FieldRemoteSelect url='admin/sys-role/options' multiple />
|
|
74
74
|
</Form.Item>
|
|
75
75
|
<Form.Item label='数据权限' name='dataPermType' rules={[{required: true}]}>
|
|
76
76
|
<FieldDictSelect typeCode='dataPermType'/>
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
ProTable,
|
|
15
15
|
ViewText,
|
|
16
16
|
} from "../../../framework";
|
|
17
|
-
import UserPerm from "./
|
|
17
|
+
import UserPerm from "./UserPermModal";
|
|
18
18
|
|
|
19
19
|
export default class UserPage extends React.Component {
|
|
20
20
|
|
|
@@ -36,13 +36,13 @@ export default class UserPage extends React.Component {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
onFinishResetPwd = async values => {
|
|
39
|
-
await HttpClient.post('admin/
|
|
39
|
+
await HttpClient.post('admin/sys-user/reset-pwd', {id: this.state.resetPwdUser.id, password: values.password})
|
|
40
40
|
this.setState({resetPwdUser: null})
|
|
41
41
|
this.tableRef.current?.reload()
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
handleDelete = r => {
|
|
45
|
-
HttpClient.post('admin/
|
|
45
|
+
HttpClient.post('admin/sys-user/delete', {id: r.id}, null).then(() => {
|
|
46
46
|
this.tableRef.current.reload();
|
|
47
47
|
})
|
|
48
48
|
}
|
|
@@ -135,7 +135,7 @@ export default class UserPage extends React.Component {
|
|
|
135
135
|
|
|
136
136
|
onFinish = async values => {
|
|
137
137
|
const isNew = !values.id;
|
|
138
|
-
const url = isNew ? 'admin/
|
|
138
|
+
const url = isNew ? 'admin/sys-user/create' : 'admin/sys-user/update';
|
|
139
139
|
const result = await HttpClient.post(url, values);
|
|
140
140
|
if (result?.data?.password) {
|
|
141
141
|
this.setState({
|
|
@@ -170,7 +170,7 @@ export default class UserPage extends React.Component {
|
|
|
170
170
|
)}
|
|
171
171
|
request={(params) => {
|
|
172
172
|
params.orgId = this.state.currentOrgId
|
|
173
|
-
return HttpClient.get('admin/
|
|
173
|
+
return HttpClient.get('admin/sys-user/page', params)
|
|
174
174
|
}}
|
|
175
175
|
columns={this.columns}
|
|
176
176
|
searchFormRender={() => (
|
|
@@ -185,7 +185,7 @@ export default class UserPage extends React.Component {
|
|
|
185
185
|
<Input/>
|
|
186
186
|
</Form.Item>
|
|
187
187
|
<Form.Item label='角色' name='roleId'>
|
|
188
|
-
<FieldRemoteSelect url='admin/
|
|
188
|
+
<FieldRemoteSelect url='admin/sys-role/options' placeholder='请选择角色'/>
|
|
189
189
|
</Form.Item>
|
|
190
190
|
<Form.Item label='状态' name='enabled'>
|
|
191
191
|
<Select allowClear placeholder='全部' options={[{value: true, label: '启用'}, {value: false, label: '停用'}]}/>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {Avatar, Tooltip} from "antd";
|
|
3
|
+
import {
|
|
4
|
+
AvatarUtils,
|
|
5
|
+
FALLBACK_AVATAR_PRESETS,
|
|
6
|
+
FieldUploadImage,
|
|
7
|
+
getToken,
|
|
8
|
+
HttpClient,
|
|
9
|
+
} from "../../framework";
|
|
10
|
+
|
|
11
|
+
const ITEM_SIZE = 44;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 头像选择:内置头像 + 自定义上传
|
|
15
|
+
* <p>
|
|
16
|
+
* 内置头像从后端 admin/public/avatar-presets 获取,移动端可复用同一接口;
|
|
17
|
+
* 上传入口以虚线圆形按钮排在列表末尾,选中/上传后直接在同一排展示结果。
|
|
18
|
+
*/
|
|
19
|
+
export default class AvatarPicker extends React.Component {
|
|
20
|
+
|
|
21
|
+
state = {
|
|
22
|
+
presets: FALLBACK_AVATAR_PRESETS,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
componentDidMount() {
|
|
26
|
+
HttpClient.get('admin/public/avatar-presets', null, {toastError: false}).then(rs => {
|
|
27
|
+
const presets = rs.data?.presets;
|
|
28
|
+
if (Array.isArray(presets) && presets.length > 0) {
|
|
29
|
+
this.setState({presets});
|
|
30
|
+
}
|
|
31
|
+
}).catch(() => {
|
|
32
|
+
// 接口不可用时保留前端兜底列表
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
render() {
|
|
37
|
+
const {value, onChange} = this.props;
|
|
38
|
+
const {presets} = this.state;
|
|
39
|
+
const uploaded = value && !AvatarUtils.isPreset(value) ? value : '';
|
|
40
|
+
const primary = getToken().colorPrimary;
|
|
41
|
+
|
|
42
|
+
return <div style={{display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: 10}}>
|
|
43
|
+
{presets.map(preset => {
|
|
44
|
+
const selected = (value || '') === preset.key;
|
|
45
|
+
return <Tooltip key={preset.key} title={preset.label}>
|
|
46
|
+
<button type="button"
|
|
47
|
+
aria-label={preset.label}
|
|
48
|
+
aria-pressed={selected}
|
|
49
|
+
onClick={() => onChange?.(preset.key)}
|
|
50
|
+
style={{
|
|
51
|
+
width: ITEM_SIZE,
|
|
52
|
+
height: ITEM_SIZE,
|
|
53
|
+
padding: 0,
|
|
54
|
+
border: 'none',
|
|
55
|
+
borderRadius: '50%',
|
|
56
|
+
background: 'transparent',
|
|
57
|
+
cursor: 'pointer',
|
|
58
|
+
lineHeight: 0,
|
|
59
|
+
boxShadow: selected ? `0 0 0 2px ${primary}` : 'none',
|
|
60
|
+
transition: 'box-shadow .2s',
|
|
61
|
+
}}>
|
|
62
|
+
<Avatar size={ITEM_SIZE} src={preset.url}/>
|
|
63
|
+
</button>
|
|
64
|
+
</Tooltip>;
|
|
65
|
+
})}
|
|
66
|
+
|
|
67
|
+
<FieldUploadImage value={uploaded}
|
|
68
|
+
onChange={(v) => onChange?.(v || '')}
|
|
69
|
+
maxCount={1}
|
|
70
|
+
shape="circle"
|
|
71
|
+
size={ITEM_SIZE}
|
|
72
|
+
triggerTitle="上传自定义头像"
|
|
73
|
+
defaultCropRatio={{width: 1, height: 1}}/>
|
|
74
|
+
</div>;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {Empty, Table, Tag} from "antd";
|
|
3
|
-
import {HttpClient
|
|
2
|
+
import {Empty, Table, Tabs, Tag} from "antd";
|
|
3
|
+
import {HttpClient} from "../../framework";
|
|
4
|
+
import RecentOps from "./RecentOps";
|
|
4
5
|
|
|
5
6
|
export default class PermView extends React.Component {
|
|
6
7
|
|
|
@@ -9,17 +10,12 @@ export default class PermView extends React.Component {
|
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
componentDidMount() {
|
|
12
|
-
HttpClient.get('admin/
|
|
13
|
+
HttpClient.get('admin/user-center/perms', null).then(rs => {
|
|
13
14
|
this.setState({data: rs.data})
|
|
14
15
|
})
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
const {data} = this.state;
|
|
19
|
-
if (!data) {
|
|
20
|
-
return <Empty description='加载中...'/>;
|
|
21
|
-
}
|
|
22
|
-
|
|
18
|
+
renderOrgPerm = (data) => {
|
|
23
19
|
const orgColumns = [
|
|
24
20
|
{
|
|
25
21
|
title: '机构名称', dataIndex: 'title', key: 'title',
|
|
@@ -38,6 +34,20 @@ export default class PermView extends React.Component {
|
|
|
38
34
|
},
|
|
39
35
|
];
|
|
40
36
|
|
|
37
|
+
return <div>
|
|
38
|
+
<div style={{marginBottom: 16}}>
|
|
39
|
+
<span>数据权限:
|
|
40
|
+
<Tag color='processing'>{data.dataPermLabel || '-'}</Tag>
|
|
41
|
+
</span>
|
|
42
|
+
</div>
|
|
43
|
+
{data.orgRows?.length
|
|
44
|
+
? <Table size='small' rowKey='key' columns={orgColumns} dataSource={data.orgRows}
|
|
45
|
+
pagination={false} expandable={{defaultExpandAllRows: true}}/>
|
|
46
|
+
: <Empty description='暂无机构数据'/>}
|
|
47
|
+
</div>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
renderFuncPerm = (data) => {
|
|
41
51
|
const menuColumns = [
|
|
42
52
|
{
|
|
43
53
|
title: '菜单名称', dataIndex: 'title', key: 'title',
|
|
@@ -60,21 +70,34 @@ export default class PermView extends React.Component {
|
|
|
60
70
|
},
|
|
61
71
|
];
|
|
62
72
|
|
|
63
|
-
return
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
:
|
|
78
|
-
|
|
73
|
+
return data.menuRows?.length
|
|
74
|
+
? <Table size='small' rowKey='key' columns={menuColumns} dataSource={data.menuRows}
|
|
75
|
+
pagination={false} expandable={{defaultExpandAllRows: true}}/>
|
|
76
|
+
: <Empty description='暂无权限数据'/>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
render() {
|
|
80
|
+
const {data} = this.state;
|
|
81
|
+
if (!data) {
|
|
82
|
+
return <Empty description='加载中...'/>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return <Tabs items={[
|
|
86
|
+
{
|
|
87
|
+
key: 'dataPerm',
|
|
88
|
+
label: '数据权限',
|
|
89
|
+
children: this.renderOrgPerm(data),
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
key: 'funcPerm',
|
|
93
|
+
label: '功能权限',
|
|
94
|
+
children: this.renderFuncPerm(data),
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
key: 'recentOps',
|
|
98
|
+
label: '最近操作',
|
|
99
|
+
children: <RecentOps/>,
|
|
100
|
+
},
|
|
101
|
+
]}/>;
|
|
79
102
|
}
|
|
80
103
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {Tag} from "antd";
|
|
3
|
+
import {HttpClient, ProTable} from "../../framework";
|
|
4
|
+
|
|
5
|
+
const columns = [
|
|
6
|
+
{
|
|
7
|
+
title: '时间', dataIndex: 'operationTime', sorter: true, width: 180,
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
title: '操作', dataIndex: 'operation',
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
title: '结果', dataIndex: 'success', width: 90,
|
|
14
|
+
render: (success) => <Tag color={success ? 'green' : 'red'}>{success ? '成功' : '失败'}</Tag>
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
title: '耗时', dataIndex: 'duration', width: 100,
|
|
18
|
+
render: (duration) => duration == null ? '-' : `${duration} ms`
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
title: 'IP', dataIndex: 'ip', width: 150,
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 个人中心"最近操作":仅展示当前登录用户的操作记录(后端按登录用户过滤)。
|
|
27
|
+
*/
|
|
28
|
+
export default function RecentOps() {
|
|
29
|
+
return <ProTable
|
|
30
|
+
rowKey='id'
|
|
31
|
+
columns={columns}
|
|
32
|
+
defaultPageSize={10}
|
|
33
|
+
emptyText='暂无操作记录(仅记录系统关键操作)'
|
|
34
|
+
request={(params) => HttpClient.get('admin/user-center/logs', params)}
|
|
35
|
+
/>
|
|
36
|
+
}
|