1、说明

后台权限列表用的是element的table组件(https://element.eleme.cn/#/zh-CN/component/table
在这里插入图片描述

2、后端返回的菜单权限数据

let data = [
    {
        "id": "1580804966837141505",
        "parentId": "0",
        "seq": 0,
        "children": [
            {
                "id": "1580805570087108610",
                "parentId": "1580804966837141505",
                "seq": 0,
                "name": "用户管理",
                "spread": false,
                "path": "index",
                "keepAlive": "0",
                "menuType": "0",
                "label": "用户管理",
                "createdAt": "2022-10-14 14:19:50",
                "updatedAt": "2022-11-07 14:22:34"
            },
            {
                "id": "1580805641843261441",
                "parentId": "1580804966837141505",
                "seq": 1,
                "name": "积分记录",
                "spread": false,
                "path": "points",
                "keepAlive": "0",
                "menuType": "0",
                "label": "积分记录",
                "createdAt": "2022-10-14 14:20:07",
                "updatedAt": "2022-11-07 14:22:39"
            }
        ],
        "name": "用户管理",
        "spread": false,
        "path": "user",
        "keepAlive": "0",
        "menuType": "0",
        "label": "用户管理",
        "createdAt": "2022-10-14 14:17:27",
        "updatedAt": "2022-10-14 14:17:27"
    },
    {
        "id": "1580805083585593346",
        "parentId": "0",
        "seq": 1,
        "children": [
            {
                "id": "1580805723388919810",
                "parentId": "1580805083585593346",
                "seq": 1,
                "name": "供应商管理",
                "spread": false,
                "path": "supplier",
                "keepAlive": "0",
                "menuType": "0",
                "label": "供应商管理",
                "createdAt": "2022-10-14 14:20:27",
                "updatedAt": "2022-11-07 14:22:45"
            },
            {
                "id": "1580805784856444930",
                "parentId": "1580805083585593346",
                "seq": 2,
                "name": "商品分类管理",
                "spread": false,
                "path": "category",
                "keepAlive": "0",
                "menuType": "0",
                "label": "商品分类管理",
                "createdAt": "2022-10-14 14:20:42",
                "updatedAt": "2022-11-07 14:22:54"
            },
            {
                "id": "1580805854662246401",
                "parentId": "1580805083585593346",
                "seq": 3,
                "name": "商品管理",
                "spread": false,
                "path": "goods",
                "keepAlive": "0",
                "menuType": "0",
                "label": "商品管理",
                "createdAt": "2022-10-14 14:20:58",
                "updatedAt": "2022-11-07 14:23:01"
            }
        ],
        "name": "供应商管理",
        "spread": false,
        "path": "supplier",
        "keepAlive": "0",
        "menuType": "0",
        "label": "供应商管理",
        "createdAt": "2022-10-14 14:17:54",
        "updatedAt": "2022-10-14 14:17:54"
    },
]

3、router/index.js

router / index.js
静态配置页面路由是在这个js里配置的,首页是没有权限的也可以访问

import Vue from "vue";
import Router from "vue-router";
Vue.use(Router);

import Layout from "@/layout";

export const constantRoutes = [
  {
    path: "/login",
    component: () => import("@/views/login/index"),
    hidden: true,
  },
  {
    path: "/404",
    component: () => import("@/views/404"),
    hidden: true,
  },
  {
    path: "/",
    component: Layout,
    redirect: "/",
    meta: { title: "首页", icon: "el-icon-s-home" },
    children: [
      {
        path: "/",
        name: "/",
        component: () => import("@/views/index/index"),
        meta: { title: "首页", icon: "el-icon-s-home" },
      },
    ],
  },
];
const createRouter = () =>
  new Router({
    mode: "history", // require service support
    scrollBehavior: () => ({ y: 0 }),
    routes: constantRoutes,
  });

const router = createRouter();

export function resetRouter() {
  const newRouter = createRouter();
  router.matcher = newRouter.matcher; // reset router
}

export default router;

4、store/getters.js

store/getters.js

const getters = {
	...//其他内容
	addRouters_: state => state.permission.addRouters,
	permissionList: state => state.user.permissionList
};
export default getters;

5、store/modules/user.js

store/modules/user.js

import { constantRoutes } from "@/router";
import { getUserMenu } from "@/api/auth";//获取登录账号的菜单权限
import Layout from "@/layout";
import router from "@/router";

const getDefaultState = () => {
  return {
    //当前用户能够访问的路由对象
    routes: [],
	permissionList:[]
  };
};

const state = getDefaultState();

function lazyComponent(item,i){
	let url = `@/views/${item.path}/${i.path}`
	let obj = {
		path: i.path,
		name: `${item.path}/${i.path}`,
		component: (resolve) => require([`@/views/${item.path}/${i.path}`], resolve),
		meta: { title: i.name, icon: "el-icon-s-grid" },
	}
	return obj
}

const mutations = {
	RESET_STATE: (state) => {
	  Object.assign(state, getDefaultState());
	},
	//过滤之后的路由对象
	SET_ROUTES: (state, routes) => {
	  state.routes = routes;
	},
	SET_PERMISSIONLIST: (state, permissionList) => {
	  state.permissionList = constantRoutes.concat(permissionList)
	},
};

const actions = {
  	...
	// 获取用户菜单信息
	GetPermissionList({ commit }){
		return new Promise((resolve, reject)=>{
			getUserMenu().then(response=>{
				let menuData = response.data
				let arr = []
				menuData.forEach((item,index)=>{
					if(item.children){
						let obj = {
							path: '/' + item.path,
							component: Layout,
							redirect: '/' + item.path,
							name:item.path,
							meta: { title: item.name, icon: "el-icon-s-grid" },
							children:[]
						}
						item.children.forEach((i,k)=>{
							let a = lazyComponent(item,i)
							obj.children.push(a)
						})
						commit('SET_PERMISSIONLIST', arr)
						commit("SET_ROUTES", arr);
						arr.push(obj)
					}
				})
				resolve(arr)
			}).catch(error => {
				reject(error)
			})
		})
	},
};

export default {
  namespaced: true,
  state,
  mutations,
  actions,
};

6、src/permission.js

src/permission.js

// 最外层
import router from "./router";
import store from "./store";
import { Message } from "element-ui";
import NProgress from "nprogress"; // progress bar
import "nprogress/nprogress.css"; // progress bar style
import { getToken } from "@/utils/auth"; // get token from cookie
import getPageTitle from "@/utils/get-page-title";
import { getUserMenu } from "@/api/auth.js";
import { generateIndexRouter } from '@/utils/util'
NProgress.configure({ showSpinner: false }); // NProgress Configuration

const whiteList = ["/login"]; // no redirect whitelist

router.beforeEach(async (to, from, next) => {
  NProgress.start();
  document.title = getPageTitle(to.meta.title);
  const hasToken = getToken();

  if (hasToken) {
    if (to.path === "/login") {
      next({ path: "/" });
      NProgress.done();
    } else {
    	//重点代码 重点代码 重点代码
      if(store.getters.permissionList.length === 0){
				store.dispatch('user/GetPermissionList').then(res=>{
					router.addRoutes(store.getters.permissionList)
					const redirect = decodeURIComponent(from.query.redirect || to.path)
					if (to.path === redirect) {
					  next({ ...to, replace: true })
					} else {
					  // 跳转到目的路由
					  next({ path: redirect })
					}
				})
			}else{
				next()
			}
			//重点代码 重点代码 重点代码 到此结束
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) {
      next();
    } else {
      next(`/login`);
      NProgress.done();
    }
  }
});

router.afterEach(() => {
  // finish progress bar
  NProgress.done();
});

7、layout/components/Sidebar/index.vue

layout/components/Sidebar/index.vue

<template>
  <div :class="{ 'has-logo': showLogo }">
    <logo v-if="showLogo" :collapse="isCollapse" />
    <el-scrollbar wrap-class="scrollbar-wrapper">
      <el-menu
        :default-active="activeMenu"
        :collapse="isCollapse"
        :background-color="variables.menuBg"
        :text-color="variables.menuText"
        :unique-opened="false"
        :active-text-color="variables.menuActiveText"
        :collapse-transition="false"
        mode="vertical"
      >
        <sidebar-item
          v-for="route in routes"
          :key="route.path"
          :item="route"
          :base-path="route.path"
        />
      </el-menu>
    </el-scrollbar>
  </div>
</template>

<script>
import { mapGetters, mapState } from "vuex";
import Logo from "./Logo";
import SidebarItem from "./SidebarItem";
import variables from "@/styles/variables.scss";

export default {
  components: { SidebarItem, Logo },
  computed: {
    ...mapGetters([
      "sidebar",
      // 'routes'
    ]),
    // 获取筛选过后的路由对象
    routes() {
		return this.$store.state.user.permissionList;//重点这个,获取处理好的路由,展示在左侧菜单里
    },
    activeMenu() {
      const route = this.$route;
      const { meta, path } = route;
      // if set path, the sidebar will highlight the path you set
      if (meta.activeMenu) {
        return meta.activeMenu;
      }
      return path;
    },
    showLogo() {
      return this.$store.state.settings.sidebarLogo;
    },
    variables() {
      return variables;
    },
    isCollapse() {
      return !this.sidebar.opened;
    },
  },
  mounted() {
    // console.log(this.routes);
  },
};
</script>

8、在过程中出现的一些问题

1、src/permission.js不加store.getters.permissionList.length === 0判断的话,会一直死循环下面的操作(就是一直请求接口),加判断第二次执行就会到else里
2、store/modules/user.js里,因为我后端有可能返回只有一级菜单,没添加二级菜单即item没有children这个字段,不加item.children判断,就会一直提示
下面的问题在这里插入图片描述
3、store/modules/user.js里,之前用的是component: () => import(url),虽然没报错,但是点击左侧菜单一直没反应,后面查了下,说是不能用import,后来改成component: (resolve) => require([@/views/${item.path}/${i.path}], resolve)

ps:layout/components/Sidebar/index.vue左侧菜单组件,这个看每个人项目用的组件吧

Logo

前往低代码交流专区

更多推荐