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

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

说明

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



实现效果4dcb655302b44506a16dda3b45613b15.png



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) {
      NProgress.start();
    }
  next();
})
router.afterEach((to, from) => {
  NProgress.done();
})
export default router;



目录
相关文章
|
17天前
|
JavaScript 数据可视化 算法
vue3+threejs可视化项目——搭建vue3+ts+antd路由布局(第一步)
vue3+threejs可视化项目——搭建vue3+ts+antd路由布局(第一步)
36 6
|
3天前
|
JavaScript
Vue实战-组件通信
Vue实战-组件通信
5 0
|
3天前
|
JavaScript
Vue实战-将通用组件注册为全局组件
Vue实战-将通用组件注册为全局组件
5 0
|
4天前
|
JavaScript
vue中watch监听路由传来的参数变化问题
vue中watch监听路由传来的参数变化问题
5 0
|
5天前
|
缓存 JavaScript
在 Vue 组件中使用计算属性和侦听器来响应路由变化
Vue Router 中,计算属性和侦听器常用于根据路由变化更新组件状态。计算属性缓存依赖,当路由参数改变时自动更新,如示例中的 `userId`。侦听器则监听 `$route` 变化,执行相应操作,例如在 `currentUserId` 示例中响应 `userId` 更新。计算属性适合简单变化,而异步操作或复杂场景可选用侦听器。Vue 3 中,`watchEffect` 减少了部分侦听场景的复杂性。总之,它们用于组件内部响应路由变化,而非直接处理路由逻辑。
12 4
|
14天前
|
资源调度 JavaScript 前端开发
【vue】vue中的路由vue-router,vue-cli脚手架详细使用教程
【vue】vue中的路由vue-router,vue-cli脚手架详细使用教程
|
14天前
|
JavaScript Go
Vue路由跳转及路由传参
Vue路由跳转及路由传参
|
15天前
|
JavaScript 前端开发 BI
采用前后端分离Vue,Ant-Design技术开发的(手麻系统成品源码)适用于三甲医院
开发环境 技术架构:前后端分离 开发语言:C#.net6.0 开发工具:vs2022,vscode 前端框架:Vue,Ant-Design 后端框架:百小僧开源框架 数 据 库:sqlserver2019
28 4
采用前后端分离Vue,Ant-Design技术开发的(手麻系统成品源码)适用于三甲医院
|
3天前
|
JavaScript
VUE里的find与filter使用与区别
VUE里的find与filter使用与区别
13 0
|
3天前
|
JavaScript
vue页面加载时同时请求两个接口
vue页面加载时同时请求两个接口

相关实验场景

更多