General background management system will have administrator and ordinary user distinction, so to do permission control

Train of thought

  1. Create the public login page to allow users to login and match different permission modules based on user identities
  2. The authentication interception is carried out on the hook before the route, and the interception condition is divided into whitelist (no interception), login state is not logged in, login state is logged in
  3. Route registration is divided into two steps. The public page is registered directly, and the permission route is registered according to the identity of the login user
  4. To resolve the problem that keep-alive is not cached when the route hierarchy is too deep

implementation

  1. Install dependencies
"dependencies": {
	"nprogress": "^ 0.2.0." "."vue-router": "^ 4.0.6"
  }
Copy the code
  1. Create the configuration file config in the SRC directory and create setting.config.js to centrally manage some configuration information
/** src/config/setting.config.js */
// Project name
export const title: string = 'Risk Control Management Platform'
// Title delimiter
export const titleSeparator: string = The '-'
// Whether the title is reversed
// if false: "page-title"
// if true: "title-page"
export const titleReverse: boolean = true
// Maximum number of cached routes
export const keepAliveMaxNum: number = 20
// Routing mode. The value can be history or hash
export const routerMode: 'hash' | 'history' = 'hash'
// Routes without token verification
export const routesWhiteList: string[] = [
  '/login'.'/login/vip'.'/ 404'.'/ 403'
]
// Whether to enable login blocking
export const loginInterception: boolean = true

Copy the code
  1. Create a router folder in the SRC directory and create index.ts router.ts
// src/router/index.ts
import { createRouter, createWebHashHistory, createWebHistory, RouteRecordRaw } from 'vue-router'
import { loginInterception, routerMode, routesWhiteList, title, titleReverse } from '@/config/setting.config'
import Layout from '@/views/layout/index.vue'
import VabProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { store } from '@/store'

// Permission routes are reloaded with each refresh
let routerLoad = false

VabProgress.configure({
  easing: 'ease'.speed: 500.trickleSpeed: 200.showSpinner: false
})

const routes: Array<RouteRecordRaw> = [
  {
    path: '/'.component: Layout,
    redirect: '/home'
  },
  {
    path: '/home'.name: 'Home'.component: () = > import('@/views/index/index.vue'),
    meta: {
      noKeepAlive: true}}, {path: '/login'.name: 'Login'.component: () = > import('@/views/login/index.vue')}, {path: '/ 403'.name: '403'.component: () = > import('@/views/403.vue')}, {path: '/:pathMatch(.*)*'.name: '404'.component: () = > import('@/views/404.vue')}]const router = createRouter({
  history: routerMode === 'hash'
    ? createWebHashHistory(import.meta.env.BASE_URL)
    : createWebHistory(import.meta.env.BASE_URL),
  routes
})

export function resetRoute (routes: any) :void {
  router.addRoute({
    path: '/'.component: Layout,
    redirect: '/home'.children: getAllFirstRoute(routes)
  })
}

