feat(frontend): add usePermission hook, PermissionButton, PermissionRoute, and 403 handling

This commit is contained in:
2026-07-02 11:59:36 +08:00
parent b047dbc5d8
commit 14d81045e6
5 changed files with 68 additions and 0 deletions

View File

@@ -19,8 +19,13 @@ api.interceptors.response.use(
if (err.response?.status === 401) {
localStorage.removeItem('token');
localStorage.removeItem('user');
localStorage.removeItem('permissions');
window.location.href = '/login';
}
if (err.response?.status === 403) {
const msg = err.response?.data?.message || '权限不足';
console.warn('[403]', msg);
}
return Promise.reject(err.response?.data || err);
},
);

View File

@@ -0,0 +1,17 @@
import React from 'react';
import { Button } from 'antd';
import type { ButtonProps } from 'antd';
import { usePermission } from '../hooks/usePermission';
interface PermissionButtonProps extends ButtonProps {
permission: string;
children: React.ReactNode;
}
const PermissionButton: React.FC<PermissionButtonProps> = ({ permission, children, ...btnProps }) => {
const { hasPermission } = usePermission();
if (!hasPermission(permission)) return null;
return <Button {...btnProps}>{children}</Button>;
};
export default PermissionButton;

View File

@@ -0,0 +1,24 @@
import React from 'react';
import { Result } from 'antd';
import { usePermission } from '../hooks/usePermission';
interface PermissionRouteProps {
permission: string;
children: React.ReactNode;
}
const PermissionRoute: React.FC<PermissionRouteProps> = ({ permission, children }) => {
const { hasPermission } = usePermission();
if (!hasPermission(permission)) {
return (
<Result
status="403"
title="无权访问"
subTitle="您没有访问此页面的权限"
/>
);
}
return <>{children}</>;
};
export default PermissionRoute;

View File

@@ -0,0 +1,21 @@
import { useMemo } from 'react';
export function usePermission() {
const permissions: string[] = useMemo(() => {
try {
return JSON.parse(localStorage.getItem('permissions') || '[]');
} catch {
return [];
}
}, []);
const hasPermission = (code: string): boolean => permissions.includes(code);
const hasAnyPermission = (...codes: string[]): boolean =>
codes.some(c => permissions.includes(c));
const hasAllPermissions = (...codes: string[]): boolean =>
codes.every(c => permissions.includes(c));
return { permissions, hasPermission, hasAnyPermission, hasAllPermissions };
}

View File

@@ -16,6 +16,7 @@ const LoginPage: React.FC = () => {
const res: any = await api.post('/auth/login', values);
localStorage.setItem('token', res.access_token);
localStorage.setItem('user', JSON.stringify(res.user));
localStorage.setItem('permissions', JSON.stringify(res.user.permissions || []));
message.success('登录成功');
navigate('/dashboard');
} catch (err: any) {