菜单 Menu

实现思路

根据路由渲染菜单,路由信息通过登录后,后端返回获得

路由

以下基于Vue 3.x 和 vue Router 4.x
\src\router\index.js

  1. const routes = [
  2. // 重定向404页面
  3. {
  4. path: '/:pathMatch(.*)*',
  5. redirect: { name: 'index' },
  6. hideen: true,
  7. meta: {
  8. name: "首页"
  9. },
  10. },
  11. // 登录页
  12. {
  13. path: '/login',
  14. name: 'login',
  15. hideen: true,
  16. component: login,
  17. meta: {
  18. name: "登录"
  19. },
  20. },
  21. // 主页
  22. {
  23. path: '/',
  24. name: 'layout',
  25. component: layout,
  26. redirect: { name: 'index' },
  27. hideen: true,
  28. meta: {
  29. name: "首页"
  30. },
  31. children: [
  32. {
  33. path: '/index',
  34. name: 'index',
  35. component: index,
  36. meta: {
  37. name: "首页"
  38. },
  39. },
  40. ]
  41. },
  42. // 这里是各个业务的路由页面
  43. {
  44. path: '/assetManagement',
  45. name: 'assetManagement',
  46. component: layout,
  47. meta: {
  48. name: "资产"
  49. },
  50. children: [
  51. {
  52. path: 'index',
  53. name: 'assetIndex',
  54. component: assetIndex,
  55. meta: {
  56. name: "资产概览"
  57. },
  58. },
  59. {
  60. path: '/assetsList',
  61. name: 'assetsList',
  62. component: assetsList,
  63. meta: {
  64. name: "资产列表"
  65. },
  66. },
  67. {
  68. path: '/assetsReport',
  69. name: 'assetsReport',
  70. component: assetsReport,
  71. meta: {
  72. name: "资产报表"
  73. },
  74. },
  75. ],
  76. },
  77. ]

子菜单

子菜单的作用,是自己递归生成二级、三级、… 、n级菜单,直到没有为止。

以下基于Vue 3.x 和 vue Router 4.x,以及ant-design-vue 2.x

  1. <template>
  2. <a-sub-menu :key="menuInfo.key" v-bind="$attrs">
  3. <template #title>
  4. <span>
  5. <BarsOutlined /><span>{{ menuInfo.meta.name }}</span>
  6. </span>
  7. </template>
  8. <template v-for="(item) in menuInfo.children" :key="item.path">
  9. <template v-if="!item.children">
  10. <a-menu-item :key="item.path">
  11. <BarsOutlined />
  12. <span>{{ item.meta.name }}</span>
  13. </a-menu-item>
  14. </template>
  15. <template v-else>
  16. <submenu :menu-info="item" :key="item.path" />
  17. </template>
  18. </template>
  19. </a-sub-menu>
  20. </template>
  21. <script>
  22. import {
  23. BarsOutlined,
  24. } from '@ant-design/icons-vue';
  25. export default {
  26. components: {
  27. BarsOutlined,
  28. },
  29. name: 'submenu',
  30. props: {
  31. menuInfo: {
  32. type: Object,
  33. default: () => ({}),
  34. },
  35. },
  36. }
  37. </script>
  38. <style>
  39. </style>

主菜单

子菜单的最外层容器

以下基于Vue 3.x 和 vue Router 4.x,以及ant-design-vue 2.x

  1. <template>
  2. <!-- 菜单栏 -->
  3. <a-menu id="baseMenu"
  4. theme="dark"
  5. mode="inline"
  6. v-model:selectedKeys="selectedKeys"
  7. @click="menuItemClick"
  8. :inlineIndent="12">
  9. <!-- 循环获取菜单 -->
  10. <template v-for="(item) in menuItem" :key="item.path">
  11. <!-- 权限控制,不显示隐藏的菜单,主要是过滤首页、登录页 -->
  12. <template v-if="!item.hideen">
  13. <!-- 没有子菜单 -->
  14. <template v-if="!item.children">
  15. <a-menu-item :key="item.path">
  16. <BarsOutlined />
  17. <span>{{ item.meta.name }}</span>
  18. </a-menu-item>
  19. </template>
  20. <!-- 有子菜单 -->
  21. <template v-else>
  22. <submenu :menu-info="item" :key="item.path" />
  23. </template>
  24. </template>
  25. </template>
  26. </a-menu>
  27. </template>
  28. <script>
  29. import { onMounted, reactive, ref, computed } from "vue";
  30. // 引入ant-design-vue相关
  31. import { BarsOutlined } from "@ant-design/icons-vue"; //引入图标组件,菜单栏图标
  32. import { message, Result } from "ant-design-vue";
  33. import { useRouter } from "vue-router"; //引入路由
  34. import { useStore } from "vuex";
  35. import { getMenu } from "/@/api/home/aside.js"; //引入获取菜单的api
  36. import submenu from "/@/views/layout/sideBar/subMenu.vue";
  37. export default {
  38. name: "baseMenu",
  39. components: {
  40. BarsOutlined,
  41. submenu,
  42. },
  43. setup() {
  44. //data 菜单的选项
  45. const store = useStore();
  46. let selectedKeys = computed({
  47. //当前已选择菜单key
  48. get: () => {
  49. store.state.menu.selectedKeys;
  50. },
  51. set: (val) => {},
  52. });
  53. //创建路由实例
  54. const router = useRouter();
  55. // 从路由里读取菜单
  56. let menuItem = reactive(router.options.routes);
  57. //function 菜单按钮点击事件
  58. const menuItemClick = (itemKey) => {
  59. // 1、提交修改已选择的菜单
  60. store.dispatch("handleSelectedKeys", itemKey.key);
  61. ///2、然后跳转路由
  62. router.push(itemKey.key);
  63. };
  64. return {
  65. selectedKeys,
  66. menuItem,
  67. menuItemClick,
  68. };
  69. },
  70. };
  71. </script>
  72. <style>
  73. #baseMenu {
  74. text-align: left;
  75. }
  76. </style>

=====================

标签页 Tab

方便用户选择最近使用的页面,以及部分功能弹出新页面进行操作。

通过ant-design-vue 2.x 的tab组件可以轻松实现

=====================

菜单、标签联动

实现思路

1、通过vuex储存标签,以及当前页的key,这个key会同时影响菜单和标签的当前选中激活的菜单、标签

  1. state: () => ({
  2. selectedKeyStore:"index", // 当前激活的页面的标签页、菜单
  3. tabPanes: [ // 全局的标签页,主要是配置首页不可关闭
  4. { title: "首页", key: "index", closable: false },
  5. ],
  6. }),

2、设置修改当前页的key的方法

  1. mutations: {
  2. // 修改当前激活页面的key
  3. changeSelectedKeyStore(state,key){
  4. state.selectedKeyStore = key
  5. },
  6. }

3、通过路由的导航守卫beforeEach,每次进入路由时,读取要进入页面的name属性,赋值给当前页的key。
这样的好处时,用户不管是点击menu,还是点击tab,还是刷新页面,都能保证当前激活的key都是准确的。

  1. import { createRouter, createWebHistory } from 'vue-router';
  2. import store from '/@/store/index.js'
  3. router.beforeEach((to, from, next) => {
  4. // 临时设置登录状态
  5. let isLogin = true;
  6. // 判断是否登录,登录了就跳转到正常页面,没登录就跳转登录页
  7. if (to.name !== 'login' && !isLogin) {
  8. console.log(`跳转到登录页`)
  9. next({ name: 'login' })
  10. } else {
  11. store.commit('changeSelectedKeyStore', to.name)
  12. next();
  13. }
  14. })