react项目权限

153 阅读7分钟

1. 设置路由:使用React Router Dom 6.0来设置项目的路由。您可以根据不同的权限配置路由,显示不同的页面或组件。

npm install react-router-dom@next

然后创建一个PrivateRoute组件,用于进行权限验证并渲染路由:

import { Route, Navigate } from 'react-router-dom';

interface PrivateRouteProps {
  permission: boolean; // 根据权限判断是否有访问权限
  redirectPath: string; // 无权限时的重定向路径
  path: string;
  element: JSX.Element;
}

const PrivateRoute: React.FC<PrivateRouteProps> = ({
  permission,
  redirectPath,
  path,
  element,
}) => {
  return permission ? (
    <Route path={path} element={element} />
  ) : (
    <Navigate to={redirectPath} replace />
  );
};

export default PrivateRoute;

接下来,在项目的根组件中,创建一个路由表并使用PrivateRoute组件进行路由配置:

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import PrivateRoute from './PrivateRoute';

// 导入需要的组件
import HomePage from './pages/HomePage';
import DashboardPage from './pages/DashboardPage';
import NotFoundPage from './pages/NotFoundPage';
import NoPermissionPage from './pages/NoPermissionPage';

const App: React.FC = () => {
  // 假设有一个isAuthenticated变量表示用户是否已登录
  const isAuthenticated = true;

  return (
    <Router>
      <Routes>
        <Route path="/" element={<HomePage />} />

        <PrivateRoute
          path="/dashboard"
          element={<DashboardPage />}
          permission={isAuthenticated}
          redirectPath="/no-permission"
        />

        <Route path="/no-permission" element={<NoPermissionPage />} />

        <Route path="*" element={<NotFoundPage />} />
      </Routes>
    </Router>
  );
};

export default App;

在上面的代码中,我们创建了一个PrivateRoute组件来实现权限验证,并根据权限决定是否显示相应的页面。如果用户已登录(isAuthenticatedtrue),则允许访问/dashboard页面,否则重定向到/no-permission页面。

2. 定义菜单数据:创建一个菜单数据文件,包含菜单的名称、路径和权限信息。根据用户的权限,动态生成菜单。

在菜单数据文件中,可以使用Ant Design的Menu组件和相关组件来定义菜单数据,并根据用户的权限动态生成菜单:

// menuData.ts

import { Menu } from 'antd';
import {
  HomeOutlined,
  DashboardOutlined,
  FileOutlined,
  PieChartOutlined,
} from '@ant-design/icons';

const { SubMenu } = Menu;

// 定义菜单数据
const menuData = [
  {
    title: '首页',
    path: '/',
    icon: <HomeOutlined />,
  },
  {
    title: '仪表盘',
    path: '/dashboard',
    icon: <DashboardOutlined />,
    children: [
      {
        title: '报表',
        path: '/dashboard/reports',
        icon: <FileOutlined />,
      },
      {
        title: '统计',
        path: '/dashboard/analytics',
        icon: <PieChartOutlined />,
      },
    ],
  },
  // 更多菜单项...
];

export default menuData;

接下来,在菜单组件中,使用Ant Design的Menu组件和相关组件来渲染菜单:

// Menu.tsx

import { Menu } from 'antd';
import { Link, useLocation } from 'react-router-dom';
import menuData from './menuData';

const { SubMenu } = Menu;

const MenuComponent: React.FC = () => {
  const location = useLocation();

  // 根据用户权限过滤菜单数据
  const filteredMenuData = menuData.filter((item) => item.permission); // 进行权限过滤的逻辑根据实际需求修改

  // 递归生成菜单项
  const renderMenuItems = (data: typeof filteredMenuData) => {
    return data.map((item) => {
      if (item.children && item.children.length > 0) {
        return (
          <SubMenu key={item.path} title={item.title} icon={item.icon}>
            {renderMenuItems(item.children)}
          </SubMenu>
        );
      }
      return (
        <Menu.Item key={item.path} icon={item.icon}>
          <Link to={item.path}>{item.title}</Link>
        </Menu.Item>
      );
    });
  };

  return (
    <Menu
      theme="dark"
      mode="inline"
      selectedKeys={[location.pathname]}
      defaultOpenKeys={['/dashboard']} // 默认展开的子菜单
    >
      {renderMenuItems(filteredMenuData)}
    </Menu>
  );
};

export default MenuComponent;

