Quellcode durchsuchen

feat: 引入mobx,重新构建路由守卫

lanjianrong vor 4 Jahren
Ursprung
Commit
447c38fc17

+ 2 - 0
.eslintrc

@@ -24,6 +24,8 @@
     "react/prop-types": 0,
     "array-bracket-spacing": ["error", "always"],
     "object-curly-spacing": ["error", "always"]
+
+    // "import/no-unresolved": [1, { "ignore": ["^@/"] }]
   },
   "settings": {
     "react": {

+ 1 - 0
.vscode/.settings.json

@@ -1,4 +1,5 @@
 {
+  "editor.tabSize": 2,
   "path-intellisense.mappings": {
     "@": "${workspaceRoot}"
   },

+ 6 - 2
package.json

@@ -149,6 +149,7 @@
     "postcss-preset-env": "6.7.0",
     "postcss-safe-parser": "4.0.1",
     "prettier": "^2.1.2",
+    "react-activation": "^0.5.5",
     "react-app-polyfill": "^1.0.6",
     "react-dev-utils": "^10.2.1",
     "resolve": "1.15.0",
@@ -176,12 +177,15 @@
     "@ant-design/icons": "^4.2.2",
     "antd": "^4.6.4",
     "axios": "^0.20.0",
+    "immutable": "^4.0.0-rc.12",
+    "mobx": "^6.0.1",
+    "mobx-react": "^7.0.0",
     "nprogress": "^0.2.0",
     "react": "^16.13.1",
-    "react-activation": "^0.5.5",
     "react-dom": "^16.13.1",
     "react-redux": "^7.2.1",
     "react-router-dom": "^5.2.0",
-    "redux": "^4.0.5"
+    "redux": "^4.0.5",
+    "redux-immutable": "^4.0.0"
   }
 }

+ 8 - 4
src/App.tsx

@@ -2,16 +2,20 @@ import Guards from '@/components/Navigation'
 import history from '@/utils/history'
 import 'nprogress/nprogress.css'
 import React from 'react'
+import { AliveScope } from 'react-activation'
 import { Router, Switch } from 'react-router-dom'
 import './index.scss'
-import { routesConfig } from './router/routes'
+import { routeConfig } from './router/routes'
 const App = () =>{
   return (
     <div className="App">
       <Router history={ history }>
-        <Switch>
-          <Guards routeConfig={routesConfig}></Guards>
-        </Switch>
+        <AliveScope>
+          <Switch>
+            <Guards routeConfig={routeConfig}></Guards>
+          </Switch>
+        </AliveScope>
+
       </Router >
     </div>
   )

+ 50 - 78
src/components/Menu/index.tsx

@@ -1,96 +1,68 @@
 import logo from '@/assets/img/logo.png'
-import { asyncLogout } from '@/store/modules/user'
-import { RootState } from '@/store/reducers'
+import { userState } from '@/store/mobx'
 import { iMenuItem } from '@/types/router'
 import { Button, Dropdown, Menu } from "antd"
-import React, { useEffect } from 'react'
-import { connect } from 'react-redux'
+import { observer } from 'mobx-react'
+import React, { Component } from 'react'
 import { Link } from "react-router-dom"
-import { Action } from 'redux'
-import { ThunkDispatch } from 'redux-thunk'
 import styles from './index.module.scss'
 import './index.scss'
 import MenuItem from './MenuItem'
 interface iMenuProps {
   list: iMenuItem[]
-  username: string
-  logoutClick? :() => void
 }
+@observer
+class NavSider extends Component<iMenuProps, any> {
+  render() {
+    const { list: MeunList } = this.props
+    console.log(userState.userInfo)
 
-const NavSider:React.FC<iMenuProps> = (props)=> {
-  const { list: MeunList, username, logoutClick } = props
-
-  useEffect(() => {
-    return () => {
-      logoutClick && logoutClick()
-    }
-  }, [])
-  return (
-    <div className="main-nav">
-      <div className="logo"><img src={logo}></img></div>
-      <div className="nav-content">
-        <div className="nav-top">
-          {
-            MeunList.map((item: iMenuItem, idx: number) =>
-              item.isTop && <MenuItem key={idx} item={item}></MenuItem>
-            )
-          }
-        </div>
-        <div className="nav-footer">
-          {
-            MeunList.map((item: iMenuItem, idx: number) =>
-              !item.isTop && <MenuItem key={idx} item={item}></MenuItem>
-            )
-          }
-          <div className="pi-text-center">
-            <Dropdown overlay={() => {
-              return (
-                <Menu>
-                  <Menu.Item key="0">
-                    <Link to="/acount/safe">账号资料</Link>
-                  </Menu.Item>
-                  <Menu.Item key="1">
-                    <Link to="/acount/safe">账号安全</Link>
-                  </Menu.Item>
-                  <Menu.Divider />
-                  <Menu.Item key="2">
-                    <Link to="/acount/safe">帮助中心</Link>
-                  </Menu.Item>
-                  <Menu.Item key="3">
-                    <Link to="/login">退出登录</Link>
-                    {/* <span onClick={logoutClick}></span> */}
-                  </Menu.Item>
-                </Menu>
+    return (
+      <div className="main-nav">
+        <div className="logo"><img src={logo}></img></div>
+        <div className="nav-content">
+          <div className="nav-top">
+            {
+              MeunList?.map((item: iMenuItem, idx: number) =>
+                item.isTop && <MenuItem key={idx} item={item}></MenuItem>
+              )
+            }
+          </div>
+          <div className="nav-footer">
+            {
+              MeunList?.map((item: iMenuItem, idx: number) =>
+                !item.isTop && <MenuItem key={idx} item={item}></MenuItem>
               )
-            }} trigger={[ 'click' ]} placement="topRight">
-              <Button size="small" className={styles.bottomBtn}>{username}</Button>
-            </Dropdown>
+            }
+            <div className="pi-text-center">
+              <Dropdown overlay={() => {
+                return (
+                  <Menu>
+                    <Menu.Item key="0">
+                      <Link to="/acount/safe">账号资料</Link>
+                    </Menu.Item>
+                    <Menu.Item key="1">
+                      <Link to="/acount/safe">账号安全</Link>
+                    </Menu.Item>
+                    <Menu.Divider />
+                    <Menu.Item key="2">
+                      <Link to="/acount/safe">帮助中心</Link>
+                    </Menu.Item>
+                    <Menu.Item key="3">
+                      {/* <Link to="/login">退出登录</Link> */}
+                      <span onClick={() => userState.logout()}></span>
+                    </Menu.Item>
+                  </Menu>
+                )
+              }} trigger={[ 'click' ]} placement="topRight">
+                <Button size="small" className={styles.bottomBtn}>{userState.userInfo.Name}</Button>
+              </Dropdown>
+            </div>
           </div>
         </div>
       </div>
-    </div>
-  )
-}
-
-// const DropMenu = () => {
-
-//   function deleteUser() {
-//     // store.dispatch(asyncLogout())
-//   }
-
-
-// }
-
-
-const mapStateToProps = (state:RootState) => {
-  return {
-    username: state.user.userInfo.Name
+    )
   }
 }
 
-const mapDispathToProps = (dispatch: ThunkDispatch<RootState, null, Action>) => {
-  return {
-    logoutClick: () => dispatch(asyncLogout())
-  }
-}
-export default connect(mapStateToProps, mapDispathToProps)(NavSider)
+export default NavSider

+ 0 - 0
src/components/Navigation/guards.tsx


+ 133 - 147
src/components/Navigation/index.tsx

@@ -1,162 +1,148 @@
-import { updateMatch } from '@/store/modules/navigation'
-import { checkPermission } from '@/store/modules/user'
-import { RootState } from '@/store/reducers'
-import { NavigationGuardsProps, RouteModol } from '@/types/router'
-import React, { Dispatch, useEffect } from 'react'
-import { connect } from 'react-redux'
+import DefaultErrorPage from '@/pages/ErrorPage'
+import { frameState, userState } from '@/store/mobx'
+import { NavigationGuardsProps, RouteModel } from '@/types/router'
+import { combinationPath } from '@/utils/util'
+import React, { Component } from 'react'
+import KeepAlive from 'react-activation'
 import { Redirect, Route } from "react-router-dom"
-import { AnyAction } from 'redux'
-
-const NavigationGuards:React.FC<NavigationGuardsProps> = props => {
-  const { location, routeConfig, match, check, permission, isLogin, saveMatch } = props
-  useEffect(() => {
-    check()
-  }, [])
-
-  const parentPath: string | null = match?.path || null // 父路由的路径
-  const targetPath: string | null  = location?.pathname || null// 目标路由的位置
-  const targetRoute: RouteModol | null = targetPath && findTargetRoute(parentPath, targetPath, routeConfig) || null
-
-  // 利用重定向使 / 访问/dashboard路由
-  if (targetRoute && targetRoute.redirect) {
-    const redirectPath = combinationRedirect(parentPath, targetRoute.redirect)
-    return <Redirect to={redirectPath}/>
-  }
-
-  // 未找到目标路由组件,返回404
-  if (!targetRoute) {
-    return <Redirect to='/404'/>
-  }
-
-  // 处理是否已登录
-  if (isLogin) {
-    return <LoginHandler targetRoute={targetRoute}></LoginHandler>
-  } else {
-    saveMatch(targetPath || '/')
-    return <NotLoginHandler targetRoute={targetRoute}></NotLoginHandler>
-  }
-}
-
+class NavigationGuards extends Component<NavigationGuardsProps, any> {
+    // constructor(props: NavigationGuardsProps) {
+    //     super(props)
+    //     userState.check()
+    // }
+    /**
+     * 判断pathTarget是否包涵pathConfig/
+     * 即,pathConfig是否为pathTarget的子路径
+     * @param pathConfig 配置文件中的路径的全路径
+     * @param pathTarget 用户请求的路径
+     */
+    static switchRoute(pathConfig: string, pathTarget: string) {
+        if (pathConfig === pathTarget) return true
+
+        const reg = new RegExp(`(^${pathConfig})(?=/)`)
+        return reg.test(pathTarget)
+    }
 
-/**
- * 判断一个路径是否是另一个路径的子路径
- * @param pathConfig - 配置文件中的路径
- * @param pathTarget - 用户请求的路径
- */
-function switchRoute (pathConfig: string, pathTarget: string):boolean {
-  if (pathConfig === pathTarget) return true
-  const reg = new RegExp(`(^${pathConfig})(?=/)`)
-  return reg.test(pathTarget)
-}
 
+    static permissionAuthentication(authArray: string[], myPermis: string) {
+        return !!authArray.find((item) => item === myPermis)
+    }
 
-/**
- * 将子组件路径还原成带前缀的路径
- * @param parentPath - 父组件路径
- * @param pathOfTargetConfig - 用户希望访问的组件的在路由配置信息中填写的路径
- * @returns {string} 拼接后的path
- */
-const combinationPath = (parentPath: string|undefined, pathOfTargetConfig: string): string => {
-  // if (parentPath && pathOfTargetConfig === '/') return parentPath
-  // if (parentPath && parentPath === pathOfTargetConfig) return parentPath
-  let combinedPath = !pathOfTargetConfig.startsWith('/') ? `/${pathOfTargetConfig}` : pathOfTargetConfig
-  combinedPath = parentPath ? `${parentPath}${combinedPath}` : combinedPath
-  return combinedPath
-}
+    shouldComponentUpdate(nextProps: any) {
+        //与上次请求的路径相同时不重新渲染
+        if (this.props.location.pathname === nextProps.location.pathname) return false
+        userState.check()
+        return true
+    }
 
-/**
- * 将重定向路径还原成带前缀的路径
- * @param parentPath - 父路由路径
- * @param redirectPath - 用户希望访问的组件的在路由配置信息中填写的重定向路径
- * @returns {string} 拼接后的重定向路径
- */
-const combinationRedirect = (parentPath: string|null, redirectPath: string): string => {
-  if (parentPath === '/' || !parentPath) {
-    return redirectPath.startsWith('/') ? redirectPath :  `/${redirectPath}`
-  } else {
-    return redirectPath.startsWith('/') ? `${parentPath}${redirectPath}` :  `${parentPath}/${redirectPath}`
-  }
-}
-/**
- * 在路由配置信息中查找用户希望访问的组件
- * @param parentPath 父组件路径
- * @param targetPath 用户当前访问的路径
- * @param routeConfig 路由配置信息
- * @returns {RouteModol|null} 路由配置信息
- */
-function findTargetRoute (parentPath: any, targetPath: string, routeConfig: RouteModol[]): RouteModol | null  {
-  // 处理目标路径是父路由的子路由根路径, 比如 /console/management => /console/management/
-  // 使其直接找到路由配置中的重定向配置项
-  if (parentPath && parentPath === targetPath) {
-    return routeConfig.find(item => item.path === '/') || null
-  }
-  for (let i = 0; i < routeConfig.length; i++) {
-    const item = routeConfig[i]
-    const path = combinationPath(parentPath, item.path)
-
-    if (targetPath && switchRoute(path, targetPath)) {
-      return { ...item, path }
+    /**
+     * 在路由配置信息中查找用户希望访问的组件
+     * @param parentPath 父路由的路径
+     * @param targetPath 用户当前希望访问的路径
+     * @param routeConfig 路由配置信息
+     * @param ErrorPage 错误页面
+     */
+    static findTargetRoute(parentPath: string, targetPath: string, routeConfig: RouteModel[]): { targetRoute: RouteModel | null | Error, realPath: string } {
+        for (let i = 0; i < routeConfig.length; i++) {
+            const item = routeConfig[i]
+            const path = combinationPath(parentPath, item.path)
+
+            if (targetPath && NavigationGuards.switchRoute(path, targetPath)) {
+                return { targetRoute: { ...item, path }, realPath: path }
+            }
+        }
+        return { targetRoute: null, realPath: "" }
     }
-  }
-  return null
-}
 
-/**
- * 已经登录->路由处理
- */
-function LoginHandler(props: any) {
-  const { targetRoute } = props
-  const { path } = targetRoute
-  if (path === '/login') {
-    return <Redirect to="/"></Redirect>
-  } else {
-    return <Route path={path} render={
-      props => (
-        <targetRoute.component {...props} routeConfig={targetRoute.childRoutes}/>
-      )
-    }></Route>
-  }
-}
+    render() {
+        console.log("我渲染了")
+        const { location, routeConfig, match } = this.props
+        //如果没有提供routeConfig则不做任何事情
+        if (!routeConfig || routeConfig.length <= 0) { return null }
+        const ErrorPage =  DefaultErrorPage
+        //父路由的路径
+        const parentPath = match && match.path
+        //用户当前希望访问的路径
+        const targetPath: string | undefined = location?.pathname
 
-/**
- * 未登录->路由处理
- */
-function NotLoginHandler(props: any) {
-  const { targetRoute } = props
-  const { auth, path } = targetRoute
-  if (auth) {
-    return <Redirect to="/login"></Redirect>
-  } else {
-    return <Route path={path} render={
-      props => (
-        <targetRoute.component {...props} routeConfig={targetRoute.childRoutes}/>
-      )
-    }></Route>
-  }
-}
 
-const mapStateToProps = (state: RootState) => {
-  const { user: { permission, isLogin } } = state
-  return {
-    permission,
-    isLogin
-  }
+        //如果访问子菜单,则跳转到子菜单的默认路由
+        if (targetPath && frameState.defaultRouteMapping.has(targetPath)) {
+            const targetDefaultRoute: string = frameState.defaultRouteMapping.get(targetPath) as string
+            return <Redirect to={targetDefaultRoute}></Redirect>
+        }
+
+        const findRes = targetPath && NavigationGuards.findTargetRoute(parentPath, targetPath, routeConfig)
+        const targetRoute: RouteModel | null | "" | undefined | Error = findRes && findRes.targetRoute
+
+        const isLogin = userState.isLogin
+
+        if (targetRoute instanceof Error) return <ErrorPage/>
+
+        if (findRes) {
+            const path: string = findRes.realPath ? findRes.realPath : ""
+            targetRoute && frameState.setCurrentRoutePath(path)
+        }
+
+        if (!targetRoute) {
+            return <ErrorPage/>
+        }
+
+        if (targetRoute.redirect && !targetRoute.component) {
+            return <Redirect to={targetRoute.redirect}></Redirect>
+        }
+
+
+      //以下部分可提出去作为用户自定义部分
+    if (isLogin) {
+        return <LoginHandler ErrorPage={ErrorPage} targetRoute={targetRoute}></LoginHandler>
+    } else {
+        return <NotLoginHandler targetRoute={targetRoute}></NotLoginHandler>
+    }
+}
 }
 
-const mapDispatchToProps = (dispatch: Dispatch<AnyAction>) => {
-  return {
-      check() {
-          dispatch(checkPermission())
-      },
-      saveMatch(path: string) {
-        dispatch(updateMatch(path))
-      }
-  }
+//已经登陆的状态下,处理路由
+function LoginHandler(props: { targetRoute: RouteModel, ErrorPage: any }): any {
+    const { targetRoute, ErrorPage } = props
+    const { path, auth } = targetRoute
+    const noCache = targetRoute.meta?.noCache ? targetRoute.meta.noCache : false
+    if (path === '/login') {
+        return <Redirect to="/console/dashboard"></Redirect>
+    } else if (!auth || NavigationGuards.permissionAuthentication(auth, userState.userInfo.Role)) {
+        return (
+            <Route path={path} render={
+                props => (
+                    noCache ?
+                        <targetRoute.component {...props} routeConfig={targetRoute.childRoutes}></targetRoute.component> :
+                        (
+                            <KeepAlive id={`${path}`}>
+                                <targetRoute.component {...props} routeConfig={targetRoute.childRoutes}></targetRoute.component>
+                            </KeepAlive>
+                        )
+                )
+            }></Route>
+        )
+    } else {
+        return <ErrorPage message="您无权访问此页"></ErrorPage>
+    }
 }
-const connector = connect(mapStateToProps, mapDispatchToProps)
-export default connector(NavigationGuards)
 
-export {
-  combinationPath
+//未登录状态下,处理路由
+function NotLoginHandler(props: { targetRoute: RouteModel}): any {
+    const { targetRoute } = props
+    const { path, auth } = targetRoute
+
+    if (auth && auth.length > 0) {
+        return <Redirect to="/login"></Redirect>
+    } else {
+        return <Route path={path} render={
+            props => (
+                <targetRoute.component {...props} routeConfig={targetRoute.childRoutes}></targetRoute.component>
+            )
+        }></Route>
+    }
 }
 
+export default NavigationGuards
+

+ 1 - 7
src/index.tsx

@@ -1,15 +1,9 @@
 import React from 'react'
 import ReactDOM from 'react-dom'
-import { Provider } from 'react-redux'
-// import { Provider} from 'react-redux'
-// import { createStore, applyMiddleware } from 'redux'
 import App from './App'
 import * as serviceWorker from './serviceWorker'
-import store from "./store"
 ReactDOM.render(
-  <Provider store={store}>
-    <App />
-  </Provider>,
+  <App />,
   document.getElementById('root')
 )
 // If you want your app to work offline and load faster, you can change

+ 6 - 4
src/layout/NavSide/index.tsx

@@ -1,13 +1,15 @@
 import Menu from "@/components/Menu"
-import Guards, { combinationPath } from "@/components/Navigation"
-import { iMenuItem, RouteModol } from "@/types/router"
+import Guards from "@/components/Navigation"
+import { iMenuItem, RouteModel } from "@/types/router"
+import { combinationPath } from "@/utils/util"
 import React from 'react'
 import { Switch } from 'react-router-dom'
 export default function NavSide(props: any) {
   const { routeConfig, match } = props
+  console.log(routeConfig)
 
-  const menuList: iMenuItem[] = routeConfig.filter((item: RouteModol) => item.menuConfig).map((item: RouteModol) => {
-    return { path: combinationPath(match.path, item.path), ...item.menuConfig }
+  const menuList: iMenuItem[] = routeConfig?.filter((item: RouteModel) => item.meta).map((item: RouteModel) => {
+    return { path: combinationPath(match.path, item.path), ...item.meta }
   })
 
   return (

src/pages/404/index.scss → src/pages/ErrorPage/index.scss


src/pages/404/index.tsx → src/pages/ErrorPage/index.tsx


+ 13 - 111
src/pages/Login/index.tsx

@@ -1,51 +1,40 @@
 import logo from '@/assets/img/loginlogo.png'
-import { initUserState, updateUserInfo } from '@/store/modules/user'
-import { iUserInfo } from '@/store/modules/user/types'
-import { RootState } from '@/store/reducers'
+import { userState } from '@/store/mobx'
 import { iFromValues, iLoginProps, iRetrieveFormProps } from '@/types/login'
 import consts from '@/utils/consts'
 import { Button, Form, Input, Modal } from 'antd'
+import { observer } from "mobx-react"
 import React, { Component } from 'react'
-import { connect } from 'react-redux'
 import { RouteComponentProps, withRouter } from 'react-router-dom'
-import { apiLogin, apiProject } from "./api"
+import { apiProject } from "./api"
 import styles from './index.module.scss'
 import './index.scss'
-
 // 正常登录Form表单
 const initLoginState = {
   projectInfo: '',
   projectCode: '',
   visible: false,
-  user: initUserState.userInfo
+  user: userState.userInfo
 }
+
 type iState = typeof initLoginState
-const mapStateToProps = (state: RootState) => {
-  return {}
-}
-const mapDispathToProps = {
-  saveUser(user: iUserInfo) {
-    return updateUserInfo(user)
-  }
-}
-// @ts-ignore
-@connect(mapStateToProps, mapDispathToProps)
+@observer
 class NormalLoginForm extends Component<iLoginProps, iState> {
 
   constructor(props: iLoginProps) {
     super(props)
     this.state = initLoginState
   }
-  onFinish = async (values: iFromValues) => {
-    const { code = -1, data }  = await apiLogin(values)
-    if (code === consts.RET_CODE.SUCCESS) {
-      this.setState({ user: data })
-      this.props.history!.push('/')
-    }
+  onFinish = (values: iFromValues) => {
+    // const { code = -1, data }  = await apiLogin(values)
+    // if (code === consts.RET_CODE.SUCCESS) {
+      // }
+    userState.login(values)
+    this.props.history!.push('/')
   }
 
   componentWillUnmount() {
-    this.props.saveUser!(this.state.user)
+    // this.props.saveUser!(this.state.user)
   }
   handleProjectCode = async (e: any) => {
     const projectCode = e.target?.value
@@ -107,93 +96,6 @@ class NormalLoginForm extends Component<iLoginProps, iState> {
     )
   }
 }
-// const NormalLoginForm:React.FC<iLoginProps> = ({ history }) => {
-//   let timer: any = null
-//   const [ projectInfo, setProjectInfo ] = useState('')
-//   const [ visible, setVisible ] = useState(false)
-//   const [ user, setUser ] = useState(initUserState.userInfo)
-//   const [ projectAnimation, setProjectAnimation ] = useState(false) // 控制project-title的动画显示
-//   const onFinish = async (values: iFromValues) => {
-//     const { code = -1, data }  = await apiLogin(values)
-//     if (code === consts.RET_CODE.SUCCESS) {
-//       console.log('data', data)
-
-//       setUser(data)
-//     }
-//   }
-//   useEffect(() => {
-//     history.push('/')
-//     return () => {
-//       store.dispatch({ type: SAVE_USER_INFO, payload: user })
-//     }
-//   }, [ user ])
-//   const handleProjectCode = (e: any) => {
-//     if (timer) clearTimeout(timer)
-//     const projectCode = e.target?.value
-//     !projectCode && (setProjectInfo(''))
-//     timer = setTimeout(() => {
-//       projectCode && apiProject(projectCode).then(({ code = -1, data }) => {
-//         if (code === consts.RET_CODE.SUCCESS) {
-//           if (data.length) {
-//             const project: iProjectInfo = data[0]
-//             project.name && setProjectInfo(project.name)
-//           } else {
-//             setProjectInfo('')
-//           }
-//         }
-//       })
-//     }, 300)
-//   }
-//   const handleForgetPsw = () => {
-//     setVisible(!visible)
-//   }
-
-// useEffect(() => {
-//   if (projectInfo) {
-//     setProjectAnimation(false)
-//   } else {
-//     setProjectAnimation(true)
-//   }
-// }, [ projectInfo ])
-//   return (
-//     <Form
-//       name="normal_login"
-//       className={styles.loginForm}
-//       initialValues={{ remember: true }}
-//       onFinish={onFinish}
-//     >
-//       <h4>纵横工程建设项目管理系统</h4>
-//       <h5 className={[ 'project-title', projectAnimation ? 'scale-out-ver-bottom' : 'scale-in-ver-bottom' ].join(' ')} >{projectInfo}</h5>
-//       <Form.Item
-//         name="code"
-//         rules={[ { required: true, message: 'Please input your Project!' } ]}
-//       >
-//         <Input placeholder="项目编号" onChange={handleProjectCode}/>
-//       </Form.Item>
-//       <Form.Item
-//         name="account"
-//         rules={[ { required: true, message: 'Please input your Account!' } ]}
-//       >
-//         <Input placeholder="登录账号" />
-//       </Form.Item>
-//       <Form.Item
-//         name="password"
-//         rules={[ { required: true, message: 'Please input your Password!' } ]}
-//       >
-//         <Input.Password type="password" placeholder="密码"
-//         />
-//       </Form.Item>
-
-//       <Form.Item>
-//         <Button type="primary" htmlType="submit">登录</Button>
-//       </Form.Item>
-//       <div className={styles.textRight}>
-//         <span onClick={handleForgetPsw}>忘记密码?</span>
-//       </div>
-//       <RetrieveForm visible={visible} setVisible={setVisible}></RetrieveForm>
-//     </Form>
-//   )
-// }
 
 // 找回密码Form表单
 const RetrieveForm:React.FC<iRetrieveFormProps>= ({ visible, setVisible }) => {

+ 1 - 1
src/pages/Management/components/leftSide/index.tsx

@@ -9,7 +9,7 @@ const leftSide:React.FC<iNavSide> = ({ childRoutes }) => {
       <div className="pi-pd-10 pi-mg-bottom-10 sidebar-title">项目设置</div>
       <div className="pi-flex-column pi-justify-start">
         { childRoutes.map((item, idx) =>
-          item.menuConfig && <Link key={idx} to={item.path} className={pathname.indexOf(item.path) !== -1 ? 'nav-link active' : 'nav-link'}>{item.menuConfig.title}</Link>
+          item.meta && <Link key={idx} to={item.path} className={pathname.indexOf(item.path) !== -1 ? 'nav-link active' : 'nav-link'}>{item.meta.title}</Link>
         )}
 				{/* <Link>项目信息</Link>
 				<Link>账号设置</Link>

+ 17 - 40
src/router/routes.ts

@@ -7,31 +7,25 @@
 // }
 
 import AsyncModuleLoader from "@/components/AsyncModuleLoader/index"
-import { RouteModol } from '@/types/router'
-export const routesConfig: RouteModol[] = [
+import { RouteModel } from '@/types/router'
+export const routeConfig: RouteModel[] = [
   {
     path: '/',
     redirect: '/console'
   },
   {
     path: '/login',
-    component: AsyncModuleLoader(() => import('@/pages/Login')),
-    auth: false
+    component: AsyncModuleLoader(() => import('@/pages/Login'))
   },
   {
     path: '/console',
     component: AsyncModuleLoader(() => import('@/layout/NavSide')),
-    auth: true,
     childRoutes: [
       {
-        path: '/',
-        redirect: '/dashboard'
-      },
-      {
         path: 'dashboard',
         component: AsyncModuleLoader(() => import('@/pages/Dashboard')),
-        auth: true, // 需要登录才可以访问
-        menuConfig: {
+        defaultChildRoute: true, //是否作为默认子路由
+        meta: {
           icon: 'chalkboard',
           title: '待办事项',
           isTop: true,
@@ -41,8 +35,7 @@ export const routesConfig: RouteModol[] = [
       {
         path: 'contract',
         component: AsyncModuleLoader(() => import('@/pages/Contract')),
-        auth: true,
-        menuConfig: {
+        meta: {
           icon: 'cogs',
           title: '合同管理',
           isTop: true,
@@ -52,8 +45,7 @@ export const routesConfig: RouteModol[] = [
       {
         path: 'safe',
         component: AsyncModuleLoader(() => import('@/pages/Safe')),
-        auth: true,
-        menuConfig: {
+        meta: {
           icon: 'user-cog',
           title: '安全巡检',
           isTop: true,
@@ -63,8 +55,7 @@ export const routesConfig: RouteModol[] = [
       {
         path: 'quality',
         component: AsyncModuleLoader(() => import('@/pages/Quality')),
-        auth: true,
-        menuConfig: {
+        meta: {
           icon: 'toolbox',
           title: '质量巡检',
           isTop: true,
@@ -74,38 +65,31 @@ export const routesConfig: RouteModol[] = [
       {
         path: 'management',
         component: AsyncModuleLoader(() => import('@/pages/Management')),
-        auth: true,
-        menuConfig: {
+        meta: {
           icon: 'cogs',
           title: '项目设置',
           isTop: false
         },
         childRoutes: [
           {
-            path: '/',
-            redirect: '/info'
-          },
-          {
             path: 'info',
+            defaultChildRoute: true,
             component: AsyncModuleLoader(() => import('@/pages/Management/Info')),
-            auth: true,
-            menuConfig: {
+            meta: {
               title: '项目信息'
             }
           },
           {
             path: 'setting',
             component: AsyncModuleLoader(() => import('@/pages/Management/Setting')),
-            auth: true,
-            menuConfig: {
+            meta: {
               title: '账号设置'
             }
           },
           {
             path: 'tender',
             component: AsyncModuleLoader(() => import('@/pages/Management/Tender')),
-            auth: true,
-            menuConfig: {
+            meta: {
               title: '标段管理'
             }
           }
@@ -118,26 +102,19 @@ export const routesConfig: RouteModol[] = [
         childRoutes: [
           {
             path: 'info',
-            component:AsyncModuleLoader(() => import('@/pages/Account/Information')),
-            auth: true
+            defaultChildRoute: true,
+            component:AsyncModuleLoader(() => import('@/pages/Account/Information'))
           },
           {
             path: 'safe',
-            component:AsyncModuleLoader(() => import('@/pages/Account/Safe')),
-            auth: true
+            component:AsyncModuleLoader(() => import('@/pages/Account/Safe'))
           },
           {
             path: 'manual',
-            component:AsyncModuleLoader(() => import('@/pages/Account/Manual')),
-            auth: true
+            component:AsyncModuleLoader(() => import('@/pages/Account/Manual'))
           }
         ]
       }
     ]
-  },
-  {
-    path: '/404',
-    component: AsyncModuleLoader(() => import('@/pages/404')),
-    auth: false
   }
 ]

+ 0 - 17
src/store/index.ts

@@ -1,17 +0,0 @@
-import { applyMiddleware, compose, createStore } from 'redux'
-import thunk from 'redux-thunk'
-import rootReducer from './reducers'
-
-//添加中间件和调试工具
-const composeEnhancers =
-    typeof window === 'object' &&
-    (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
-        (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose
-
-
-const store = createStore(
-  rootReducer,
-  composeEnhancers(applyMiddleware(thunk))
-)
-
-export default store

+ 73 - 0
src/store/mobx/frame/index.ts

@@ -0,0 +1,73 @@
+import { routeConfig } from '@/router/routes'
+import { RouteModel } from "@/types/router"
+import { combinationPath } from "@/utils/util"
+import { action, observable } from "mobx"
+//处理用户写在@/router/route.config.ts中的路由配置信息
+function handelRouteConfig(routeConfig: RouteModel | RouteModel[]) {
+    //创建path与title的映射关系,供面包屑和分页组件使用
+    const pathNameMapping: Map<string, string> = new Map()
+    //处理完成之后的配置信息
+    const handledRouteConfig: RouteModel | RouteModel[] = JSON.parse(JSON.stringify(routeConfig))
+    //各子菜单的默认路由
+    const defaultRouteMapping: Map<string, string> = new Map()
+
+    const doHandelRouteConfig = (routeItem: RouteModel | RouteModel[], parentRoute?: RouteModel) => {
+        if (Array.isArray(routeItem)) {
+            for (const route of routeItem) {
+                doHandelRouteConfig(route)
+            }
+        } else {
+            routeItem.path = combinationPath(parentRoute?.path, routeItem.path)
+            const routeName = routeItem.meta?.title ? routeItem.meta.title : routeItem.path
+            pathNameMapping.set(routeItem.path, routeName)
+
+            if (routeItem.defaultChildRoute && parentRoute) {
+                defaultRouteMapping.set(parentRoute.path, routeItem.path)
+            }
+
+            if (routeItem.childRoutes) {
+                for (const route of routeItem.childRoutes) {
+                    doHandelRouteConfig(route, routeItem)
+                }
+            }
+        }
+
+    }
+
+    doHandelRouteConfig(handledRouteConfig)
+
+    return {
+        pathNameMapping,
+        handledRouteConfig,
+        defaultRouteMapping
+    }
+
+}
+
+class RouterState {
+    constructor() {
+        const { pathNameMapping, handledRouteConfig, defaultRouteMapping } = handelRouteConfig(routeConfig)
+        this.routeNameMapping = pathNameMapping
+        this.handledRouteConfig = handledRouteConfig
+        this.defaultRouteMapping = defaultRouteMapping
+        console.log("routeNameMapping", pathNameMapping)
+        console.log("handledRouteConfig", handledRouteConfig)
+        console.log("defaultRouteMapping", defaultRouteMapping)
+    }
+
+    @observable routeNameMapping: Map<string, string> = new Map();
+    @observable defaultRouteMapping: Map<string, string> = new Map();
+    @observable handledRouteConfig: RouteModel | RouteModel[] = [];
+    @observable currentRoutePath!: string;
+
+    @action setRouteNameMapping(key: string, value: string) {
+        this.routeNameMapping.set(key, value)
+    }
+
+    @action setCurrentRoutePath(cpath: string) {
+        this.currentRoutePath = cpath
+    }
+
+}
+
+export default new RouterState()

+ 7 - 0
src/store/mobx/index.ts

@@ -0,0 +1,7 @@
+import frameState from './frame'
+import userState from './user'
+export {
+  userState,
+  frameState
+}
+

+ 64 - 0
src/store/mobx/user/index.ts

@@ -0,0 +1,64 @@
+import { apiLogout } from '@/components/Menu/api'
+import { apiLogin } from '@/pages/Login/api'
+import { iFromValues } from '@/types/login'
+import { delUserInfo, getUserInfo, saveUserInfo } from '@/utils/common/user'
+import consts from '@/utils/consts'
+import { action, computed, observable } from 'mobx'
+import { iUserInfo } from './types'
+class UserState {
+  @observable userInfo: iUserInfo = {
+    Id: '',
+    ProjectId: '',
+    Account: '',
+    Name: '',
+    Company: '',
+    Role: '',
+    Mobile: '',
+    Telephone: '',
+    Csrf: ''
+  }
+
+  @computed get isLogin() {
+    return !!this.userInfo.Id
+  }
+  @computed get permission() {
+    return this.userInfo.Role
+  }
+
+  @action login(values: iFromValues) {
+    apiLogin(values).then(({ code = -1, data }) => {
+      if (code === consts.RET_CODE.SUCCESS) {
+        saveUserInfo(data)
+        this.userInfo = data
+      }
+    })
+  }
+
+  @action logout() {
+    apiLogout().then(({ code = -1 }) => {
+      if (code === consts.RET_CODE.SUCCESS) {
+        delUserInfo()
+        this.userInfo = {
+          Id: '',
+          ProjectId: '',
+          Account: '',
+          Name: '',
+          Company: '',
+          Role: '',
+          Mobile: '',
+          Telephone: '',
+          Csrf: ''
+        }
+      }
+    })
+  }
+
+  @action check() {
+    const user: iUserInfo | null = getUserInfo()
+    if (user) {
+      this.userInfo = user
+    }
+  }
+}
+
+export default new UserState()

+ 11 - 0
src/store/mobx/user/types.ts

@@ -0,0 +1,11 @@
+export interface iUserInfo {
+  Id: string
+  ProjectId: string
+  Account: string
+  Name: string
+  Company: string
+  Role: string
+  Mobile?: string
+  Telephone?: string
+  Csrf?: string
+}

+ 0 - 35
src/store/modules/navigation/index.ts

@@ -1,35 +0,0 @@
-import { CLEAR_MATCH, iNavigationState, navigationActionTypes, UPDATE_MATCH } from "./types"
-
-// state
-export const initState:iNavigationState = {
-  match: '' // 目标路由
-}
-
-// reducer
-export function navigationReducer(state = initState, action: navigationActionTypes): iNavigationState {
-  switch (action.type) {
-    case UPDATE_MATCH:
-      return { ...state, match: action.payload }
-      break
-    case CLEAR_MATCH:
-      return initState
-      break
-    default:
-      return state
-      break
-  }
-}
-
-// action
-export function updateMatch(path: string): navigationActionTypes{
-  return {
-    type: UPDATE_MATCH,
-    payload: path
-  }
-}
-
-export function delMatch(): navigationActionTypes {
-  return {
-    type: CLEAR_MATCH
-  }
-}

+ 0 - 19
src/store/modules/navigation/types.ts

@@ -1,19 +0,0 @@
-export interface iNavigationState {
-  match: string
-}
-
-// action
-
-export const UPDATE_MATCH = 'update_match'
-export const CLEAR_MATCH = 'clear_match'
-
-interface updateMatchAction {
-  type: typeof UPDATE_MATCH
-  payload: string
-}
-
-interface clearMatchAction {
-  type: typeof CLEAR_MATCH
-}
-
-export type navigationActionTypes = updateMatchAction | clearMatchAction

+ 0 - 67
src/store/modules/user/index.ts

@@ -1,67 +0,0 @@
-import { apiLogout } from '@/components/Menu/api'
-import { Action } from 'redux'
-import { ThunkDispatch } from 'redux-thunk'
-import { delUserInfo, getUserInfo, saveUserInfo } from './../../../utils/common/user'
-import { RootState } from './../../reducers'
-import { CHECK_PERMISSION, DEL_USER_INFO, iUserInfo, iUserInfoState, SAVE_USER_INFO, UserInfoActionTypes } from './types'
-export const initUserState: iUserInfoState = {
-  userInfo: {
-    Id: '',
-    ProjectId: '',
-    Account: '',
-    Name: '',
-    Company: '',
-    Role: '',
-    Mobile: '',
-    Telephone: '',
-    Csrf: ''
-  },
-  permission: '',
-  isLogin: false
-}
-// reducer
-export function userInfoReducer(state = initUserState, action: UserInfoActionTypes): iUserInfoState {
-  switch (action.type) {
-    case SAVE_USER_INFO:
-      saveUserInfo(action.payload)
-      return { ...state, userInfo: action.payload, isLogin: true, permission: action.payload.Role }
-      break
-    case DEL_USER_INFO:
-      return { ...initUserState }
-    case CHECK_PERMISSION:
-      return { ...action.payload }
-    default:
-      return state
-  }
-}
-
-// action
-export function updateUserInfo(userInfo: iUserInfo): UserInfoActionTypes{
-  return {
-    type: SAVE_USER_INFO,
-    payload: userInfo
-  }
-}
-
-export function deleteUserInfo(): UserInfoActionTypes {
-    delUserInfo()
-    return {
-      type: DEL_USER_INFO
-    }
-}
-export const asyncLogout = () => async (dispatch:ThunkDispatch<RootState, null, Action>) => {
-  await apiLogout()
-  dispatch(deleteUserInfo())
-}
-
-export function checkPermission(): UserInfoActionTypes {
-  const userInfo: iUserInfo = getUserInfo()
-  return {
-    type: CHECK_PERMISSION,
-    payload: {
-      userInfo,
-      isLogin: userInfo && userInfo?.Id ? true : false,
-      permission: userInfo?.Role || ''
-    }
-  }
-}

+ 0 - 38
src/store/modules/user/types.ts

@@ -1,38 +0,0 @@
-
-// state
-export interface iUserInfo {
-  Id: string
-  ProjectId: string
-  Account: string
-  Name: string
-  Company: string
-  Role: string
-  Mobile?: string
-  Telephone?: string
-  Csrf?: string
-}
-export interface iUserInfoState {
-  userInfo: iUserInfo
-  permission: string | []
-  isLogin: boolean
-}
-// action
-export const SAVE_USER_INFO = 'save_user_info'
-export const DEL_USER_INFO = 'del_user_info'
-export const CHECK_PERMISSION = 'check_permission'
-
-interface SaveUserInfoAction {
-  type: typeof SAVE_USER_INFO
-  payload: iUserInfo
-}
-
-interface DelUserInfoAction {
-  type: typeof DEL_USER_INFO
-}
-
-interface CheckPermissionAction {
-  type: typeof CHECK_PERMISSION
-  payload: iUserInfoState
-}
-
-export type UserInfoActionTypes = SaveUserInfoAction | DelUserInfoAction | CheckPermissionAction

+ 0 - 8
src/store/reducers.ts

@@ -1,8 +0,0 @@
-import { combineReducers } from "redux"
-import { userInfoReducer } from './modules/user'
-const rootReducer = combineReducers({
-  user: userInfoReducer
-})
-
-export default rootReducer
-export type RootState = ReturnType<typeof rootReducer>

+ 13 - 10
src/types/router.d.ts

@@ -1,21 +1,24 @@
 import { RouteProps } from 'react-router'
-interface RouteModol {
+interface RouteModel {
   path: string;
-  component?: Object;
-  auth?: boolean; // 登录鉴权
+  component?: any;
+  auth?: string[]; // 登录鉴权
   childRoutes?: RouteModol[];
   redirect?: string;
-  menuConfig?: iMenu;
+  defaultChildRoute?:boolean, //是否作为默认子路由
+  meta?: iMenu;
 }
 
 interface iNavSide extends RouteProps {
-  childRoutes: RouteModol[]
+  childRoutes: RouteModel[]
 }
 interface iMenu {
   icon?: string;
   title: string;
   isTop?: boolean;
   sort?: number;
+  //为了确保下次访问页面的时候数据还在,默认是false表示缓存数据。
+  noCache?: boolean
 }
 
 interface iMenuItem {
@@ -29,13 +32,13 @@ interface NavigationGuardsProps extends RouteProps {
   routeConfig: RouteModol[];
   match?: any;
   location?: any
-  permission: string | []
-  isLogin: boolean
-  check: () => void
-  saveMatch: (path: string) => void
+  // permission: string | []
+  // isLogin: boolean
+  // check: () => void
+  // saveMatch: (path: string) => void
 }
 export {
-  RouteModol,
+  RouteModel,
   NavigationGuardsProps,
   iNavSide,
   iMenu,

+ 1 - 1
src/utils/common/user.ts

@@ -1,5 +1,5 @@
+import { iUserInfo } from '@/store/mobx/user/types'
 import { storage } from '@/utils/util'
-import { iUserInfo } from './../../store/modules/user/types'
 const USER_INFO = 'user_info' // 用户个人信息
 /**
  * 保存用户信息到本地存储中

+ 15 - 1
src/utils/util.ts

@@ -64,10 +64,24 @@ const debounce = (fn: Function, delay: number) => {
   }
 }
 
+
+/**
+ * 将子组件路径还原成带前缀的路径
+ * @param parentPath - 父组件路径
+ * @param pathOfTargetConfig - 用户希望访问的组件的在路由配置信息中填写的路径
+ * @returns {string} 拼接后的path
+ */
+const combinationPath = (parentPath: string|undefined, pathOfTargetConfig: string): string => {
+  let combinedPath = !pathOfTargetConfig.startsWith('/') ? `/${pathOfTargetConfig}` : pathOfTargetConfig
+  combinedPath = parentPath ? `${parentPath}${combinedPath}` : combinedPath
+  return combinedPath
+}
+
 export {
   getCookie,
   storage,
   throttle,
-  debounce
+  debounce,
+  combinationPath
 }