Vue 开发实战实战篇 # 31:如何将菜单和路由结合

Posted 凯小默

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue 开发实战实战篇 # 31:如何将菜单和路由结合相关的知识,希望对你有一定的参考价值。

说明

【Vue 开发实战】学习笔记。

实现效果

BasicLayout.vue

<template>
    <div :class="[`nav-theme-$navTheme`, `nav-layout-$navLayout`]">
        <a-layout id="components-layout-demo-side" style="min-height: 100vh">
            <a-layout-sider
                v-if="navLayout === 'left'"
                v-model="collapsed"
                width="256px"
                collapsible
                :theme="navTheme"
                :trigger="null"
            >
                <div class="logo">Ant Design Vue Pro</div>
                <SiderMenu :theme="navTheme"/>
            </a-layout-sider>
            <a-layout>
                <a-layout-header style="background: #fff; padding: 0">
                    <a-icon
                        class="trigger"
                        :type="collapsed ? 'menu-unfold' : 'menu-fold'"
                        @click="collapsed = !collapsed"
                    ></a-icon>
                    <Header />
                </a-layout-header>
                <a-layout-content style="margin: 0 16px">
                    <router-view></router-view>
                </a-layout-content>
                <a-layout-footer style="text-align: center">
                    <Footer />
                </a-layout-footer>
            </a-layout>
        </a-layout>
        <SettingDrawer />
    </div>
</template>

<script>
import Header from "./Header";
import SiderMenu from "./SiderMenu";
import Footer from "./Footer";
import SettingDrawer from "../components/SettingDrawer";
export default 
    data() 
        return 
            collapsed: false,
        ;
    ,
    components: 
        Header,
        SiderMenu,
        Footer,
        SettingDrawer,
    ,
    computed: 
        navTheme() 
            return this.$route.query.navTheme || "dark";
        ,
        navLayout() 
            return this.$route.query.navLayout || "left";
        ,
    ,
;
</script>

<style lang="less" scoped>
.trigger 
    padding: 0 20px;
    line-height: 64px;
    font-size: 20px;
    &:hover 
        background-color: #eeeeee;
    

.logo 
    height: 64px;
    line-height: 64px;
    text-align: center;
    overflow: hidden;

.nav-theme-dark 
    .logo 
        color: #fff;
    

</style>

SiderMenu.vue

<template>
    <div style="width: 256px">
        <a-menu
            :selectedKeys="selectedKeys"
            :openKeys.sync="openKeys"
            mode="inline"
            :theme="theme"
        >
            <template v-for="item in menuData">
                <a-menu-item v-if="!item.children"
                    :key="item.path"
                    @click="() => $router.push(path: item.path, query: $router.query)"
                >
                    <a-icon v-if="item.meta.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 SubMenu from "./SubMenu.vue";
export default 
    props: 
        theme: 
            type: String,
            default: "dark"
        
    ,
    components: 
        "sub-menu": SubMenu
    ,
    data() 
        this.selectedKeysMap = ;
        this.openKeysMap = ;
        const menuData = this.getMenuData(this.$router.options.routes)
        return 
            collapsed: false,
            menuData,
            selectedKeys: this.selectedKeysMap[this.$route.path],
            openKeys: this.collapsed ? [] : this.openKeysMap[this.$route.path]
        ;
    ,
    watch: 
        "$route.path": function(val) 
            this.selectedKeys = this.selectedKeysMap[val];
            this.openKeys = this.collapsed ? [] : this.openKeysMap[val];
        
    ,
    methods: 
        getMenuData(routes = [], parentKeys = [], selectedKeys) 
            const menuData = [];
            routes.forEach(item => 
                if(item.name && !item.hideInMenu) 
                    this.openKeysMap[item.path] = parentKeys;
                    this.selectedKeysMap[item.path] = [selectedKeys || item.path];
                    const newItem = ...item;
                    delete newItem.children;
                    if(item.children && !item.hideChildrenInMenu) 
                        newItem.children = this.getMenuData(item.children, [...parentKeys, item.path]);
                    else
                        this.getMenuData(
                            item.children, 
                            selectedKeys ? parentKeys : [...parentKeys, item.path],
                            selectedKeys || item.path
                        );
                    
                    menuData.push(newItem);
                else if(
                    !item.hideInMenu &&
                    !item.hideChildrenInMenu &&
                    item.children
                ) 
                    menuData.push(...this.getMenuData(item.children, [...parentKeys, item.path]));
                
            );
            return menuData;
        
    ,