我们使用Ant Design的MenuSubMenu组件来渲染菜单,使用Link组件来实现路由导航。

在菜单项的渲染中,当菜单项有子菜单时,我们通过递归调用renderMenuItems函数来生成子菜单。

最后,将该MenuComponent组件添加到您的项目中,以在适当的位置显示生成的菜单。

3. 权限管理:使用React Context或Redux等状态管理工具来管理用户的权限信息。根据用户的权限,动态渲染菜单和按钮。

要使用React Context或Redux等状态管理工具来管理用户的权限信息,并根据用户的权限动态渲染菜单和按钮,可以按照以下步骤进行实现。

  1. 创建权限上下文(Context)或Redux状态管理:
// AuthContext.tsx (使用React Context示例)
import React, { createContext, useContext, useState } from 'react';

interface AuthContextProps {
  isAuthenticated: boolean;
  userPermissions: string[];
  login: () => void;
  logout: () => void;
}

const AuthContext = createContext<AuthContextProps>({
  isAuthenticated: false,
  userPermissions: [],
  login: () => {},
  logout: () => {},
});

export const AuthProvider: React.FC = ({ children }) => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [userPermissions, setUserPermissions] = useState<string[]>([]);

  const login = () => {
    // 执行登录逻辑
    setIsAuthenticated(true);
  };

  const logout = () => {
    // 执行登出逻辑
    setIsAuthenticated(false);
  };

  return (
    <AuthContext.Provider
      value={{ isAuthenticated, userPermissions, login, logout }}
    >
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => useContext(AuthContext);

  1. 在根组件中应用权限上下文(Context)或Redux状态管理:
// App.tsx (使用React Context示例)
import { AuthProvider } from './AuthContext';
import Routes from './Routes'; // 导入路由组件

const App: React.FC = () => {
  return (
    <AuthProvider> {/* 在根组件中应用权限上下文 */}
      <Routes /> {/* 渲染路由组件 */}
    </AuthProvider>
  );
};

export default App;

  1. 在菜单组件中根据权限信息渲染菜单:
// Menu.tsx
import { useAuth } from './AuthContext';
import menuData from './menuData';

const Menu: React.FC = () => {
  const { userPermissions } = useAuth(); // 获取用户权限信息

  const filteredMenuData = menuData.filter((item) =>
    item.permission ? userPermissions.includes(item.permission) : true
  );

  // 渲染菜单项...

  return <ul>{/* 菜单内容 */}</ul>;
};

export default Menu;

  1. 在按钮组件中根据权限信息渲染按钮:
// Button.tsx
import { useAuth } from './AuthContext';

interface ButtonProps {
  permission: string;
  children: React.ReactNode;
}

const Button: React.FC<ButtonProps> = ({ permission, children }) => {
  const { userPermissions } = useAuth(); // 获取用户权限信息

  return userPermissions.includes(permission) ? (
    <button>{children}</button>
  ) : null;
};

export default Button;

我们使用React Context来创建了一个权限上下文(AuthContext),并在根组件中应用该上下文。然后,在菜单组件和按钮组件中使用useAuth来获取用户的权限信息,并根据权限信息来渲染菜单和按钮。

4. 菜单组件:创建一个菜单组件,将菜单数据作为参数传入。根据菜单数据生成菜单的层级结构,并根据用户的权限进行控制。

  1. 创建菜单组件,并接受菜单数据和用户权限作为参数:
// Menu.tsx
import { Menu } from 'antd';

interface MenuItem {
  name: string;
  path: string;
  permission?: string;
  children?: MenuItem[];
}

interface MenuProps {
  menuData: MenuItem[];
  userPermissions: string[];
}

const MenuComponent: React.FC<MenuProps> = ({ menuData, userPermissions }) => {
  // 递归生成菜单项
  const renderMenuItems = (data: MenuItem[]) => {
    return data.map((item) => {
      if (item.permission && !userPermissions.includes(item.permission)) {
        return null;
      }

      if (item.children && item.children.length > 0) {
        return (
          <Menu.SubMenu key={item.path} title={item.name}>
            {renderMenuItems(item.children)}
          </Menu.SubMenu>
        );
      }

      return (
        <Menu.Item key={item.path}>{item.name}</Menu.Item>
      );
    });
  };

  return (
    <Menu mode="vertical">
      {renderMenuItems(menuData)}
    </Menu>
  );
};

export default MenuComponent;

  1. 在父组件中传入菜单数据和用户权限,并使用菜单组件进行渲染:
// App.tsx
import MenuComponent from './Menu';
import menuData from './menuData';
import { useAuth } from './AuthContext';

const App: React.FC = () => {
  const { userPermissions } = useAuth(); // 从权限上下文或状态中获取用户权限信息

  return (
    <div>
      {/* 其他组件 */}
      <MenuComponent menuData={menuData} userPermissions={userPermissions} />
      {/* 其他组件 */}
    </div>
  );
};

export default App;

我们创建了一个MenuComponent菜单组件,接受菜单数据和用户权限作为参数。然后,在renderMenuItems函数中,我们使用递归的方式生成菜单的层级结构,并根据用户的权限进行控制。最后,在父组件App中,我们传入了菜单数据和用户权限,并使用MenuComponent进行渲染。

5. 按钮权限:在需要进行权限控制的按钮组件上,根据用户的权限决定是否显示或禁用该按钮。

要根据用户的权限决定是否显示或禁用按钮组件,可以使用条件渲染和disabled属性来实现。以下是一个示例代码,演示如何在需要进行权限控制的按钮组件上根据用户的权限决定是否显示或禁用该按钮:

import { Button } from 'antd';
import { useAuth } from './AuthContext';

interface ButtonProps {
  permission: string;
  children: React.ReactNode;
}

const PermissionButton: React.FC<ButtonProps> = ({ permission, children }) => {
  const { userPermissions } = useAuth(); // 获取用户权限信息

  const hasPermission = userPermissions.includes(permission);

  return hasPermission ? (
    <Button>{children}</Button>
  ) : null;
};

export default PermissionButton;

在上述代码中,我们创建了一个PermissionButton权限按钮组件,接受permissionchildren作为参数。在组件内部,我们使用useAuth来获取用户的权限信息,并根据用户的权限决定是否显示按钮。如果用户拥有该按钮所需的权限,则渲染Button组件;否则,返回null,即不渲染该按钮。

您可以在需要进行权限控制的地方使用PermissionButton组件,并设置相应的permission和按钮内容,例如:

import PermissionButton from './PermissionButton';

const Page: React.FC = () => {
  return (
    <div>
      {/* 其他组件 */}
      <PermissionButton permission="edit" >编辑</PermissionButton>
      {/* 其他组件 */}
    </div>
  );
};

我们将PermissionButton组件用于需要进行权限控制的地方,并通过设置permission属性来指定该按钮所需的权限。根据用户的权限,按钮将相应地显示或隐藏。

6. 权限检查与重定向:在需要权限验证的页面或组件中,进行权限检查。如果用户没有权限访问该页面,可以进行重定向到无权限页面或其他逻辑处理。

1、在路由组件中进行权限检查并进行重定向或其他逻辑处理:

// Routes.tsx
import { Routes, Route, Navigate } from 'react-router-dom';
import { useAuth } from './AuthContext';

// 导入需要进行权限控制的页面组件
import HomePage from './HomePage';
import DashboardPage from './DashboardPage';
import NoPermissionPage from './NoPermissionPage';

const PrivateRoute: React.FC<{ path: string; permissions: string[] }> = ({
  path,
  permissions,
  ...rest
}) => {
  const { isAuthenticated, userPermissions } = useAuth(); // 获取用户的权限信息和认证状态

  const checkPermissions = (permissions: string[]) => {
    // 进行权限检查的逻辑,根据实际需求进行修改
    return permissions.some((permission) =>
      userPermissions.includes(permission)
    );
  };

  return isAuthenticated && checkPermissions(permissions) ? (
    <Route path={path} {...rest} />
  ) : (
    <Navigate to="/no-permission" replace state={{ from: path }} />
  );
};

const AppRoutes: React.FC = () => {
  return (
    <Routes>
      <Route path="/" element={<HomePage />} />
      <PrivateRoute
        path="/dashboard"
        permissions={['dashboard:view']}
        element={<DashboardPage />}
      />
      <Route path="/no-permission" element={<NoPermissionPage />} />
      {/* 其他路由配置 */}
    </Routes>
  );
};

export default AppRoutes;

在上述代码中,我们使用新的RoutesRoute组件来进行路由配置。然后,我们创建了一个PrivateRoute组件,它接受pathpermissions作为参数,并在组件内部进行权限检查。如果用户有所需的权限,则渲染对应的路由组件;否则,通过Navigate组件进行重定向到无权限页面。