Yansen 2 jaren geleden
bovenliggende
commit
4991b80797

+ 17
- 2
src/pages/roles/index.jsx Bestand weergeven

@@ -1,7 +1,22 @@
1
-import React from 'react'
1
+import React from 'react';
2
+import { Row, Col } from 'antd';
3
+import RoleList from './list';
4
+import MenuList from './menus';
2 5
 
3 6
 export default (props) => {
7
+
8
+  const [current, setCurrent] = React.useState();
9
+
4 10
   return (
5
-    <div></div>
11
+    <div>
12
+      <Row gutter={24}>
13
+        <Col span={8}>
14
+          <RoleList onChange={setCurrent} current={current} />
15
+        </Col>
16
+        <Col span={16}>
17
+          <MenuList role={current} />
18
+        </Col>
19
+      </Row>
20
+    </div>
6 21
   )
7 22
 }

+ 107
- 20
src/pages/roles/list.jsx Bestand weergeven

@@ -1,28 +1,117 @@
1 1
 import React from 'react';
2
-import { Button, Input, List, Popconfirm, Tree, Space, Modal } from 'antd';
2
+import { Button, Input, List, Popconfirm, Spin, Modal, message } from 'antd';
3
+import useBool from '@/utils/hooks/useBool';
4
+import classNames from 'classnames';
5
+import {
6
+  getRoleList,
7
+  getRoleDetail,
8
+  saveRole,
9
+  updateRole,
10
+  deleteRole,
11
+} from '@/services/role';
12
+import './style.less';
13
+
14
+const Header = (props) => {
15
+  return (
16
+    <div className='role-list-header'>
17
+      <div className='role-list-header-body'>{props.children}</div>
18
+      <div className='role-list-header-action'>{props.action}</div>
19
+    </div>
20
+  )
21
+}
3 22
 
