import { useCallback, useMemo, useRef, useState } from 'react';
|
import { ProTable } from '@ant-design/pro-components';
|
import type { ActionType } from '@ant-design/pro-components';
|
import { App as AntdApp, Descriptions, Drawer, Tag } from 'antd';
|
import { createRoleColumns, getRoleStatusMeta, getRoleTypeMeta } from './columns';
|
import { getRoleDetail, getRoleList, toggleRoleStatus } from './service';
|
import type { RoleListQuery, RoleVO } from './types';
|
|
const Roles = () => {
|
const actionRef = useRef<ActionType>();
|
const { message, modal } = AntdApp.useApp();
|
const [detail, setDetail] = useState<RoleVO | null>(null);
|
const [detailOpen, setDetailOpen] = useState(false);
|
const [detailLoading, setDetailLoading] = useState(false);
|
|
const handleDetail = useCallback(async (record: RoleVO) => {
|
setDetailOpen(true);
|
setDetailLoading(true);
|
|
try {
|
const data = await getRoleDetail({ id: record.id });
|
setDetail(data);
|
} catch {
|
// 错误已由 request 统一提示
|
setDetailOpen(false);
|
} finally {
|
setDetailLoading(false);
|
}
|
}, []);
|
|
const handleToggle = useCallback(
|
(record: RoleVO) => {
|
const nextAction = record.status === 'ENABLED' ? '禁用' : '启用';
|
modal.confirm({
|
title: `${nextAction}角色`,
|
content: `确定要${nextAction}角色「${record.roleName}」吗?`,
|
okText: nextAction,
|
cancelText: '取消',
|
okButtonProps: { danger: record.status === 'ENABLED' },
|
onOk: async () => {
|
await toggleRoleStatus({ id: record.id });
|
message.success(`角色已${nextAction}`);
|
actionRef.current?.reload();
|
},
|
});
|
},
|
[message, modal],
|
);
|
|
const columns = useMemo(
|
() => createRoleColumns({ onDetail: handleDetail, onToggle: handleToggle }),
|
[handleDetail, handleToggle],
|
);
|
|
return (
|
<div className="role-page">
|
<ProTable<RoleVO, RoleListQuery>
|
rowKey="id"
|
actionRef={actionRef}
|
columns={columns}
|
cardProps={{ className: 'table-card' }}
|
search={{ labelWidth: 'auto', defaultCollapsed: false }}
|
scroll={{ x: 960 }}
|
options={false}
|
pagination={{
|
defaultPageSize: 10,
|
showSizeChanger: true,
|
hideOnSinglePage: false,
|
showTotal: (total) => `共 ${total} 条`,
|
}}
|
request={async (params) => {
|
const { current = 1, pageSize = 10, roleName, status } = params;
|
const keyword = roleName?.trim().toLowerCase();
|
const list = await getRoleList();
|
const filtered = list.filter((role) => {
|
const matchesKeyword =
|
!keyword ||
|
role.roleName.toLowerCase().includes(keyword) ||
|
role.roleCode.toLowerCase().includes(keyword);
|
const matchesStatus = !status || role.status === status;
|
return matchesKeyword && matchesStatus;
|
});
|
const start = (current - 1) * pageSize;
|
|
return {
|
data: filtered.slice(start, start + pageSize),
|
total: filtered.length,
|
success: true,
|
};
|
}}
|
/>
|
|
<Drawer
|
title="角色详情"
|
width={460}
|
open={detailOpen}
|
loading={detailLoading}
|
onClose={() => {
|
setDetailOpen(false);
|
setDetail(null);
|
}}
|
>
|
{detail && (
|
<Descriptions column={1} bordered size="small">
|
<Descriptions.Item label="角色名称">{detail.roleName}</Descriptions.Item>
|
<Descriptions.Item label="角色编码">{detail.roleCode}</Descriptions.Item>
|
<Descriptions.Item label="角色类型">
|
<Tag color={getRoleTypeMeta(detail.roleType).color} bordered={false}>
|
{getRoleTypeMeta(detail.roleType).text}
|
</Tag>
|
</Descriptions.Item>
|
<Descriptions.Item label="状态">
|
<Tag color={getRoleStatusMeta(detail.status).color} bordered={false}>
|
{getRoleStatusMeta(detail.status).text}
|
</Tag>
|
</Descriptions.Item>
|
<Descriptions.Item label="描述">{detail.description || '-'}</Descriptions.Item>
|
</Descriptions>
|
)}
|
</Drawer>
|
</div>
|
);
|
};
|
|
export default Roles;
|