Ant Design Pro 提供了完整的权限管理解决方案。下面是基于 TypeScript 的权限按钮实现方案,结合了 @umijs/plugin-access
插件和自定义组件。
import React from 'react';
import { useModel, useAccess, Access } from 'umi';
import { Button, Card, Space, PageHeader, Tabs, Typography, message, Popconfirm } from 'antd';
import type { ProColumns } from '@ant-design/pro-table';
import ProTable from '@ant-design/pro-table';
import { PlusOutlined, EditOutlined, DeleteOutlined, DownloadOutlined } from '@ant-design/icons';
const { TabPane } = Tabs;
const { Title } = Typography;
// 用户数据类型
interface User {
id: string;
name: string;
role: string;
department: string;
}
// 权限模型类型
export type AccessType = 'canView' | 'canCreate' | 'canEdit' | 'canDelete' | 'canExport';
// 产品数据类型
interface Product {
id: string;
name: string;
category: string;
price: number;
stock: number;
createdAt: string;
status: 'active' | 'inactive';
}
// 权限按钮组件
interface AuthButtonProps {
accessKey: AccessType;
title?: string;
icon?: React.ReactNode;
type?: 'primary' | 'dashed' | 'link' | 'text' | 'default';
danger?: boolean;
ghost?: boolean;
shape?: 'circle' | 'round';
size?: 'large' | 'middle' | 'small';
onClick?: () => void;
style?: React.CSSProperties;
confirmText?: string;
disabled?: boolean;
children?: React.ReactNode;
}
const AuthButton: React.FC = (props) => {
const access = useAccess();
const hasAccess = access[props.accessKey];
// 如果设置了确认框
if (props.confirmText) {
return hasAccess ? (
) : null;
}
return hasAccess ? (
) : null;
};
// 产品列表页面
const ProductPage: React.FC = () => {
// 获取全局状态中的用户信息
const { initialState } = useModel('@@initialState');
const currentUser = initialState?.currentUser as User | undefined;
// 权限管理
const access = useAccess();
// 模拟产品数据
const mockProducts: Product[] = [
{ id: '1', name: 'MacBook Pro', category: 'Laptop', price: 1999, stock: 15, createdAt: '2023-01-15', status: 'active' },
{ id: '2', name: 'iPhone 15', category: 'Phone', price: 999, stock: 30, createdAt: '2023-02-20', status: 'active' },
{ id: '3', name: 'iPad Pro', category: 'Tablet', price: 799, stock: 10, createdAt: '2023-03-05', status: 'inactive' },
];
// 列定义
const columns: ProColumns[] = [
{
title: '产品名称',
dataIndex: 'name',
key: 'name',
},
{
title: '类别',
dataIndex: 'category',
key: 'category',
},
{
title: '价格($)',
dataIndex: 'price',
key: 'price',
valueType: 'money',
},
{
title: '库存',
dataIndex: 'stock',
key: 'stock',
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
valueEnum: {
active: { text: '激活', status: 'Success' },
inactive: { text: '未激活', status: 'Default' },
},
},
{
title: '操作',
key: 'action',
render: (text, record) => (
}
onClick={() => handleEdit(record)}
/>
}
danger
confirmText="确定要删除此产品吗?"
onClick={() => handleDelete(record)}
/>
),
},
];
// 操作处理函数
const handleCreate = () => {
message.success('打开创建产品模态框');
};
const handleEdit = (product: Product) => {
message.info(`编辑产品: ${product.name}`);
};
const handleDelete = (product: Product) => {
message.success(`产品 ${product.name} 已删除`);
};
const handleExport = () => {
message.success('开始导出数据');
};
return (
}
onClick={handleCreate}
>
添加产品
}
onClick={handleExport}
>
导出数据
}
/>
columns={columns}
dataSource={mockProducts}
rowKey="id"
search={false}
options={{ density: true, reload: true }}
pagination={{
pageSize: 10,
}}
/>
无权查看产品列表
请联系管理员获取访问权限
产品统计报表
总销售额: $12,500
总库存: 55
}
onClick={handleExport}
style={{ marginTop: 16 }}
>
导出报表
{/* 权限提示 */}
访问受限
您没有查看产品列表或报表的权限
{currentUser?.role && (
当前角色: {currentUser.role}
)}
);
};
export default ProductPage;
在 src/access.ts
中定义权限规则:
// src/access.ts
import type { InitialState } from './app';
export default function access(initialState: { initialState?: InitialState }): Record {
const { currentUser } = initialState?.initialState ?? {};
// 角色权限映射
const rolePermissions = {
admin: ['canView', 'canCreate', 'canEdit', 'canDelete', 'canExport'],
editor: ['canView', 'canCreate', 'canEdit'],
viewer: ['canView'],
};
// 如果没有当前用户,则认为没有任何权限
if (!currentUser) {
return {
canView: false,
canCreate: false,
canEdit: false,
canDelete: false,
canExport: false,
};
}
// 根据用户角色返回权限对象
const permissions = {
canView: rolePermissions[currentUser.role]?.includes('canView') || false,
canCreate: rolePermissions[currentUser.role]?.includes('canCreate') || false,
canEdit: rolePermissions[currentUser.role]?.includes('canEdit') || false,
canDelete: rolePermissions[currentUser.role]?.includes('canDelete') || false,
canExport: rolePermissions[currentUser.role]?.includes('canExport') || false,
};
return permissions;
}
在 src/models/global.ts
中管理用户信息:
// src/models/global.ts
import { useState } from 'react';
export type GlobalState = {
currentUser?: User;
};
export type User = {
id: string;
name: string;
role: 'admin' | 'editor' | 'viewer';
department?: string;
};
export default () => {
const [global, setGlobal] = useState(() => ({
currentUser: {
id: '001',
name: '管理员',
role: 'admin',
department: '技术部',
},
}));
return {
global,
setGlobal,
};
};
控制层级 | 实现方式 | 适用场景 |
---|---|---|
页面级 | Access 组件 |
控制整个模块或页面的访问权限 |
区域级 | Access + 条件渲染 |
控制页面中的特定区域 |
按钮级 | AuthButton 组件 |
控制单个操作按钮 |
// 普通按钮
创建
// 危险操作(带确认框)
删除
// 图标按钮
}
shape="circle"
/>
用户状态 | UI 表现 | 代码实现 |
---|---|---|
无权限查看内容 | 展示提示信息 | Access 组件的 accessible 属性 |
无操作权限 | 按钮不显示 | AuthButton 内部处理 |
特殊权限需求 | 提示联系管理员 | 自定义无权限提示组件 |
可以通过修改 access.ts
增加更多维度权限控制:
// 基于角色的权限控制
const permissions = {
// ...
canApprove: currentUser.role === 'admin',
};
// 基于部门的条件权限
if (currentUser.department === '财务部') {
permissions.canCreate = false;
}
// 基于时间/条件的权限
const now = new Date();
permissions.canExport = permissions.canExport && now.getHours() > 9 && now.getHours() < 18;
权限划分原则
canXxx
格式保持清晰API 级权限验证
// 在 services 中增加权限验证
export async function deleteProduct(id: string) {
// 检查前端权限
const access = useAccess();
if (!access.canDelete) {
throw new Error('无权执行此操作');
}
return request(`/api/products/${id}`, {
method: 'DELETE',
});
}
敏感操作二次确认
权限审计功能
// 添加权限变更日志
function handlePermissionChange() {
console.log(`[权限变更] 用户 ${currentUser.name} 尝试操作 ${action}`);
// 发送到审计服务...
}
按需加载大权限系统
// 动态加载特殊权限模块
const AdminPanel = React.lazy(() => import('./AdminPanel'));
}>
这个基于 Ant Design Pro 和 TypeScript 的权限按钮系统提供了完整的解决方案:
在实际企业项目中,这样的权限系统可以覆盖 95% 以上的权限控制场景,并通过合理的模块划分保持代码的可维护性和可扩展性。