Просмотр исходного кода

enhance permission authenticate

vbea 2 лет назад
Родитель
Сommit
1f791a0d9c

BIN
Strides-Admin/src/assets/avatar-default.png


BIN
Strides-Admin/src/assets/navigation_account.png


BIN
Strides-Admin/src/assets/navigation_logout.png


BIN
Strides-Admin/src/assets/navigation_message.png


BIN
Strides-Admin/src/assets/navigation_notification.png


+ 116 - 47
Strides-Admin/src/layout/components/Navbar.vue

@@ -10,51 +10,78 @@
       id="breadcrumb-container"
       class="breadcrumb-container" />
 
-    <div class="right-menu">
-      <template v-if="device !== 'mobile'">
+    <div
+      class="right-menu"
+      v-show="device !== 'mobile'">
+      <el-dropdown
+        @command="(v) => handleCommand(v)"
+        placement="top-start"
+        trigger="hover">
         <div class="right-menu-item">
-          <img
-            src="../../assets/navigation_account.png"
-            class="account"/>
-          <span class="trademarker">{{username}}</span>
-        </div>
-        <div
-          class="right-menu-image"
-          @click="onClickNotificationButton">
-          <img src="../../assets/navigation_notification.png"/>
-        </div>
-        <div
-          class="right-menu-image"
-          @click="onClickMessageButton">
-          <img src="../../assets/navigation_message.png"/>
+          <i class="el-icon-user-solid account"></i>
+          <span class="trademarker" v-if="username">
+            Hi, {{username}}
+          </span>
         </div>
-        <div
-          class="right-menu-image"
-          @click="logout">
-          <img src="../../assets/navigation_logout.png"/>
-        </div>
-      </template>
+        <el-dropdown-menu
+          slot="dropdown"
+          class="account-dropdown">
+          <div class="account-dropdown-item">
+            <img
+              class="avatar"
+              src="../../assets/avatar-default.png"
+            />
+            <div class="flexl">
+              <div class="flexc">
+                <span class="username">{{username}}</span>
+                <span class="role" v-if="role">{{role}}</span>
+              </div>
+              <span class="email"><{{email}}></span>
+            </div>
+          </div>
+          <el-dropdown-item divided command="logout" style="padding: 5px 20px;">
+            <span>Log Out</span>
+          </el-dropdown-item>
+        </el-dropdown-menu>
+      </el-dropdown>
+      <!-- <div
+        class="right-menu-image"
+        @click="onClickNotificationButton">
+        <i class="el-icon-message"></i>
+      </div> -->
+      <div
+        class="right-menu-image"
+        @click="onClickMessageButton">
+        <i class="el-icon-message-solid"></i>
+      </div>
+      <div
+        class="right-menu-image"
+        @click="logout">
+        <i class="el-icon-switch-button bold"></i>
+      </div>
+    </div>
+    <div
+      class="right-menu"
+      v-show="device == 'mobile'">
       <el-dropdown
         class="avatar-container"
         @command="(v) => handleCommand(v)"
-        trigger="click"
-        v-else>
+        trigger="click">
         <div class="avatar-wrapper">
-          <!-- <img :src="adminIcon" class="user-avatar"> -->
           <i class="el-icon-menu"/>
         </div>
         <el-dropdown-menu
           slot="dropdown"
           class="user-dropdown">
-          <el-dropdown-item>
+          <el-dropdown-item command="">
             <span class="fixed-username">Hi, {{username}}</span>
           </el-dropdown-item>
           <el-dropdown-item divided command="onClickMessageButton">
             <span>Message</span>
           </el-dropdown-item>
-          <el-dropdown-item divided command="onClickNotificationButton">
+          <!--el-dropdown-item divided command="onClickNotificationButton">
             <span>Email</span>
-          </el-dropdown-item>
+          </el-dropdown-item-->
           <el-dropdown-item divided command="logout">
             <span>Log Out</span>
           </el-dropdown-item>
@@ -68,20 +95,12 @@
 import { mapGetters } from 'vuex'
 import Breadcrumb from '@/components/Breadcrumb'
 import Hamburger from '@/components/Hamburger'