;
</script>

SubMenu.vue

<template functional>
    <a-sub-menu :key="props.menuInfo.path">
        <span slot="title">
            <a-icon v-if="props.menuInfo.meta.icon" :type="props.menuInfo.meta.icon" />
            <span> props.menuInfo.meta.title </span>
        </span>
        <template v-for="item in props.menuInfo.children">
            <a-menu-item v-if="!item.children"
                :key="item.path"
                @click="() => parent.$router.push(path: item.path, query: parent.$router.query)"
            >
                <a-icon v-if="item.meta.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-sub-menu>
</template>
<script>
export default 
    props: ["menuInfo"],
;
</script>

路由配置

import Vue from "vue";
import VueRouter from "vue-router";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
import NotFound from "../views/404";

Vue.use(VueRouter);

const routes = [
	
		path: "/user",
        hideInMenu: true,
		component: () =>
			import(/* webpackChunkName: "layout" */ "../layouts/UserLayout"),
		children: [
			
				path: "/user",
				redirect: "/user/login"
			,
			
				path: "/user/login",
				name: "login",
				component: () =>
					import(/* webpackChunkName: "user" */ "../views/User/Login"),
			,
			
				path: "/user/register",
				name: "register",
				component: () =>
					import(/* webpackChunkName: "user" */ "../views/User/Register"),
			
		],
	,
	
		path: "/",
		component: () =>
			import(/* webpackChunkName: "layout" */ "../layouts/BasicLayout"),
		children: [
			
				path: "/",
				redirect: "/dashboard"
			,
			
				path: "/dashboard",
				redirect: "/dashboard/analysis"
			,
			
				path: "/dashboard",
				name: "dashboard",
                meta:  icon: "dashboard", title: "仪表盘" ,
				component:  render: h => h("router-view"),
				children: [
					
						path: "/dashboard/analysis",
						name: "analysis",
                        meta:  title: "分析页" ,
						component: () =>
							import(/* webpackChunkName: "dashboard" */ "../views/Dashboard/Analysis"),
					,
				]
			,
			
				path: "/form",
				name: "form",
                meta:  icon: "form", title: "表单" ,
				component:  render: h => h("router-view"),
				children: [
					
						path: "/form",
						redirect: "/form/basic-form"
					,
					
						path: "/form/basic-form",
						name: "basicform",
                        meta:  title: "基础表单" ,
						component: () =>
							import(/* webpackChunkName: "form" */ "../views/Forms/BasicForm"),
					,
					
						path: "/form/step-form",
						name: "stepform",
                        hideChildrenInMenu: true,
                        meta:  title: "分步表单" ,
                        component: () =>
							import(/* webpackChunkName: "form" */ "../views/Forms/StepForm"),
						children: [
							
								path: "/form/step-form",
								redirect: "/form/step-form/info"
							,
							
								path: "/form/step-form/info",
								name: "info",
								component: () =>
									import(/* webpackChunkName: "form" */ "../views/Forms/StepForm/Step1"),
							,
							
								path: "/form/step-form/confirm",
								name: "confirm",
								component: () =>
									import(/* webpackChunkName: "form" */ "../views/Forms/StepForm/Step2"),
							,
							
								path: "/form/step-form/result",
								name: "result",
								component: () =>
									import(/* webpackChunkName: "form" */ "../views/Forms/StepForm/Step3"),
							,
						]
					,
				]
			
		],
	,
	
		path: "*",
		name: "404",
        hideInMenu: true,
		component: NotFound
	
];

const router = new VueRouter(
	mode: "history",
	base: process.env.BASE_URL,
	routes
);

// 路由守卫
router.beforeEach((to, from, next) => 
    if(to.path !== from.path) 

以上是关于Vue 开发实战实战篇 # 31:如何将菜单和路由结合的主要内容,如果未能解决你的问题,请参考以下文章

Vue 开发实战实战篇 # 29:如何设计一个高扩展性的路由

VUE项目实战21用户列表开发-基本UI布局

Vue 开发实战生态篇 # 20:选择何种模式的路由及底层原理

VUE项目实战20实现首页路由重定向及左侧菜单路由链接

Vue 开发实战实战篇 # 28:如何自定义Webpack和Babel配置

Vue 开发实战实战篇 # 41:如何管理系统中使用的图标