imeeting/frontend/src/pages/RolePermissionBinding.tsx

195 lines
5.9 KiB
TypeScript

import { Button, Card, Col, message, Row, Space, Table, Tag, Tree, Typography } from "antd";
import type { DataNode } from "antd/es/tree";
import { useEffect, useMemo, useState } from "react";
import { listPermissions, listRolePermissions, listRoles, saveRolePermissions } from "../api";
import type { SysPermission, SysRole } from "../types";
const { Title, Text } = Typography;
type PermissionNode = SysPermission & { key: number; children?: PermissionNode[] };
function buildPermissionTree(list: SysPermission[]): PermissionNode[] {
const map = new Map<number, PermissionNode>();
const roots: PermissionNode[] = [];
list.forEach((item) => {
map.set(item.permId, { ...item, key: item.permId, children: [] });
});
map.forEach((node) => {
if (node.parentId && map.has(node.parentId)) {
map.get(node.parentId)!.children!.push(node);
} else {
roots.push(node);
}
});
const sortNodes = (nodes: PermissionNode[]) => {
nodes.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
nodes.forEach((n) => n.children && sortNodes(n.children));
};
sortNodes(roots);
return roots;
}
function toTreeData(nodes: PermissionNode[]): DataNode[] {
return nodes.map((node) => ({
key: node.permId,
title: (
<Space>
<span>{node.name}</span>
{node.permType === "button" && <Tag color="blue"></Tag>}
</Space>
),
children: node.children ? toTreeData(node.children) : undefined
}));
}
export default function RolePermissionBinding() {
const [roles, setRoles] = useState<SysRole[]>([]);
const [permissions, setPermissions] = useState<SysPermission[]>([]);
const [loadingRoles, setLoadingRoles] = useState(false);
const [loadingPerms, setLoadingPerms] = useState(false);
const [saving, setSaving] = useState(false);
const [selectedRoleId, setSelectedRoleId] = useState<number | null>(null);
const [checkedPermIds, setCheckedPermIds] = useState<number[]>([]);
const selectedRole = useMemo(
() => roles.find((r) => r.roleId === selectedRoleId) || null,
[roles, selectedRoleId]
);
const loadRoles = async () => {
setLoadingRoles(true);
try {
const list = await listRoles();
setRoles(list || []);
} finally {
setLoadingRoles(false);
}
};
const loadPermissions = async () => {
setLoadingPerms(true);
try {
const list = await listPermissions();
setPermissions(list || []);
} catch (e) {
message.error("加载权限失败,请确认接口已实现");
} finally {
setLoadingPerms(false);
}
};
const loadRolePermissions = async (roleId: number) => {
try {
const list = await listRolePermissions(roleId);
setCheckedPermIds(list || []);
} catch (e) {
setCheckedPermIds([]);
message.error("加载角色权限失败,请确认接口已实现");
}
};
useEffect(() => {
loadRoles();
loadPermissions();
}, []);
useEffect(() => {
if (selectedRoleId) {
loadRolePermissions(selectedRoleId);
} else {
setCheckedPermIds([]);
}
}, [selectedRoleId]);
const treeData = useMemo(() => toTreeData(buildPermissionTree(permissions)), [permissions]);
const handleSave = async () => {
if (!selectedRoleId) {
message.warning("请先选择角色");
return;
}
setSaving(true);
try {
await saveRolePermissions(selectedRoleId, checkedPermIds);
message.success("角色权限绑定已保存");
} catch (e) {
message.error("保存失败,请确认接口已实现");
} finally {
setSaving(false);
}
};
return (
<div className="page-shell">
<div className="page-header">
<div>
<Title level={4} className="page-title"></Title>
<Text type="secondary" className="page-subtitle"></Text>
</div>
<Button type="primary" onClick={handleSave} loading={saving} disabled={!selectedRoleId}>
</Button>
</div>
<Row gutter={[24, 24]}>
<Col xs={24} lg={10}>
<Card title="选择角色" bordered={false} className="surface-card">
<Table
rowKey="roleId"
size="middle"
loading={loadingRoles}
dataSource={roles}
rowSelection={{
type: "radio",
selectedRowKeys: selectedRoleId ? [selectedRoleId] : [],
onChange: (keys) => setSelectedRoleId(keys[0] as number)
}}
pagination={{ pageSize: 8 }}
columns={[
{ title: "ID", dataIndex: "roleId", width: 80 },
{ title: "角色编码", dataIndex: "roleCode" },
{ title: "角色名称", dataIndex: "roleName" },
{
title: "状态",
dataIndex: "status",
width: 90,
render: (v) => (v === 1 ? <Tag color="green"></Tag> : <Tag color="red"></Tag>)
}
]}
/>
</Card>
</Col>
<Col xs={24} lg={14}>
<Card
title="配置权限"
bordered={false}
className="surface-card"
extra={
<Text type="secondary">
{selectedRole ? `当前角色:${selectedRole.roleName}` : "未选择角色"}
</Text>
}
>
<Tree
checkable
selectable={false}
treeData={treeData}
checkedKeys={checkedPermIds}
onCheck={(keys) => setCheckedPermIds(keys as number[])}
defaultExpandAll
/>
{!permissions.length && !loadingPerms && (
<div style={{ marginTop: 12 }}>
<Text type="secondary"></Text>
</div>
)}
</Card>
</Col>
</Row>
</div>
);
}