Router and Navigation
Router and Nav are the key skeleton for organizing the system.
In 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, you have to follow configurations of route item
Config
First let us know what configuration items are provided config route.
// if set to true, it 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
// 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
// set router name. It must be set,in order to avoid problems with <keep-alive>.
name: 'router-name'
meta: {
// required roles to access to this route, support multiple roles.
// if not set, this route is accesible for everyone.
roles: ['admin', 'editor'],
// Required permissions to access this route, support multiple permissions
// If not set, the permission will not be checked to access this route
permissions: ['view menu zip', 'manage user'],
// the title of the route to show in various components (e.g. sidebar, breadcrumbs).
title: 'title'
// svg icon class
icon: 'svg-name'
// 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
}
Code
Example:
{
path: '/permission',
component: Layout,
redirect: '/permission/index',
alwaysShow: true, // will always show the root menu
meta: {
title: 'permission',
icon: 'lock',
permissions: ['view menu permission'], // Only accessible for role/user which has 'view menu permission' permission
},
children: [...],
}
Router
There are two types of routes: constantRoutes
and asyncRoutes
.
constantRoutes: represents routes which do not require authorized access such as login page, 404, general pages...
asyncRoutes: represents pages which require dynamic permissions/roles and are dynamically added through addRoutes
. The details will be introduced on the Roles and Permissions.
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
The 404 page must be the latest one to be loaded in the 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
Sidebar
The project sidebar is mainly based on the el-menu
of element-ui.
As mentioned before, the sidebar is dynamically generated by getting the routes and combining with the role/permission system, but also need to support the multi-level of nested routes, so here is also used to the recursive components.
Many default sidebar styles of element-ui
have been modified. 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 create nested menu. If you define more than one route in children
, it will automaticly become the nested mode. If the children
has only one route item, root menu will be shown instead. 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 with children.length===1, because alwaysShow===true
{
path: '/icon',
component: Layout,
alwaysShow: true,
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' }},
],
}
Click the sidebar to refresh the current route
With traditional web appication before SPA (Single Page Application), when user clicks on the sidebar, it will request the page again. This behaviour keeps user clicking on the menu to refresh the page. But in SPA, this behaviour is not the same. vue-router
will intercept your routing, it determines your URL does not change, so it will not trigger any hook or view changes. There are many discussions related to this issue.
yyx990803
also 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(),
}
})
}
Note: Don't forget to add a unique key
to router-view
, such as <router-view :key="$route.path"></router-view>
.
We could apply the ugly query
suffix behide the URL, such as xxx.com/article/list?t=1496832345025
to create unique URL when clicking on the sidebar
Or we can use redirect
method to jump to the same page when detecting the current clicked route is same as the current route.
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.$nextTick(() => {
this.$router.replace({
path: '/redirect' + fullPath,
});
});
The redirect
page is redirected back to the original page
// redirect.vue
// https://github.com/tuandm/laravue/blob/master/resources/js/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
},
};
Breadcrumb
This project also provides a breadcrumb navigation, which is also dynamically generated by watching $route change. It is the same with the menu, you can also config it in the routing. You can also add some custom attributes in route.meta attr for your logic. For example, you can declare breadcrumb:false
in the route so that it is not displayed in breadcrumb.
Corresponding code: @/components/Breadcrumb
Sidebar external-link
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/tuandm/laravue',
meta: { title: 'externalLink', icon: 'link' },
},
],
},