# Router and Nav

Router and Nav are the key skeleton for organizing a management system.

This project router and nav are bound together, so you only have to configure the route under @/router/index.js and the sidebar nav will be dynamically generated automatically. This greatly reduces the workload of manually editing the sidebar nav. Of course, so you need to follow many conventions in configuring the route.

# Config

First let us know what configuration items are provided config route.

// set router name. It must be set,in order to avoid problems with <keep-alive>.
name: 'router-name'

meta: {
  // when you route a children below the declaration of more than one route,
  // it will automatically become a nested mode - such as the component page
  // when there is only one, the child route will be displayed as the root route
  // if you want to display your root route
  // regardless of the number of children declarations under the route
  // you can set alwaysShow: true
  // so that it will ignore the previously defined rules and always show the root route
  alwaysShow: true

  // if set to true, lt will not appear in sidebar nav.
  // e.g. login or 401 page or as some editing pages /edit/1 (Default: false)
  hidden: true

  // this route cannot be clicked in breadcrumb navigation when noRedirect is set
  redirect: noRedirect

  // required roles to navigate to this route. Support multiple permissions stacking.
  // if not set means it doesn't need any permission.
  roles: ['admin', 'editor']

  // the title of the route to show in various components (e.g. sidebar, breadcrumbs).
  title: 'title'

  // svg icon class
  icon: 'svg-name' // or el-icon-x

  // when set true, the route will not be cached by <keep-alive> (default false)
  noCache: true

  // if false, the item will hidden in breadcrumb(default is true)
  breadcrumb: false

  // if set to true, it can be fixed in tags-view (default false)
  affix: true // this is very useful in some scenarios, // click on the article to enter the article details page,

  // When you set, the related item in the sidebar will be highlighted
  // for example: a list page route of an article is: /article/list
  // at this time the route is /article/1, but you want to highlight the route of the article list in the sidebar,
  // you can set the following
  activeMenu: '/article/list'
}

Example:

{
  path: '/permission',
  component: Layout,
  redirect: '/permission/index',
  meta: { // you can set roles in root nav
    hidden: true,
    alwaysShow: true,
    roles: ['admin','editor']
  },
  children: [{
    path: 'index',
    component: _import('permission/index'),
    name: 'permission',
    meta: {
      title: 'permission',
      icon: 'lock',
      roles: ['admin','editor'], // or you can only set roles in sub nav
      noCache: true
    }
  }]
}

# Router

There are two types of routes here , constantRoutes and asyncRoutes.

constantRoutes: represents routes that do not require dynamic access, such as login page, 404, general page, and so on.

asyncRoutes: represents pages that require dynamic judgment permissions and are dynamically added through addRouters. The details will be introduced on the permission.

TIP

All routing pages here use the router lazy loading, as described in document

If you want to know more about browserHistory and hashHistory, please refer to Build & Deploy.

The other configurations are no different from the vue-router official, so check the documentation for yourself.

WARNING

There is one thing to be careful about is that the 404 page must be the last to load, if it is declared in constantRoutes. Later declared pages will be blocked to 404, see the details of the problem: addRoutes when you've got a wildcard route for 404s does not work

The project sidebar is mainly based on the el-menu of element-ui.

Also introduced in the front, the sidebar is generated dynamically by reading the route and combined with the permission judge, but also need to support the infinite nesting of routes, so here is also used to the recursive components.

Code: @/layout/components/Sidebar

This also modify many default sidebar styles of element-ui. All css can be found in @/styles/sidebar.scss and can be modified to suit your needs.

Here need to pay attention. The general sidebar has two forms, submenu andel-menu-item. One is a nested submenu, the other is a direct link. As shown below:

The sidebar has already helped you to make a judgment. When you route a children below the declaration of more than >1 routes, it will automatically become a nested mode. If the sub-route is exactly equal to one, the sub-route is displayed as a root route in the sidebar by default. If you do not want to, you can disable this feature by setting alwaysShow: true in the root route. Such as:

// no submenu, because children.length===1
{
  path: '/icon',
  component: Layout,
  children: [{
    path: 'index',
    component: ()=>import('svg-icons/index'),
    name: 'icons',
    meta: { title: 'icons', icon: 'icon'}
  }]
},

// has submenu, because children.length>=1
{
  path: '/components',
  component: Layout,
  name: 'component-demo',
  meta: {
    title: 'components',
    icon: 'component'
  },
  children: [
    { path: 'tinymce', component: ()=>import('components-demo/tinymce'), name: 'tinymce-demo', meta: { title: 'tinymce' }},
    { path: 'markdown', component: ()=>import('components-demo/markdown'), name: 'markdown-demo', meta: { title: 'markdown' }},
  ]
}

