index.tsx 10.0 KB

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