v3+ts–3. Routing module (including permission verification)

Foreword: The route’s permission verification method is that the backend returns the permission identification field, and the front end verifies the permission identification field. If the verification passes, the corresponding route will be displayed, and if it fails, it will not be displayed. Therefore, routes are divided into two types: those that require permission verification and those that do not.

1. Basic preparation

(1)Create file

File introduction:

router folder

  1. actionRouter.ts routes that require permission verification
  2. index.ts routing entry
  3. staticRouter.ts is a route that does not require permission verification and can be accessed by anyone.

stores

  1. permissionRouter.ts Verifies state management content related to routing permissions

utils folder

  1. router.ts writes routing guards

2. Routing page

views/home/index.vue (layout container)

<template>
  <div class="common-layout height100">
    <el-container class="height100">
      <el-aside width="200px">
        <!-- Side content -->
        <sideBar />
      </el-aside>
      <el-container>
        <el-header>
          <!-- Title content -->
          <headerBar/>
        </el-header>
        <el-main>
          <!-- Content body -->
          <router-view></router-view>
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>

<script setup lang="ts">
import headerBar from "@/components/headerBar/index.vue";
import sideBar from "@/components/sideBar/index.vue";

</script>

staticRouter.ts file

export default [
  {
    path: '/home',
    name: 'home',
    component:()=>import('@/views/home/index.vue'), //home contains a layout container. If a layout container is needed, add this component to the outermost route. If not You need to layout the container, just like /login below, write it separately outside and add hidden: true to meta.
    meta:{
      index:1,//Identifies whether it is a first-level menu
    },
    children:[
      {
        path: '/home/userDetails',
        component: () => import('@/views/testPool/index.vue'),
        meta:{
          title:'Test title-1'
        },
      },
      {
        path: '/home/abcd',
        component: () => import('@/views/testPool/index.vue'),
        meta:{
          title:'Test title-2'
        },
      },
    ],
  },
  {
    path: '/login',
    name: 'login',
    component: () => import('@/views/login/index.vue'),
    meta:{
      hidden:true, // Identifies whether to display in the sidebar menu
    }
  },
]

actionRouter.ts file

export default [
  {
    path: '/userlist',
    name: 'userlist',
    component:()=>import('@/views/home/index.vue'),
    meta:{
      index:1,
      title:'List'
    },
    children:[{
        path: '/userlist/userlist1-1',
        name: 'userlist1-1',
        component: () => import('@/views/writerlist/index.vue'),
        meta:{
          perStr:'userlist page Str',
          title:'Writer List',
        },
      },{
        path: '/userlist/userlist2-2',
        name: 'userlist2-2',
        component: () => import('@/views/userList/index.vue'),
        meta:{
          perStr:'userlist page Str',
          title:'User List',
        },
      }
    ],
  },
  {
    path: '/userDetails',
    component: () => import('@/views/testPool/index.vue'),
    meta:{
      hidden:true,
      perStr:'Details page Str',
    },
  },
  
]

index.ts file

import { createRouter, createWebHistory } from 'vue-router'
import staticRouter from "./staticRouter";
import actionRouter from "./actionRouter";

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    //First load routes that do not require permission verification
    ...staticRouter,
    {
      path:'/',
      component:()=>import('@/views/home/index.vue'),
    },
  ]
})

export const perRouter = [
  //Export the routes that need to be verified and the 404 page for backup
  ...actionRouter,
  {// Match the content that the previous route failed to match, that is, the route input is incorrect. This route must be written at the end of all routes.
    path:'/:path(.*)',
    component:() => import('@/views/404/index.vue'),
  }
]
export default router

utils/Auth.ts

import Cookies from 'js-cookie'
export const setToken = (token:string)=>{
  return Cookies.set('myToken', token, { expires: 1 })
}
export const getToken = ()=>{
  return Cookies.get('myToken')
}
export const removeToken = ()=>{
  //Add a common prefix when storing user information, and you can delete it in batches when logging out.
  for (const key in localStorage) {
    if(key.includes('xinba-')){
      localStorage.removeItem(key)
    }
  }
  return Cookies.remove('myToken')
}

utils/router.ts (routing guard-important)

