@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 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.7"
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: 'middle',
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,15 @@ 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
+ 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
- <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
+ 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
- <FieldDictSelect typeCode='statusColor'/>
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, 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,13 @@ 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
+ 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
- <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
+ 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, Popconfirm, Select, Transfer, Tree} from 'antd'
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
- <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
- );
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, 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,16 @@ 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
+ 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={350} style={{paddingRight: 8}}>
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
- }