Vue3 根据角色权限动态加载路由

文章目录

Vue3 根据角色权限动态加载路由

效果

普通用户:

管理员:

超级管理员:

目录结构

复制代码
├─assets
├─components
├─menus
│  ├─admin
│  ├─manager
│  └─user
├─router
│  ├─admin
│  ├─common
│  ├─manager
│  └─user
├─stores
├─utils
└─views
    ├─main
    │  ├─admin
    │  ├─manager
    │  └─user
    └─not-found

代码实现

路由代码

普通用户:

js 复制代码
export const userRoutes = [
    {
        path: "/main/user/OneUser",
        component: () => import("@/views/main/user/OneUser.vue")
    },
    {
        path: "/main/user/TwoUser",
        component: () => import("@/views/main/user/TwoUser.vue")
    },
    {
        path: "/main/user/ThreeUser",
        component: () => import("@/views/main/user/ThreeUser.vue")
    },
    {
        path: "/main/user/FourUser",
        component: () => import("@/views/main/user/FourUser.vue")
    },
];

管理员:

js 复制代码
export const managerRoutes = [
    {
        path: "/main/manager/OneManager",
        component: () => import("@/views/main/manager/OneManager.vue"),
    },
    {
        path: "/main/manager/TwoManager",
        component: () => import("@/views/main/manager/TwoManager.vue"),
    },
];

超级管理员:

js 复制代码
export const adminRoutes = [
    {
        path: "/main/admin/OneAdmin",
        component: () => import("@/views/main/admin/OneAdmin.vue"),
    },
    {
        path: "/main/admin/TwoAdmin",
        component: () => import("@/views/main/admin/TwoAdmin.vue"),
    }
];

动态加载路由代码

javascript 复制代码
import {adminMenus} from "@/menus/admin/adminMenus.js";
import {managerMenus} from "@/menus/manager/managerMenus.js";
import {userMenus} from "@/menus/user/userMenus.js";
import {adminRoutes} from "@/router/admin/adminRoutes.js";
import router from "@/router/index.js";
import {managerRoutes} from "@/router/manager/managerRoutes.js";
import {userRoutes} from "@/router/user/userRoutes.js";
import {localCache} from "@/utils/cache.js";
import {defineStore} from "pinia";
import {ref} from "vue";

const useLoginStore = defineStore("loginStore", () => {
    const role = ref("");
    const menus = ref([]);
    const firstRoute = ref(null);
    const routesLoaded = ref(false);
    const addedRouteNames = ref([]);

    function login(roleParam) {
        role.value = roleParam;
        routesLoaded.value = false;
        loadRoutesByRole(role.value);
        loadMenusByRole(role.value);
        localCache.setCache("role", role.value);
        router.push("/main");
    }

    function logout() {
        removeDynamicRoutes();
        role.value = "";
        menus.value = [];
        firstRoute.value = null;
        routesLoaded.value = false;
        localCache.removeCache("role");
        router.push("/login");
    }

    function loadLocalData() {
        role.value = localCache.getCache("role");
        if (role.value && !routesLoaded.value) {
            addedRouteNames.value = [];
            loadRoutesByRole(role.value);
            loadMenusByRole(role.value);
        }
    }

    function loadRoutesByRole(role) {
        if (routesLoaded.value) return;
        let accessRoutes = [];
        if (role === "admin") {
            accessRoutes = [...adminRoutes, ...managerRoutes, ...userRoutes];
        } else if (role === "manager") {
            accessRoutes = [...managerRoutes, ...userRoutes];
        } else if (role === "user") {
            accessRoutes = [...userRoutes];
        }

        addedRouteNames.value = [];
        accessRoutes.forEach((route, index) => {
            if (index === 0 && !firstRoute.value) {
                firstRoute.value = route;
            }
            router.addRoute("main", route);
            if (route.name) {
                addedRouteNames.value.push(route.name);
            }
        });
        routesLoaded.value = true;
    }

    function removeDynamicRoutes() {
        addedRouteNames.value.forEach(name => {
            if (name && router.hasRoute(name)) {
                router.removeRoute(name);
            }
        });

        addedRouteNames.value = [];
        routesLoaded.value = false;
    }

    function loadMenusByRole(role) {
        if (role === "admin") {
            menus.value = [...adminMenus, ...managerMenus, ...userMenus];
        } else if (role === "manager") {
            menus.value = [...managerMenus, ...userMenus];
        } else if (role === "user") {
            menus.value = [...userMenus];
        }
    }

    return {role, menus, firstRoute, login, logout, loadLocalData};
});

export default useLoginStore;

侧边菜单栏代码

