index.tsx 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. import React, {
  2. useCallback,
  3. useEffect,
  4. useMemo,
  5. useRef,
  6. useState,
  7. } from "react";
  8. import { CaretUpOutlined, CaretDownOutlined } from "@ant-design/icons";
  9. import styles from "./index.module.scss";
  10. import SpinLoding from "@/components/SpinLoding";
  11. import { Route, Switch, useLocation } from "react-router-dom";
  12. import AuthRoute from "@/components/AuthRoute";
  13. import classNames from "classnames";
  14. import history from "@/utils/history";
  15. import { Button, Form, Input, Modal, Popconfirm } from "antd";
  16. import { Base64 } from "js-base64";
  17. import encodeStr from "@/utils/pass";
  18. import { passWordEditAPI } from "@/store/action/layout";
  19. import { getTokenInfo, removeTokenInfo } from "@/utils/storage";
  20. import { MessageFu } from "@/utils/message";
  21. import logoImg from "@/assets/img/logo.png";
  22. import { useDispatch, useSelector } from "react-redux";
  23. import { A5_APIgetList } from "@/store/action/A5Section";
  24. import { A4_APIgetRoleAll } from "@/store/action/A4Role";
  25. import { A2_APIgetListFile } from "@/store/action/A2Dict";
  26. import NotFound from "@/components/NotFound";
  27. import tabLeftArr from "./data";
  28. import { RootState } from "@/store";
  29. import { RouterType } from "@/types";
  30. import { A1_APIOgetHardCoded } from "@/store/action/A1Project";
  31. function Layout() {
  32. const dispatch = useDispatch();
  33. useEffect(() => {
  34. // 获取权限信息(整个项目的关于权限的信息--已过滤)
  35. dispatch(A4_APIgetRoleAll());
  36. // 进页面获取 部门 列表 信息(给 用户管理、字典管理 项目管理-内控文件下拉框 页面使用)
  37. dispatch(A5_APIgetList());
  38. // 获取字典的 -内控文件属性 列表(角色管理 项目管理-内控文件下拉框 页面使用)
  39. dispatch(A2_APIgetListFile());
  40. // 获取 项目管理-项目文件-左侧一级写死目录
  41. dispatch(A1_APIOgetHardCoded())
  42. }, [dispatch]);
  43. // 左侧菜单 和 路由 信息
  44. const [list, setList] = useState([] as RouterType);
  45. //获取权限信息(id数组,已过滤)
  46. const A4RoleAll = useSelector((state: RootState) => state.A4Role.A4RoleAll);
  47. // 通过权限信息 动态 显示 侧边栏
  48. useEffect(() => {
  49. const arr = [...tabLeftArr];
  50. arr.forEach((v) => {
  51. if (A4RoleAll.includes(v.id)) v.done = true;
  52. else v.done = false;
  53. });
  54. setList(arr.filter((v) => v.done));
  55. }, [A4RoleAll]);
  56. // 第一个页面不是 项目 管理 的时候 动态 跳转
  57. useEffect(() => {
  58. if (list && list[0] && list[0].id !== "1000") {
  59. history.replace(list[0].path);
  60. }
  61. }, [list]);
  62. // 点击跳转
  63. const pathCutFu = useCallback((path: string) => {
  64. history.push(path);
  65. }, []);
  66. // 当前路径选中的左侧菜单
  67. const location = useLocation();
  68. const [path, setPath] = useState("");
  69. useEffect(() => {
  70. const arr = location.pathname.split("/");
  71. let pathTemp = "/";
  72. if (arr[1]) pathTemp = "/" + arr[1];
  73. setPath(pathTemp);
  74. }, [location]);
  75. const userInfo = useMemo(() => {
  76. return getTokenInfo().user;
  77. }, []);
  78. // 修改密码相关
  79. const [open, setOpen] = useState(false);
  80. // 拿到新密码的输入框的值
  81. const oldPasswordValue = useRef("");
  82. const checkPassWord = (rule: any, value: any = "") => {
  83. if (value !== oldPasswordValue.current)
  84. return Promise.reject("新密码不一致!");
  85. else return Promise.resolve(value);
  86. };
  87. const onFinish = async (values: any) => {
  88. // 通过校验之后发送请求
  89. if (values.oldPassword === values.newPassword)
  90. return MessageFu.warning("新旧密码不能相同!");
  91. const obj = {
  92. oldPassword: encodeStr(Base64.encode(values.oldPassword)),
  93. newPassword: encodeStr(Base64.encode(values.newPassword)),
  94. };
  95. const res: any = await passWordEditAPI(obj);
  96. if (res.code === 0) {
  97. MessageFu.success("修改成功!");
  98. loginExit();
  99. }
  100. };
  101. // 点击退出登录
  102. const loginExit = () => {
  103. removeTokenInfo();
  104. history.push("/login");
  105. };
  106. return (
  107. <div className={styles.Layout}>
  108. {/* 左边 */}
  109. <div className="layoutLeft">
  110. <div className="layoutLeftTop">
  111. <img src={logoImg} alt="" />
  112. </div>
  113. {/* 左边主体 */}
  114. <div className="layoutLeftMain">
  115. {list.map((v) => (
  116. <div
  117. className={classNames(
  118. "layoutLRowBox",
  119. path === v.path ? "active" : ""
  120. )}
  121. key={v.id}
  122. onClick={() => pathCutFu(v.path)}
  123. >
  124. {v.name}
  125. </div>
  126. ))}
  127. </div>
  128. </div>
  129. {/* 右边 */}
  130. <div className="layoutRight">
  131. <div className="layoutRightTop">
  132. {/* 用户相关 */}
  133. <div className="user">
  134. {userInfo.realName}
  135. <div className="userInco userInco1">
  136. <CaretUpOutlined rev={undefined} />
  137. </div>
  138. <div className="userInco userInco2">
  139. <CaretDownOutlined rev={undefined} />
  140. </div>
  141. <div className="userSet">
  142. <div>
  143. <span onClick={() => setOpen(true)}>修改密码</span>
  144. <Popconfirm
  145. placement="bottom"
  146. title="确定退出吗?"
  147. okText="确定"
  148. cancelText="取消"
  149. onConfirm={loginExit}
  150. okButtonProps={{ loading: false }}
  151. >
  152. 退出登录
  153. </Popconfirm>
  154. </div>
  155. </div>
  156. </div>
  157. </div>
  158. {/* 右边主体 */}
  159. <div className="layoutRightMain">
  160. {/* 二级路由页面 */}
  161. <div className="mainBoxR">
  162. <React.Suspense fallback={<SpinLoding />}>
  163. <Switch>
  164. {list.map((v) => (
  165. <AuthRoute key={v.id} exact path={v.path} component={v.Com} />
  166. ))}
  167. <Route path="*" component={NotFound} />
  168. </Switch>
  169. </React.Suspense>
  170. </div>
  171. </div>
  172. </div>
  173. {/* 点击修改密码打开的对话框 */}
  174. <Modal
  175. destroyOnClose
  176. open={open}
  177. title="修改密码"
  178. onCancel={() => setOpen(false)}
  179. footer={
  180. [] // 设置footer为空,去掉 取消 确定默认按钮
  181. }
  182. >
  183. <Form
  184. scrollToFirstError={true}
  185. name="basic"
  186. labelCol={{ span: 5 }}
  187. wrapperCol={{ span: 16 }}
  188. onFinish={onFinish}
  189. autoComplete="off"
  190. >
  191. <Form.Item
  192. label="旧密码"
  193. name="oldPassword"
  194. rules={[{ required: true, message: "不能为空!" }]}
  195. >
  196. <Input.Password maxLength={15} />
  197. </Form.Item>
  198. <Form.Item
  199. label="新密码"
  200. name="newPassword"
  201. rules={[
  202. { required: true, message: "不能为空!" },
  203. { min: 6, max: 15, message: "密码长度为6-15个字符!" },
  204. ]}
  205. >
  206. <Input.Password
  207. maxLength={15}
  208. onChange={(e) => (oldPasswordValue.current = e.target.value)}
  209. />
  210. </Form.Item>
  211. <Form.Item
  212. label="确定新密码"
  213. name="checkPass"
  214. rules={[{ validator: checkPassWord }]}
  215. >
  216. <Input.Password maxLength={15} />
  217. </Form.Item>
  218. <Form.Item wrapperCol={{ offset: 14, span: 16 }}>
  219. <Button onClick={() => setOpen(false)}>取消</Button>
  220. &emsp;
  221. <Button type="primary" htmlType="submit">
  222. 确定
  223. </Button>
  224. </Form.Item>
  225. </Form>
  226. </Modal>
  227. </div>
  228. );
  229. }
  230. // 使用 React.memo 来优化组件,避免组件的无效更新,类似 类组件里面的PureComponent
  231. const MemoLayout = React.memo(Layout);
  232. export default MemoLayout;