index.tsx 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. import React, { useCallback, useEffect, useRef, useState } from 'react'
  2. import { CaretRightOutlined } from '@ant-design/icons'
  3. import styles from './index.module.scss'
  4. import SpinLoding from '@/components/SpinLoding'
  5. import { Route, Switch, useLocation } from 'react-router-dom'
  6. import AuthRoute from '@/components/AuthRoute'
  7. import classNames from 'classnames'
  8. import history from '@/utils/history'
  9. import { Button, Form, Input, Modal } from 'antd'
  10. import { Base64 } from 'js-base64'
  11. import encodeStr from '@/utils/pass'
  12. import { API_getRoleArr, passWordEditAPI } from '@/store/action/layout'
  13. import { getTokenInfo, removeTokenInfo } from '@/utils/storage'
  14. import { MessageFu } from '@/utils/message'
  15. import logoImg from '@/assets/img/logo2.png'
  16. import NotFound from '@/components/NotFound'
  17. import { RouterType, RouterTypeRow } from '@/types'
  18. import tabLeftArr, { routerSon } from './data'
  19. import MyPopconfirm from '@/components/MyPopconfirm'
  20. import { TypeZ5Role } from '../Z_system/Z5role/type'
  21. function Layout() {
  22. // 当前路径选中的左侧菜单
  23. const location = useLocation()
  24. const [path, setPath] = useState('')
  25. useEffect(() => {
  26. const arr = location.pathname.split('/')
  27. let pathTemp = '/'
  28. if (arr[1]) pathTemp = '/' + arr[1]
  29. setPath(pathTemp)
  30. }, [location])
  31. // 获取用户权限信息
  32. const getUserAuthFu = useCallback(async () => {
  33. const userInfo = getTokenInfo().user
  34. // 获取权限
  35. const getRoleRes = await API_getRoleArr(userInfo.roleId)
  36. if (getRoleRes.code === 0) {
  37. const getRoleArr: TypeZ5Role[] = getRoleRes.data || []
  38. const isOkIdArr: number[] = []
  39. getRoleArr.forEach(v1 => {
  40. if (v1.children) {
  41. v1.children.forEach(v2 => {
  42. if (v2.authority) isOkIdArr.push(v2.id)
  43. })
  44. }
  45. })
  46. // 是管理员
  47. if (userInfo.isAdmin === 1) {
  48. // push角色管理
  49. isOkIdArr.push(9900)
  50. // isOkIdArr.push(9901)
  51. }
  52. const tempArr: RouterTypeRow = []
  53. tabLeftArr.forEach(v1 => {
  54. if (v1.son && v1.son[0]) {
  55. v1.son.forEach(v2 => {
  56. if (isOkIdArr.includes(v2.id)) {
  57. tempArr.push(v2)
  58. }
  59. })
  60. }
  61. })
  62. setRouterCom(tempArr)
  63. // 如果当前页面没有权限了,跳转有权限的第一个页面
  64. const urlAll = window.location.hash
  65. const isNowPath = urlAll.replace('#', '')
  66. const pathArr = tempArr.map(v => v.path)
  67. const routerSonArr = routerSon.map(v => v.path)
  68. const lastArr = [...pathArr, ...routerSonArr]
  69. let flagPush = true
  70. lastArr.forEach(v => {
  71. if (v === '/') {
  72. if (isNowPath === '/') flagPush = false
  73. } else {
  74. if (isNowPath.includes(v)) flagPush = false
  75. }
  76. })
  77. if (flagPush) history.push(pathArr[0])
  78. const resList = tabLeftArr.map(v => ({
  79. ...v,
  80. son: v.son.filter(c => isOkIdArr.includes(c.id))
  81. }))
  82. setList(resList)
  83. }
  84. }, [])
  85. useEffect(() => {
  86. getUserAuthFu()
  87. }, [getUserAuthFu])
  88. // 左侧菜单 信息
  89. const [list, setList] = useState([] as RouterType)
  90. // 路由信息(过滤之后的)
  91. const [RouterCom, setRouterCom] = useState<RouterTypeRow>([])
  92. // useEffect(() => {
  93. // console.log(123, list);
  94. // }, [list]);
  95. // 点击跳转
  96. const pathCutFu = useCallback((path: string) => {
  97. history.push(path)
  98. }, [])
  99. // 修改密码相关
  100. const [open, setOpen] = useState(false)
  101. // 拿到新密码的输入框的值
  102. const oldPasswordValue = useRef('')
  103. const checkPassWord = (rule: any, value: any = '') => {
  104. if (value !== oldPasswordValue.current) return Promise.reject('新密码不一致!')
  105. else return Promise.resolve(value)
  106. }
  107. const onFinish = async (values: any) => {
  108. // 通过校验之后发送请求
  109. if (values.oldPassword === values.newPassword) return MessageFu.warning('新旧密码不能相同!')
  110. const regex =
  111. /^(?=.*[A-Za-z])(?=.*\d)|(?=.*[A-Za-z])(?=.*[!@#$%^&*])|(?=.*\d)(?=.*[!@#$%^&*])[A-Za-z\d!@#$%^&*]{8,}$/
  112. const flagPass = regex.test(values.newPassword)
  113. if (!flagPass) {
  114. return MessageFu.warning('密码要求由数字、字母或特殊字符中2种方式组成')
  115. }
  116. const obj = {
  117. oldPassword: encodeStr(Base64.encode(values.oldPassword)),
  118. newPassword: encodeStr(Base64.encode(values.newPassword))
  119. }
  120. const res: any = await passWordEditAPI(obj)
  121. if (res.code === 0) {
  122. MessageFu.success('修改成功!')
  123. loginExit()
  124. }
  125. }
  126. // 点击退出登录
  127. const loginExit = () => {
  128. removeTokenInfo()
  129. history.push('/login')
  130. }
  131. // 点击用户 出来 退出登录 修改密码
  132. const [isUserBtnShow, setIsUserBtnShow] = useState(false)
  133. // path的高亮判断
  134. const pathAcFu = useCallback(
  135. (url: string) => {
  136. let flag = false
  137. if (url === '/') {
  138. if (path.includes('/_') || path === '/') flag = true
  139. } else {
  140. if (path.includes(url)) flag = true
  141. }
  142. return flag
  143. },
  144. [path]
  145. )
  146. return (
  147. <div className={styles.Layout}>
  148. {/* 左边 */}
  149. <div className='layoutLeft'>
  150. <div className='layoutLeftTop'>
  151. <img src={logoImg} alt='' />
  152. </div>
  153. {/* 左边主体 */}
  154. <div className='layoutLeftMain mySorrl'>
  155. {list.map(v => (
  156. <div className={classNames('layoutLRowBox')} key={v.id} hidden={!v.son.length}>
  157. {/* 这个项目没有一级目录 */}
  158. <div className='layoutLRowBoxTxt'>{v.name}</div>
  159. {v.son.map(v2 => (
  160. <div
  161. key={v2.id}
  162. className={classNames('layoutLRowBoxRow', pathAcFu(v2.path) ? 'active' : '')}
  163. onClick={() => pathCutFu(v2.path)}
  164. >
  165. {v2.name}
  166. </div>
  167. ))}
  168. </div>
  169. ))}
  170. </div>
  171. </div>
  172. {/* 右边 */}
  173. <div className='layoutRight'>
  174. <div className='layoutRightTop'>
  175. {/* 用户相关 */}
  176. <div
  177. className={classNames('user', isUserBtnShow ? 'userShow' : '')}
  178. onMouseLeave={() => setIsUserBtnShow(false)}
  179. >
  180. <div className='userNameBox' onClick={() => setIsUserBtnShow(true)}>
  181. {getTokenInfo().user.realName || getTokenInfo().user.userName || '匿名'}
  182. <div className='userInco userInco2'>
  183. <CaretRightOutlined />
  184. </div>
  185. </div>
  186. <div className='userSet'>
  187. <div>
  188. <span onClick={() => setOpen(true)}>修改密码</span>
  189. <MyPopconfirm txtK='退出登录' onConfirm={loginExit} Dom='退出登录' loc='bottom' />
  190. </div>
  191. </div>
  192. </div>
  193. </div>
  194. {/* 右边主体 */}
  195. <div className='layoutRightMain'>
  196. {/* 二级路由页面 */}
  197. <div className='mainBoxR'>
  198. <React.Suspense fallback={<SpinLoding />}>
  199. <Switch>
  200. {/* 左侧tab栏页面 */}
  201. {RouterCom.map(v => (
  202. <AuthRoute key={v.id} exact path={v.path} component={v.Com} />
  203. ))}
  204. {/* 非tab栏页面 */}
  205. {routerSon.map(v => (
  206. <AuthRoute key={v.id} exact path={v.path} component={v.Com} />
  207. ))}
  208. <Route path='*' component={NotFound} />
  209. </Switch>
  210. </React.Suspense>
  211. </div>
  212. </div>
  213. </div>
  214. {/* 点击修改密码打开的对话框 */}
  215. <Modal
  216. destroyOnClose
  217. open={open}
  218. title='修改密码'
  219. onCancel={() => setOpen(false)}
  220. footer={
  221. [] // 设置footer为空,去掉 取消 确定默认按钮
  222. }
  223. >
  224. <Form
  225. scrollToFirstError={true}
  226. name='basic'
  227. labelCol={{ span: 5 }}
  228. wrapperCol={{ span: 16 }}
  229. onFinish={onFinish}
  230. autoComplete='off'
  231. >
  232. <Form.Item
  233. label='旧密码'
  234. name='oldPassword'
  235. rules={[{ required: true, message: '不能为空!' }]}
  236. getValueFromEvent={e => e.target.value.replace(/\s+/g, '')}
  237. >
  238. <Input.Password maxLength={20} />
  239. </Form.Item>
  240. <Form.Item
  241. label='新密码'
  242. name='newPassword'
  243. rules={[
  244. { required: true, message: '不能为空!' },
  245. { min: 6, max: 20, message: '密码长度为6-20个字符!' }
  246. ]}
  247. getValueFromEvent={e => e.target.value.replace(/\s+/g, '')}
  248. >
  249. <Input.Password
  250. maxLength={15}
  251. onChange={e => (oldPasswordValue.current = e.target.value)}
  252. />
  253. </Form.Item>
  254. <Form.Item
  255. label='确定新密码'
  256. name='checkPass'
  257. rules={[{ validator: checkPassWord }]}
  258. getValueFromEvent={e => e.target.value.replace(/\s+/g, '')}
  259. >
  260. <Input.Password maxLength={15} />
  261. </Form.Item>
  262. <Form.Item wrapperCol={{ offset: 14, span: 16 }}>
  263. <Button onClick={() => setOpen(false)}>取消</Button>
  264. &emsp;
  265. <Button type='primary' htmlType='submit'>
  266. 确定
  267. </Button>
  268. </Form.Item>
  269. </Form>
  270. </Modal>
  271. </div>
  272. )
  273. }
  274. // 使用 React.memo 来优化组件,避免组件的无效更新,类似 类组件里面的PureComponent
  275. const MemoLayout = React.memo(Layout)
  276. export default MemoLayout