若依菜单改造(一):侧边栏显示不同菜单组

发布时间 2023-05-30 13:16:17作者: shayloyuki

需求

点击菜单上某个图标,侧边栏的菜单项内容进行切换。效果如下:

image

拆解需求:

  1. 副标题:居中、可切换
    image

  2. 副标题下的菜单内容可切换。

解决思路

若依的菜单设置分为两种:

  1. 菜单管理 中设置
  2. router.js 中书写公共路由或动态路由(根据用户权限加载)

此处应该设置动态路由,方便根据用户权限加载。

若在 菜单管理 中设置,无法令菜单内容可切换;因此,只能在router.js 中书写动态路由。

重点

  1. 副标题样式可参照标题,只不过文本内容居中。
  2. 菜单组内容切换,可给 el-menu 传入不同的内容项,通过 Vuex 实现点击切换。

代码

副标题代码

image

MenuTitle组件
  <div
    class="menu-title-container"
    :class="{ collapse: collapse }"
    :style="{
      backgroundColor:
        sideTheme === 'theme-dark'
          ? variables.menuBackground
          : variables.menuLightBackground,
    }"
  >
    <transition name="menuTitleFade">
      <router-link
        v-if="collapse"
        key="collapse"
        class="menu-title-link"
        :to="indexLink"
      >
        <img v-if="logo" :src="logo" class="menu-title-logo" />
        <h1
          v-else
          class="menu-title"
          :style="{
            color:
              sideTheme === 'theme-dark'
                ? variables.logoTitleColor
                : variables.logoLightTitleColor,
          }"
        >
          {{ title }}
        </h1>
      </router-link>
      <router-link v-else key="expand" class="menu-title-link" :to="indexLink">
        <img v-if="logo" :src="logo" class="menu-title-logo" />
        <h1
          class="menu-title"
          :style="{
            color:
              sideTheme === 'theme-dark'
                ? variables.logoTitleColor
                : variables.logoLightTitleColor,
          }"
        >
          {{ title }}
        </h1>
      </router-link>
    </transition>
  </div>

菜单组代码

image

SideBar/index.vue
  computed: {
  // 其他代码
    ...mapState(["settings"]),
    ...mapGetters([
      "sidebarRouters",
      "sidebar",
      "menuBarIndex",
      "menuProjectId",
    ]),
    menuBarList() {
      return [
        {
          title: "系统空间",
          indexLink: "/projects/projectList",
          menus: this.sidebarRouters.filter(
            (ele) =>
              !ele.meta ||
              !ele.meta.menuTitle ||
              ele.meta.menuTitle !== "项目空间"
          ),
        },
        {
          title: "项目空间",
          indexLink: `/project/pro-index?projectId=${this.menuProjectId}`,
          menus: this.ProTitleMenus,
        },
      ];
    },
    ProTitleMenus() {
      let res = this.sidebarRouters.filter(
        (ele) => ele.meta && ele.meta.menuTitle === "项目空间"
      );
      this.loopAdd(res);
      return res;
    },
  },

怎样区分不同的菜单组?——给路由的元信息添加 menuTitle 标记

此处 系统空间 的路由是在 菜单管理 中配置的,那么 项目空间 的路由就在 router.js 中手动书写。

