SiderMenu.vue
<template>
<div>
<a-button type="primary" style="margin-bottom: 16px"> LOGO </a-button>
<a-menu
:default-selected-keys="['1']"
:default-open-keys="['2']"
mode="inline"
:theme="theme"
:inline-collapsed="collapsed"
>
<template v-for="item in list">
<a-menu-item v-if="!item.children" :key="item.key">
<a-icon type="pie-chart" />
<span>{{ item.title }}</span>
</a-menu-item>
<sub-menu v-else :key="item.key + '1'" :menu-info="item" />
</template>
</a-menu>
</div>
</template>
<script>
import { mapState } from "vuex";
import { Menu } from "ant-design-vue";
const SubMenu = {
template: `
<a-sub-menu :key="menuInfo.key" v-bind="$props" v-on="$listeners">
<span slot="title">
<a-icon type="mail" /><span>{{ menuInfo.title }}</span>
</span>
<template v-for="item in menuInfo.children">
<a-menu-item v-if="!item.children" :key="item.key">
<a-icon type="pie-chart" />
<span>{{ item.title }}</span>
</a-menu-item>
<sub-menu v-else :key="item.key" :menu-info="item" />
</template>
</a-sub-menu>
`,
name: "SubMenu",
// must add isSubMenu: true
isSubMenu: true,
props: {
...Menu.SubMenu.props,
// Cannot overlap with properties within Menu.SubMenu.props
menuInfo: {
type: Object,
default: () => ({}),
},
},
};
export default {
components: {
"sub-menu": SubMenu,
},
props: {
theme: {
type: String,
default: "dark",
},
},
data() {
return {
list: [
{
key: "1",
title: "Option 1",
},
{
key: "2",
title: "Navigation 2",
children: [
{
key: "2.1",
title: "Navigation 2.1",
children: [{ key: "2.1.1", title: "Option 2.1.1" }],
},
],
},
{
key: "3",
title: "Navigation 3",
children: [
{
key: "3.1",
title: "Navigation 3.1",
},
],
},
],
};
},
computed: {
...mapState(["collapsed"]),
},
mounted() {},
methods: {},
};
</script>
<style lang="less" scoped></style>
vue.config.js
module.exports = {
runtimeCompiler: true
}
SiderMenu.vue
<template>
<div>
<a-button type="primary" style="margin-bottom: 16px"> LOGO </a-button>
<a-menu
:default-selected-keys="[$route.path]"
:default-open-keys="['/'+ $route.path.split('/')[1]]"
mode="inline"
:theme="theme"
:inline-collapsed="collapsed"
@click="onClick"
>
<template v-for="item in menu">
<a-menu-item v-if="!item.children" :key="item.path">
<a-icon :type="item.meta.icon" />
<span>{{ item.meta.title }}</span>
</a-menu-item>
<sub-menu v-else :key="item.path " :menu-info="item" />
</template>
</a-menu>
</div>
</template>
<script>
import { mapState } from "vuex";
import { Menu } from "ant-design-vue";
const SubMenu = {
template: `
<a-sub-menu :key="menuInfo.key" v-bind="$props" v-on="$listeners">
<span slot="title">
<a-icon :type="menuInfo.meta.icon" /><span>{{ menuInfo.meta.title }}</span>
</span>
<template v-for="item in menuInfo.children">
<a-menu-item v-if="!item.children" :key="item.path">
<span>{{ item.meta.title }}</span>
</a-menu-item>
<sub-menu v-else :key="item.key" :menu-info="item" />
</template>
</a-sub-menu>
`,
name: "SubMenu",
// must add isSubMenu: true
isSubMenu: true,
props: {
...Menu.SubMenu.props,
// Cannot overlap with properties within Menu.SubMenu.props
menuInfo: {
type: Object,
default: () => ({}),
},
},
};
export default {
components: {
"sub-menu": SubMenu,
},
props: {
theme: {
type: String,
default: "dark",
},
},
data() {
return {
menu: [],
};
},
computed: {
...mapState(["collapsed"]),
},
mounted() {
// 根据路由获取菜单数据
this.getMenuData(this.$router.options.routes, this.menu);
},
methods: {
getMenuData(routes, arr) {
routes.forEach((el) => {
if (!el.hiden) {
arr.push(el);
if (el.children) {
// 这里使用一个变量接收子数组作为递归的数据源
// 将item.children置空用来接收不隐藏的数据
let a = el.children;
el.children = [];
this.getMenuData(a, el.children);
}
}
});
return arr;
},
onClick(item) {
this.$router.push(item.key);
},
},
};
</script>
<style lang="less" scoped></style>
路由文件 router/index.js
import Vue from "vue";
import VueRouter from "vue-router";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
Vue.use(VueRouter);
const routes = [
{
path: "/",
redirect: "/home",
meta: {
icon: "weibo",
title: "首页",
},
component: () => import("@/layouts/BasicLayout"),
children: [
{
path: "/home",
name: "home",
meta: {
icon: "weibo",
title: "首页",
},
component: () => import("@/views/Home/Home"),
},
],
},
{
path: "/order",
name: "order",
meta: {
icon: "apple",
title: "订单",
},
component: () => import("@/layouts/BasicLayout"),
children: [
{
path: "/order/list",
name: "orderlist",
meta: {
icon: "apple",
title: "订单列表",
},
component: () => import("@/views/Order/OrderList"),
children: [
{
path: "/order/list/list1",
name: "list1",
component: () => import("@/views/Order/list1"),
meta: {
icon: "weibo",
title: "list1",
},
},
{
path: "/order/list/list2",
name: "list2",
component: () => import("@/views/Order/list2"),
meta: {
icon: "weibo",
title: "list1",
},
}
],
},
{
path: "/order/detail",
name: "orderdetail",
hiden: true,
meta: {
title: "订单详情",
},
component: () => import("@/views/Order/OrderDetail"),
},
{
path: "/order/comment",
name: "ordercomment",
meta: {
title: "订单评论",
},
component: () => import("@/views/Order/OrderComment"),
},
],
},
{
path: "/my",
name: "my",
meta: {
icon: "weibo",
title: "我的",
},
component: () => import("@/layouts/BasicLayout"),
children: [
{
path: "/my/index",
name: "myinfo",
component: () => import("@/views/My/My"),
meta: {
icon: "weibo",
title: "我的",
},
},
],
},
{
path: "/user",
component: { render: (h) => h("router-view") },
redirect: "/user/login",
hiden: true,
children: [
{
path: "/user/login",
name: "Login",
component: () => import("@/views/User/Login"),
},
{
path: "/user/register",
name: "Register",
component: () => import("@/views/User/Register"),
},
],
},
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes,
});
router.beforeEach((to, from, next) => {
NProgress.start();
next();
});
router.afterEach(() => {
NProgress.done();
});
export default router;