import router from "@/router/index";
import { setToken,getToken} from "@/utils/Auth";
import permissionRouter from '@/stores/permissionRouter'
let noTokenRouter = ['/login']
router.beforeEach(async(to,form,next)=>{
  // If it is a whitelist route, it will be allowed directly. It is mainly used for some pages that do not require login, such as login and registration.
  if(noTokenRouter.includes(to.path))next()

  // If there is no token, go directly to the login page
  let myToken = getToken()
  if(!myToken)next({path:'/login'})
  
  //Introduce status management of verification permissions
  const myPerStore = permissionRouter()
  // perRouterFlag identifies whether the route that requires permission verification has been loaded. If false means it has not been loaded, verify the permission and then add the route.
  if(!myPerStore.perRouterFlag){
    // addRouteFn() is a method in state management to obtain authorized routes. The final result returned is the verified route.
    const addRouterList = await myPerStore.addRouteFn()
    addRouterList.forEach(item=>{
      router.addRoute(item)
    })
    // Jump to the current route again through next({...to}). If this step is not redirected, the page will be empty when the newly added route refreshes the page.
    next({...to})
  }else{
    // If the route has been loaded, jump normally
    next()
  }
})

export default router

stores/permissionRouter.ts (permission status management-core)

import { ref, computed, reactive } from 'vue'
import { defineStore } from 'pinia'
import {perRouter} from '@/router/index' //Introduce routes that require permission verification
import type { RouteRecordRaw } from 'vue-router'

// Method to verify permissions. The parameters are permission identification array and routing array.
function filterRoute(permissionArr:string[],routerList:RouteRecordRaw[]){
  let newRouter:RouteRecordRaw[] = reactive([])
  routerList.forEach((item:RouteRecordRaw)=>{
    // If perStr is not included, it means that permission verification is not required and can be displayed directly.
    // If included, verify whether perStr is in the returned permission list
    if(!item?.meta?.perStr||permissionArr.includes(item.meta.perStr as string)){
      if(item.children){
        // If there is a child, call it recursively
        item.children = filterRoute(permissionArr,item.children)
      }
      newRouter.push(item)
    }
  })
  return newRouter
}

export default defineStore('permission', () => {

  let permissionArr:string[] = reactive([]) // Array to store permission identifiers
  let perRouterFlag = ref(false); //Is the route loaded?

  // Method to verify routing permissions
  async function addRouteFn(){
    // Get the permission ID of local storage. If not, call the interface to get it.
    let catchUserPermission = JSON.parse(localStorage.getItem('xinba-userPermission')||"[]")
    if(catchUserPermission.length>0){
      catchUserPermission.forEach((item:string)=>{
        permissionArr.push(item)
      })
      perRouterFlag.value = true;//After the acquisition is completed, set this value to true, indicating that the route has been acquired.
    }else{
      //Calculate the interface to obtain the permission list
      ['userlist page Str'].forEach((item:string)=>{
        permissionArr.push(item)
      })
      perRouterFlag.value = true;//After the acquisition is completed, set this value to true, indicating that the route has been acquired.
      localStorage.setItem('xinba-userPermission',JSON.stringify(permissionArr))
    }

    return filterRoute(permissionArr,perRouter)
  }
  
  return { addRouteFn, perRouterFlag }
})

3. Implementation of sidebar effects

components/sideBar/checkPermission.ts

import router from '@/router';
import type { RouteRecordRaw } from 'vue-router'
type RouArr = RouteRecordRaw[]
// Filter out meta:{hidden:true} in routing
function filterRouter(router:RouArr):RouArr{
  return router.filter((item:RouteRecordRaw)=>{
    // If hidden is true, return false and filter out
    if(item.meta?.hidden)return false
    // If there is a child, continue the recursive call
    if(item.children & amp; & amp;item.children.length>0){
      item.children = filterRouter(item.children)
      return {...item}
    }else{
      return [item]
    }
  })
}
const allRouter = router.getRoutes()

export const routerArr = filterRouter(allRouter.filter(item=>item.meta.index==1))

components/sideBar/index.vue

<template>
  <div class="side-box">
    <el-menu
        :default-active="defaultActive"
        class="el-menu-vertical-demo"
        router
      >
      <template v-for="(route,index) in routerArr" :key="index">
        <el-sub-menu v-if="route.children & amp; & amp;route.children.length>0" :index="route.path">
          <template #title>
            <el-icon><location /></el-icon>
            <span>{<!-- -->{ route.name }}</span>
          </template>
          <el-menu-item v-for="(item,i) in route.children" :key="i" :index="item.path">{<!-- --> { item.meta?.title||'Unknown title' }}</el-menu-item>
        </el-sub-menu>
        <el-menu-item v-else :index="route.path">
          <el-icon><Menu /></el-icon>
          <span>{<!-- -->{ route.name }}</span>
        </el-menu-item>
      </template>
    </el-menu>
  </div>
</template>

<script setup lang="ts">
import { computed, reactive, ref } from "vue";
import { useRoute } from "vue-router";
import { routerArr } from "./checkPermission";
const myUseRoute = useRoute()

const defaultActive = computed(()=>{
  return myUseRoute.path
})
</script>
<style lang="scss" scoped>
.side-box{
  height: 100%;
  .el-menu{
    height: 100%;
  }
}
</style>

Page effect