vue动态路由

前言:

        此篇文章是按照自己的理解做的一个demo,仅供参考。学这块知识的时候在网上也看过不少的文章,每篇文章都有自己的风格,直接使用他人的逻辑总感觉懵懵的,所以决定自己尝试写一遍。

正文:

        此种方式路由权限业务逻辑是:

                1、前端在页面上定义好全部的路由

                2、通过用户不同向后台请求不同的用户权限数据

                3、请求回来的数据和全部的路由进行比对,取出来作为路由配置

        # 全部路由:

import Right from "../pages/pages/user/rights.vue";
import Role from "../pages/pages/user/roles.vue";
import User from "../pages/pages/user/user.vue";
import Operation from "../pages/pages/system/operation.vue";
import Department from "../pages/pages/cards/department.vue";
import Event from "../pages/pages/cards/Event.vue";
import Leader from "../pages/pages/cards/Leader.vue";
import SpecialColumn from "../pages/pages/cards/specialColumn.vue";

export const DynamicRoutes = [
  {
    name: "用户管理",
    path: "/home/user",
    component: User,
  },
  {
    name: "权限管理",
    path: "/home/right",
    component: Right,
  },
  {
    name: "角色管理",
    path: "/home/role",
    component: Role,
  },
  {
    name: "操作日志",
    path: "/home/operation",
    component: Operation,
  },
  {
    name: "部门管理",
    path: "/home/department",
    component: Department,
  },
  {
    name: "重大事件",
    path: "/home/event",
    component: Event,
  },
  {
    name: "领导人卡片",
    path: "/home/leader",
    component: Leader,
  },
  {
    name: "重点专题",
    path: "/home/specialcolumn",
    component: SpecialColumn,
  },
];

 # 本地模拟接口响应路由数据 (admin和user)

//模拟走接口请求回来的数据

//管理员角色
export const admin = [
  {
    name: "用户管理",
    children: [
      {
        name: "用户管理",
      },
      {
        name: "角色管理",
      },
      {
        name: "权限管理",
      },
    ],
  },
  {
    name: "卡片内容管理",
    children: [
      { name: "部门管理" },
      { name: "重大事件" },
      { name: "领导人卡片" },
      { name: "重点专题" },
    ],
  },
  {
    name: "系统管理",
    children: [
      {
        name: "操作日志",
      },
    ],
  },
];
//用户角色
export const user = [
  {
    name: "卡片内容管理",
    children: [
      { name: "部门管理" },
      { name: "重大事件" },
      { name: "领导人卡片" },
      { name: "重点专题" },
    ],
  },
];

# 路由配置

import { createWebHashHistory, createRouter } from "vue-router";
import Login from "../pages/login/login.vue";
import Home from "../pages/home/home.vue";
import { matchRouters } from "../utils/matchRoutes";
import useUser from "../store/user";

//初始化路由
const routes = [
  {
    path: "/",
    redirect: "/login",
  },
  {
    path: "/login",
    name: "Login",
    component: Login,
  },
  {
    path: "/home",
    component: Home,
    name: "Home",
    children: [],
  },
];

const router = createRouter({
  history: createWebHashHistory(),
  routes,
});
router.beforeEach((to, from, next) => {
  let userStore = useUser();
  let token = userStore.GET_TOKEN;
  let menu = userStore.GET_MENU;
  // console.log(token, "token", menu);
  if (!token && to.name == "Login") {
    next();
  } else {
    if (to.name == "Login") {
        userStore.logOutFn();
        next()
    } else {
      if (menu.length) {
        if (userStore.GET_ISLOADROUTE) {
          next();
        } else {
          userStore.SET_ISLOADROUTE(true);
          matchRouters(menu);
          userStore.SET_MENULIST(menu);
          if(to.path == '/home'){
            //重定向
            next(menu[0].children[0].path)
          }
          next({ ...to, replace: true });
        }
      }else{
        next('/login')
      }
    }
  }
});
export default router;

# 路由匹配函数  

        根据name进行对比,给请求回来的数据添加上path

// 匹配路由
import { DynamicRoutes } from "../router/dynamic-routes";
import router from "../router";

export function matchRouters(routes: any) {
  routes.forEach((v: any) => {
    if (v.children && v.children.length > 0) {
      matchRouters(v.children);
    } else {
      DynamicRoutes.forEach((item) => {
        if (v.name === item.name) {
          //通过router.addRouter追加路由
          router.addRoute("Home", item);
          v.path = item.path;
        }
      });
    }
  });
}

 # store

import { defineStore } from "pinia";

const useUser = defineStore("user", {
  state: () => {
    return {
      token: sessionStorage.getItem('token')||'',
      menu: JSON.parse(sessionStorage.getItem("menu")) || [], //接口响应数据
      memuList: [],
      isLoadRoute:false, //路由是否已经进行过匹配
    };
  },
  getters: {
    GET_MENU(): any {
      return this.menu;
    },
    GET_TOKEN(): string {
      return this.token;
    },
    GET_ISLOADROUTE():boolean{
        return this.isLoadRoute;
    }
  },
  actions: {
    SET_TOKEN(value: string) {
      sessionStorage.setItem("token", value);
      this.token = value;
    },
    SET_MENU(value: string) {
      sessionStorage.setItem("menu", JSON.stringify(value));
      this.menu = value;
    },
    SET_MENULIST(value:[]) {
      sessionStorage.setItem("menulist", JSON.stringify(value));
      this.memuList = value;
    },
    SET_ISLOADROUTE(value:boolean){
        this.isLoadRoute = value
    },
    //退出登录
    logOutFn(){
        sessionStorage.clear()
        this.SET_ISLOADROUTE(false)
    }
  },
});
export default useUser;

 # 登录页面

        用户名如果是 admin 就是管理员,其他的都视为 普通用户 user

