@jiangood/open-admin 3.0.5 → 3.0.7
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 +1 -1
- package/src/framework/biz/PermActions.tsx +122 -5
- package/src/framework/components/OrgSwitcher/index.tsx +58 -0
- package/src/framework/components/index.ts +1 -0
- package/src/layouts/admin/HeaderRight.jsx +3 -3
- package/src/layouts/admin/index.jsx +5 -1
- package/src/layouts/index.tsx +2 -1
- package/src/pages/system/article/index.jsx +10 -10
- package/src/pages/system/dict/index.jsx +30 -13
- package/src/pages/system/file/index.jsx +20 -8
- package/src/pages/system/job/index.jsx +10 -12
- package/src/pages/system/role/index.jsx +11 -67
- package/src/pages/system/user/index.jsx +12 -15
- package/src/pages/userCenter/index.jsx +99 -0
- package/src/pages/userCenter/permView.jsx +80 -0
- package/src/pages/account/changePassword.jsx +0 -65
- package/src/pages/account/index.jsx +0 -87
package/package.json
CHANGED
|
@@ -1,26 +1,143 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Space } from 'antd';
|
|
2
|
+
import { Button, Dropdown, Popconfirm, Space } from 'antd';
|
|
3
|
+
import { EllipsisOutlined } from '@ant-design/icons';
|
|
3
4
|
import { PermUtils } from '../utils';
|
|
4
5
|
|
|
5
|
-
interface
|
|
6
|
-
|
|
6
|
+
export interface PermAction {
|
|
7
|
+
/** 按钮文本 */
|
|
8
|
+
label: React.ReactNode;
|
|
9
|
+
/** 权限码,无权限时隐藏该操作(如 `sys-user:update`) */
|
|
10
|
+
perm?: string;
|
|
11
|
+
/** 点击回调(配合 confirm 时在确认后触发) */
|
|
12
|
+
onClick?: () => void;
|
|
13
|
+
/** 确认提示文案,设置后点击需二次确认 */
|
|
14
|
+
confirm?: React.ReactNode;
|
|
15
|
+
/** 危险操作样式(红色),常与 confirm 配合用于删除 */
|
|
16
|
+
danger?: boolean;
|
|
17
|
+
/** 是否禁用 */
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
/** 图标 */
|
|
20
|
+
icon?: React.ReactNode;
|
|
21
|
+
/** 按钮类型,仅非下拉模式生效 */
|
|
22
|
+
type?: 'primary' | 'dashed' | 'link' | 'text' | 'default';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface PermActionsProps {
|
|
26
|
+
/** 是否折叠为「第一个操作 + 更多下拉」,多余操作进入 `...` 菜单 */
|
|
27
|
+
more?: boolean;
|
|
28
|
+
/** 按钮尺寸 */
|
|
29
|
+
size?: 'small' | 'middle' | 'large';
|
|
30
|
+
/** 传统写法:包裹带 `perm` 属性的子元素,按 `perm` 过滤。不推荐,建议改用 `actions` 数据驱动 */
|
|
7
31
|
children?: React.ReactNode;
|
|
32
|
+
/** 数据驱动模式:操作数组,按每个 action 的 perm 过滤 */
|
|
33
|
+
actions?: PermAction[];
|
|
8
34
|
}
|
|
9
35
|
|
|
10
36
|
export class PermActions extends React.Component<PermActionsProps> {
|
|
37
|
+
static defaultProps: Partial<PermActionsProps> = {
|
|
38
|
+
more: false,
|
|
39
|
+
size: 'middle',
|
|
40
|
+
};
|
|
41
|
+
|
|
11
42
|
render() {
|
|
12
|
-
const { children,
|
|
43
|
+
const { children, more, size, actions } = this.props;
|
|
44
|
+
|
|
45
|
+
if (actions) {
|
|
46
|
+
return this.renderActions(actions, more, size);
|
|
47
|
+
}
|
|
13
48
|
|
|
14
49
|
const checkPerm = (element) => {
|
|
15
50
|
const _props = element?.props;
|
|
16
51
|
return !_props?.perm || PermUtils.hasPermission(_props.perm);
|
|
17
52
|
};
|
|
18
53
|
|
|
54
|
+
if (children != null) {
|
|
55
|
+
console.warn(
|
|
56
|
+
'[PermActions] children 写法已不推荐,请改用 actions 数据驱动模式:' +
|
|
57
|
+
'<PermActions actions={[{ label, perm, onClick }]} />'
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
19
61
|
const nodes = React.Children.toArray(children).filter(
|
|
20
62
|
(child) => child != null && checkPerm(child)
|
|
21
63
|
);
|
|
22
64
|
|
|
23
|
-
if (
|
|
65
|
+
if (nodes.length === 0) return null;
|
|
24
66
|
return <Space>{nodes}</Space>;
|
|
25
67
|
}
|
|
68
|
+
|
|
69
|
+
private renderButton(action: PermAction, size: 'small' | 'middle' | 'large') {
|
|
70
|
+
const button = (
|
|
71
|
+
<Button
|
|
72
|
+
size={size}
|
|
73
|
+
type={action.type}
|
|
74
|
+
icon={action.icon}
|
|
75
|
+
danger={action.danger}
|
|
76
|
+
disabled={action.disabled}
|
|
77
|
+
onClick={action.confirm ? undefined : action.onClick}
|
|
78
|
+
>
|
|
79
|
+
{action.label}
|
|
80
|
+
</Button>
|
|
81
|
+
);
|
|
82
|
+
if (action.confirm) {
|
|
83
|
+
return (
|
|
84
|
+
<Popconfirm title={action.confirm} onConfirm={action.onClick}>
|
|
85
|
+
{button}
|
|
86
|
+
</Popconfirm>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
return button;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private renderActions(
|
|
93
|
+
actions: PermAction[],
|
|
94
|
+
more?: boolean,
|
|
95
|
+
size: 'small' | 'middle' | 'large' = 'small'
|
|
96
|
+
) {
|
|
97
|
+
const visible = actions.filter((a) => !a.perm || PermUtils.hasPermission(a.perm));
|
|
98
|
+
if (visible.length === 0) return null;
|
|
99
|
+
|
|
100
|
+
if (!more || visible.length <= 1) {
|
|
101
|
+
const buttons = visible.map((action, index) => (
|
|
102
|
+
<React.Fragment key={index}>{this.renderButton(action, size)}</React.Fragment>
|
|
103
|
+
));
|
|
104
|
+
return <Space>{buttons}</Space>;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const [first, ...rest] = visible;
|
|
108
|
+
|
|
109
|
+
const items = rest.map((action, index) => {
|
|
110
|
+
const label = action.confirm ? (
|
|
111
|
+
<Popconfirm title={action.confirm} onConfirm={action.onClick}>
|
|
112
|
+
<span>{action.label}</span>
|
|
113
|
+
</Popconfirm>
|
|
114
|
+
) : (
|
|
115
|
+
action.label
|
|
116
|
+
);
|
|
117
|
+
return {
|
|
118
|
+
key: `more-${index}`,
|
|
119
|
+
label,
|
|
120
|
+
icon: action.icon,
|
|
121
|
+
danger: action.danger,
|
|
122
|
+
disabled: action.disabled,
|
|
123
|
+
onClick: (info) => {
|
|
124
|
+
if (action.confirm) {
|
|
125
|
+
info.domEvent.preventDefault();
|
|
126
|
+
info.domEvent.stopPropagation();
|
|
127
|
+
} else {
|
|
128
|
+
action.onClick?.();
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<Space.Compact>
|
|
136
|
+
{this.renderButton(first, size)}
|
|
137
|
+
<Dropdown menu={{ items }} placement="bottomRight">
|
|
138
|
+
<Button size={size} icon={<EllipsisOutlined/>}/>
|
|
139
|
+
</Dropdown>
|
|
140
|
+
</Space.Compact>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
26
143
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {TreeSelect} from "antd";
|
|
3
|
+
import {HttpUtils} from "../../utils";
|
|
4
|
+
|
|
5
|
+
interface OrgNode {
|
|
6
|
+
title: string;
|
|
7
|
+
key: string;
|
|
8
|
+
children?: OrgNode[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const countUnits = (nodes: OrgNode[] = []): number =>
|
|
12
|
+
nodes.reduce((sum, n) => sum + 1 + countUnits(n.children), 0);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 组织机构切换器:切换当前组织机构(单位),默认隐藏,由 Layouts 的 orgSwitcher 插槽按需渲染。
|
|
16
|
+
*/
|
|
17
|
+
export class OrgSwitcher extends React.Component<{}, {tree: OrgNode[]; currentOrgId: string | null}> {
|
|
18
|
+
state = {tree: [], currentOrgId: null};
|
|
19
|
+
|
|
20
|
+
componentDidMount() {
|
|
21
|
+
HttpUtils.get("admin/myOrgs").then(data => {
|
|
22
|
+
if (data) {
|
|
23
|
+
this.setState({tree: data.tree || [], currentOrgId: data.currentOrgId || null});
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
handleChange = (orgId: string) => {
|
|
29
|
+
HttpUtils.post("admin/switchOrg", {orgId}).then(() => {
|
|
30
|
+
setTimeout(() => location.reload(), 1000);
|
|
31
|
+
}).catch(() => {
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
render() {
|
|
36
|
+
const {tree, currentOrgId} = this.state;
|
|
37
|
+
|
|
38
|
+
if (countUnits(tree) === 0) {
|
|
39
|
+
return <span style={{marginRight: 8, color: "#999"}}>暂无组织机构</span>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (countUnits(tree) === 1) {
|
|
43
|
+
return <span style={{marginRight: 8}}>{tree[0].title}</span>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<TreeSelect
|
|
48
|
+
style={{width: 160, marginRight: 8}}
|
|
49
|
+
treeData={tree}
|
|
50
|
+
value={currentOrgId || undefined}
|
|
51
|
+
placeholder="选择组织机构"
|
|
52
|
+
onChange={this.handleChange}
|
|
53
|
+
treeDefaultExpandAll
|
|
54
|
+
showSearch
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -38,7 +38,7 @@ export class HeaderRight extends React.Component {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
account = () => {
|
|
41
|
-
PageUtils.open('/
|
|
41
|
+
PageUtils.open('/userCenter', '个人中心')
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
openArticle = (code, title) => {
|
|
@@ -61,7 +61,7 @@ export class HeaderRight extends React.Component {
|
|
|
61
61
|
}))
|
|
62
62
|
|
|
63
63
|
const menuItems = [
|
|
64
|
-
{key: '
|
|
64
|
+
{key: 'userCenter', label: '个人中心'},
|
|
65
65
|
...articleItems,
|
|
66
66
|
{key: 'logout', label: '退出登录'},
|
|
67
67
|
]
|
|
@@ -76,7 +76,7 @@ export class HeaderRight extends React.Component {
|
|
|
76
76
|
|
|
77
77
|
<Dropdown menu={{
|
|
78
78
|
onClick: ({key}) => {
|
|
79
|
-
if (key === '
|
|
79
|
+
if (key === 'userCenter') {
|
|
80
80
|
this.account()
|
|
81
81
|
} else if (key === 'logout') {
|
|
82
82
|
this.logout();
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import {Layout, Menu, Skeleton, Watermark} from 'antd';
|
|
3
3
|
|
|
4
4
|
import "./index.less"
|
|
5
|
-
import {HttpUtils, NamedIcon, PageUtils, GlobalData, TreeUtils, history, Link, ARTICLE_HEADER_LEFT} from "../../framework";
|
|
5
|
+
import {HttpUtils, NamedIcon, PageUtils, GlobalData, TreeUtils, history, Link, ARTICLE_HEADER_LEFT, OrgSwitcher} from "../../framework";
|
|
6
6
|
|
|
7
7
|
import { HeaderRight } from "./HeaderRight";
|
|
8
8
|
import { TabLayout } from "./TabLayout";
|
|
@@ -204,6 +204,10 @@ return <Layout className='main-layout'>
|
|
|
204
204
|
<div key={a.code} className='item' style={{cursor: 'pointer', padding: '0 12px', whiteSpace: 'nowrap'}}
|
|
205
205
|
onClick={() => PageUtils.open('/article/' + a.code, a.title)}>{a.title}</div>
|
|
206
206
|
))}
|
|
207
|
+
{this.props.showOrgSwitcher?.({
|
|
208
|
+
activeTopMenu: this.getActiveTopMenu(),
|
|
209
|
+
loginInfo: this.props.loginInfo,
|
|
210
|
+
}) && <OrgSwitcher/>}
|
|
207
211
|
{this.props.headerExtra?.({
|
|
208
212
|
activeTopMenu: this.getActiveTopMenu(),
|
|
209
213
|
loginInfo: this.props.loginInfo,
|
package/src/layouts/index.tsx
CHANGED
|
@@ -29,6 +29,7 @@ export interface HeaderExtraContext {
|
|
|
29
29
|
|
|
30
30
|
interface LayoutsProps {
|
|
31
31
|
headerExtra?: (context: HeaderExtraContext) => React.ReactNode;
|
|
32
|
+
showOrgSwitcher?: (context: HeaderExtraContext) => boolean;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
export class Layouts extends React.Component<LayoutsProps> {
|
|
@@ -128,7 +129,7 @@ export class Layouts extends React.Component<LayoutsProps> {
|
|
|
128
129
|
return (
|
|
129
130
|
<ErrorBoundary minimal>
|
|
130
131
|
<ConfigProvider {...configProps}>
|
|
131
|
-
{showPageFrame ? <PageFrame url={pathname + search}/> : ready ? <AdminLayout headerExtra={this.props.headerExtra} loginInfo={GlobalData.getLoginInfo()}/> : (
|
|
132
|
+
{showPageFrame ? <PageFrame url={pathname + search}/> : ready ? <AdminLayout headerExtra={this.props.headerExtra} showOrgSwitcher={this.props.showOrgSwitcher} loginInfo={GlobalData.getLoginInfo()}/> : (
|
|
132
133
|
<PageLoading messages={[
|
|
133
134
|
!siteInfoLoaded && '加载站点信息...',
|
|
134
135
|
!loginChecked && '检查登录中...',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {PlusOutlined} from '@ant-design/icons'
|
|
2
|
-
import {Button, Form, Input, InputNumber, Modal
|
|
2
|
+
import {Button, Form, Input, InputNumber, Modal} from 'antd'
|
|
3
3
|
import React from 'react'
|
|
4
4
|
import {
|
|
5
5
|
DictUtils,
|
|
@@ -85,15 +85,15 @@ export default class extends React.Component {
|
|
|
85
85
|
title: '操作',
|
|
86
86
|
dataIndex: 'option',
|
|
87
87
|
render: (_, record) => {
|
|
88
|
-
return
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
88
|
+
return <PermActions
|
|
89
|
+
more
|
|
90
|
+
size="small"
|
|
91
|
+
actions={[
|
|
92
|
+
{label: '预览', onClick: () => this.handlePreview(record)},
|
|
93
|
+
{label: '编辑', perm: 'article:update', onClick: () => this.handleEdit(record)},
|
|
94
|
+
{label: '删除', perm: 'article:delete', confirm: '确定删除?', onClick: () => this.handleDelete(record)},
|
|
95
|
+
]}
|
|
96
|
+
/>;
|
|
97
97
|
},
|
|
98
98
|
},
|
|
99
99
|
]
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import {DeleteOutlined, EditOutlined, PlusOutlined} from '@ant-design/icons';
|
|
2
|
-
import {Button, Card, Descriptions, Empty, Form, Input, InputNumber, Popconfirm, Splitter, Tree, Tag, TreeSelect, Typography} from 'antd';
|
|
2
|
+
import {AutoComplete, Button, Card, Descriptions, Empty, Form, Input, InputNumber, Popconfirm, Splitter, Tree, Tag, TreeSelect, Typography} from 'antd';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import {
|
|
5
5
|
PermActions,
|
|
6
6
|
FieldBoolean,
|
|
7
|
-
FieldDictSelect,
|
|
8
7
|
FormModal,
|
|
9
8
|
HttpUtils,
|
|
10
9
|
Page,
|
|
@@ -12,6 +11,18 @@ import {
|
|
|
12
11
|
ViewSwitch,
|
|
13
12
|
} from "../../../framework";
|
|
14
13
|
|
|
14
|
+
const COLOR_OPTIONS = [
|
|
15
|
+
{value: 'SUCCESS', label: '成功'},
|
|
16
|
+
{value: 'PROCESSING', label: '处理中'},
|
|
17
|
+
{value: 'ERROR', label: '错误'},
|
|
18
|
+
{value: 'WARNING', label: '警告'},
|
|
19
|
+
{value: 'DEFAULT', label: '默认'},
|
|
20
|
+
{value: 'RED', label: '红色'},
|
|
21
|
+
{value: 'BLUE', label: '蓝色'},
|
|
22
|
+
{value: 'GREEN', label: '绿色'},
|
|
23
|
+
{value: 'GRAY', label: '灰色'},
|
|
24
|
+
]
|
|
25
|
+
|
|
15
26
|
export default class extends React.Component {
|
|
16
27
|
|
|
17
28
|
state = {
|
|
@@ -134,16 +145,14 @@ export default class extends React.Component {
|
|
|
134
145
|
{
|
|
135
146
|
title: '操作', dataIndex: 'option',
|
|
136
147
|
render: (_, record) => {
|
|
137
|
-
return
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
</PermActions>
|
|
146
|
-
);
|
|
148
|
+
return <PermActions
|
|
149
|
+
more
|
|
150
|
+
size="small"
|
|
151
|
+
actions={[
|
|
152
|
+
{label: '编辑', perm: 'sys-dict:update', onClick: () => this.handleItemEdit(record)},
|
|
153
|
+
{label: '删除', perm: 'sys-dict:delete', confirm: '是否确定删除字典项', onClick: () => this.handleItemDelete(record)},
|
|
154
|
+
]}
|
|
155
|
+
/>;
|
|
147
156
|
},
|
|
148
157
|
},
|
|
149
158
|
]
|
|
@@ -249,7 +258,15 @@ export default class extends React.Component {
|
|
|
249
258
|
<Input/>
|
|
250
259
|
</Form.Item>
|
|
251
260
|
<Form.Item label='颜色' name='color'>
|
|
252
|
-
<
|
|
261
|
+
<AutoComplete
|
|
262
|
+
allowClear
|
|
263
|
+
maxLength={20}
|
|
264
|
+
options={COLOR_OPTIONS}
|
|
265
|
+
filterOption={(input, option) =>
|
|
266
|
+
(option.value + option.label).toLowerCase().includes(input.toLowerCase())
|
|
267
|
+
}
|
|
268
|
+
placeholder='如 SUCCESS、#ff0000'
|
|
269
|
+
/>
|
|
253
270
|
</Form.Item>
|
|
254
271
|
<Form.Item label='序号' name='seq'>
|
|
255
272
|
<InputNumber/>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import {Button, Form, Input, Modal
|
|
2
|
+
import {Button, Form, Input, Modal} from 'antd'
|
|
3
3
|
import {CloudUploadOutlined} from "@ant-design/icons";
|
|
4
4
|
|
|
5
|
-
import {PermActions, FieldDateRange, FieldDictSelect, FieldUploadFile, HttpUtils, Page, ProTable, UrlUtils} from "../../../framework";
|
|
5
|
+
import {PermActions, DictUtils, FieldDateRange, FieldDictSelect, FieldUploadFile, HttpUtils, Page, ProTable, UrlUtils} from "../../../framework";
|
|
6
6
|
|
|
7
7
|
export default class extends React.Component {
|
|
8
8
|
|
|
@@ -27,6 +27,14 @@ export default class extends React.Component {
|
|
|
27
27
|
dataIndex: 'originName',
|
|
28
28
|
width: 200,
|
|
29
29
|
},
|
|
30
|
+
{
|
|
31
|
+
title: '状态',
|
|
32
|
+
dataIndex: 'status',
|
|
33
|
+
width: 120,
|
|
34
|
+
render(status) {
|
|
35
|
+
return DictUtils.dictTag('fileStatus', status) || (status || '-')
|
|
36
|
+
},
|
|
37
|
+
},
|
|
30
38
|
{
|
|
31
39
|
title: '存储名称',
|
|
32
40
|
dataIndex: 'objectName',
|
|
@@ -66,12 +74,13 @@ export default class extends React.Component {
|
|
|
66
74
|
title: '操作',
|
|
67
75
|
dataIndex: 'option',
|
|
68
76
|
render: (_, record) => (
|
|
69
|
-
<PermActions
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
77
|
+
<PermActions
|
|
78
|
+
more
|
|
79
|
+
size="small"
|
|
80
|
+
actions={[
|
|
81
|
+
{label: '删除', perm: 'sys-file:delete', confirm: '是否确定删除文件信息', onClick: () => this.handleDelete(record)},
|
|
82
|
+
]}
|
|
83
|
+
/>
|
|
75
84
|
),
|
|
76
85
|
},
|
|
77
86
|
]
|
|
@@ -98,6 +107,9 @@ export default class extends React.Component {
|
|
|
98
107
|
<Form.Item label='对象名称' name='objectName'>
|
|
99
108
|
<Input/>
|
|
100
109
|
</Form.Item>
|
|
110
|
+
<Form.Item label='状态' name='status'>
|
|
111
|
+
<FieldDictSelect typeCode='fileStatus'/>
|
|
112
|
+
</Form.Item>
|
|
101
113
|
<Form.Item label='类型' name='type'>
|
|
102
114
|
<FieldDictSelect typeCode='materialType'/>
|
|
103
115
|
</Form.Item>
|
|
@@ -8,9 +8,7 @@ import {
|
|
|
8
8
|
Input,
|
|
9
9
|
message,
|
|
10
10
|
Modal,
|
|
11
|
-
Popconfirm,
|
|
12
11
|
Select,
|
|
13
|
-
Space,
|
|
14
12
|
Switch,
|
|
15
13
|
Tag
|
|
16
14
|
} from 'antd'
|
|
@@ -153,16 +151,16 @@ export default class extends React.Component {
|
|
|
153
151
|
fixed: 'right',
|
|
154
152
|
render: (_, record) => {
|
|
155
153
|
|
|
156
|
-
return
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
154
|
+
return <PermActions
|
|
155
|
+
more
|
|
156
|
+
size="small"
|
|
157
|
+
actions={[
|
|
158
|
+
{label: '执行记录', onClick: () => this.showExecuteRecord(record)},
|
|
159
|
+
{label: '执行一次', perm: 'job:trigger', onClick: () => this.handleTriggerJob(record)},
|
|
160
|
+
{label: '编辑', perm: 'job:update', onClick: () => this.handleEdit(record)},
|
|
161
|
+
{label: '删除', perm: 'job:delete', confirm: '是否确定删除?', onClick: () => this.handleDelete(record)},
|
|
162
|
+
]}
|
|
163
|
+
/>;
|
|
166
164
|
},
|
|
167
165
|
},
|
|
168
166
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {PlusOutlined} from '@ant-design/icons'
|
|
2
|
-
import {Button, Form, Input, InputNumber, Modal,
|
|
2
|
+
import {Button, Form, Input, InputNumber, Modal, Transfer} from 'antd'
|
|
3
3
|
import React from 'react'
|
|
4
4
|
import {FieldBoolean, FormModal, HttpUtils, Page, PageUtils, PermActions, ProTable, ViewText} from "../../../framework";
|
|
5
5
|
|
|
@@ -14,12 +14,6 @@ export default class extends React.Component {
|
|
|
14
14
|
userList: [],
|
|
15
15
|
targetKeys: [],
|
|
16
16
|
selectedKeys: [],
|
|
17
|
-
|
|
18
|
-
menuOpen: false,
|
|
19
|
-
menuTree: [],
|
|
20
|
-
menuTreeLoading: false,
|
|
21
|
-
menuChecked: [],
|
|
22
|
-
menuHalfChecked: []
|
|
23
17
|
}
|
|
24
18
|
|
|
25
19
|
modalRef = React.createRef()
|
|
@@ -112,22 +106,16 @@ export default class extends React.Component {
|
|
|
112
106
|
dataIndex: 'option',
|
|
113
107
|
render: (_, record) => {
|
|
114
108
|
|
|
115
|
-
return
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
<Popconfirm perm='sys-role:delete' title='是否确定删除系统角色'
|
|
126
|
-
onConfirm={() => this.handleDelete(record)}>
|
|
127
|
-
<Button size='small'>删除</Button>
|
|
128
|
-
</Popconfirm>
|
|
129
|
-
</PermActions>
|
|
130
|
-
);
|
|
109
|
+
return <PermActions
|
|
110
|
+
more
|
|
111
|
+
size="small"
|
|
112
|
+
actions={[
|
|
113
|
+
{label: '用户设置', perm: 'sys-role:grant-permission', onClick: () => this.handleEditUser(record)},
|
|
114
|
+
{label: '权限设置', perm: 'sys-role:grant-permission', onClick: () => PageUtils.open('/system/role/rolePerm?id=' + record.id, '角色权限设置')},
|
|
115
|
+
{label: '编辑', perm: 'sys-role:update', onClick: () => this.handleEdit(record)},
|
|
116
|
+
{label: '删除', perm: 'sys-role:delete', confirm: '是否确定删除系统角色', onClick: () => this.handleDelete(record)},
|
|
117
|
+
]}
|
|
118
|
+
/>;
|
|
131
119
|
},
|
|
132
120
|
},
|
|
133
121
|
]
|
|
@@ -145,25 +133,6 @@ export default class extends React.Component {
|
|
|
145
133
|
})
|
|
146
134
|
}
|
|
147
135
|
|
|
148
|
-
handleEditMenu = (record) => {
|
|
149
|
-
this.setState({menuOpen: true, selectedRecord: record, menuTreeLoading: true})
|
|
150
|
-
HttpUtils.get('admin/sysRole/ownMenu', {id: record.id}).then(rs => {
|
|
151
|
-
this.setState({menuChecked: rs.checked, menuHalfChecked: rs.halfChecked})
|
|
152
|
-
})
|
|
153
|
-
HttpUtils.get('admin/sysRole/menuTree').then(rs => {
|
|
154
|
-
this.setState({menuTree: rs, menuTreeLoading: false})
|
|
155
|
-
})
|
|
156
|
-
}
|
|
157
|
-
handleGrantMenu = () => {
|
|
158
|
-
const params = {
|
|
159
|
-
id: this.state.selectedRecord.id,
|
|
160
|
-
menuIds: [...this.state.menuChecked, ...this.state.menuHalfChecked]
|
|
161
|
-
}
|
|
162
|
-
HttpUtils.post('admin/sysRole/grantMenu', params).then(rs => {
|
|
163
|
-
this.setState({menuOpen: false})
|
|
164
|
-
})
|
|
165
|
-
}
|
|
166
|
-
|
|
167
136
|
render() {
|
|
168
137
|
return <Page
|
|
169
138
|
title="角色管理"
|
|
@@ -248,31 +217,6 @@ export default class extends React.Component {
|
|
|
248
217
|
|
|
249
218
|
|
|
250
219
|
</Modal>
|
|
251
|
-
|
|
252
|
-
<Modal title={'角色授权菜单权限' + "【" + this.state.selectedRecord?.name + '】'}
|
|
253
|
-
open={this.state.menuOpen}
|
|
254
|
-
destroyOnHidden
|
|
255
|
-
mask={{ closable: false }}
|
|
256
|
-
width={800}
|
|
257
|
-
onCancel={() => this.setState({menuOpen: false})}
|
|
258
|
-
onOk={this.handleGrantMenu}
|
|
259
|
-
loading={this.state.menuTreeLoading}
|
|
260
|
-
>
|
|
261
|
-
<Tree
|
|
262
|
-
height={600}
|
|
263
|
-
treeData={this.state.menuTree}
|
|
264
|
-
multiple
|
|
265
|
-
checkable
|
|
266
|
-
checkedKeys={{checked: this.state.menuChecked}}
|
|
267
|
-
onCheck={(keys, e) => {
|
|
268
|
-
this.setState({menuChecked: keys, menuHalfChecked: e.halfCheckedKeys})
|
|
269
|
-
}}
|
|
270
|
-
defaultExpandAll
|
|
271
|
-
titleRender={node => {
|
|
272
|
-
return <span title={node.perm}>{node.title}</span>
|
|
273
|
-
}}
|
|
274
|
-
/>
|
|
275
|
-
</Modal>
|
|
276
220
|
</Page>
|
|
277
221
|
|
|
278
222
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {PlusOutlined} from '@ant-design/icons';
|
|
2
|
-
import {Button, Card, Form, Input, Modal,
|
|
2
|
+
import {Button, Card, Form, Input, Modal, Select, Splitter, message} from 'antd';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import {
|
|
5
5
|
PermActions,
|
|
@@ -123,19 +123,16 @@ export default class extends React.Component {
|
|
|
123
123
|
dataIndex: 'option',
|
|
124
124
|
fixed: 'right',
|
|
125
125
|
render: (_, record) => {
|
|
126
|
-
return <PermActions
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
<Button size='small'>删除</Button>
|
|
137
|
-
</Popconfirm>
|
|
138
|
-
</PermActions>;
|
|
126
|
+
return <PermActions
|
|
127
|
+
more
|
|
128
|
+
size="small"
|
|
129
|
+
actions={[
|
|
130
|
+
{label: '编辑', perm: 'sys-user:update', onClick: () => this.handleEdit(record)},
|
|
131
|
+
{label: '授权', perm: 'sys-user:grant-permission', onClick: () => this.permRef.current.show(record)},
|
|
132
|
+
{label: '重置密码', perm: 'sys-user:reset-password', onClick: () => this.resetPwd(record)},
|
|
133
|
+
{label: '删除', perm: 'sys-user:delete', confirm: '是否确定删除用户', onClick: () => this.handleDelete(record)},
|
|
134
|
+
]}
|
|
135
|
+
/>;
|
|
139
136
|
},
|
|
140
137
|
},
|
|
141
138
|
];
|
|
@@ -160,7 +157,7 @@ export default class extends React.Component {
|
|
|
160
157
|
|
|
161
158
|
return <Page title="用户管理" description="管理系统用户">
|
|
162
159
|
<Splitter>
|
|
163
|
-
<Splitter.Panel defaultSize={
|
|
160
|
+
<Splitter.Panel defaultSize={240} style={{paddingRight: 8}}>
|
|
164
161
|
<Card size='small'>
|
|
165
162
|
<OrgTree onChange={this.onSelectOrg}/>
|
|
166
163
|
</Card>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {Button, Card, Descriptions, Form, Input, Modal} from "antd";
|
|
3
|
+
import PermView from "./permView";
|
|
4
|
+
import {HttpUtils, history, Page} from "../../framework";
|
|
5
|
+
|
|
6
|
+
export default class extends React.Component {
|
|
7
|
+
|
|
8
|
+
formRef = React.createRef();
|
|
9
|
+
|
|
10
|
+
state = {
|
|
11
|
+
info: {},
|
|
12
|
+
changePwdOpen: false,
|
|
13
|
+
changePwdSuccess: false,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
componentDidMount() {
|
|
17
|
+
HttpUtils.get('admin/userCenter/info').then(rs => {
|
|
18
|
+
this.setState({info: rs})
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
onPwdFinish = (values) => {
|
|
23
|
+
HttpUtils.post('admin/userCenter/update-pwd', values).then(() => {
|
|
24
|
+
this.setState({changePwdOpen: false, changePwdSuccess: true});
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
pwdValidator = (rule, value) => {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
HttpUtils.get("admin/sysUser/pwd-strength", {password: value}, {showError: false}).then(response => {
|
|
31
|
+
const rs = response.data
|
|
32
|
+
if (!rs.success) {
|
|
33
|
+
reject(rs.message)
|
|
34
|
+
}
|
|
35
|
+
resolve()
|
|
36
|
+
})
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
render() {
|
|
41
|
+
const {info, changePwdOpen, changePwdSuccess} = this.state;
|
|
42
|
+
return <Page backgroundGray>
|
|
43
|
+
|
|
44
|
+
<Card title="个人信息"
|
|
45
|
+
style={{marginBottom: 16}}
|
|
46
|
+
extra={<Button onClick={() => this.setState({changePwdOpen: true})}>修改密码</Button>}>
|
|
47
|
+
<Descriptions column={2} size="middle">
|
|
48
|
+
<Descriptions.Item label="用户名称">{info.name}</Descriptions.Item>
|
|
49
|
+
<Descriptions.Item label="账号">{info.account}</Descriptions.Item>
|
|
50
|
+
<Descriptions.Item label="手机号码">{info.phone}</Descriptions.Item>
|
|
51
|
+
<Descriptions.Item label="用户邮箱">{info.email}</Descriptions.Item>
|
|
52
|
+
<Descriptions.Item label="所属单位">{info.unit}</Descriptions.Item>
|
|
53
|
+
<Descriptions.Item label="所属机构">{info.org}</Descriptions.Item>
|
|
54
|
+
<Descriptions.Item label="所属角色">{(info.roles || []).join('、') || '-'}</Descriptions.Item>
|
|
55
|
+
<Descriptions.Item label="创建日期">{info.createTime}</Descriptions.Item>
|
|
56
|
+
</Descriptions>
|
|
57
|
+
</Card>
|
|
58
|
+
|
|
59
|
+
<Card title="我的权限">
|
|
60
|
+
<PermView/>
|
|
61
|
+
</Card>
|
|
62
|
+
|
|
63
|
+
<Modal open={changePwdOpen} title="修改密码" okText="确定" cancelText="取消"
|
|
64
|
+
onCancel={() => this.setState({changePwdOpen: false})}
|
|
65
|
+
onOk={() => this.formRef?.submit()}>
|
|
66
|
+
<Form ref={this.formRef} onFinish={this.onPwdFinish} style={{maxWidth: 400}}>
|
|
67
|
+
<Form.Item name='oldPassword'
|
|
68
|
+
label='原密码'
|
|
69
|
+
rules={[{required: true, message: '请输入原密码'}]}
|
|
70
|
+
>
|
|
71
|
+
<Input.Password></Input.Password>
|
|
72
|
+
</Form.Item>
|
|
73
|
+
<Form.Item name='newPassword'
|
|
74
|
+
label='新密码'
|
|
75
|
+
extra={'请输入字母、数字、特殊字符'}
|
|
76
|
+
rules={[
|
|
77
|
+
{required: true, message: '请输入新密码'},
|
|
78
|
+
{
|
|
79
|
+
validator: this.pwdValidator
|
|
80
|
+
}
|
|
81
|
+
]}
|
|
82
|
+
>
|
|
83
|
+
<Input.Password></Input.Password>
|
|
84
|
+
</Form.Item>
|
|
85
|
+
</Form>
|
|
86
|
+
</Modal>
|
|
87
|
+
|
|
88
|
+
<Modal open={changePwdSuccess} title="提示" okText="确定"
|
|
89
|
+
onCancel={() => this.setState({changePwdSuccess: false})}
|
|
90
|
+
onOk={() => {
|
|
91
|
+
this.setState({changePwdSuccess: false});
|
|
92
|
+
history.push('/public/login');
|
|
93
|
+
}}>
|
|
94
|
+
修改密码成功
|
|
95
|
+
</Modal>
|
|
96
|
+
|
|
97
|
+
</Page>
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {Empty, Table, Tag} from "antd";
|
|
3
|
+
import {HttpUtils, Gap} from "../../framework";
|
|
4
|
+
|
|
5
|
+
export default class PermView extends React.Component {
|
|
6
|
+
|
|
7
|
+
state = {
|
|
8
|
+
data: null,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
componentDidMount() {
|
|
12
|
+
HttpUtils.get('admin/userCenter/perms').then(rs => {
|
|
13
|
+
this.setState({data: rs})
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
render() {
|
|
18
|
+
const {data} = this.state;
|
|
19
|
+
if (!data) {
|
|
20
|
+
return <Empty description='加载中...'/>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const orgColumns = [
|
|
24
|
+
{
|
|
25
|
+
title: '机构名称', dataIndex: 'title', key: 'title',
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
title: '状态', dataIndex: 'status', key: 'status', width: 140,
|
|
29
|
+
render: (status) => {
|
|
30
|
+
if (status === 'mine') {
|
|
31
|
+
return <Tag color='blue'>我的机构</Tag>;
|
|
32
|
+
}
|
|
33
|
+
if (status === 'owned') {
|
|
34
|
+
return <Tag color='green'>已授权</Tag>;
|
|
35
|
+
}
|
|
36
|
+
return '-';
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const menuColumns = [
|
|
42
|
+
{
|
|
43
|
+
title: '菜单名称', dataIndex: 'title', key: 'title',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
title: '权限名称', dataIndex: 'perms', key: 'perms',
|
|
47
|
+
render: (perms) => (perms || []).join('、') || '-',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
title: '状态', dataIndex: 'status', key: 'status', width: 140,
|
|
51
|
+
render: (status) => {
|
|
52
|
+
if (status === 'all') {
|
|
53
|
+
return <Tag color='green'>已授权</Tag>;
|
|
54
|
+
}
|
|
55
|
+
if (status === 'partial') {
|
|
56
|
+
return <Tag color='orange'>部分授权</Tag>;
|
|
57
|
+
}
|
|
58
|
+
return '-';
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
return <div>
|
|
64
|
+
<div style={{marginBottom: 16}}>
|
|
65
|
+
<span>数据权限:
|
|
66
|
+
<Tag color='processing'>{data.dataPermLabel || '-'}</Tag>
|
|
67
|
+
</span>
|
|
68
|
+
</div>
|
|
69
|
+
{data.orgRows && data.orgRows.length
|
|
70
|
+
? <Table size='small' rowKey='key' columns={orgColumns} dataSource={data.orgRows}
|
|
71
|
+
pagination={false} expandable={{defaultExpandAllRows: true}}/>
|
|
72
|
+
: <Empty description='暂无机构数据'/>}
|
|
73
|
+
<Gap/>
|
|
74
|
+
{data.menuRows && data.menuRows.length
|
|
75
|
+
? <Table size='small' rowKey='key' columns={menuColumns} dataSource={data.menuRows}
|
|
76
|
+
pagination={false} expandable={{defaultExpandAllRows: true}}/>
|
|
77
|
+
: <Empty description='暂无权限数据'/>}
|
|
78
|
+
</div>;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import {Button, Form, Input, Modal} from "antd";
|
|
3
|
-
import {HttpUtils, history} from "../../framework";
|
|
4
|
-
|
|
5
|
-
export default class extends React.Component {
|
|
6
|
-
|
|
7
|
-
state = {
|
|
8
|
-
successModalOpen: false,
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
onFinish = (values) => {
|
|
12
|
-
HttpUtils.post('admin/userCenter/update-pwd', values).then(() => {
|
|
13
|
-
this.setState({successModalOpen: true});
|
|
14
|
-
})
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
validator = (rule, value) => {
|
|
18
|
-
return new Promise((resolve, reject) => {
|
|
19
|
-
HttpUtils.get("admin/sysUser/pwd-strength", {password: value}, {showError: false}).then(response => {
|
|
20
|
-
const rs = response.data
|
|
21
|
-
if (!rs.success) {
|
|
22
|
-
reject(rs.message)
|
|
23
|
-
}
|
|
24
|
-
resolve()
|
|
25
|
-
})
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
render() {
|
|
31
|
-
return <div>
|
|
32
|
-
<Modal open={this.state.successModalOpen} title="提示" okText="确定"
|
|
33
|
-
onCancel={() => this.setState({successModalOpen: false})}
|
|
34
|
-
onOk={() => {
|
|
35
|
-
this.setState({successModalOpen: false});
|
|
36
|
-
history.push('/public/login');
|
|
37
|
-
}}>
|
|
38
|
-
修改密码成功
|
|
39
|
-
</Modal>
|
|
40
|
-
|
|
41
|
-
<Form onFinish={this.onFinish} style={{maxWidth: 400}}>
|
|
42
|
-
|
|
43
|
-
<Form.Item name='newPassword'
|
|
44
|
-
label='新密码'
|
|
45
|
-
extra={'请输入字母、数字、特殊字符'}
|
|
46
|
-
rules={[
|
|
47
|
-
{required: true},
|
|
48
|
-
{
|
|
49
|
-
validator: this.validator
|
|
50
|
-
}
|
|
51
|
-
]}
|
|
52
|
-
>
|
|
53
|
-
<Input.Password></Input.Password>
|
|
54
|
-
</Form.Item>
|
|
55
|
-
|
|
56
|
-
<Form.Item wrapperCol={{offset: 5}} style={{marginTop: 40}}>
|
|
57
|
-
<Button type="primary" htmlType="submit">
|
|
58
|
-
确定
|
|
59
|
-
</Button>
|
|
60
|
-
</Form.Item>
|
|
61
|
-
</Form>
|
|
62
|
-
|
|
63
|
-
</div>
|
|
64
|
-
}
|
|
65
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import React from "react";
|
|
2
|
-
import {Avatar, Card, Col, Row, Tabs} from "antd";
|
|
3
|
-
import ChangePassword from "./changePassword";
|
|
4
|
-
import {HttpUtils, Page} from "../../framework";
|
|
5
|
-
|
|
6
|
-
export default class extends React.Component {
|
|
7
|
-
|
|
8
|
-
state = {
|
|
9
|
-
info: {}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
componentDidMount() {
|
|
13
|
-
HttpUtils.get('admin/userCenter/info').then(rs => {
|
|
14
|
-
this.setState({info: rs})
|
|
15
|
-
})
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
render() {
|
|
19
|
-
const {info} = this.state;
|
|
20
|
-
return <Page backgroundGray title="个人中心" description="查看和编辑个人资料">
|
|
21
|
-
|
|
22
|
-
<Row gutter={[16, 16]}>
|
|
23
|
-
<Col md={6} sm={24}>
|
|
24
|
-
<Card title='个人信息'>
|
|
25
|
-
|
|
26
|
-
<div className='flex-center' style={{marginBottom: 32}}>
|
|
27
|
-
<Avatar size={128} title='点击修改头像'/>
|
|
28
|
-
</div>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<table className='oa-table'>
|
|
32
|
-
<tbody>
|
|
33
|
-
<tr>
|
|
34
|
-
<td>用户名称</td>
|
|
35
|
-
<td align='right'>{info.name}</td>
|
|
36
|
-
</tr>
|
|
37
|
-
<tr>
|
|
38
|
-
<td>账号</td>
|
|
39
|
-
<td align='right'>{info.account}</td>
|
|
40
|
-
</tr>
|
|
41
|
-
<tr>
|
|
42
|
-
<td>手机号码</td>
|
|
43
|
-
<td>{info.phone}</td>
|
|
44
|
-
</tr>
|
|
45
|
-
<tr>
|
|
46
|
-
<td>用户邮箱</td>
|
|
47
|
-
<td>{info.email}</td>
|
|
48
|
-
</tr>
|
|
49
|
-
<tr>
|
|
50
|
-
<td>所属部门</td>
|
|
51
|
-
<td>{info.unit} {info.dept}</td>
|
|
52
|
-
</tr>
|
|
53
|
-
<tr>
|
|
54
|
-
<td>所属角色</td>
|
|
55
|
-
<td>{info.roles}</td>
|
|
56
|
-
</tr>
|
|
57
|
-
<tr>
|
|
58
|
-
<td>创建日期</td>
|
|
59
|
-
<td>{info.createTime}</td>
|
|
60
|
-
</tr>
|
|
61
|
-
</tbody>
|
|
62
|
-
</table>
|
|
63
|
-
</Card>
|
|
64
|
-
</Col>
|
|
65
|
-
<Col md={18} sm={24}>
|
|
66
|
-
<Card title='个人设置'>
|
|
67
|
-
<Tabs
|
|
68
|
-
items={[
|
|
69
|
-
{
|
|
70
|
-
label: '修改密码', key: 'pwd', children: <div>
|
|
71
|
-
<ChangePassword/>
|
|
72
|
-
</div>
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
]}
|
|
76
|
-
tabPlacement='left'
|
|
77
|
-
>
|
|
78
|
-
</Tabs>
|
|
79
|
-
</Card>
|
|
80
|
-
</Col>
|
|
81
|
-
</Row>
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
</Page>
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
}
|