`router.js` 中配置路由
	// 动态路由:加上 permissions 配置项即可
  /* === 项目空间路由 start === */
  // 项目首页
  {
    path: `/project`,
    component: Layout,
    // redirect: "noRedirect",
    meta: {
      menuTitle: `项目空间`,
    },
    children: [
      {
        path: "pro-index",
        component: () =>
          import(/* webpackChunkName: 'proIndex' */ "@/views/pro-index/index"),
        name: "ProIndex",
        meta: {
          title: `项目首页`,
          icon: "dashboard",
          activeMenu: `/project/pro-index`,
        },
      },
    ],
  },
  // 组织管理
  {
    path: "/project/organize",
    component: Layout,
    redirect: "noRedirect",
    meta: {
      menuTitle: `项目空间`,
      title: `组织管理`,
      icon: "cascader",
    },
    alwaysShow: true,
    children: [
      {
        path: "pro-role",
        component: () =>
          import(
            /* webpackChunkName: 'proRole' */ "@/views/organize/pro-role/index.vue"
          ),
        name: "ProRole",
        meta: {
          title: `项目角色`,
          icon: "peoples",
        },
      },
      {
        path: "pro-user/:roleId(\\d+)",
        hidden: true,
        component: () => import("@/views/organize/pro-role/proAuthUser"),
        name: "ProAuthUser",
        meta: { title: "分配用户" },
      },
    ],
  },
  // 项目配置
  {
    path: "/project/pro-config",
    component: Layout,
    redirect: "noRedirect",
    meta: {
      menuTitle: `项目空间`,
      title: `项目配置`,
      icon: "system",
    },
    alwaysShow: true,
    children: [
      {
        path: "pro-setting",
        component: () =>
          import(
            /* webpackChunkName: 'proSetting' */ "@/views/pro-config/pro-setting/index.vue"
          ),
        name: "ProSetting",
        meta: {
          title: `项目设置`,
          icon: "redis",
        },
      },
      {
        path: "color-setting",
        component: () =>
          import(
            /* webpackChunkName: 'colorSetting' */ "@/views/pro-config/color-setting/index.vue"
          ),
        name: "ColorSetting",
        meta: {
          title: `颜色设置`,
          icon: "color",
        },
      },
      {
        path: "fact-decomp",
        component: () =>
          import(
            /* webpackChunkName: 'factDecomp' */ "@/views/pro-config/fact-decomp/index.vue"
          ),
        name: "FactDecomp",
        meta: {
          title: `工厂分解`,
          icon: "list",
        },
      },
    ],
  },
  /* === 项目空间路由 end === */

Vuex 实现切换数据

路由导航守卫:每次默认进入系统空间,若去往的路由地址的 menuTitle 是项目空间,则进入项目空间。

路由导航守卫
router.beforeEach((to, from, next) => {
  // 重置
  store.commit("menuBar/setIndex", 0);
  let f1 = to.matched[0] && to.matched[0].meta.menuTitle;
  if (f1) {
    // 进入项目空间
    store.commit("menuBar/setIndex", 1);
    next();
  } else {
    next();
  }
});

Vuex 代码

src/store/modules/menuBar.js
const state = {
  // 默认为第一列:系统空间 0
  menuBarIndex: JSON.parse(window.localStorage.getItem("menuBarIndex") || 0),
  // 项目空间的 id
  menuProjectId: JSON.parse(window.localStorage.getItem("menuProjectId" || 0)),
  // 项目空间的 名称
  menuProjectName: window.localStorage.getItem("menuProjectName" || ""),
};

const mutations = {
  setIndex(state, payload) {
    state.menuBarIndex = JSON.parse(payload);
    window.localStorage.setItem("menuBarIndex", payload);
  },
  setProjectId(state, payload) {
    state.menuProjectId = JSON.parse(payload);
    window.localStorage.setItem("menuProjectId", payload);
  },
  setProjectName(state, payload) {
    state.menuProjectName = payload;
    window.localStorage.setItem("menuProjectName", payload);
  },
};

const getters = {};

const actions = {};

export default {
  namespaced: true,
  state,
  mutations,
  getters,
  actions,
};
src/store/getters.js
const getters = {
	// ……其他代码
  menuBarIndex: (state) => state.menuBar.menuBarIndex,
  menuProjectId: (state) => state.menuBar.menuProjectId,
  menuProjectName: (state) => state.menuBar.menuProjectName,
};
export default getters;
src/store/index.js
import Vue from "vue";
import Vuex from "vuex";
import getters from "./getters";
import menuBar from "./modules/menuBar";

Vue.use(Vuex);

const store = new Vuex.Store({
  modules: {
    menuBar,
  },
  getters,
});

export default store;