当前位置: 首页 > news >正文

react多级权限路由

技术栈:react-router-dom、react18、antd、vite-plugin-mock

 1、配置mock

其他的配置我删了,主要是配置mock

import react from '@vitejs/plugin-react'
import viteESLintPlugin from 'vite-plugin-eslint'
import { loadEnv } from 'vite'
import { wrapperEnv } from './scripts/utils'
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
import { viteMockServe } from 'vite-plugin-mock'
import { resolve } from 'path'
import type { ConfigEnv, UserConfig } from 'vite'export default ({command,mode}: ConfigEnv): UserConfig =>{return {plugins: [react(),viteESLintPlugin({// 开发阶段因为 ESLint 的错误, 不再会打断开发failOnError: false}),viteMockServe({mockPath: './mock',ignore: /^_/,localEnabled: !isBuild,prodEnabled: isBuild,}),],}
}

2、添加mock接口

在src同级下创建mock/user/index.ts

添加接口数据(模拟数据)

//用户信息数据
function createUserList() {return [{userId: 1,username: 'admin',password: '111111',token: 'Admin Token',permission: [{ name: 'home'},{ name: 'dashboard'},{ name: 'form', children: ['formBas', 'formDes']},{ name: 'table', children: ['tableBas', 'tableDes']}],},{userId: 2,username: 'system',password: '111111',token: 'System Token',permission: ['home', 'dashboard'],},]
}export default [// 用户登录接口{url: '/api/user/login',//请求地址method: 'post',//请求方式response: ({ body }) => {//获取请求体携带过来的用户名与密码const { username, password } = body;//调用获取用户信息函数,用于判断是否有此用户const checkUser = createUserList().find((item) => item.username === username && item.password === password,)//没有token返回失败信息if (!checkUser) {return { code: 201, data: { message: '未登录' } }}//如果有返回成功信息const { token } = checkUserreturn { code: 200, data: { token } }},},// 获取用户信息{url: '/api/user/info',method: 'get',response: (request) => {//获取请求头携带tokenconst token = request.headers.token;//查看用户信息是否包含有次token用户const checkUser = createUserList().find((item) => item.token === token)//没有返回失败的信息if (!checkUser) {return { code: 201, data: { message: '获取用户信息失败' } }}//如果有返回成功信息return { code: 200, data: {checkUser} }},},// 权限分配{url: '/api/user/permission',method: 'get',response: (request) => {//获取请求头携带tokenconst token = request.headers.token;//查看用户信息是否包含有次token用户const checkUser = createUserList().find((item) => item.token === token)console.log(checkUser);//没有返回失败的信息if (!checkUser) {return { code: 201, data: { message: '获取失败' }}}return { code: 200, data: { permission: checkUser.permission} }}}
]

3、请求权限接口

在登录的时候请求权限接口,这里不操作,就一个请求而已,注意的是,请求的权限和token数据可以通过toolkit和react-redux进行状态管理(还是要借助数据存储,不然数据会丢失)

4、配置守卫路由

讲需要的路由进行添加,包括权限以及icon等需要的数据配置在meta中

router/route/index.tsx

import { Navigate } from "react-router-dom";
import React from "react";
import HomePage from "@/views/home/homePage";
import DashboardPage from "@/views/dashboard/dashboardPage";
import BasignerPage from "@/views/form/basicPage";
import DesigneerPage from "@/views/form/designerPage";
import TabBasicPage from "@/views/table/basicPage";
import TabDesignerPage from "@/views/table/designerPage";
import {PieChartOutlined,HomeOutlined,ContainerOutlined,FormOutlined,TableOutlined,
} from "@ant-design/icons";const routes: Routes = [{path: "home",name: "首页",element: <HomePage />,meta: {title: "首页",icon: <HomeOutlined />,permission: ["home"],},},{path: "dashboard",name: "数据大屏",element: <DashboardPage />,meta: {title: "数据大屏",icon: <PieChartOutlined />,permission: ["dashboard"],},},{path: "*",element: <Navigate to="/home" />,name: "",meta: {title: "首页",icon: <HomeOutlined />,permission: ["home"],},},{path: "form",name: "表单",meta: {title: "表单",icon: <FormOutlined />,permission: ["form"],},children: [{path: "/form/basic",name: "基础表单",// element: <BasignerPage />,meta: {title: "基础表单",icon: <FormOutlined />,permission: ["formBas"],},children: [{path: "/form/basic/basic",name: "操作表单",element: <DashboardPage />,meta: {title: "基础表单1",icon: <FormOutlined />,permission: ["formBas"],},},],},{path: "/form/designer",name: "高级表单",element: <DesigneerPage />,meta: {title: "高级表单",icon: <FormOutlined />,permission: ["formDes"],},},],},{path: "table",name: "表格",meta: {title: "表格",icon: <ContainerOutlined />,permission: ["table"],},children: [{path: "/table/basic",name: "基础表格",element: <TabBasicPage />,meta: {title: "基础表格",icon: <TableOutlined />,permission: ["tableBas"],},},{path: "/table/designer",name: "高级表格",element: <TabDesignerPage />,meta: {title: "高级表格",icon: <TableOutlined />,permission: ["tableDes"],},},],},
];
export default routes;

上段代码是所有的权限路由,但是还有一个忽略的地方就是login和/路由,需要单独的在router操作如下:

router/index.tsx

import { createBrowserRouter, redirect } from "react-router-dom";
import React from "react";
import { LayoutGuard } from "./utils/guard";
import UserLogin from "@/views/login/loginPage";
import routes from "./routes";
import PermissionChecker from "./utils/permission";
import { getItem } from "@/utils/storeages";const router = createBrowserRouter([{path: "/login",name: "login",element: <UserLogin />,loader: () => {const token = getItem("token");if (token) {return redirect("/home");}return null;},},{path: "/",name: "model",element: <LayoutGuard />,meta: {},// children: [...routes],children: [...PermissionChecker()],},
]);export default router;

上面有一个LayoutGuard组件,其实是用来当作路由守卫用的,主要是针对强行跳转。

这里主要是管理后台所有的组件入口

router/utils/LayoutGuard.tsx

import AuthGuard from "./AuthGuard";
import ModelPage from "@/views/model";
import React from "react";export const LayoutGuard = () => {return (<AuthGuard><ModelPage /></AuthGuard>);
};

router/utils/AuthGuard.tsx

这里主要是针对重定向的操作,我这里没加404/403等页面,可以自己加上

import React, { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { useSelector } from "react-redux";
import PermissionChecker from "./permission";
function AuthGuard({ children }: { children: JSX.Element }) {// console.log(PermissionChecker())const navigate = useNavigate();const token = useSelector((state: any) => state.userSlice.token);useEffect(() => {if (token) {// 如果已经登录,则重定向到登录页面navigate("home");} else {navigate("login");}}, [navigate]);return <>{children}</>;
}export default AuthGuard;

5、配置权限路由

这里是路由管理的核心,涉及到权限,用到的主要是后端返回的权限以及前端自己配置的router/routes/index.ts下面的权限路由。

router/utils/permission,.ts

主要用于权限过滤

import routes from "../routes";
import { getItem } from "../../utils/storeages";export default function PermissionChecker() {const userPermission: any = getItem("permission") || []; //这里是本地存储的数据,拿取权限let newRoutes: any = [];// 检查并过滤路由const filterRoutes = (routes: any[]) => {return routes.filter((route) => {if (route.meta && route.meta.permission) {const permission = route.meta.permission;if (permission.some((item: any) => userPermission.includes(item))) {// 如果当前路由有子路由,递归地过滤子路由if (route.children) {route.children = filterRoutes(route.children);}return true; // 符合条件,保留此路由}}return false; // 不符合条件,移除此路由});};// 使用 filterRoutes 函数处理所有路由newRoutes = filterRoutes(routes);return newRoutes;
}

6、asides路径跳转配置

这里主要是针对侧边栏跳转的展示配置,

src/views/model/asides/utils/routePromission.ts

主要是icon以及文字展示(当然都由权限控制)

import type { MenuProps } from "antd";// 定义菜单项类型
type MenuItem = Required<MenuProps>["items"][number];// 定义路由项类型
interface RouteItem {name: string;meta: {permission: string[];icon?: string;};path: string;children?: RouteItem[];
}// 创建菜单项的函数
function getItems(label: React.ReactNode,key: React.Key,icon?: React.ReactNode,children?: MenuItem[],
): MenuItem {return {key,icon,children,label,} as MenuItem;
}// 获取经过权限过滤的路由// 处理路由并转换成菜单项
const routePromissionMeta = (routes: RouteItem[]): MenuItem[] => {const processRoutes = (routeList: RouteItem[],currentDepth = 0,maxDepth = 10,): MenuItem[] => {if (currentDepth > maxDepth) return []; // 防止递归过深const result: MenuItem[] = [];for (const element of routeList) {if (element.meta.permission && element.name) {let keyPath = element.path;if (currentDepth !== 0) {keyPath = element.path.substring(1);}if (element.children && element.children.length > 0) {const children = processRoutes(element.children, currentDepth + 1);result.push(getItems(element.name, keyPath, element.meta.icon, children),);} else {result.push(getItems(element.name, keyPath, element.meta.icon));}}}return result;};return processRoutes(routes);
};// 导出处理后的菜单项
export default routePromissionMeta;

在asides组件中使用

src/views/model/asides/index.tsx

import { Menu, Layout } from "antd";import React from "react";
import { useNavigate } from "react-router-dom";
import loginName from "@/assets/images/logo.png";
import nameWhite from "@/assets/images/name_white.png";
import "./index.less";
import { useSelector } from "react-redux";
import { MenuInfo } from "rc-menu/lib/interface";
import routePromissionMeta from "./utils/routePromission";
import PermissionChecker from "@/router/utils/permission";
const { Sider } = Layout;const SiderPage: React.FC = () => {const routes = PermissionChecker();const items = [...routePromissionMeta(routes)];console.log(items);const navigate = useNavigate();const { menuStatus } = useSelector((state) => state.settingSlice);const clickSide = (e: MenuInfo) => {console.log(e);navigate(`/${e.key}`);};return (<><Sider collapsed={menuStatus}><div className="demo-logo-vertical" /><div className="logo"><img src={loginName} alt="" className="logo-img" />{!menuStatus ? (<img src={nameWhite} alt="" className="logo-img-white" />) : null}</div><Menutheme="dark"defaultSelectedKeys={["Home"]}mode="inline"items={items}onClick={(e) => {clickSide(e);}}/></Sider></>);
};export default SiderPage;

到这里配置就结束了,通过react的使用来看,权限配置更加灵活多变,不像vue那样指定路由守卫操作。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【云原生】Pass容器研发基础——汇总篇
  • 集合-List去重
  • jquery div触发粘贴事件
  • Linux驱动入门实验班——Hello驱动(后附百问网课程视频链接)
  • ARM CoreLink 系列 5.1.1 -- CI-700 System Address Map 】
  • 全开源智慧停车场微信小程序源码/智能停车系统源码/停车自助缴费系统/停车场管理收费+物业管理+物联网+自助缴费功能
  • MySQL- 索引下推
  • C++ 知识点(长期更新)
  • 分布式版本控制概述
  • 如何使用 Go 连接 MO
  • 检索增强生成算法
  • CMake常用语法、函数
  • NacosRce到docker逃逸实战
  • HCIP第九章(MPLS理论)
  • Spring Cloud全解析:配置中心之springCloudConfig使用消息总线进行动态刷新
  • 【跃迁之路】【699天】程序员高效学习方法论探索系列(实验阶段456-2019.1.19)...
  • Angular 响应式表单 基础例子
  • CoolViewPager:即刻刷新,自定义边缘效果颜色,双向自动循环,内置垂直切换效果,想要的都在这里...
  • JavaScript中的对象个人分享
  • Java编程基础24——递归练习
  • PHP面试之三:MySQL数据库
  • vue2.0开发聊天程序(四) 完整体验一次Vue开发(下)
  • 百度贴吧爬虫node+vue baidu_tieba_crawler
  • 从零到一:用Phaser.js写意地开发小游戏(Chapter 3 - 加载游戏资源)
  • 对象管理器(defineProperty)学习笔记
  • 浮现式设计
  • 我有几个粽子,和一个故事
  • 线上 python http server profile 实践
  • 写给高年级小学生看的《Bash 指南》
  • 原生JS动态加载JS、CSS文件及代码脚本
  • 在GitHub多个账号上使用不同的SSH的配置方法
  • 如何用纯 CSS 创作一个货车 loader
  • 选择阿里云数据库HBase版十大理由
  • ​​​​​​​​​​​​​​汽车网络信息安全分析方法论
  • ​​​​​​​sokit v1.3抓手机应用socket数据包: Socket是传输控制层协议,WebSocket是应用层协议。
  • #WEB前端(HTML属性)
  • #我与虚拟机的故事#连载20:周志明虚拟机第 3 版:到底值不值得买?
  • $.ajax,axios,fetch三种ajax请求的区别
  • $emit传递多个参数_PPC和MIPS指令集下二进制代码中函数参数个数的识别方法
  • (1)Android开发优化---------UI优化
  • (13)DroneCAN 适配器节点(一)
  • (html5)在移动端input输入搜索项后 输入法下面为什么不想百度那样出现前往? 而我的出现的是换行...
  • (二十九)STL map容器(映射)与STL pair容器(值对)
  • (六)激光线扫描-三维重建
  • (每日一问)基础知识:堆与栈的区别
  • (原創) 如何動態建立二維陣列(多維陣列)? (.NET) (C#)
  • (转载)虚幻引擎3--【UnrealScript教程】章节一:20.location和rotation
  • (最简单,详细,直接上手)uniapp/vue中英文多语言切换
  • .bat批处理(十):从路径字符串中截取盘符、文件名、后缀名等信息
  • .class文件转换.java_从一个class文件深入理解Java字节码结构
  • .equals()到底是什么意思?
  • .NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划
  • .NET Reactor简单使用教程
  • .net 桌面开发 运行一阵子就自动关闭_聊城旋转门家用价格大约是多少,全自动旋转门,期待合作...
  • .netcore 如何获取系统中所有session_如何把百度推广中获取的线索(基木鱼,电话,百度商桥等)同步到企业微信或者企业CRM等企业营销系统中...