Explorar o código

feat: 增加路由权限控制

lanjianrong %!s(int64=5) %!d(string=hai) anos
pai
achega
e8dc255c18

+ 1 - 2
config/defaultSettings.js

@@ -11,7 +11,6 @@ const proSettings = {
   title: '产品',
   pwa: false,
   iconfontUrl: '//at.alicdn.com/t/font_2276974_wqk3gxkzrpr.js',
-  splitMenus: true,
-
+  splitMenus: true
 }
 export default proSettings

+ 23 - 43
config/routes.js

@@ -1,27 +1,27 @@
 export default [
   {
-    path: '/user',
-    component: '../layouts/UserLayout',
-    routes: [
-      {
-        name: 'login',
-        path: '/user/login',
-        component: './user/login'
-      }
-    ]
-  },
-  {
     path: '/',
     component: '../layouts/SecurityLayout',
     routes: [
       {
+        path: '/user',
+        component: '../layouts/UserLayout',
+        routes: [
+          {
+            name: 'login',
+            path: '/user/login',
+            component: './user/login'
+          }
+        ]
+      },
+      {
         path: '/',
         component: '../layouts/BasicLayout',
-        authority: ['admin', 'user'],
+        authority: ['admin', 'clientAccess'],
         routes: [
           {
             path: '/',
-            redirect: '/workbench'
+            redirect: '/workbench/dashboard'
           },
           {
             path: '/workbench',
@@ -37,6 +37,12 @@ export default [
                 name: 'workbench',
                 icon: 'list',
                 component: './workbench/Dashboard'
+              },
+              {
+                path: '/workbench/admin',
+                name: 'admin',
+                component: './Admin',
+                authority: ['admin']
               }
             ]
           },
@@ -53,13 +59,15 @@ export default [
                 path: '/customer/contact',
                 name: 'contact',
                 icon: 'user-business',
-                component: './customer/Contact'
+                component: './customer/Contact',
+                authority: ['admin', 'clientAccess']
               },
               {
                 path: '/customer/company',
                 name: 'company',
                 icon: 'city',
-                component: './customer/Company'
+                component: './customer/Company',
+                authority: ['admin', 'companyAccess']
               },
               {
                 path: '/customer/finance',
@@ -145,35 +153,7 @@ export default [
               }
             ]
           }
-          // {
-          //   path: '/admin',
-          //   name: 'admin',
-          // icon: 'crown',
-          //   component: './Admin',
-          //   authority: ['admin'],
-          //   routes: [
-          //     {
-          //       path: '/admin/sub-page',
-          //       name: 'sub-page',
-          // icon: 'smile',
-          //       component: './Welcome',
-          //       authority: ['admin'],
-          //     },
-          //   ],
-          // },
-          // {
-          //   name: 'list.table-list',
-          // icon: 'table',
-          //   path: '/list',
-          //   component: './ListTableList',
-          // },
-          // {
-          //   component: './404',
-          // },
         ]
-      },
-      {
-        component: './404'
       }
     ]
   },

+ 2 - 2
src/components/Authorized/CheckPermissions.jsx