vue 复制代码
<script setup>
import useLoginStore from "@/stores/loginStore.js";
import {pathToMenu} from "@/utils/map.js";
import {Location} from "@element-plus/icons-vue";
import {computed} from "vue";
import {useRoute, useRouter} from "vue-router";

defineProps({
  isCollapse: {
    type: Boolean,
    default: true
  }
});

const router = useRouter();
const route = useRoute();
const loginStore = useLoginStore();

const menus = computed(() => loginStore.menus || []);

const handleItem = (item) => {
  router.push(item.path);
};

const defaultActive = computed(() => {
  if (loginStore.menus && loginStore.menus.length > 0) {
    const menuItem = pathToMenu(route.path, loginStore.menus);
    return menuItem?.id || "";
  }
  return "";
});
</script>

<template>
  <div class="left-menu">
    <el-menu
        active-text-color="#ffd04b"
        text-color="#fff"
        background-color="#001529"
        :default-active="defaultActive"
        :collapse="isCollapse"
        :collapse-transition="true"
    >
      <template v-for="menu in menus" :key="menu.id">
        <el-sub-menu :index="menu.id">
          <template #title>
            <el-icon>
              <location/>
            </el-icon>
            <span>{{ menu.name }}</span>
          </template>
          <template v-for="menuItem in menu.menuItems" :key="menuItem.id">
            <el-menu-item :index="menuItem.id" @click="handleItem(menuItem)">{{ menuItem.name }}</el-menu-item>
          </template>
        </el-sub-menu>
      </template>
    </el-menu>
  </div>
</template>

<style scoped>
.left-menu {
  height: 100%;
  background-color: #001529;
}

.el-menu {
  border-right: none;
}
</style>

主页代码

vue 复制代码
<script setup>
import LeftMenu from "@/components/LeftMenu.vue";
import useLoginStore from "@/stores/loginStore.js";
import {computed, ref} from "vue";

const loginStore = useLoginStore();
const isCollapse = ref(false);
const role = computed(() => loginStore.role);

const doLogout = () => {
  loginStore.logout();
};
</script>

<template>
  <main>
    <el-container>
      <el-aside :width="isCollapse ? '64px' : '200px'">
        <LeftMenu :isCollapse="isCollapse"/>
      </el-aside>
      <el-container>
        <el-header>
          <div class="content">
            <div>
              <el-radio-group v-model="isCollapse">
                <el-radio-button :value="true">收起</el-radio-button>
                <el-radio-button :value="false">展开</el-radio-button>
              </el-radio-group>
            </div>
            <div class="info">
              <el-dropdown>
                <span class="user-info">
                  <el-avatar :size="30" src="https://www.baidu.com/img/flexible/logo/pc/result.png"/>
                  <span class="name">角色:{{ role }}</span>
                </span>
                <template #dropdown>
                  <el-dropdown-menu>
                    <el-dropdown-item divided @click="doLogout">退出登录</el-dropdown-item>
                  </el-dropdown-menu>
                </template>
              </el-dropdown>
            </div>
          </div>
        </el-header>
        <el-main>
          <router-view></router-view>
        </el-main>
      </el-container>
    </el-container>
  </main>
</template>

<style scoped lang="css">
main {
  height: 100%;

  .el-container {
    height: 100%;
  }
}

.content {
  height: 100%;
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  padding: 0 18px;

  .info {
    .user-info {
      display: flex;
      flex-direction: row;
      align-items: center;
      cursor: pointer;

      .name {
        margin-left: 5px;
      }
    }
  }
}

.el-main {
  background-color: #f0f2f5;
}
</style>

代码下载

相关推荐
kyriewen14 小时前
别再 console.log 了:5 个 Chrome DevTools 调试技巧,用过就回不去了
前端·javascript·面试
IT_陈寒16 小时前
Python搞不定字符串编码?这破玩意坑我两小时!
前端·人工智能·后端
To_OC16 小时前
LC 1 两数之和:面试第一道必考题,暴力解法直接被面试官 pass
javascript·算法·leetcode
DigitalOcean17 小时前
Laravel 开发者已在 DigitalOcean 上开通超过 10 万台服务器
前端·laravel
星始流年18 小时前
从 Tool 到 Skill——基于 LangChain 的服务端Skill实现
前端·langchain·agent
李惟18 小时前
开源本地通信库,纯客户端 RPC,像聊天一样通信
前端
YAwu1118 小时前
深入解析 React 炫彩鼠标跟随标题组件:从坐标定位到动画性能
前端·react.js
GuWenyue18 小时前
排序效率低?5分钟吃透快速排序,性能飙升至O(nlogn)
前端·javascript·面试
OpenTiny社区18 小时前
🎨 看完 GenUI SDK 源码我悟了!
前端·vue.js·github
叁两18 小时前
前端转型AI Agent该如何学习?(前置篇)
前端·人工智能·node.js