refactor: 重构分页逻辑并统一代码风格
1. 新增分页数据处理hook和工具函数,实现分页状态统一管理 2. 替换所有页面的表格分页配置为标准current/page模式 3. 统一项目内字符串引号风格为双引号 4. 调整权限树面板样式和布局代码格式
This commit is contained in:
@@ -1,163 +1,156 @@
|
||||
import { useMemo } from 'react';
|
||||
import { Tree } from '@arco-design/web-react';
|
||||
import type { Permission } from '../api/types';
|
||||
import { useMemo } from "react";
|
||||
import { Tree } from "@arco-design/web-react";
|
||||
import type { Permission } from "../api/types";
|
||||
|
||||
type PermissionTreeNode = {
|
||||
key: string;
|
||||
title: string;
|
||||
children?: PermissionTreeNode[];
|
||||
key: string;
|
||||
title: string;
|
||||
children?: PermissionTreeNode[];
|
||||
};
|
||||
|
||||
type PermissionModuleConfig = {
|
||||
prefix: string;
|
||||
title: string;
|
||||
subject: string;
|
||||
prefix: string;
|
||||
title: string;
|
||||
subject: string;
|
||||
};
|
||||
|
||||
const systemPermissionModules: PermissionModuleConfig[] = [
|
||||
{ prefix: 'permission', title: '权限管理', subject: '权限' },
|
||||
{ prefix: 'role', title: '角色管理', subject: '角色' },
|
||||
{ prefix: 'admin-user', title: '人员管理', subject: '人员' },
|
||||
{ prefix: 'user', title: '用户管理', subject: '用户' },
|
||||
{ prefix: 'log', title: '日志管理', subject: '日志' },
|
||||
{ prefix: "permission", title: "权限管理", subject: "权限" },
|
||||
{ prefix: "role", title: "角色管理", subject: "角色" },
|
||||
{ prefix: "admin-user", title: "人员管理", subject: "人员" },
|
||||
{ prefix: "user", title: "用户管理", subject: "用户" },
|
||||
{ prefix: "log", title: "日志管理", subject: "日志" },
|
||||
];
|
||||
|
||||
const businessPermissionModules: PermissionModuleConfig[] = [
|
||||
{ prefix: 'drama', title: '短剧管理', subject: '短剧' },
|
||||
{ prefix: 'episode', title: '剧集管理', subject: '剧集' },
|
||||
{ prefix: 'media', title: '媒体管理', subject: '媒体' },
|
||||
{ prefix: 'payment', title: '支付管理', subject: '支付' },
|
||||
{ prefix: "drama", title: "短剧管理", subject: "短剧" },
|
||||
{ prefix: "episode", title: "剧集管理", subject: "剧集" },
|
||||
{ prefix: "media", title: "媒体管理", subject: "媒体" },
|
||||
{ prefix: "payment", title: "支付管理", subject: "支付" },
|
||||
];
|
||||
|
||||
const actionNames: Record<string, string> = {
|
||||
read: '查看',
|
||||
create: '创建',
|
||||
update: '更新',
|
||||
delete: '删除',
|
||||
manage: '管理',
|
||||
read: "查看",
|
||||
create: "创建",
|
||||
update: "更新",
|
||||
delete: "删除",
|
||||
manage: "管理",
|
||||
};
|
||||
|
||||
function permissionKey(id: number) {
|
||||
return `permission:${id}`;
|
||||
return `permission:${id}`;
|
||||
}
|
||||
|
||||
function permissionIdFromKey(key: string) {
|
||||
const id = Number(key.replace('permission:', ''));
|
||||
return Number.isInteger(id) ? id : null;
|
||||
const id = Number(key.replace("permission:", ""));
|
||||
return Number.isInteger(id) ? id : null;
|
||||
}
|
||||
|
||||
function getPermissionPrefix(code: string) {
|
||||
return code.split(':')[0];
|
||||
return code.split(":")[0];
|
||||
}
|
||||
|
||||
function getPermissionAction(code: string) {
|
||||
return code.split(':')[1] || code;
|
||||
return code.split(":")[1] || code;
|
||||
}
|
||||
|
||||
function getPermissionTitle(permission: Permission, module?: PermissionModuleConfig) {
|
||||
return permission.name || (module ? `${actionNames[getPermissionAction(permission.code)]}${module.subject}` : permission.code);
|
||||
return permission.name || (module ? `${actionNames[getPermissionAction(permission.code)]}${module.subject}` : permission.code);
|
||||
}
|
||||
|
||||
function buildPermissionModuleNode(
|
||||
permissions: Permission[],
|
||||
module: PermissionModuleConfig,
|
||||
): PermissionTreeNode | null {
|
||||
const children = permissions
|
||||
.filter((permission) => getPermissionPrefix(permission.code) === module.prefix)
|
||||
.map((permission) => ({
|
||||
key: permissionKey(permission.id),
|
||||
title: getPermissionTitle(permission, module),
|
||||
}));
|
||||
function buildPermissionModuleNode(permissions: Permission[], module: PermissionModuleConfig): PermissionTreeNode | null {
|
||||
const children = permissions
|
||||
.filter((permission) => getPermissionPrefix(permission.code) === module.prefix)
|
||||
.map((permission) => ({
|
||||
key: permissionKey(permission.id),
|
||||
title: getPermissionTitle(permission, module),
|
||||
}));
|
||||
|
||||
if (!children.length) {
|
||||
return null;
|
||||
}
|
||||
if (!children.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
key: `module:${module.prefix}`,
|
||||
title: module.title,
|
||||
children,
|
||||
};
|
||||
return {
|
||||
key: `module:${module.prefix}`,
|
||||
title: module.title,
|
||||
children,
|
||||
};
|
||||
}
|
||||
|
||||
function buildPermissionTree(permissions: Permission[]): PermissionTreeNode[] {
|
||||
const knownPrefixes = new Set([
|
||||
...systemPermissionModules.map((module) => module.prefix),
|
||||
...businessPermissionModules.map((module) => module.prefix),
|
||||
]);
|
||||
const systemChildren = systemPermissionModules
|
||||
.map((module) => buildPermissionModuleNode(permissions, module))
|
||||
.filter((node): node is PermissionTreeNode => Boolean(node));
|
||||
const businessChildren = businessPermissionModules
|
||||
.map((module) => buildPermissionModuleNode(permissions, module))
|
||||
.filter((node): node is PermissionTreeNode => Boolean(node));
|
||||
const otherPermissions = permissions
|
||||
.filter((permission) => !knownPrefixes.has(getPermissionPrefix(permission.code)))
|
||||
.map((permission) => ({
|
||||
key: permissionKey(permission.id),
|
||||
title: permission.name,
|
||||
}));
|
||||
const knownPrefixes = new Set([
|
||||
...systemPermissionModules.map((module) => module.prefix),
|
||||
...businessPermissionModules.map((module) => module.prefix),
|
||||
]);
|
||||
const systemChildren = systemPermissionModules
|
||||
.map((module) => buildPermissionModuleNode(permissions, module))
|
||||
.filter((node): node is PermissionTreeNode => Boolean(node));
|
||||
const businessChildren = businessPermissionModules
|
||||
.map((module) => buildPermissionModuleNode(permissions, module))
|
||||
.filter((node): node is PermissionTreeNode => Boolean(node));
|
||||
const otherPermissions = permissions
|
||||
.filter((permission) => !knownPrefixes.has(getPermissionPrefix(permission.code)))
|
||||
.map((permission) => ({
|
||||
key: permissionKey(permission.id),
|
||||
title: permission.name,
|
||||
}));
|
||||
|
||||
if (otherPermissions.length) {
|
||||
businessChildren.push({
|
||||
key: 'module:other',
|
||||
title: '其他权限',
|
||||
children: otherPermissions,
|
||||
});
|
||||
}
|
||||
if (otherPermissions.length) {
|
||||
businessChildren.push({
|
||||
key: "module:other",
|
||||
title: "其他权限",
|
||||
children: otherPermissions,
|
||||
});
|
||||
}
|
||||
|
||||
return [
|
||||
{ key: 'group:system', title: '系统权限', children: systemChildren },
|
||||
{ key: 'group:business', title: '业务权限', children: businessChildren },
|
||||
];
|
||||
return [
|
||||
{ key: "group:system", title: "系统权限", children: systemChildren },
|
||||
{ key: "group:business", title: "业务权限", children: businessChildren },
|
||||
];
|
||||
}
|
||||
|
||||
function collectExpandedKeys(nodes: PermissionTreeNode[]) {
|
||||
const keys: string[] = [];
|
||||
const visit = (items: PermissionTreeNode[]) => {
|
||||
items.forEach((item) => {
|
||||
if (item.children?.length) {
|
||||
keys.push(item.key);
|
||||
visit(item.children);
|
||||
}
|
||||
});
|
||||
};
|
||||
visit(nodes);
|
||||
return keys;
|
||||
const keys: string[] = [];
|
||||
const visit = (items: PermissionTreeNode[]) => {
|
||||
items.forEach((item) => {
|
||||
if (item.children?.length) {
|
||||
keys.push(item.key);
|
||||
visit(item.children);
|
||||
}
|
||||
});
|
||||
};
|
||||
visit(nodes);
|
||||
return keys;
|
||||
}
|
||||
|
||||
export function PermissionTreePicker({
|
||||
permissions,
|
||||
value,
|
||||
onChange,
|
||||
permissions,
|
||||
value,
|
||||
onChange,
|
||||
}: {
|
||||
permissions: Permission[];
|
||||
value: number[];
|
||||
onChange: (permissionIds: number[]) => void;
|
||||
permissions: Permission[];
|
||||
value: number[];
|
||||
onChange: (permissionIds: number[]) => void;
|
||||
}) {
|
||||
const treeData = useMemo(() => buildPermissionTree(permissions), [permissions]);
|
||||
const expandedKeys = useMemo(() => collectExpandedKeys(treeData), [treeData]);
|
||||
const checkedKeys = value.map((id) => permissionKey(id));
|
||||
const treeData = useMemo(() => buildPermissionTree(permissions), [permissions]);
|
||||
const expandedKeys = useMemo(() => collectExpandedKeys(treeData), [treeData]);
|
||||
const checkedKeys = value.map((id) => permissionKey(id));
|
||||
|
||||
return (
|
||||
<div className="permission-tree-panel">
|
||||
<Tree
|
||||
blockNode
|
||||
checkable
|
||||
checkedStrategy="child"
|
||||
checkedKeys={checkedKeys}
|
||||
defaultExpandedKeys={expandedKeys}
|
||||
selectable={false}
|
||||
showLine
|
||||
treeData={treeData}
|
||||
onCheck={(keys) => {
|
||||
onChange(
|
||||
keys
|
||||
.map((key) => permissionIdFromKey(key))
|
||||
.filter((id): id is number => id !== null),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="permission-tree-panel">
|
||||
<Tree
|
||||
blockNode
|
||||
checkable
|
||||
checkedStrategy="child"
|
||||
checkedKeys={checkedKeys}
|
||||
defaultExpandedKeys={expandedKeys}
|
||||
selectable={false}
|
||||
showLine
|
||||
treeData={treeData}
|
||||
onCheck={(keys) => {
|
||||
onChange(keys.map((key) => permissionIdFromKey(key)).filter((id): id is number => id !== null));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
.permission-tree-panel {
|
||||
height: 360px;
|
||||
overflow: auto;
|
||||
margin-bottom: 15px;
|
||||
padding: 12px 16px;
|
||||
border: 1px solid #e5e6eb;
|
||||
border-radius: 6px;
|
||||
|
||||
@@ -1,117 +1,147 @@
|
||||
import { useState } from 'react';
|
||||
import { Button, Card, Form, Input, Message, Modal, Space, Table, Tag } from '@arco-design/web-react';
|
||||
import { IconPlus } from '@arco-design/web-react/icon';
|
||||
import type { Permission, Role } from '../api/types';
|
||||
import { api } from '../api/client';
|
||||
import { usePageData } from '../hooks/usePageData';
|
||||
import { PermissionTreePicker } from './PermissionTreePicker';
|
||||
import './index.css';
|
||||
import { useState } from "react";
|
||||
import { Button, Card, Form, Input, Message, Modal, Space, Table, Tag } from "@arco-design/web-react";
|
||||
import { IconPlus } from "@arco-design/web-react/icon";
|
||||
import type { Permission, Role } from "../api/types";
|
||||
import { api } from "../api/client";
|
||||
import { usePageData } from "../hooks/usePageData";
|
||||
import { PermissionTreePicker } from "./PermissionTreePicker";
|
||||
import "./index.css";
|
||||
|
||||
function RolesPage() {
|
||||
const { data, loading, reload } = usePageData<Role>('/api/admin/v1/roles');
|
||||
const permissions = usePageData<Permission>('/api/admin/v1/permissions');
|
||||
const [createForm] = Form.useForm();
|
||||
const [createVisible, setCreateVisible] = useState(false);
|
||||
const [creatingRole, setCreatingRole] = useState(false);
|
||||
const [createPermissionIds, setCreatePermissionIds] = useState<number[]>([]);
|
||||
const [assignmentVisible, setAssignmentVisible] = useState(false);
|
||||
const [selectedRole, setSelectedRole] = useState<Role | null>(null);
|
||||
const [selectedPermissionIds, setSelectedPermissionIds] = useState<number[]>([]);
|
||||
const [savingPermissions, setSavingPermissions] = useState(false);
|
||||
const { data, loading, reload, changePage } = usePageData<Role>("/api/admin/v1/roles");
|
||||
const permissions = usePageData<Permission>("/api/admin/v1/permissions");
|
||||
const [createForm] = Form.useForm();
|
||||
const [createVisible, setCreateVisible] = useState(false);
|
||||
const [creatingRole, setCreatingRole] = useState(false);
|
||||
const [createPermissionIds, setCreatePermissionIds] = useState<number[]>([]);
|
||||
const [assignmentVisible, setAssignmentVisible] = useState(false);
|
||||
const [selectedRole, setSelectedRole] = useState<Role | null>(null);
|
||||
const [selectedPermissionIds, setSelectedPermissionIds] = useState<number[]>([]);
|
||||
const [savingPermissions, setSavingPermissions] = useState(false);
|
||||
|
||||
const openCreateRole = () => {
|
||||
createForm.resetFields();
|
||||
setCreatePermissionIds([]);
|
||||
setCreateVisible(true);
|
||||
};
|
||||
const openCreateRole = () => {
|
||||
createForm.resetFields();
|
||||
setCreatePermissionIds([]);
|
||||
setCreateVisible(true);
|
||||
};
|
||||
|
||||
const createRole = async (values: { code: string; name: string; description?: string }) => {
|
||||
setCreatingRole(true);
|
||||
try {
|
||||
await api.post('/api/admin/v1/roles', {
|
||||
...values,
|
||||
permissionIds: createPermissionIds,
|
||||
});
|
||||
Message.success('角色创建成功');
|
||||
setCreateVisible(false);
|
||||
await reload();
|
||||
} finally {
|
||||
setCreatingRole(false);
|
||||
}
|
||||
};
|
||||
const createRole = async (values: { code: string; name: string; description?: string }) => {
|
||||
setCreatingRole(true);
|
||||
try {
|
||||
await api.post("/api/admin/v1/roles", {
|
||||
...values,
|
||||
permissionIds: createPermissionIds,
|
||||
});
|
||||
Message.success("角色创建成功");
|
||||
setCreateVisible(false);
|
||||
await reload();
|
||||
} finally {
|
||||
setCreatingRole(false);
|
||||
}
|
||||
};
|
||||
|
||||
const openAssignPermissions = (role: Role) => {
|
||||
setSelectedRole(role);
|
||||
setSelectedPermissionIds(role.permissions.map((permission) => permission.id));
|
||||
setAssignmentVisible(true);
|
||||
};
|
||||
const openAssignPermissions = (role: Role) => {
|
||||
setSelectedRole(role);
|
||||
setSelectedPermissionIds(role.permissions.map((permission) => permission.id));
|
||||
setAssignmentVisible(true);
|
||||
};
|
||||
|
||||
const saveRolePermissions = async () => {
|
||||
if (!selectedRole) {
|
||||
return;
|
||||
}
|
||||
setSavingPermissions(true);
|
||||
try {
|
||||
await api.patch(`/api/admin/v1/roles/${selectedRole.id}/permissions`, {
|
||||
permissionIds: selectedPermissionIds,
|
||||
});
|
||||
Message.success('角色权限已更新');
|
||||
setAssignmentVisible(false);
|
||||
await reload();
|
||||
} finally {
|
||||
setSavingPermissions(false);
|
||||
}
|
||||
};
|
||||
const saveRolePermissions = async () => {
|
||||
if (!selectedRole) {
|
||||
return;
|
||||
}
|
||||
setSavingPermissions(true);
|
||||
try {
|
||||
await api.patch(`/api/admin/v1/roles/${selectedRole.id}/permissions`, {
|
||||
permissionIds: selectedPermissionIds,
|
||||
});
|
||||
Message.success("角色权限已更新");
|
||||
setAssignmentVisible(false);
|
||||
await reload();
|
||||
} finally {
|
||||
setSavingPermissions(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="section-card" title="角色管理" extra={<Button type="primary" icon={<IconPlus />} onClick={openCreateRole}>新增角色</Button>}>
|
||||
<Table rowKey="id" loading={loading} data={data.list} pagination={{ total: data.pagination.total, pageSize: data.pagination.pageSize }} columns={[
|
||||
{ title: 'ID', dataIndex: 'id', width: 80 },
|
||||
{ title: '编码', dataIndex: 'code' },
|
||||
{ title: '名称', dataIndex: 'name' },
|
||||
{ title: '权限列表', render: (_, record: Role) => <Space wrap>{record.permissions.map((item) => <Tag key={item.id}>{item.name}</Tag>)}</Space> },
|
||||
{ title: '操作', width: 140, render: (_, record: Role) => <Button size="small" onClick={() => openAssignPermissions(record)}>分配权限</Button> },
|
||||
]} />
|
||||
<Modal title="新增角色" visible={createVisible} onCancel={() => setCreateVisible(false)} footer={null}>
|
||||
<Form form={createForm} layout="vertical" onSubmit={createRole}>
|
||||
<Form.Item label="角色编码" field="code" rules={[{ required: true }]}>
|
||||
<Input placeholder="请输入角色编码,例如 drama_operator" />
|
||||
</Form.Item>
|
||||
<div className="form-help">角色的唯一标识,创建后不可修改</div>
|
||||
<Form.Item label="角色名称" field="name" rules={[{ required: true }]}>
|
||||
<Input placeholder="请输入角色名称" />
|
||||
</Form.Item>
|
||||
<Form.Item label="角色描述" field="description">
|
||||
<Input.TextArea placeholder="请输入角色描述" autoSize={{ minRows: 2, maxRows: 4 }} />
|
||||
</Form.Item>
|
||||
<div className="form-label">权限设置</div>
|
||||
<PermissionTreePicker
|
||||
permissions={permissions.data.list}
|
||||
value={createPermissionIds}
|
||||
onChange={setCreatePermissionIds}
|
||||
/>
|
||||
<div className="form-help">为角色分配系统权限,决定拥有此角色的用户可以进行的操作</div>
|
||||
<div className="modal-actions">
|
||||
<Button onClick={() => setCreateVisible(false)}>取消</Button>
|
||||
<Button type="primary" htmlType="submit" loading={creatingRole}>创建</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Modal>
|
||||
<Modal
|
||||
title={`分配权限${selectedRole ? ` - ${selectedRole.name}` : ''}`}
|
||||
visible={assignmentVisible}
|
||||
confirmLoading={savingPermissions}
|
||||
onOk={saveRolePermissions}
|
||||
onCancel={() => setAssignmentVisible(false)}
|
||||
>
|
||||
<PermissionTreePicker
|
||||
permissions={permissions.data.list}
|
||||
value={selectedPermissionIds}
|
||||
onChange={setSelectedPermissionIds}
|
||||
/>
|
||||
</Modal>
|
||||
</Card>
|
||||
);
|
||||
return (
|
||||
<Card
|
||||
className="section-card"
|
||||
title="角色管理"
|
||||
extra={
|
||||
<Button type="primary" icon={<IconPlus />} onClick={openCreateRole}>
|
||||
新增角色
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Table
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
data={data.list}
|
||||
pagination={{
|
||||
current: data.pagination.page,
|
||||
total: data.pagination.total,
|
||||
pageSize: data.pagination.pageSize,
|
||||
onChange: changePage,
|
||||
}}
|
||||
columns={[
|
||||
{ title: "ID", dataIndex: "id", width: 80 },
|
||||
{ title: "编码", dataIndex: "code" },
|
||||
{ title: "名称", dataIndex: "name" },
|
||||
{
|
||||
title: "权限列表",
|
||||
render: (_, record: Role) => (
|
||||
<Space wrap>
|
||||
{record.permissions.map((item) => (
|
||||
<Tag key={item.id}>{item.name}</Tag>
|
||||
))}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
width: 140,
|
||||
render: (_, record: Role) => (
|
||||
<Button size="small" onClick={() => openAssignPermissions(record)}>
|
||||
分配权限
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Modal title="新增角色" visible={createVisible} onCancel={() => setCreateVisible(false)} footer={null}>
|
||||
<Form form={createForm} layout="vertical" onSubmit={createRole}>
|
||||
<Form.Item label="角色编码" field="code" rules={[{ required: true }]}>
|
||||
<Input placeholder="请输入角色编码,例如 drama_operator" />
|
||||
</Form.Item>
|
||||
<div className="form-help">角色的唯一标识,创建后不可修改</div>
|
||||
<Form.Item label="角色名称" field="name" rules={[{ required: true }]}>
|
||||
<Input placeholder="请输入角色名称" />
|
||||
</Form.Item>
|
||||
<Form.Item label="角色描述" field="description">
|
||||
<Input.TextArea placeholder="请输入角色描述" autoSize={{ minRows: 2, maxRows: 4 }} />
|
||||
</Form.Item>
|
||||
<div className="form-label">权限设置</div>
|
||||
<PermissionTreePicker permissions={permissions.data.list} value={createPermissionIds} onChange={setCreatePermissionIds} />
|
||||
<div className="form-help">为角色分配系统权限,决定拥有此角色的用户可以进行的操作</div>
|
||||
<div className="modal-actions">
|
||||
<Button onClick={() => setCreateVisible(false)}>取消</Button>
|
||||
<Button type="primary" htmlType="submit" loading={creatingRole}>
|
||||
创建
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Modal>
|
||||
<Modal
|
||||
title={`分配权限${selectedRole ? ` - ${selectedRole.name}` : ""}`}
|
||||
visible={assignmentVisible}
|
||||
confirmLoading={savingPermissions}
|
||||
onOk={saveRolePermissions}
|
||||
onCancel={() => setAssignmentVisible(false)}
|
||||
>
|
||||
<PermissionTreePicker permissions={permissions.data.list} value={selectedPermissionIds} onChange={setSelectedPermissionIds} />
|
||||
</Modal>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default RolesPage;
|
||||
|
||||
Reference in New Issue
Block a user