@@ -20,7 +20,7 @@ const checkPermissions = (authority, currentAuthority, target, Exception) => {
 
   if (Array.isArray(authority)) {
     if (Array.isArray(currentAuthority)) {
-      if (currentAuthority.some((item) => authority.includes(item))) {
+      if (currentAuthority.some(item => authority.includes(item))) {
         return target
       }
     } else if (authority.includes(currentAuthority)) {
@@ -32,7 +32,7 @@ const checkPermissions = (authority, currentAuthority, target, Exception) => {
 
   if (typeof authority === 'string') {
     if (Array.isArray(currentAuthority)) {
-      if (currentAuthority.some((item) => authority === item)) {
+      if (currentAuthority.some(item => authority === item)) {
         return target
       }
     } else if (authority === currentAuthority) {

+ 2 - 2
src/components/Authorized/renderAuthorize.js

@@ -7,7 +7,7 @@ let CURRENT = 'NULL'
  * use  authority or getAuthority
  * @param {string|()=>String} currentAuthority
  */
-const renderAuthorize = (Authorized) => (currentAuthority) => {
+const renderAuthorize = Authorized => currentAuthority => {
   if (currentAuthority) {
     if (typeof currentAuthority === 'function') {
       CURRENT = currentAuthority()
@@ -27,4 +27,4 @@ const renderAuthorize = (Authorized) => (currentAuthority) => {
 }
 
 export { CURRENT }
-export default (Authorized) => renderAuthorize(Authorized)
+export default Authorized => renderAuthorize(Authorized)

+ 47 - 60
src/layouts/BasicLayout.jsx

@@ -3,33 +3,22 @@
  * You can view component api by:
  * https://github.com/ant-design/ant-design-pro-layout
  */
-import ProLayout, { DefaultFooter } from '@ant-design/pro-layout'
+import ProLayout from '@ant-design/pro-layout'
 import zhCN from 'antd/lib/locale/zh_CN'
-import React, { useEffect, useMemo, useRef } from 'react'
+import React, { useMemo, useRef } from 'react'
 import { Link, useIntl, connect, history } from 'umi'
-import { Result, Button, message, ConfigProvider } from 'antd'
+import { Result, ConfigProvider } from 'antd'
 import Authorized from '@/utils/Authorized'
 import RightContent from '@/components/GlobalHeader/RightContent'
-import { getMatchMenu } from '@umijs/route-utils'
 import logo from '../assets/logo.svg'
 import styles from './BasicLayout.less'
 import '@icon-park/react/styles/index.less'
 import { IconPark } from '@/components/SvgIcon'
 import BasicModal from '@/components/Modal'
 import { BasicDrawer } from '@/components/Modal'
+import { matchPath } from 'umi'
 
-const noMatch = (
-  <Result
-    status={403}
-    title="403"
-    subTitle="Sorry, you are not authorized to access this page."
-    extra={
-      <Button type="primary">
-        <Link to="/user/login">Go Login</Link>
-      </Button>
-    }
-  />
-)
+const noMatch = <Result status={403} title="403" subTitle="抱歉, 您没有权限访问此页面" />
 
 /**
  * use Authorized check all menu item
@@ -46,32 +35,6 @@ const menuDataRender = menuList =>
     return Authorized.check(item.authority, localItem, null)
   })
 
-// const defaultFooterDom = (
-//   <DefaultFooter
-//     copyright={`${new Date().getFullYear()} 蚂蚁集团体验技术部出品`}
-//     links={[
-//       {
-//         key: 'Ant Design Pro',
-//         title: 'Ant Design Pro',
-//         href: 'https://pro.ant.design',
-//         blankTarget: true,
-//       },
-//       {
-//         key: 'github',
-//         title: <GithubOutlined />,
-//         href: 'https://github.com/ant-design/ant-design-pro',
-//         blankTarget: true,
-//       },
-//       {
-//         key: 'Ant Design',
-//         title: 'Ant Design',
-//         href: 'https://ant.design',
-//         blankTarget: true,
-//       },
-//     ]}
-//   />
-// );
-
 const BasicLayout = props => {
   const {
     dispatch,
@@ -80,16 +43,17 @@ const BasicLayout = props => {
     location = {
       pathname: '/',
       key: ''
-    }
+    },
+    route: routeForAuthorized
   } = props
   const menuDataRef = useRef([])
-  useEffect(() => {
-    if (dispatch) {
-      dispatch({
-        type: 'user/fetchCurrent'
-      })
-    }
-  }, [])
+  // useEffect(() => {
+  //   if (dispatch) {
+  //     dispatch({
+  //       type: 'user/fetchCurrent'
+  //     })
+  //   }
+  // }, [])
   /**
    * init variables
    */
@@ -103,13 +67,33 @@ const BasicLayout = props => {
     }
   } // get children authority
 
-  const authorized = useMemo(
-    () =>
-      getMatchMenu(location.pathname || '/', menuDataRef.current).pop() || {
-        authority: undefined
-      },
-    [location.pathname]
-  )
+  // const authorized = useMemo(() => {
+  //   return (
+  //     getMatchMenu(location.pathname || '/', menuDataRef.current).pop() || {
+  //       authority: undefined
+  //     }
+  //   )
+  // }, [location.pathname])
+
+  const authorized = useMemo(() => {
+    const path = location.pathname
+    // 必然匹配了
+    const list = [routeForAuthorized]
+    let current = routeForAuthorized
+    while (current) {
+      if (!current.children) break
+      current = current.children
+        .filter(it => it.path)
+        .sort((a, b) => b.path.length - a.path.length)
+        .find(it => matchPath(path, it))
+      if (!current) break
+      list.unshift(current)
+    }
+    return {
+      authority: list.map(it => it.authority).find(it => it)
+    }
+  }, [routeForAuthorized, location.pathname])
+
   const { formatMessage } = useIntl()
   return (
     <ProLayout
@@ -148,12 +132,15 @@ const BasicLayout = props => {
       menuDataRender={menuDataRender}
       rightContentRender={() => <RightContent />}
       postMenuData={menuData => {
+        // console.log('menuData', menuData)
         menuDataRef.current = menuData || []
         return menuData || []
       }}>
-      <ConfigProvider locale={zhCN}>{children}</ConfigProvider>
-      {/* <Authorized authority={authorized.authority} noMatch={noMatch}> */}
-      {/* </Authorized> */}
+      <ConfigProvider locale={zhCN}>
+        <Authorized authority={authorized.authority} noMatch={noMatch}>
+          {children}
+        </Authorized>
+      </ConfigProvider>
       <BasicDrawer />
       <BasicModal />
     </ProLayout>

+ 1 - 1
src/locales/zh-CN/menu.js

@@ -2,7 +2,7 @@ export default {
   'menu.welcome': '欢迎',
   'menu.more-blocks': '更多区块',
   'menu.home': '首页',
-  'menu.admin': '管理页',
+  'menu.workbench.admin': '管理页',
   'menu.admin.sub-page': '二级管理页',
   'menu.workbench': '工作台',
   'menu.workbench.workbench': '代办事项',

+ 2 - 4
src/models/login.js

@@ -31,7 +31,6 @@ const Model = {
 
         if (redirect) {
           const redirectUrlParams = new URL(redirect)
-
           if (redirectUrlParams.origin === urlParams.origin) {
             redirect = redirect.substr(urlParams.origin.length)
 
@@ -39,12 +38,12 @@ const Model = {
               redirect = redirect.substr(redirect.indexOf('#') + 1)
             }
           } else {
-            window.location.href = '/workbench/dashboard'
+            window.location.href = '/'
             return
           }
         }
 
-        history.replace(redirect || '/workbench/dashboard')
+        history.replace(redirect || '/')
       }
     },
 
@@ -63,7 +62,6 @@ const Model = {
   },
   reducers: {
     changeLoginStatus(state, { payload }) {
-      // setAuthority(payload.currentAuthority)
       return { ...state, status: payload.code }
     }
   }

+ 1 - 1
src/pages/Customer/Test/index.jsx

@@ -3,7 +3,7 @@ import React from 'react'
 import { connect } from 'dva'
 
 const Test = () => {
-  return <div>111</div>
+  return <div />
 }
 
 export default connect(({ customer }) => ({

+ 1 - 0
src/pages/user/login/index.jsx

@@ -52,6 +52,7 @@ const Login = props => {
         }}
         onFinish={async values => {
           handleSubmit(values)
+          return Promise.resolve()
         }}>
         <Tabs activeKey={type} onChange={setType} className={styles.tabMargin}>
           <Tabs.TabPane

+ 27 - 21
src/utils/authority.js

@@ -1,33 +1,39 @@
 import { reloadAuthorized } from './Authorized' // use localStorage to store the authority info, which might be sent from server in actual project.
+import { getDvaApp } from 'umi'
 
-export function getAuthority(str) {
-  const authorityString =
-    typeof str === 'undefined' && localStorage ? localStorage.getItem('antd-pro-authority') : str // authorityString could be admin, "admin", ["admin"]
-
-  let authority
+export function getAuthority() {
+  // eslint-disable-next-line react-hooks/rules-of-hooks
+  const dva = getDvaApp()
+  // eslint-disable-next-line no-underscore-dangle
+  const currentUser = dva._store.getState()?.user?.currentUser
+  const roles = currentUser?.roles
+  const authority = []
+  if (currentUser.isAdmin) {
+    authority.push('admin')
+    return authority
+  }
 
-  try {
-    if (authorityString) {
-      authority = JSON.parse(authorityString)
+  // eslint-disable-next-line no-restricted-syntax
+  for (const key in roles) {
+    if (Object.hasOwnProperty.call(roles, key)) {
+      const item = roles[key]
+      if (item && Array.isArray(item)) {
+        item.forEach(i => {
+          authority.push(`${key + i.replace(i[0], i[0].toUpperCase())}`)
+        })
+      }
     }
-  } catch (e) {
-    authority = authorityString
   }
 
   if (typeof authority === 'string') {
-    return [ authority ]
-  } // preview.pro.ant.design only do not use in your production.
-  // preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。
-
-  if (!authority && ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site') {
-    return [ 'admin' ]
+    return [authority]
   }
 
   return authority
 }
-// export function setAuthority(authority) {
-//   const proAuthority = typeof authority === 'string' ? [ authority ] : authority
-//   localStorage.setItem('antd-pro-authority', JSON.stringify(proAuthority)) // auto reload
+export function setAuthority(authority) {
+  const proAuthority = typeof authority === 'string' ? [authority] : authority
+  localStorage.setItem('antd-pro-authority', JSON.stringify(proAuthority)) // auto reload
 
-//   reloadAuthorized()
-// }
+  reloadAuthorized()
+}