4 23
 export default (props) => {
5
-  const data = [
6
-    'Racing car sprays burning fuel into crowd.',
7
-    'Japanese princess to wed commoner.',
8
-    'Australian walks 100km after outback crash.',
9
-    'Man charged over missing wedding girl.',
10
-    'Los Angeles battles huge wildfires.',
11
-  ];
12
-
13
-  const [visible, setVisible] = React.useState(false);
24
+  const { current, onChange } = props;
25
+
26
+  const [loading, startLoading, cancelLoading] = useBool();
27
+  const [visible, openModel, hideModel] = useBool();
14 28
   const [inText, setInText] = React.useState();
29
+  const [list, setList] = React.useState([]);
30
+
31
+  const onAdd = () => {
32
+    openModel();
33
+    setInText();
34
+    onChange();
35
+  }
36
+
37
+  const onEdit = (item) => {
38
+    openModel();
39
+    setInText(item.name);
40
+    onChange(item);
41
+  }
42
+
43
+  const onDelete = (item) => {
44
+    startLoading();
45
+    // 删除角色
46
+    deleteRole(item.id).then(() => {
47
+      setList(list.filter(x => x.id !== item.id));
48
+      cancelLoading();
49
+      hideModel();
50
+      onChange();
51
+    }).catch(() => {
52
+      cancelLoading();
53
+    })
54
+  }
55
+
56
+  const handleOk = () => {
57
+    if (!inText) {
58
+      message.warn('请输入角色名称');
59
+      return;
60
+    }
61
+    startLoading();
62
+    const data = { name: inText };
63
+
64
+    if (currentRef.current) {
65
+      // 修改角色
66
+      updateRole(currentRef.current.id, data).then(res => {
67
+        setList(list.map(x => x.id === currentRef.current.id ? res : x));
68
+        cancelLoading();
69
+        hideModel();
70
+      }).catch(() => {
71
+        cancelLoading();
72
+      })
73
+    } else {
74
+      // 新增角色
75
+      saveRole(data).then((res) => {
76
+        setList([res].concat(list));
77
+        cancelLoading();
78
+        hideModel();
79
+        onChange(res);
80
+      }).catch(() => {
81
+        cancelLoading();
82
+      })
83
+    }
84
+  }
85
+
86
+  const handleCancel = () => {
87
+    if (loading) {
88
+      return ;
89
+    }
90
+
91
+    hideModel();
92
+  }
93
+
94
+  React.useEffect(() => {
95
+    startLoading();
96
+    getRoleList({ pageSize: 100 }).then((res) => {
97
+      setList(res.records || []);
98
+      cancelLoading();
99
+    }).catch(() => {
100
+      cancelLoading();
101
+    });
102
+  }, []);
15 103
 
16 104
   return (
17
-    <>
105
+    <Spin spinning={loading}>
18 106
       <List
19
-        header='角色列表'
107
+        header={<Header action={<Button type='primary' onClick={onAdd}>新增</Button>}>角色列表</Header>}
20 108
         style={{ background: '#fff' }}
21 109
         bordered
22
-        dataSource={data}
110
+        dataSource={list}
23 111
         renderItem={item => (
24 112
           <List.Item
25
-            style={{ width: '100%' }}
113
+            className={classNames('role-list-item', { active: current && current.id === item.id })}
114
+            onClick={() => onChange(item)}
26 115
             actions={[
27 116
               <Button key='edit' type='link' onClick={() => onEdit(item)}>编辑</Button>,
28 117
               <Popconfirm key='delete' title="确认删除?" onConfirm={() => onDelete(item)}>
@@ -30,20 +119,18 @@ export default (props) => {
30 119
               </Popconfirm>
31 120
             ]}
32 121
           >
33
-            <div onClick={() => props.onChange(item)}>
34
-              {item}
35
-            </div>
122
+            {item.name}
36 123
           </List.Item>
37 124
         )}
38 125
       />
39 126
       <Modal
40
-        title="Basic Modal"
127
+        title="请输入角色名称"
41 128
         open={visible}
42 129
         onOk={handleOk}
43
-        onCancel={() => setVisible(false)}
130
+        onCancel={handleCancel}
44 131
       >
45 132
         <Input value={inText} onChange={e => setInText(e.target.value)} />
46 133
       </Modal>
47
-    </>
134
+    </Spin>
48 135
   )
49 136
 }

+ 49
- 0
src/pages/roles/menus.jsx Bestand weergeven

@@ -0,0 +1,49 @@
1
+import React from 'react';
2
+import { Button, Card, Tree, Modal } from 'antd';
3
+import useBool from '@/utils/hooks/useBool';
4
+import arr2Tree from '@/utils/arr2Tree';
5
+
6
+export default (props) => {
7
+  const { role } = props;
8
+  
9
+  const [loading, startLoading, cancelLoading] = useBool();
10
+  const [list, setList] = React.useState([]);
11
+  const [treeData, setTreeData] = React.useState([]);
12
+
13
+  const title = role ? `${role.name} - 授权菜单` : '授权菜单';
14
+
15
+  const onSelect = (selectedKeys, info) => {
16
+    console.log('selected', selectedKeys, info);
17
+  };
18
+  const onCheck = (checkedKeys, info) => {
19
+    console.log('onCheck', checkedKeys, info);
20
+  };
21
+
22
+  React.useEffect(() => {
23
+    if (!role) {
24
+      setList([]);
25
+    }
26
+  }, [role]);
27
+  
28
+  React.useEffect(() => {
29
+    // 先转为需要的格式
30
+    const arr = list.map(x => ({ title: x.name, key: x.id, parentId: x.parentId }));
31
+    // 再转为 tree
32
+    setTreeData(arr2Tree(arr));
33
+  }, [list]);
34
+
35
+  return (
36
+    <Card
37
+      loading={loading}
38
+      title={title}
39
+      extra={<Button type='primary' ghost>保存</Button>}
40
+    >
41
+      <Tree
42
+        checkable
43
+        onSelect={onSelect}
44
+        onCheck={onCheck}
45
+        treeData={treeData}
46
+      />
47
+    </Card>
48
+  )
49
+}

+ 19
- 0
src/pages/roles/style.less Bestand weergeven

@@ -0,0 +1,19 @@
1
+
2
+.role-list-header {
3
+  width: 100%;
4
+  display: flex;
5
+
6
+  .role-list-header-body {
7
+    flex: 1;
8
+  }
9
+
10
+  .role-list-header-action {
11
+    flex: 0
12
+  }
13
+}
14
+
15
+.role-list-item {
16
+  &.active {
17
+    background: #e6f7ff;
18
+  }
19
+}

+ 2
- 28
src/pages/user/Role.jsx Bestand weergeven

@@ -1,14 +1,8 @@
1 1
 import React from 'react';
2 2
 import { Button, Row, Col, Card, List, Popconfirm, Tree, Space, Modal } from 'antd';
3
+import List from './list';
3 4
 
4 5
 export default (props) => {
5
-  const data = [
6
-    'Racing car sprays burning fuel into crowd.',
7
-    'Japanese princess to wed commoner.',
8
-    'Australian walks 100km after outback crash.',
9
-    'Man charged over missing wedding girl.',
10
-    'Los Angeles battles huge wildfires.',
11
-  ];
12 6
 
13 7
   const treeData = [
14 8
     {
@@ -64,27 +58,6 @@ export default (props) => {
64 58
     <div>
65 59
       <Row gutter={24}>
66 60
         <Col span={8}>
67
-          <List
68
-            header='角色列表'
69
-            style={{ background: '#fff' }}
70
-            bordered
71
-            dataSource={data}
72
-            renderItem={item => (
73
-              <List.Item
74
-                style={{ width: '100%' }}
75
-                actions={[
76
-                  <Button key='edit' type='link' onClick={() => onEdit(item)}>编辑</Button>,
77
-                  <Popconfirm key='delete' title="确认删除?" onConfirm={() => onDelete(item)}>
78
-                    <Button type='link' danger>删除</Button>
79
-                  </Popconfirm>
80
-                ]}
81
-              >
82
-                <div onClick={() => setCurrent(item)}>
83
-                  {item}
84
-                </div>
85
-              </List.Item>
86
-            )}
87
-          />
88 61
         </Col>
89 62
         <Col span={16}>
90 63
           <Card title='授权菜单' extra={<Button type='primary' ghost>保存</Button>}>
@@ -100,6 +73,7 @@ export default (props) => {
100 73
           </Card>
101 74
         </Col>
102 75
       </Row>
76
+
103 77
     </div>
104 78
   )
105 79
 }

+ 1
- 1
src/routes/routes.jsx Bestand weergeven

@@ -29,7 +29,7 @@ import StockClassificationList from '@/pages/stockClassification/list';
29 29
 import StockClassificationEdit from '@/pages/stockClassification/edit';
30 30
 import RotationChartList from '@/pages/rotationChart/list';
31 31
 import RotationChartEdit from '@/pages/rotationChart/edit';
32
-import Roles from '@/pages/user/Role';
32
+import Roles from '@/pages/roles/index';
33 33
 
34 34
 /**
35 35
  * meta 用来扩展自定义数据数据

+ 22
- 0
src/services/role.js Bestand weergeven

@@ -0,0 +1,22 @@
1
+import { restful } from '@/utils/request';
2
+
3
+/**
4
+ * 构造 Service
5
+ * @returns
6
+ */
7
+ const [
8
+  getRoleList,
9
+  getRoleDetail,
10
+  saveRole,
11
+  updateRole,
12
+  deleteRole,
13
+ ] = restful('/roles');
14
+
15
+
16
+ export {
17
+  getRoleList,
18
+  getRoleDetail,
19
+  saveRole,
20
+  updateRole,
21
+  deleteRole,
22
+ }

+ 32
- 0
src/utils/arr2Tree.js Bestand weergeven

@@ -0,0 +1,32 @@
1
+
2
+export default function arr2Tree(arr = [], parent = 'parentId') {
3
+  // 转换为字典
4
+  const dict = arr.reduce((acc, item) => {
5
+    return {
6
+      ...acc,
7
+      [item.id]: {
8
+        ...item,
9
+        children: []
10
+      }
11
+    }
12
+  }, {});
13
+
14
+  // 挂载父子节点
15
+  const rootId = -1;
16
+  const tree = [];
17
+  for (let item of arr) {
18
+    const parentNodeId = item[parent];
19
+
20
+    if (parentNodeId === rootId) {
21
+      tree.push(item);
22
+    } else {
23
+      const parentNode = dict[parentNodeId];
24
+      if (parentNode) {
25
+        parentNode.children.push(item);
26
+      }
27
+    }
28
+  }
29
+
30
+  return tree;
31
+}
32
+

+ 13
- 0
src/utils/hooks/useBool.js Bestand weergeven

@@ -0,0 +1,13 @@
1
+import React from "react";
2
+
3
+export default function useLoading(initial = false) {
4
+  const [loading, setLoading] = React.useState(initial);
5
+  const loadingRef = React.useRef();
6
+  loadingRef.current = loading;
7
+
8
+  const setTrue = React.useCallback(() => setLoading(true), []);
9
+  const setFalse = React.useCallback(() => setLoading(false), []);
10
+  const toggle = React.useCallback(() => setLoading(!loadingRef.current), []);
11
+
12
+  return [loading, setTrue, setFalse, setLoading, toggle];
13
+}

+ 16
- 1
src/utils/request.js Bestand weergeven

@@ -9,12 +9,13 @@ const instance = axios.create({
9 9
 
10 10
 // 添加请求拦截器
11 11
 instance.interceptors.request.use(function (config) {
12
-  const { headers = {}, responseType = 'json', download = false } = config;
12
+  const { headers = {}, responseType = 'json', download = false, successTip = true } = config;
13 13
   const token = sessionStorage.getItem('token') || '';
14 14
 
15 15
   // 在发送请求之前做些什么
16 16
   return {
17 17
     ...config,
18
+    successTip,
18 19
     headers: {
19 20
       ...headers,
20 21
       responseType: download ? 'blob' : responseType,
@@ -41,6 +42,10 @@ instance.interceptors.response.use(function (response) {
41 42
       sessionStorage.setItem('token', data.data.token);
42 43
     }
43 44
 
45
+    if (config.successTip) {
46
+      message.success(typeof success === 'string' ? successTip : '操作成功');
47
+    }
48
+
44 49
     return data.data;
45 50
   } else if (data.code === 1001) {
46 51
     if (!config.silent) {
@@ -111,6 +116,16 @@ export function queryDict(apiRequest) {
111 116
   };
112 117
 }
113 118
 
119
+export function restful(url) {
120
+  const list = params => instance.get(url, { params, successTip: false });
121
+  const get = id => instance.get(`${url}/${id}`);
122
+  const add = data => instance.post(url, data);
123
+  const update = (id, data) => instance.put(`${url}/${id}`, data);
124
+  const del = id => instance.delete(`${url}/${id}`);
125
+
126
+  return [list, get, add, update, del];
127
+}
128
+
114 129
 function downloadBlob(response) {
115 130
   let fileName = '未知文件';
116 131
   const contentDisposition = response.headers['content-disposition'];