<template>
  <div class="login">
    <a-form
      :model="formState"
      name="basic"
      :label-col="{ span: 8 }"
      :wrapper-col="{ span: 16 }"
      autocomplete="off"
      @finish="onFinish"
      @finishFailed="onFinishFailed"
    >
      <a-form-item
        label="Username"
        name="username"
        :rules="[{ required: true, message: 'Please input your username!' }]"
      >
        <a-input v-model:value="formState.username" />
      </a-form-item>

      <a-form-item
        label="Password"
        name="password"
        :rules="[{ required: true, message: 'Please input your password!' }]"
      >
        <a-input-password v-model:value="formState.password" />
      </a-form-item>
      <a-form-item :wrapper-col="{ offset: 8, span: 16 }">
        <a-button type="primary" html-type="submit">Submit</a-button>
      </a-form-item>
    </a-form>
  </div>
</template>
<script setup lang="ts">
import { user, admin } from "../../utils/path";
import { reactive } from "vue";
import { useRouter } from "vue-router";
import useUser from "../../store/user";
const router = useRouter();
const useStore = useUser();
const formState = reactive({
  username: "",
  password: "",
});
function onFinish() {
  useStore.SET_TOKEN(formState.username);
  if (formState.username == "admin") {
    useStore.SET_MENU(admin);
  } else {
    useStore.SET_MENU(user);
  }
  router.replace("/home");
}
function onFinishFailed() {}
</script>
<style scoped>
.login {
  width: 30%;
  margin: 200px auto;
}
</style>

  # 页面布局 (home.vue)

<!-- home -->
<template>
  <div class="home">
    <!-- 左侧数据 -->
    <div class="left">
      <Slider />
    </div>
    <!-- 右侧数据 -->
    <div class="right">
      <!-- 头部 -->
      <Home />
      <!-- main -->
      <Main />
    </div>
  </div>
</template>
<script setup lang="ts">
import Slider from "@/pages/layout/slide.vue";
import Home from "@/pages/layout/header.vue";
import Main from "@/pages/layout/main.vue";
</script>
<style lang="scss" scoped>
.home {
  display: flex;
  justify-content: space-between;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  .left {
    width: 260px;
    height: 100%;
    background-color: skyblue;
  }
  .right {
    display: flex;
    flex-direction: column;
    width: calc(100% - 280px);
    height: 100%;
  }
}
</style>

# 侧边栏 (slide.vue)

<!-- 侧边栏 -->
<template>
  <div class="slide">
    <div class="title">LYF后台管理系统</div>
    <a-menu
      mode="inline"
      v-model:openKeys="openKeys"
      v-model:selectedKeys="selectedKeys"
      theme="dark"
    >
      <a-sub-menu v-for="m in memu" :key="m.name">
        <template #title>
          <span>{{ m.name }}</span>
        </template>
        <a-menu-item v-for="item in m.children" :key="item.name">
          <router-link :to="item.path">{{ item.name }}</router-link>
        </a-menu-item>
      </a-sub-menu>
    </a-menu>
  </div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import useUser from "../../store/user";
import { useRoute } from "vue-router";
const route = useRoute();
const userStore = useUser();
let memu = userStore.memuList as any;
//数组扁平化
function flatMenus() {
  let new_menu = memu
    ?.map((item: any) => {
      return item.children;
    })
    .flat();
  return new_menu;
}
//根据当前路由的name属性找selectedKey
const getSelectedKeys = () => {
  const currentSelectedKey = flatMenus()?.find(
    (item: any) => item.path === route.path
  );
  return currentSelectedKey ? [currentSelectedKey.name] : [];
};
//根据currentSelectedKey找父级name
const getOpenKeys = () => {
  const selectedKey = getSelectedKeys();
  if (selectedKey.length === 0) {
    return [];
  }
  const [key] = selectedKey;
  let currrentOpenKey = memu.find((item: any) => {
    return item.children?.some((v: any) => {
      return v.name == key;
    });
  });
  return currrentOpenKey ? [currrentOpenKey.name] : [];
};
let selectedKeys = ref(getSelectedKeys());
let openKeys = ref(getOpenKeys());
</script>
<style scoped lang="scss">
.slide {
  width: 100%;
  height: 100%;
  color: #fff;
  overflow: auto;
  background-color: #001529;
  .title {
    font-size: 18px;
    text-align: center;
    margin: 100px 0 20px 0;
  }
}
</style>

 # header.vue

<template>
  <div class="header">
    <div></div>
    <div class="log_out" @click.stop="logOut">退出</div>
  </div>
</template>
<script lang="ts" setup>
import { useRouter } from "vue-router";
import useUser from "../../store/user";
const router = useRouter();
const userStore = useUser();
function logOut() {
  userStore.logOutFn();
  router.replace("/login");
}
</script>
<style lang="scss" scoped>
.header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 60px;
  width: 100%;
  padding: 0 20px;
  margin-bottom: 20px;
  background-color: orange;
  box-sizing: border-box;
  .log_out {
    cursor: pointer;
  }
}
</style>

 # main.vue

<template>
  <div class="main">
    <router-view></router-view>
  </div>
</template>
<style lang="scss" scoped>
.main {
  width: 100%;
  height: calc(100% - 80px);
  background-color: bisque;
  overflow: hidden;
}
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值