-import ErrorLog from '@/components/ErrorLog'
-import Screenfull from '@/components/Screenfull'
-import SizeSelect from '@/components/SizeSelect'
-import Search from '@/components/HeaderSearch'
-import {getUserName} from '../../utils/auth.js'
+import {getUserName, getEmail, getRoleName} from '../../utils/auth.js'
 
 export default {
   components: {
     Breadcrumb,
-    Hamburger,
-    ErrorLog,
-    Screenfull,
-    SizeSelect,
-    Search
+    Hamburger
   },
   computed: {
     ...mapGetters([
@@ -92,11 +111,15 @@ export default {
   },
   data() {
     return {
-      username: 'Admin'
+      username: 'Admin',
+      email: '',
+      role: ''
     }
   },
   created() {
     this.username = getUserName()
+    this.email = getEmail()
+    this.role = getRoleName()
   },
   methods: {
     toggleSideBar() {
@@ -118,13 +141,15 @@ export default {
       })
     },
     onClickMessageButton() {
-      console.log(' click message button .............')
+      this.$router.push({
+        path: "/notification-management/in-app-notification"
+      })
     },
-    onClickNotificationButton() {
+    /*onClickNotificationButton() {
       console.log(' click notification button .............')
-    },
+    },*/
     handleCommand(v) {
-      this[v]()
+      if (v) this[v]()
     }
   }
 }
@@ -203,6 +228,8 @@ export default {
 
     .right-menu-item {
       display: flex;
+      color: #555;
+      font-size: 24px;
       padding: 0 30px;
       align-items: center;
       justify-content: center;
@@ -210,14 +237,14 @@ export default {
     
     .right-menu-image {
       cursor: pointer;
+      color: #888;
+      font-size: 24px;
       padding: 8px 20px;
       border-left: 1px solid #eee;
-      img {
-        width: 22px;
-        height: 22px;
+      .bold {
+        font-weight: bold;
       }
     }
-
   }
 }
 </style>
@@ -233,4 +260,46 @@ export default {
     padding-right: 10px;
     text-transform: capitalize;
   }
+  .user-dropdown {
+    padding: 0 !important;
+  }
+  .account-dropdown {
+    padding: 0 !important;
+    min-width: 200px;
+  }
+  .account-dropdown-item {
+    display: flex;
+    font-size: 24px;
+    align-items: center;
+    padding: 10px 20px;
+    margin-right: 20px;
+    font-family: sans-serif;
+    font-style: normal;
+    font-weight: normal;
+  }
+  .avatar {
+    width: 50px;
+    height: 50px;
+  }
+  .username {
+    padding-left: 10px;
+    font-size: 16px;
+    text-transform: capitalize;
+    color: #333333;
+    transform: scale(.9);
+  }
+  .email {
+    padding-left: 6px;
+    font-size: 13px;
+    color: #333333;
+    transform: scale(.9);
+  }
+  .role {
+    color: #ccc;
+    font-size: 12px;
+    padding-left: 5px;
+  }
+  .account-dropdown .el-dropdown-menu__item::before {
+    display: none;
+  }
 </style>

+ 4 - 1
Strides-Admin/src/layout/components/Sidebar/index.vue

@@ -33,12 +33,15 @@ import Logo from './Logo'
 import SidebarItem from './SidebarItem'
 import variables from '@/styles/variables.scss'
 import { getAuthRoutes } from '@/utils/auth'
+import permission from '@/utils/permission'
 
 export default {
   components: { SidebarItem, Logo },
   created() {
     this.authRoutesList = getAuthRoutes()
-    this.filterRoutes(this.$router.options.routes)
+    if (!this.permission_routes.length) {
+      this.$store.commit('permission/SET_ROUTES', this.authRoutesList);
+    }
     //this.selectedItemIndex = this.activeMenu
   },
   data() {

+ 1 - 1
Strides-Admin/src/main.js

@@ -7,7 +7,7 @@ import store from '@/store'
 import 'element-ui/lib/theme-chalk/index.css'
 import '@/styles/index.scss' // global css
 import './icons' // icon
-import './permission' // permission control
+import './router/permission' // permission control
 import vueWaves from '@/directive/waves'
 import locale from 'element-ui/lib/locale/lang/en'
 

+ 4 - 3
Strides-Admin/src/router/SupportRouter.js

@@ -14,7 +14,7 @@ export default {
     {
       path: '/support-management/feedbacks',
       component: () => import('@/views/feedback/FeedbackManagement'),
-      name: 'transactions',
+      name: 'feedbacks',
       meta: {
         title: 'Feedbacks',
         icon: 'sidebar-submenu-item',
@@ -25,7 +25,7 @@ export default {
     {
       path: '/support-management/tickets',
       component: () => import('@/views/feedback/FeedbackManagement'),
-      name: 'reservations',
+      name: 'support-tickets',
       meta: {
         title: 'Ticket Management',
         icon: 'sidebar-submenu-item',
@@ -36,7 +36,7 @@ export default {
     {
       path: '/support-management/maintenance',
       component: () => import('@/views/feedback/FeedbackManagement'),
-      name: 'reservations',
+      name: 'support-maintenance',
       meta: {
         title: 'Maintenance Management',
         icon: 'sidebar-submenu-item',
@@ -48,6 +48,7 @@ export default {
       path: '/support-management/feedback/:id',
       component: () => import('@/views/feedback/Detail'),
       name: 'feedback-view',
+      hidden: true,
       meta: {
         title: 'View Feedback',
         activeMenu: '/support-management/feedbacks'

+ 2 - 8
Strides-Admin/src/router/index.js

@@ -22,18 +22,12 @@ Vue.use(VueRouter)
 const constantRoutes = [
   {
     path: '/',
-    redirect: '/dashboard'
+    redirect: '/redirect'
   },
   {
     path: '/redirect',
-    component: Layout,
     hidden: true,
-    children: [
-      {
-        path: '/redirect/:path*',
-        component: () => import('@/views/redirect/index'),
-      }
-    ]
+    component: () => import('@/views/redirect')
   },
   {
     path: '/login',

+ 102 - 0
Strides-Admin/src/router/permission.js

@@ -0,0 +1,102 @@
+import router from './index'
+import store from '../store'
+import { Message } from 'element-ui'
+import NProgress from 'nprogress' // progress bar
+import 'nprogress/nprogress.css' // progress bar style
+import { getToken, setToken, getAuthRoutes } from '@/utils/auth' // get token from cookie
+import permission from '@/utils/permission'
+import getPageTitle from '@/utils/getPageTitle'
+
+NProgress.configure({ showSpinner: false }) // NProgress Configuration
+
+const whiteList = ['/login', '/redirect', '/404'] // no redirect whitelist
+
+router.beforeEach(async(to, from, next) => {
+  // start progress bar
+  NProgress.start()
+
+  // set page title
+  document.title = getPageTitle(to.meta.title)
+
+  // determine whether the user has logged in
+  const hasToken = getToken()
+  //console.log("权限控制", to, from);
+  const routes = store.getters.resources
+  if (routes.length == 0) {
+    let rs = getAuthRoutes();
+    if (rs && rs.length) {
+      store.commit("user/SET_RESOURCES", rs);
+      routes.push(...rs);
+    }
+  }
+  //console.log("路由", routes);
+  let validPath = !permission.enable_404;
+  if (!validPath && to.path && routes.length) {
+    for (let _route of routes) {
+      if (to.path == _route[permission.RESOURCE_KEY] || (to.meta && to.meta.activeMenu == _route[permission.RESOURCE_KEY])) {
+        validPath = true;
+        break;
+      }
+    }
+  }
+  if (hasToken) {
+    //console.log("权限通过", validPath);
+    if (to.path === '/login') {
+      // if is logged in, redirect to the home page
+      next({ path: '/' })
+      NProgress.done() // hack: https://github.com/PanJiaChen/vue-element-admin/pull/2939
+    } else if (!validPath && whiteList.indexOf(to.path) == -1) {
+      next({ path: '/' })
+      NProgress.done()
+    } else {
+      next();
+      /* 下列逻辑不用,启用自定义权限控制
+      const hasRoles = store.getters.roles && store.getters.roles.length > 0
+      if (hasRoles) {
+        next()
+      } else {
+        try {
+          // get user info
+          // note: roles must be a object array! such as: ['admin'] or ,['developer','editor']
+          const { roles } = await store.dispatch('user/getInfo')
+
+          // generate accessible routes map based on roles
+          const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
+
+          // dynamically add accessible routes
+          router.addRoutes(accessRoutes)
+
+          // hack method to ensure that addRoutes is complete
+          // set the replace: true, so the navigation will not leave a history record
+          next({ ...to, replace: true })
+        } catch (error) {
+          // remove token and go to login page to re-login
+          await store.dispatch('user/resetToken')
+          Message.error(error || 'Has Error')
+          next(`/login?redirect=${to.path}`)
+          NProgress.done()
+        }
+      }*/
+    }
+  } else {
+    //console.log("权限未通过", to.path);
+    /* has no token*/
+    if (whiteList.indexOf(to.path) !== -1) {
+      // in the free login whitelist, go directly
+      next()
+      NProgress.done()
+    } else if (!validPath) {
+      next({ path: hasToken ? '/login' : '/' })
+      NProgress.done()
+    } else {
+      // other pages that do not have permission to access are redirected to the login page.
+      next(`/login?redirect=${to.path}`)
+      NProgress.done()
+    }
+  }
+})
+
+router.afterEach(() => {
+  // finish progress bar
+  NProgress.done()
+})

+ 1 - 0
Strides-Admin/src/store/getters.js

@@ -13,5 +13,6 @@ const getters = {
   errorLogs: state => state.errorLog.logs,
   selectedSite: state => state.site.selectedSite,
   selectedUser: state => state.userManagement.selectedUser,
+  resources: state => state.user.resources
 }
 export default getters

+ 26 - 2
Strides-Admin/src/store/modules/permission.js

@@ -1,4 +1,6 @@
-import { asyncRoutes, constantRoutes } from '@/router'
+import { asyncRoutes, resetRouter, constantRoutes } from '@/router'
+import { getAuthRoutes } from '@/utils/auth'
+import permission from '@/utils/permission'
 
 /**
  * Use meta.role to determine if the current user has permission
@@ -41,8 +43,10 @@ const state = {
 
 const mutations = {
   SET_ROUTES: (state, routes) => {
+    resetRouter()
     state.addRoutes = routes
-    state.routes = constantRoutes.concat(routes)
+    state.routes = constantRoutes
+    filterRoutes(state.routes)
   }
 }
 
@@ -61,6 +65,26 @@ const actions = {
   }
 }
 
+function filterRoutes(routes) {
+  //console.log("路由遍历", routes);
+  routes.forEach((route) => {
+    const authRoute = state.addRoutes.find(authRoute => (authRoute[permission.RESOURCE_KEY] === route.path))
+    if (authRoute) {
+      route.hidden = false
+      if (route.meta) {
+        route.meta.onlyView = authRoute.onlyView || false
+      } else {
+        route.meta = { onlyView: authRoute.onlyView }
+      }
+    } else {
+      route.hidden = true
+    }
+    if (route.children) {
+      filterRoutes(route.children)
+    }
+  })
+}
+
 export default {
   namespaced: true,
   state,

+ 5 - 1
Strides-Admin/src/store/modules/user.js

@@ -16,7 +16,8 @@ const state = {
   name: '',
   avatar: '',
   introduction: '',
-  roles: []
+  roles: [],
+  resources: []
 }
 
 const mutations = {
@@ -34,6 +35,9 @@ const mutations = {
   },
   SET_ROLES: (state, roles) => {
     state.roles = roles
+  },
+  SET_RESOURCES: (state, resources) => {
+    state.resources = resources
   }
 }
 

+ 2 - 2
Strides-Admin/src/styles/index.scss

@@ -39,7 +39,7 @@ label {
   background-clip: padding-box;
 }
 ::-webkit-scrollbar-thumb:hover {
-  background-color: #A5A5A5;
+  background-color: rgba($--color-primary, 0.5);
 }
 
 #app {
@@ -368,7 +368,7 @@ input:-webkit-autofill:active  {
 }
 
 .el-table thead {
-  color: #000;
+  color: #000 !important;
   th {
     background-color: rgba($--color-primary, 0.2);
   }

+ 26 - 18
Strides-Admin/src/utils/permission.js

@@ -1,21 +1,29 @@
 import store from '@/store'
 
-/**
- * @param {Array} value
- * @returns {Boolean}
- * @example see @/views/permission/directive.vue
- */
-export default function checkPermission(value) {
-  if (value && value instanceof Array && value.length > 0) {
-    const roles = store.getters && store.getters.roles
-    const permissionRoles = value
+export default {
+  enable_404: true,
+  RESOURCE_KEY: "resourcePath",
+  /**
+   * @param {Array} value
+   * @returns {Boolean}
+   * @example see @/views/permission/directive.vue
+   */
+  checkPermission: (value) => {
+    if (value && value instanceof Array && value.length > 0) {
+      const roles = store.getters && store.getters.roles
+      const permissionRoles = value
 
-    const hasPermission = roles.some(role => {
-      return permissionRoles.includes(role)
-    })
-    return hasPermission
-  } else {
-    console.error(`need roles! Like v-permission="['admin','editor']"`)
-    return false
-  }
-}
+      const hasPermission = roles.some(role => {
+        return permissionRoles.includes(role)
+      })
+      return hasPermission
+    } else {
+      console.error(`need roles! Like v-permission="['admin','editor']"`)
+      return false
+    }
+  },
+  getTestResourceList: () => ([
+    {"resourcePath": '/dashboard'},
+    {"resourcePath": '/test'}
+  ])
+}

+ 3 - 1
Strides-Admin/src/views/access/DialogDetail.vue

@@ -275,7 +275,9 @@ export default {
       }
       this.loading = false;
       this.$nextTick(() => {
-        this.$refs['acsForm'].clearValidate();
+        if (this.$refs['acsForm']) {
+          this.$refs['acsForm'].clearValidate();
+        }
       })
     },
     getOptions() {

+ 0 - 1
Strides-Admin/src/views/limit/Add.vue

@@ -157,7 +157,6 @@
           <el-button
             style="margin-left: 20px;"
             type="primary"
-            native-type="submit"
             @click="handleClickSaveButton">
             Save
           </el-button>

+ 1 - 1
Strides-Admin/src/views/limit/CreditLimit.vue

@@ -146,7 +146,7 @@
           cancelButtonText: 'Cancel',
           type: 'warning'
         }).then(res => {
-          this.handleDeleteLimit(row.creditLimitId)
+          this.handleDeleteLimit(row.groupCreditPk)
         })
       },
       handleDeleteLimit(id) {

+ 4 - 2
Strides-Admin/src/views/login/login.vue

@@ -115,6 +115,7 @@ import Password from './Password.vue'
 import OtpView from './otp.vue'
 import api from '@/http/api/user'
 import {parseTime} from '@/utils/index'
+import permission from '@/utils/permission.js'
 const { isNavigationFailure, NavigationFailureType } = VueRouter
 
 export default {
@@ -314,15 +315,16 @@ export default {
         if (response.data.roleName === "MCST") {
           path = '/site-management'
         } else if (response.data.resources.length > 0) {
+          this.$store.commit('permission/SET_ROUTES', response.data.resources);
           let validPath = false;
           for (let _route of response.data.resources) {
-            if (path.indexOf(_route.resourcePath) >= 0) {
+            if (path.indexOf(_route[permission.RESOURCE_KEY]) >= 0) {
               validPath = true;
               break
             }
           }
           if (!validPath) {
-            path =  response.data.resources[0].resourcePath;
+            path =  response.data.resources[0][permission.RESOURCE_KEY];
           }
         }
         this.$router.push({

+ 0 - 1
Strides-Admin/src/views/ocpp/OCPPOperations.vue

@@ -315,7 +315,6 @@
           <el-button
             style="margin-left: 15px;"
             type="primary"
-            native-type="submit"
             :loading="btnLoading"
             @click="perform">
             Perform

+ 43 - 0
Strides-Admin/src/views/redirect.vue

@@ -0,0 +1,43 @@
+<template>
+  <div></div>
+</template>
+
+<script>
+import store from '@/store'
+import permission from '@/utils/permission.js'
+import { getAuthRoutes } from '@/utils/auth.js'
+export default {
+  data() {
+    return {
+      
+    }
+  },
+  created() {
+    this.checkPermission()
+  },
+  methods: {
+    checkPermission() {
+      let path = '/dashboard'
+      const routes = getAuthRoutes();
+      if (routes.length > 0) {
+        let validPath = false;
+        for (let _route of routes) {
+          if (path.indexOf(_route[permission.RESOURCE_KEY]) >= 0) {
+            validPath = true;
+            break
+          }
+        }
+        if (!validPath) {
+          path =  routes[0][permission.RESOURCE_KEY];
+        }
+      }
+      this.$router.push({
+        path: path
+      })
+    }
+  }
+}
+</script>
+
+<style>
+</style>