@jiangood/open-admin 3.0.5 → 3.0.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.
package/package.json CHANGED
@@ -48,5 +48,5 @@
48
48
  "test:e2e": "playwright test",
49
49
  "test:e2e:ui": "playwright test --ui"
50
50
  },
51
- "version": "3.0.5"
51
+ "version": "3.0.6"
52
52
  }
@@ -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 PermActionsProps {
6
- gap?: boolean;
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: 'small',
40
+ };
41
+
11
42
  render() {
12
- const { children, gap = true } = this.props;
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 (!gap) return <>{nodes}</>;
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
+ }
@@ -7,5 +7,6 @@ export * from './Page'
7
7
  export * from './ValueType'
8
8
  export * from './ContextMenu'
9
9
  export * from './DownloadModal'
10
+ export * from './OrgSwitcher'
10
11
  export { default as ErrorBoundary } from './ErrorBoundary'
11
12
  export { default as FormModal } from './FormModal'
@@ -38,7 +38,7 @@ export class HeaderRight extends React.Component {
38
38
  }
39
39
 
40
40
  account = () => {
41
- PageUtils.open('/account', '个人中心')
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: 'account', label: '个人中心'},
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 === 'account') {
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,
@@ -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, Popconfirm} from 'antd'
2
+ import {Button, Form, Input, InputNumber, Modal} from 'antd'
3
3
  import React from 'react'
4
4
  import {
5
5
  DictUtils,
@@ -85,15 +85,14 @@ export default class extends React.Component {
85
85
  title: '操作',
86
86
  dataIndex: 'option',
87
87
  render: (_, record) => {
88
- return (
89
- <PermActions>
90
- <Button size='small' onClick={() => this.handlePreview(record)}>预览</Button>
91
- <Button size='small' perm='article:update' onClick={() => this.handleEdit(record)}>编辑</Button>
92
- <Popconfirm perm='article:delete' title='确定删除?' onConfirm={() => this.handleDelete(record)}>
93
- <Button size='small'>删除</Button>
94
- </Popconfirm>
95
- </PermActions>
96
- );
88
+ return <PermActions
89
+ more
90
+ actions={[
91
+ {label: '预览', onClick: () => this.handlePreview(record)},
92
+ {label: '编辑', perm: 'article:update', onClick: () => this.handleEdit(record)},
93
+ {label: '删除', perm: 'article:delete', confirm: '确定删除?', onClick: () => this.handleDelete(record)},
94
+ ]}
95
+ />;
97
96
  },
98
97
  },
99
98
  ]
@@ -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,13 @@ export default class extends React.Component {
134
145
  {
135
146
  title: '操作', dataIndex: 'option',
136
147
  render: (_, record) => {
137
- return (
138
- <PermActions>
139
- <Button size='small' perm='sys-dict:update'
140
- onClick={() => this.handleItemEdit(record)}>编辑</Button>
141
- <Popconfirm perm='sys-dict:delete' title='是否确定删除字典项'
142
- onConfirm={() => this.handleItemDelete(record)}>
143
- <Button size='small'>删除</Button>
144
- </Popconfirm>
145
- </PermActions>
146
- );
148
+ return <PermActions
149
+ more
150
+ actions={[
151
+ {label: '编辑', perm: 'sys-dict:update', onClick: () => this.handleItemEdit(record)},
152
+ {label: '删除', perm: 'sys-dict:delete', confirm: '是否确定删除字典项', onClick: () => this.handleItemDelete(record)},
153
+ ]}
154
+ />;
147
155
  },
148
156
  },
149
157
  ]
@@ -249,7 +257,15 @@ export default class extends React.Component {
249
257
  <Input/>
250
258
  </Form.Item>
251
259
  <Form.Item label='颜色' name='color'>
252
- <FieldDictSelect typeCode='statusColor'/>
260
+ <AutoComplete
261
+ allowClear
262
+ maxLength={20}
263
+ options={COLOR_OPTIONS}
264
+ filterOption={(input, option) =>
265
+ (option.value + option.label).toLowerCase().includes(input.toLowerCase())
266
+ }
267
+ placeholder='如 SUCCESS、#ff0000'
268
+ />
253
269
  </Form.Item>
254
270
  <Form.Item label='序号' name='seq'>
255
271
  <InputNumber/>
@@ -1,8 +1,8 @@
1
1
  import React from 'react'
2
- import {Button, Form, Input, Modal, Popconfirm} from 'antd'
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,12 @@ export default class extends React.Component {
66
74
  title: '操作',
67
75
  dataIndex: 'option',
68
76
  render: (_, record) => (
69
- <PermActions>
70
- <Popconfirm perm='sys-file:delete' title='是否确定删除文件信息'
71
- onConfirm={() => this.handleDelete(record)}>
72
- <a>删除</a>
73
- </Popconfirm>
74
- </PermActions>
77
+ <PermActions
78
+ more
79
+ actions={[
80
+ {label: '删除', perm: 'sys-file:delete', confirm: '是否确定删除文件信息', onClick: () => this.handleDelete(record)},
81
+ ]}
82
+ />
75
83
  ),
76
84
  },
77
85
  ]