/** * solve the keep-alive problem *@param routes
 * @param result* /
function getAllFirstRoute (routes: any, result: any = []) :any {
  routes.forEach((route: any) = >{ result.push({ ... route,children: [].path: route.meta.realPath
    })
    if (route.children && route.children.length > 0) {
      result = getAllFirstRoute(route.children, result)
    }
  })
  return result
}

router.beforeEach(async (to, from, next) => {
  VabProgress.start()
  let hasToken = store.getters['user/token']
  if(! loginInterception) hasToken =true
  if (routesWhiteList.includes(to.path)) {
    // Whitelist does not validate
    next()
  } else if (hasToken) {
    / / have to log in
    if (routerLoad) {
      next()
    } else {
      await store.dispatch('router/setRoutes')
      routerLoad = true
      next(to.path)
    }
  } else {
    / / not logged in
    next(store.getters['router/logoutUrl'])
  }
  VabProgress.done()
})

router.afterEach(route= > {
  if (route.meta && route.meta.title) {
    if (titleReverse) {
      document.title = `${route.meta.title} - ${title}`
    } else {
      document.title = `${title} - ${route.meta.title}`}}})export default router

Copy the code
  1. Status management Added router module, updated state.d.ts, and created router.ts
// src/state/state.d.ts
declare namespace MyStore {
  type language = 'zh-cn' | 'en'
  type RouterRoleType = 'administrators' | 'headquarters' | 'subsidiary'
  interface RouteMeta {
    title: string,
    icon: string,
    hidden: boolean,
    noKeepAlive: boolean,
    fullPath: string,
    realPath: string
  }
  interface Route {
    path: string,
    component: any,
    redirect: string,
    name: string,
    meta: RouteMeta,
    children: Array<Route>
  }
  interface State {
    count: number
  }
  interface SettingState {
    language: language
  }
  interface RouterState {
    roleType: RouterRoleType,
    routes: Route,
    cachedRoutes: Array<Route>
  }
}

Copy the code
// src/state/modules/router.ts
// import routers from '@/router/router.json'
import routers from '@/router/router'
import { convertRouter } from '@/utils/routes'
import { resetRoute } from '@/router'
export default {
  name: 'router'.namespaced: true.state: () = > {
    const state: MyStore.RouterState = {
      roleType: 'headquarters'.// Role type
      routes: {
        children: []},cachedRoutes: []}return state
  },
  getters: {
    logoutUrl () {
      return '/login'}},mutations: {
    setRoutes (state: MyStore.RouterState, routes: MyStore.Route) {
      state.routes = routes
    },
    setRoleType (state: MyStore.RouterState, type: MyStore.RouterRoleType) {
      state.roleType = type
    },
    setCachedRoutes (state: MyStore.RouterState, routes: Array<MyStore.Route>) {
      state.cachedRoutes = routes
    }
  },
  actions: {
    async setRoutes ({ commit, state }: any) {
      // @ts-ignore
      const routes = routers[state.roleType]
      commit('setRoutes', routes)
      const syncRoutes = convertRouter([routes])
      resetRoute(syncRoutes)
    },
    async setCachedRoutes ({ commit }: any, routes: any) {
      commit('setCachedRoutes', routes)
    }
  }
}

Copy the code
  1. Create tool methods to resolve routes
// src/utils/router.ts
export function convertRouter (asyncRoutes: Array<any>, parentUrl: string = ' ') :any {
  return asyncRoutes.map(route= > {
    // Dynamic splicing component, but after publishing online invalid, temporarily suspended
    // if (route.component) {
    // if (route.component === 'Layout') {
    // route.component = () => import('.. /views/layout/index.vue')
    // } else {
    // const index = route.component.indexOf('views')
    // const path =
    // index > 0 ? route.component.slice(index) : `views/${route.component}`
    // // route.component = () => import(`.. /${path}/index.vue`)
    // route.component = defineAsyncComponent(() => import(`.. /${path}/index.vue`))
    / /}
    // }
    / / meta
    // fullPath is used for menu activation
    // realPath component full path, used to register routes
    if(! route.meta) route.meta = {hidden: false.fullPath: ' '.realPath: ' ' }
    if(! parentUrl) {ParentUrl is not passed in at the first level
      route.meta.fullPath = route.path
      route.meta.realPath = route.path
    } else if (route.meta.hidden) {
      // If the current is hidden and not displayed in menu, fullPath is set to the upper-level route
      route.meta.fullPath = parentUrl
      route.meta.realPath = `${parentUrl}/${route.path}`
    } else {
      route.meta.fullPath = `${parentUrl}/${route.path}`
      route.meta.realPath = `${parentUrl}/${route.path}`
    }

    if (route.children && route.children.length) { route.children = convertRouter(route.children, route.meta.fullPath) }
    if (route.children && route.children.length === 0) delete route.children
    return route
  })
}

Copy the code
  1. Update the status manager router/roleType on the login page, then jump to the page, and the route under the current permission will be automatically matched and registered in the front hook.