“This is the first day of my participation in the First Challenge 2022. For details: First Challenge 2022.”
preface
When doing Vue management system, will encounter a requirement: each user’s permission is not the same, so he can access the page (routing), can operate the menu options are not the same, if controlled by the back end, we need to achieve dynamic routing, dynamic rendering side menu bar.
Dynamic routing
- In the example management system, each user has different permissions and can access different routing pages. The accessible routing pages are dynamically configured by the back-end based on permissions
- The front-end needs to dynamically add and delete routes according to the routing table returned by the back-end interface, so as to generate the routes owned by the user.
Key points: Implement dynamic routing API
- Router.addroute () // Adds a route while the application is already running
- Router.removeroute () // Remove the route while the application is already running
Define common page routes (which any user will have)
For example, no matter what user can access the login page, error page 404.
import { createRouter, createWebHashHistory } from 'vue-router'
const publicRoutes = [
{
path: '/'.redirect: { path: '/login'}}, {path: '/login'.name: 'login'.component: () = > import('.. /views/login')}, {path: '/ 404'.name: '404'.component: () = > import('.. /views/404')}, {path: '/home'.name: 'home'.component: () = > import('.. /views/home'),
redirect: '/welcome'.children: [{path: '/:pathMatch(.*)*'.// Capture all routes or 404 Not found routes
component: () = > import('.. /views/welcome'}]}]const router = createRouter({
history: createWebHashHistory(),
routes: publicRoutes
})
export default router
Copy the code
Interface data: The routing data of the interface is simulated here (data simplification is carried out here for demonstration purposes, and data structure format may be converted in actual situation)
navigationList : [
{
id: 1.icon: 'icon-jurassic_user'.name: 'User Management'.url: '/user'
},
{
id: 2.icon: 'icon-jurassic_user'.name: 'Role Management'.url: '/role'
},
{
id: 3.icon: 'icon-shebei'.name: 'Equipment Management'.url: '/device'}]Copy the code
Time to Add dynamic routes (router.beforeeach)
Use the global front-guard router.beforeEach to determine whether a dynamic route has been added before jumping to a route. If not, obtain the data to add the route. (Router. beforeEach also does login interception, omitted here)
import store from '@/store'
// I use a vuex variable asyncRoutestMark to indicate whether a route is concatenated
router.beforeEach((to, from, next) = > {
if(! store.state.asyncRoutestMark) {// navigationList is the data returned by the simulation interface above
// All new routes are child routes of home.
// Meta stores information that can be used for permission verification or other purposes
navigationList.forEach( navigation= > {
router.addRoute('home', {
path: navigation.url,
meta: { name: navigation.name, isAsync: true.icon: navigation.icon },
name: menu.url,
component: () = > import(`.. /views/${menu.url}`)})})console.log(router.getRoutes(), 'View existing routes')
store.commit('setAsyncRoutestMark'.true) // Change the identity to true after adding the routenext({ ... to,replace: true }) // The route is redirected
} else {
next()
}
})
Copy the code
Using the router.getroutes () method to view the existing route, we will see that the new route has been added.
So we have dynamic routing!
Dynamic side menu bar
- This is the effect we want to achieve, based on the interface data dynamic rendering, can automatically render regardless of the level, level 1 menu, level 2 menu, level 3 or more (but generally only up to level 3 haha).
Many component libraries can achieve this function, here we will use Ant Design of Vue component library embedded menu component (as shown below) to achieve, there are parent menu, child menu, the parent menu is wrapped with A-sub-menu, child menu is directly used a-menu-item, You can go to the documentation and see how the components are used.
Interface data: This simulates the menu data of the interface (the actual situation may require conversion of data structure format)
menuList :[
{
url: ' '.name: 'People Management'.icon: 'icon-renyuan'.menuId: 1.children: [{url: '/user'.name: 'User Management'.icon: 'icon-jurassic_user'.menuId: 1001.children: []}, {url: '/role'.name: 'Role Management'.icon: 'icon-jiaose'.menuId: 1002.children: []}]}, {url: '/device'.name: 'Equipment Management'.icon: 'icon-shebei'.menuId: 2}]Copy the code
Important: Component recursion
Using the V-for loop menu data array, render the component library Ant Design of Vue’s menu components in two cases.
- If there are children, then render the a-sub-menu and wrap the component itself, passing the children data to the calling component itself, that is, the recursive calling component itself, then the calling component itself will repeat the above logical judgment until there are no children. That is, the second case is encountered, terminating the recursive call.
- If there are no children, then a-menu-item is displayed.
Below is the menu component, named MenuList, which is used in recursive calls to render the menu according to different data
No icon version
<template>
<template v-for="menu in menuList" :key="menu.menuId">
<a-sub-menu v-if="menu.children && menu.children.length" :key="menu.menuId">
<template #title>{{ menu.name }}</template>
<MenuList :menuList="menu.children" />
</a-sub-menu>
<a-menu-item :key="menu.menuId" v-else>
<span>{{ menu.name }}</span>
</a-menu-item>
</template>
</template>
<script setup>
import { defineProps } from 'vue'
defineProps({
menuList: {
type: Array.default: () = >[]}})</script>
Copy the code
Results the following
Icon version
The icon is matched according to the icon of the interface data. There are many methods, such as iconFont, SVG, PNG, mainly to correspond to the name of the icon. Here, the component library provides the iconFont method using icon.
<template>
<template v-for="menu in menuList" :key="menu.menuId">
<a-sub-menu v-if="menu.children && menu.children.length" :key="menu.menuId">
<template #icon>
<icon-font :type="menu.icon" />
</template>
<template #title>{{ menu.name }}</template>
<MenuList :menuList="menu.children" />
</a-sub-menu>
<a-menu-item :key="menu.menuId" v-else>
<template #icon>
<icon-font :type="menu.icon" />
</template>
<span>{{ menu.name }}</span>
</a-menu-item>
</template>
</template>
<script setup>
import { defineProps } from 'vue'
import { createFromIconfontCN } from '@ant-design/icons-vue'
const IconFont = createFromIconfontCN({
scriptUrl: '//at.alicdn.com/t/font_2572336_4hg62uu7hxd.js'
})
defineProps({
menuList: {
type: Array.default: () = >[]}})</script>
Copy the code
The effect is as follows:
Now we have a dynamic side menu bar!
conclusion
If this article is helpful to you, I am very happy, if there are mistakes or better suggestions welcome to put forward!