@@ -98,6 +106,9 @@ export default class extends React.Component {
98
106
  <Form.Item label='对象名称' name='objectName'>
99
107
  <Input/>
100
108
  </Form.Item>
109
+ <Form.Item label='状态' name='status'>
110
+ <FieldDictSelect typeCode='fileStatus'/>
111
+ </Form.Item>
101
112
  <Form.Item label='类型' name='type'>
102
113
  <FieldDictSelect typeCode='materialType'/>
103
114
  </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,15 @@ export default class extends React.Component {
153
151
  fixed: 'right',
154
152
  render: (_, record) => {
155
153
 
156
- return (
157
- <Space>
158
- <Button size='small' onClick={() => this.showExecuteRecord(record)}>执行记录</Button>
159
- <Button size='small' perm='job:trigger' onClick={() => this.handleTriggerJob(record)}>执行一次</Button>
160
- <Button size='small' perm='job:update' onClick={() => this.handleEdit(record)}> 编辑 </Button>
161
- <Popconfirm perm='job:delete' title='是否确定删除?' onConfirm={() => this.handleDelete(record)}>
162
- <Button size='small'>删除</Button>
163
- </Popconfirm>
164
- </Space>
165
- );
154
+ return <PermActions
155
+ more
156
+ actions={[
157
+ {label: '执行记录', onClick: () => this.showExecuteRecord(record)},
158
+ {label: '执行一次', perm: 'job:trigger', onClick: () => this.handleTriggerJob(record)},
159
+ {label: '编辑', perm: 'job:update', onClick: () => this.handleEdit(record)},
160
+ {label: '删除', perm: 'job:delete', confirm: '是否确定删除?', onClick: () => this.handleDelete(record)},
161
+ ]}
162
+ />;
166
163
  },
167
164
  },
168
165
 
@@ -1,5 +1,5 @@
1
1
  import {PlusOutlined} from '@ant-design/icons'
2
- import {Button, Form, Input, InputNumber, Modal, Popconfirm, Select, Transfer, Tree} from 'antd'
2
+ import {Button, Form, Input, InputNumber, Modal, Select, Transfer, Tree} from 'antd'
3
3
  import React from 'react'
4
4
  import {FieldBoolean, FormModal, HttpUtils, Page, PageUtils, PermActions, ProTable, ViewText} from "../../../framework";
5
5
 
@@ -112,22 +112,15 @@ export default class extends React.Component {
112
112
  dataIndex: 'option',
113
113
  render: (_, record) => {
114
114
 
115
- return (
116
- <PermActions>
117
- <Button size='small' perm='sys-role:grant-permission'
118
- onClick={() => this.handleEditUser(record)}>用户设置</Button>
119
-
120
- <Button size='small' perm='sys-role:grant-permission'
121
- onClick={() => PageUtils.open('/system/role/rolePerm?id=' + record.id, '角色权限设置')}>权限设置</Button>
122
-
123
- <Button size='small' perm='sys-role:update'
124
- onClick={() => this.handleEdit(record)}>编辑</Button>
125
- <Popconfirm perm='sys-role:delete' title='是否确定删除系统角色'
126
- onConfirm={() => this.handleDelete(record)}>
127
- <Button size='small'>删除</Button>
128
- </Popconfirm>
129
- </PermActions>
130
- );
115
+ return <PermActions
116
+ more
117
+ actions={[
118
+ {label: '用户设置', perm: 'sys-role:grant-permission', onClick: () => this.handleEditUser(record)},
119
+ {label: '权限设置', perm: 'sys-role:grant-permission', onClick: () => PageUtils.open('/system/role/rolePerm?id=' + record.id, '角色权限设置')},
120
+ {label: '编辑', perm: 'sys-role:update', onClick: () => this.handleEdit(record)},
121
+ {label: '删除', perm: 'sys-role:delete', confirm: '是否确定删除系统角色', onClick: () => this.handleDelete(record)},
122
+ ]}
123
+ />;
131
124
  },
132
125
  },
133
126
  ]
@@ -1,5 +1,5 @@
1
1
  import {PlusOutlined} from '@ant-design/icons';
2
- import {Button, Card, Form, Input, Modal, Popconfirm, Select, Splitter, message} from 'antd';
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,15 @@ export default class extends React.Component {
123
123
  dataIndex: 'option',
124
124
  fixed: 'right',
125
125
  render: (_, record) => {
126
- return <PermActions>
127
- <Button size='small' perm='sys-user:update' onClick={() => this.handleEdit(record)}> 编辑 </Button>
128
-
129
- <Button size='small' perm='sys-user:grant-permission'
130
- onClick={() => this.permRef.current.show(record)}> 授权 </Button>
131
-
132
- <Button size='small' perm='sys-user:reset-password' onClick={() => this.resetPwd(record)}>重置密码</Button>
133
-
134
- <Popconfirm perm='sys-user:delete' title={'是否确定删除用户'}
135
- onConfirm={() => this.handleDelete(record)}>
136
- <Button size='small'>删除</Button>
137
- </Popconfirm>
138
- </PermActions>;
126
+ return <PermActions
127
+ more
128
+ actions={[
129
+ {label: '编辑', perm: 'sys-user:update', onClick: () => this.handleEdit(record)},
130
+ {label: '授权', perm: 'sys-user:grant-permission', onClick: () => this.permRef.current.show(record)},
131
+ {label: '重置密码', perm: 'sys-user:reset-password', onClick: () => this.resetPwd(record)},
132
+ {label: '删除', perm: 'sys-user:delete', confirm: '是否确定删除用户', onClick: () => this.handleDelete(record)},
133
+ ]}
134
+ />;
139
135
  },
140
136
  },
141
137
  ];
@@ -160,7 +156,7 @@ export default class extends React.Component {
160
156
 
161
157
  return <Page title="用户管理" description="管理系统用户">
162
158
  <Splitter>
163
- <Splitter.Panel defaultSize={350} style={{paddingRight: 8}}>
159
+ <Splitter.Panel defaultSize={240} style={{paddingRight: 8}}>
164
160
  <Card size='small'>
165
161
  <OrgTree onChange={this.onSelectOrg}/>
166
162
  </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
- }