index.jsx 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. import React, { useState, useEffect, useRef } from 'react';
  2. import { history, Link } from 'umi';
  3. import {
  4. Button,
  5. Popconfirm,
  6. Modal,
  7. Form,
  8. Input,
  9. message,
  10. Tree,
  11. Checkbox,
  12. Row,
  13. Col,
  14. Divider,
  15. } from 'antd';
  16. import { PlusOutlined } from '@ant-design/icons';
  17. import { getRoleList, addRole, deleteRole, updateRole, getMenuList } from '@/services/role';
  18. import { getPermissionList, addPermission, getRoleRermissionDetail } from '@/services/permission';
  19. import { PageHeaderWrapper } from '@ant-design/pro-layout';
  20. import moment from 'moment';
  21. import PageTable from '@/components/PageTable';
  22. import RoleCheckbox from '@/components/RoleCheckbox';
  23. const formatterTime = (val) => {
  24. return val && val !== '-' ? moment(val).format('YYYY-MM-DD') : '-';
  25. };
  26. const FormItem = Form.Item;
  27. export default (props) => {
  28. //列表
  29. const actionRef = useRef();
  30. // 角色表单
  31. const formItemLayout = { labelCol: { span: 6 }, wrapperCol: { span: 14 } };
  32. const [form] = Form.useForm();
  33. const [editModal, setEditModal] = useState(false);
  34. const [loading, setLoading] = useState(false);
  35. const [roleId, setRoleId] = useState();
  36. const [roleMId, setRoleMId] = useState();
  37. // 授权弹窗
  38. const [showModal, setShowModal] = useState(false);
  39. const [treeList, setTreeList] = useState([]);
  40. const [permissionAllList, setPermissionAllList] = useState([]);
  41. const [primordialData, setPrimordialData] = useState([]);
  42. const [currenList, setCurrentList] = useState([]);
  43. const [title, setTitle] = useState('');
  44. //选中的权限列表
  45. const [rolePermissionList, setRolePermissionList] = useState([]);
  46. const [pLoading, setPLoading] = useState(false);
  47. let parentList = [];
  48. let pid = '';
  49. //列表编辑
  50. const handelEdit = (val) => {
  51. setRoleId(val.roleId);
  52. form.setFieldsValue(val);
  53. setEditModal(true);
  54. };
  55. //列表删除
  56. const handleDelete = (id) => {
  57. deleteRole(id)
  58. .then(() => {
  59. message.success('删除成功');
  60. actionRef.current.reload();
  61. })
  62. .catch((err) => {
  63. message.error(err);
  64. });
  65. };
  66. //列表角色授权
  67. const handelEmpower = (val) => {
  68. setRoleMId(val.roleId);
  69. setShowModal(true);
  70. getRoleRermissionDetail({ roleId: val.roleId })
  71. .then((res) => {
  72. setRolePermissionList(
  73. res.map((item) => {
  74. return item.permissionId;
  75. }),
  76. );
  77. })
  78. .catch((err) => {
  79. message.error(err);
  80. });
  81. };
  82. // 表单提交
  83. const Submit = (values) => {
  84. setLoading(true);
  85. if (roleId) {
  86. updateRole(roleId, values).then(() => {
  87. setLoading(false);
  88. message.success(`修改成功`);
  89. onCancel();
  90. actionRef.current.reload();
  91. });
  92. } else {
  93. addRole(values)
  94. .then(() => {
  95. setLoading(false);
  96. message.success(`保存成功`);
  97. onCancel();
  98. actionRef.current.reload();
  99. })
  100. .catch((err) => {
  101. setLoading(false);
  102. message.error(err.message || err);
  103. });
  104. }
  105. };
  106. //角色弹窗关闭
  107. const onCancel = () => {
  108. setRoleId();
  109. form.resetFields();
  110. setEditModal(false);
  111. };
  112. //授权弹窗关闭
  113. const onEmpowerCancel = () => {
  114. setRoleMId();
  115. setShowModal(false);
  116. setRolePermissionList([]);
  117. setCurrentList([]);
  118. parentList = [];
  119. pid = '';
  120. };
  121. //递归取出树结构中的所以项便于加入最终选中的菜单项
  122. const treeToArray = (tree) => {
  123. if (tree.children) {
  124. return [{ title: tree.title, key: tree.key }, ...tree.children];
  125. } else return { title: tree.title, key: tree.key };
  126. };
  127. // 选择左侧的菜单多选框
  128. const onCheck = (checkedKeys, info) => {
  129. //当前选中数组
  130. let list2;
  131. let list3 = [...rolePermissionList];
  132. //权限数据
  133. var list4 = list3.filter((item) => item.split(':').length == 2);
  134. if (info.checked) {
  135. if (info.node.children) {
  136. list2 = treeToArray(info.node).map((item) => {
  137. return item.key;
  138. });
  139. list2.forEach((item) => {
  140. if (list3.indexOf(item) === -1) {
  141. list3.push(item);
  142. }
  143. });
  144. } else {
  145. list2 = info.node.key;
  146. if (list3.indexOf(list2) === -1) {
  147. list3.push(list2);
  148. }
  149. }
  150. } else {
  151. setTitle();
  152. setCurrentList([]);
  153. //取消菜单
  154. if (info.node.children) {
  155. list2 = treeToArray(info.node).map((item) => {
  156. return item.key;
  157. });
  158. list2.forEach((item) => {
  159. if (list3.indexOf(item) !== -1) {
  160. list3.splice(list3.indexOf(item), 1);
  161. list4.map((v) => {
  162. if (v.split(':')[0] == item) {
  163. list3.splice(list3.indexOf(v), 1);
  164. }
  165. });
  166. }
  167. });
  168. } else {
  169. list2 = info.node.key;
  170. if (list3.indexOf(list2) !== -1) {
  171. list3.splice(list3.indexOf(list2), 1);
  172. list4.map((v) => {
  173. if (v.split(':')[0] == list2) {
  174. list3.splice(list3.indexOf(list2), 1);
  175. }
  176. });
  177. }
  178. }
  179. }
  180. if (list3.length < checkedKeys.length + list4.length) {
  181. // 说明父节点是因为子节点全部选中才选中实际上菜单数组中没有父节点的数据
  182. checkedKeys.forEach((item) => {
  183. if (list3.indexOf(item) === -1) {
  184. list3.push(item);
  185. }
  186. });
  187. } else if (list3.length > checkedKeys.length + list4.length) {
  188. // 说明父节点是因为子节点全部取消才取消实际上菜单数组中多了父节点的数据
  189. list3.forEach((item) => {
  190. if (checkedKeys.indexOf(item) === -1 && list4.indexOf(item) === -1) {
  191. list3.splice(list3.indexOf(item), 1);
  192. }
  193. });
  194. }
  195. setRolePermissionList(list3);
  196. };
  197. // 选择左侧菜单文字
  198. const onSelect = (checkedKeys, info) => {
  199. if (info.selected) {
  200. var list = permissionAllList.filter((item) => item.resourceId === info.node.key);
  201. setTitle(info.node.title);
  202. setCurrentList(list);
  203. } else {
  204. setTitle();
  205. setCurrentList([]);
  206. }
  207. };
  208. const setParnentList = (val) => {
  209. pid = primordialData.filter((item) => item.menuId === val)[0].menuPId;
  210. if (pid !== '-1') {
  211. parentList.push(pid);
  212. setParnentList(pid);
  213. }
  214. };
  215. const handelOk = () => {
  216. var list = [...rolePermissionList];
  217. if (list.length == 0) {
  218. message.info('授权内容不能为空');
  219. return;
  220. }
  221. setPLoading(true);
  222. //菜单数据
  223. var list4 = rolePermissionList.filter((item) => item.split(':').length !== 2);
  224. list4.forEach((item) => {
  225. setParnentList(item);
  226. });
  227. parentList = [...new Set(parentList)];
  228. parentList.forEach((item) => {
  229. if (list.indexOf(item) === -1) {
  230. list.push(item);
  231. }
  232. });
  233. list = list.map((item) => {
  234. return { permissionId: item };
  235. });
  236. addPermission(roleMId, list)
  237. .then(() => {
  238. message.success('授权成功');
  239. setPLoading(false);
  240. onEmpowerCancel();
  241. })
  242. .catch((err) => {
  243. message.error(err);
  244. setPLoading(false);
  245. });
  246. };
  247. // 将一维对象数组装换为树形数组
  248. const translateDataToTree = (data) => {
  249. //没有父节点的数据
  250. let parents = data.filter((value) => value.menuPId == '-1');
  251. //有父节点的数据
  252. let children = data.filter((value) => value.menuPId !== '-1');
  253. //定义转换方法的具体实现
  254. let translator = (parents, children) => {
  255. //遍历父节点数据
  256. parents.forEach((parent) => {
  257. //遍历子节点数据
  258. children.forEach((current, index) => {
  259. //此时找到父节点对应的一个子节点
  260. if (current.menuPId === parent.menuId) {
  261. //对子节点数据进行深复制,这里只支持部分类型的数据深复制,对深复制不了解的童靴可以先去了解下深复制
  262. let temp = JSON.parse(JSON.stringify(children));
  263. //让当前子节点从temp中移除,temp作为新的子节点数据,这里是为了让递归时,子节点的遍历次数更少,如果父子关系的层级越多,越有利
  264. temp.splice(index, 1);
  265. //让当前子节点作为唯一的父节点,去递归查找其对应的子节点
  266. translator([current], temp);
  267. //把找到子节点放入父节点的children属性中
  268. typeof parent.children !== 'undefined'
  269. ? parent.children.push(current)
  270. : (parent.children = [current]);
  271. }
  272. });
  273. });
  274. };
  275. //调用转换方法
  276. translator(parents, children);
  277. //返回最终的结果
  278. return parents;
  279. };
  280. useEffect(() => {
  281. getMenuList()
  282. .then((res) => {
  283. setPrimordialData(res);
  284. const list = translateDataToTree(res);
  285. const list2 = list.map((item) => {
  286. if (item.children?.length > 0) {
  287. return {
  288. title: item.name,
  289. key: item.menuId,
  290. children: item.children.map((v) => {
  291. return { title: v.name, key: v.menuId };
  292. }),
  293. };
  294. } else {
  295. return { title: item.name, key: item.menuId };
  296. }
  297. });
  298. setTreeList(list2);
  299. })
  300. .catch((err) => {
  301. message.error(err);
  302. });
  303. getPermissionList()
  304. .then((res) => {
  305. setPermissionAllList(res);
  306. })
  307. .catch((err) => {
  308. message.error(err);
  309. });
  310. }, []);
  311. useEffect(() => {
  312. if (roleId) {
  313. } else {
  314. form.resetFields();
  315. }
  316. }, [roleId]);
  317. const actions = () => [
  318. <Button key="add" type="primary" icon={<PlusOutlined />} onClick={() => setEditModal(true)}>
  319. 新增
  320. </Button>,
  321. ];
  322. const columns = [
  323. {
  324. title: '角色名',
  325. dataIndex: 'name',
  326. key: 'name',
  327. },
  328. {
  329. title: '创建时间',
  330. dataIndex: 'createDate',
  331. key: 'createDate',
  332. render: formatterTime,
  333. },
  334. {
  335. title: '操作',
  336. valueType: 'option',
  337. render: (_, record) => [
  338. <a key={1} onClick={() => handelEdit(record)}>
  339. 编辑
  340. </a>,
  341. <Popconfirm
  342. key={2}
  343. title="您是否确认删除 ?"
  344. onConfirm={() => handleDelete(record.roleId)}
  345. okText="确定"
  346. cancelText="取消"
  347. >
  348. <a href="#">删除</a>
  349. </Popconfirm>,
  350. <a key={3} onClick={() => handelEmpower(record)}>
  351. 角色授权
  352. </a>,
  353. ],
  354. },
  355. ];
  356. return (
  357. <PageHeaderWrapper>
  358. <PageTable
  359. request={getRoleList}
  360. // expfunc={exportPersonList}
  361. columns={columns}
  362. actionRef={actionRef}
  363. rowKey="roleId"
  364. options={false}
  365. toolBarRender={actions}
  366. search={false}
  367. />
  368. <Modal
  369. forceRender
  370. title={roleId ? '角色编辑' : '角色新增'}
  371. visible={editModal}
  372. onCancel={onCancel}
  373. keyboard={false}
  374. maskClosable={false}
  375. destroyOnClose={true}
  376. footer={null}
  377. >
  378. <Form {...formItemLayout} onFinish={Submit} form={form}>
  379. <FormItem label="角色名" name="name" rules={[{ required: true, message: '请输入' }]}>
  380. <Input placeholder="请输入" />
  381. </FormItem>
  382. <FormItem label=" " colon={false}>
  383. <Button type="default" onClick={onCancel}>
  384. 取消
  385. </Button>
  386. <Button
  387. type="primary"
  388. loading={loading}
  389. htmlType="Submit"
  390. style={{ marginLeft: '4em' }}
  391. >
  392. 确认
  393. </Button>
  394. </FormItem>
  395. </Form>
  396. </Modal>
  397. <Modal
  398. title="角色授权"
  399. visible={showModal}
  400. onCancel={onEmpowerCancel}
  401. keyboard={false}
  402. maskClosable={false}
  403. destroyOnClose={true}
  404. onOk={handelOk}
  405. confirmLoading={pLoading}
  406. >
  407. <Row gutter={24}>
  408. <Col span={12}>
  409. <Divider orientation="center">菜单</Divider>
  410. <Tree
  411. height={500}
  412. checkable
  413. defaultExpandAll
  414. onSelect={onSelect}
  415. onCheck={onCheck}
  416. checkStrictly
  417. checkedKeys={rolePermissionList}
  418. treeData={treeList}
  419. />
  420. </Col>
  421. <Col span={12}>
  422. <RoleCheckbox
  423. value={rolePermissionList}
  424. title={title}
  425. currenList={currenList}
  426. onchange={setRolePermissionList}
  427. />
  428. </Col>
  429. </Row>
  430. </Modal>
  431. </PageHeaderWrapper>
  432. );
  433. };