vue 后台管理系统(1)路由篇
vue-router 结构一致的配置
import Vue from 'vue';
import Router from 'vue-router';
import Layout from './index.vue';
Vue.use(Router);
/**
* 这种配置方式有以下弊端
* 1. product 命名空间重复写多次 代码臃肿
* 2. 导航的链式关系需要额外的代码逻辑指定(preNav), 比如 商品管理 > 商品列表 > 商品详情 > 商品编辑
* 您可能会想到路由链式关系可以通过路由段来匹配:/goods/product/:id/edit 一个路由段对应一个路由
* => (1) /goods (2) /goods/product (3) /goods/product/:id (4) /goods/product/:id/edit
* 这样做比较麻烦,而且也没有相关API支持
* 3. 从 $route.matched 获取当前匹配的路径是行不通的,比如访问 /goods/product/1/edit 只匹配到了两个
(1) /goods
(2) /goods/product/1/edit
*
*/
export const routes = [
{
name: 'M1',
component: Layout,
path: '/goods',
redirect: '/goods/product',
meta: {
title: '商品管理'
},
children: [
{
name: 'M2',
component: () => import('../Product.vue'),
path: 'product',
meta: {
title: '商品列表',
activeMenu: 'M2'
}
},
{
name: 'M3',
component: () => import('../Product.vue'),
path: 'product/add',
meta: {
title: '增加商品',
activeMenu: 'M2'
}
},
{
name: 'M4',
component: () => import('../Product.vue'),
path: 'product/:id',
meta: {
title: '商品详情',
activeMenu: 'M2'
}
},
{
name: 'M5',
component: () => import('../Product.vue'),
path: 'product/:id/edit',
meta: {
title: '编辑商品',
activeMenu: 'M2',
preNav: 'M4'
}
}
]
}
];
export default new Router({
routes: routes
});
自动注入
页面文件目录按照某种规格编写,读取文件目录生成路由,与 nuxt 类似,约束性太强,而且目录看起来总觉得很奇怪,故不采用。
自定义结构
路由结构层次清晰,能满足菜单/导航/权限 等配置。
import Vue from 'vue';
import Router from 'vue-router';
import Layout from './index.vue';
Vue.use(Router);
/**
* routes 包含了导航链式关系 以及 菜单关系
*/
export const routes = [
{
name: 'M1',
component: Layout,
path: '/product',
redirect: '/product/index',
meta: {
title: '商品管理'
},
children: [
{
name: 'M2',
component: () => import('../Product.vue'),
path: 'index',
meta: {
title: '商品列表'
}
},
{
name: 'M3',
component: () => import('../Brand.vue'),
path: 'brand',
meta: {
title: '品牌管理'
}
},
{
name: 'M4',
component: () => import('../Category.vue'),
path: 'category',
meta: {
title: '类目管理'
}
}
]
},
{
name: 'M5',
component: Layout,
path: '/order',
redirect: '/order/index',
meta: {
title: '订单管理'
},
children: [
{
name: 'M6',
component: () => import('../OrderList.vue'),
path: 'index',
meta: {
title: '订单列表'
},
children: [
{
name: 'M7',
component: () => import('../OrderDetail.vue'),
path: 'add',
hidden: true,
meta: {
title: '创建订单'
}
},
{
name: 'M8',
component: () => import('../OrderDetail.vue'),
path: ':orderId',
hidden: true,
meta: {
title: '订单详情'
},
children: [
{
name: 'M9',
component: () => import('../OrderDetail.vue'),
path: 'edit',
meta: {
title: '编辑订单'
}
}
]
}
]
}
]
}
];
/**
* 生成 vue-router routes
*/
const makeRoutes = () => {
const rList = [];
const rawChildren = (parent, children = [], top) => {
children.forEach(item => {
item.path = (parent.path + '/' + item.path).replace('//', '/');
const child = {
...item
};
delete child.children;
top.children.push(child);
rawChildren(item, item.children, top);
});
};
routes.forEach(item => {
const child = {
...item,
children: []
};
rawChildren(child, item.children || [], child);
rList.push(child);
});
return rList;
};
export default new Router({
routes: makeRoutes()
});
左侧菜单配置:
<template>
<el-menu
class="menu"
:default-active="activeMenu"
background-color="#282c43"
text-color="#fff"
active-text-color="#fc652f"
>
<MenuItem v-for="item in routes" :menu="item" :key="item.name" :routes="routes" />
</el-menu>
</template>
<script>
// sidebar.vue
import { routes } from '../router';
import MenuItem from './menu-item';
export default {
components: {
MenuItem
},
data() {
return {
routes,
isCollapse: true
};
},
computed: {
activeMenu() {
const meta = this.$route.meta;
return meta && meta.menuValue ? meta.menuValue : this.$route.name;
}
}
};
</script>
<style scoped>
.menu {
border-right: 0;
}
.menu-item {
padding: 0 12px;
line-height: 40px;
}
</style>
<template>
<div v-if="menu.meta && !menu.hidden">
<template v-if="!hasChildren(menu)" >
<el-menu-item>
<template slot="title">
<span slot="title">{{ menu.meta.title }}</span>
</template>
</el-menu-item>
</template>
<template v-else>
<el-submenu :index="getMenuIndex(menu)">
<template slot="title">
<i class="el-icon-user-solid" v-if="showIcon"></i>
<span slot="title">{{ menu.meta.title }}</span>
</template>
<child-menu
v-for="child in menu.children"
:key="child.name"
:menu="child"
:show-icon="false"
/>
</el-submenu>
</template>
</div>
</template>
<script>
// menu-item.vue
import router from '../router';
export default {
name: 'child-menu',
props: {
menu: {
type: Object,
default: null
},
'show-icon': {
type: Boolean,
default: true
}
},
created() {
this.$watch('menu', v => {
console.log(v);
});
},
methods: {
getMenuIndex($route) {
return $route.name;
},
hasChildren(menu) {
if (!menu.children) return false;
return !!menu.children.filter(menu => !menu.hidden).length;
},
gotoPath(route) {
if (route.name !== this.$route.name) {
this.$router.push({ name: route.name });
}
},
hasPermission(route) {
return true;
}
}
};
</script>
<style scoped>
.menu {
border-right: 0;
}
.menu-item {
padding: 0 12px;
line-height: 40px;
}
</style>
导航面包屑:
<template>
<div>
<span v-for="bread in breadList" :key="bread.name">
{{ bread.meta.title }}
</span>
</div>
</template>
<script>
import { routes } from './router';
export default {
data() {
return {
routes,
breadList: []
};
},
created() {
let res = null;
const find = (list, path) => {
list.forEach(item => {
const curPath = [...path].concat(item);
if (item.name === this.$route.name) {
res = curPath;
}
item.children && find(item.children, curPath);
});
};
find(routes, []);
this.breadList = res;
}
};
</script>