unique-opened

You can set unique-opened in Sidebar/index.vue. To control the sidebar, whether to keep only one submenu expanded.

# Nested Routes

If you have a nested Route, such as @/views/nested, Don't forget to manually add an < router-view > to the root file of the secondary directory.

 <!-- parent view  -->
<template>
  <div>
    <!-- xxx html dom  -->
    <router-view />
  </div>
</template>

Such as: @/views/nested/menu1/index.vue.

Note: As many <router-view> as the level of routes nested.


# Click the sidebar to refresh the current route

Before using the development model of spa(single page application), each time the user clicks the sidebar will request this page again, the user gradually developed the habit of clicking the current route in the sidebar to refresh the view. But now the spa is not the same, the user clicks the currently highlighted route and does not refresh the view, because the vue-router will intercept your routing, it determines your url does not change, so it will not trigger any hook or view changes.Related issue, the community has also heated discussions on the issue.

yyx990803also said that he wanted to add a way to brighten the view, but later he changed his mind again/(ㄒ o ㄒ)/~~ But demand is here, what should we do? He said it would not trigger anything without changing the current URL, so can I force the trigger? The hack is simple. By changing the url query to trigger the view changes。We listen to each link's click event on the sidebar, each click will push a different query for the router to ensure that the view is refreshed.

clickLink(path) {
  this.$router.push({
    path,
    query: {
      //Ensure that each click, query is not the same
      //to ensure that refresh the view
      t: +new Date()
    }
  })
}

ps: Don't forget to add a unique key to router-view, such as <router-view :key="$route.path"></router-view>.

But there's also a drawback the ugly query suffix behind url, such as xxx.com/article/list?t=1496832345025

You can know from the previous issue that there are many other options. In my company project, the solution adopted is to determine whether the currently clicked menu route is consistent with the current route. However, when it is consistent, it will jump to a dedicated Redirect page, which will redirect the route to Go to the page, this will have a refresh effect.

Example

Click on the global size switch button shown in the image and you will see that the page of app-main has been refreshed. It uses the method of redirecting to the Redirect page and then redirecting back to the original page.

Redirect page to /redirect when clicking

const { fullPath } = this.$route
this.$router.replace({
  path: '/redirect' + fullPath
})

The redirect page is redirected back to the original page

// redirect.vue
// https://github.com/midfar/vue3-element-admin/blob/main/src/views/redirect/index.vue
export default {
  beforeCreate() {
    const { params, query } = this.$route
    const { path } = params
    this.$router.replace({ path: '/' + path, query })
  },
  render: function(h) {
    return h() // avoid warning message
  }
}

This project also packages a breadcrumb navigation, which is also dynamically generated by the watch $route change. It is the same with the menu, you can also config it in the routing. You can also add some custom attributes to your business needs in route.meta attr. For example, you can declare breadcrumb:false in the route so that it is not displayed in breadcrumb.

Corresponding code: @/components/Breadcrumb

Previous versions of scroll were handled with css

overflow-y: scroll;

::-webkit-scrollbar {
  display: none;
}

But hack by css has some problems, in Firefox or other lower versions of the browser will be less beautiful. Second, in the case of sidebar collapses, limited to menu ofelement-ui, can not be handled in this way.

So the current version uses el-scrollbar to handle the sidebar scrolling problem.

You can also configure an external-link in the sidebar. As long as you fill in the legal url path in path, you will be able to open this page when you click on the sidebar.

E.g.

{
  "path": "external-link",
  "component": Layout,
  "children": [
    {
      "path": "https://github.com/midfar/vue3-element-admin",
      "meta": { "title": "externalLink", "icon": "link" }
    }
  ]
}

In some scenarios, users need to expand some of the sub-menu in the sidebar by default, as shown below:

default-openeds.jpg

Can be set through default-openeds, first find Sidebar Code

 <el-menu
        :default-openeds="['/example','/nested']" // Add this line of code
        :default-active="activeMenu"
        :collapse="isCollapse"
        :background-color="variables.menuBg"
        :text-color="variables.menuText"
        :unique-opened="false"
        :active-text-color="variables.menuActiveText"
        :collapse-transition="false"
        mode="vertical"
      >
        <sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
      </el-menu>

Note: default-openeds = "['example', 'nested']" is filled with route-path of submenu