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>

代码下载

相关推荐
达达尼昂1 小时前
Claude 多 Agent 系统:从零搭建一个 4 Agent 团队
前端·架构·ai编程
Aolith2 小时前
我是怎么把个人论坛首页性能从80分优化到100分的(附踩坑全记录)
vue.js·性能优化
费曼学习法2 小时前
React 18 并发模式(Concurrent Mode):Fiber 架构的终极进化
javascript·react.js
容智信息2 小时前
AI Agent(智能体)的输出格式应该从 Markdown 转向 HTML吗?
前端·人工智能·rust·编辑器·html·prompt
_风满楼2 小时前
TDD 进阶:换个角度看会议室预约
前端·javascript·github
Amy_yang2 小时前
uni-app 安卓端纯前端预览 DOCX 的实现思路
前端·vue.js
x_y_2 小时前
分享一个自己总结的前端开发skill~ requirement-to-delivery
前端·ai编程
梨子同志2 小时前
CSS Grid
前端·css
子兮曰2 小时前
SuperSplat 深度解析:7.6K Stars 的浏览器端 3D 高斯泼溅编辑器 — 在 Web 上编辑现实
前端·javascript·webgl