feat: 完善 RBAC 权限体系与权限管理页面
This commit is contained in:
@@ -1,8 +1,12 @@
|
||||
import React, { useEffect, useState, useMemo, useCallback } from 'react';
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { validateResponse } from '../../utils/validate';
|
||||
import { operationLogsSchema } from '../../api/schemas';
|
||||
import { Table, Select, DatePicker, Space, Tag, Tooltip } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import api from '../../api';
|
||||
import { message } from '../../ui/app-message';
|
||||
import { getErrorMessage } from '../../utils/error';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
@@ -22,36 +26,38 @@ const statusMap: Record<string, { text: string; color: string }> = {
|
||||
};
|
||||
|
||||
const OperationLogsPage: React.FC = () => {
|
||||
const [data, setData] = useState<any[]>([]);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(20);
|
||||
const [filterModule, setFilterModule] = useState<string | undefined>();
|
||||
const [dateRange, setDateRange] = useState<[string, string] | null>(null);
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const params: any = { page, pageSize };
|
||||
if (filterModule) params.module = filterModule;
|
||||
if (dateRange) {
|
||||
params.startDate = dateRange[0];
|
||||
params.endDate = dateRange[1];
|
||||
const {
|
||||
data: fetchResult = { data: [], total: 0 },
|
||||
isLoading,
|
||||
isFetching,
|
||||
} = useQuery<{ data: any[]; total: number }>({
|
||||
queryKey: ['operation-logs', page, pageSize, filterModule, dateRange],
|
||||
queryFn: async () => {
|
||||
try {
|
||||
const params: any = { page, pageSize };
|
||||
if (filterModule) params.module = filterModule;
|
||||
if (dateRange) {
|
||||
params.startDate = dateRange[0];
|
||||
params.endDate = dateRange[1];
|
||||
}
|
||||
return validateResponse<{ data: any[]; total: number }>(
|
||||
operationLogsSchema,
|
||||
await api.get('/operation-logs', { params }),
|
||||
);
|
||||
} catch (e: unknown) {
|
||||
message.error(getErrorMessage(e, '加载失败,请稍后重试'));
|
||||
return { data: [], total: 0 };
|
||||
}
|
||||
const res: any = await api.get('/operation-logs', { params });
|
||||
setData(res.data);
|
||||
setTotal(res.total);
|
||||
} catch (e: unknown) {
|
||||
const err = e as { message?: string };
|
||||
message.error(err?.message || '加载失败,请稍后重试');
|
||||
}
|
||||
setLoading(false);
|
||||
}, [page, pageSize, filterModule, dateRange]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [fetchData]);
|
||||
},
|
||||
});
|
||||
const data = fetchResult.data;
|
||||
const total = fetchResult.total;
|
||||
const loading = isLoading || isFetching;
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { validateResponse } from '../../utils/validate';
|
||||
import { permissionTreeSchema } from '../../api/schemas';
|
||||
import { Card, Tag, Input, Space, Spin, Empty } from 'antd';
|
||||
import api from '../../api';
|
||||
import { message } from '../../ui/app-message';
|
||||
import { getErrorMessage } from '../../utils/error';
|
||||
|
||||
interface PermissionItem {
|
||||
id: number;
|
||||
@@ -12,10 +16,25 @@ interface PermissionItem {
|
||||
}
|
||||
|
||||
const PermissionsPage: React.FC = () => {
|
||||
const [permTree, setPermTree] = useState<{ group: string; permissions: PermissionItem[] }[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [search, setSearch] = useState('');
|
||||
|
||||
const { data: permTree = [], isLoading } = useQuery({
|
||||
queryKey: ['rbac', 'permissions', 'tree'],
|
||||
queryFn: async () => {
|
||||
try {
|
||||
return validateResponse<{ group: string; permissions: PermissionItem[] }[]>(
|
||||
permissionTreeSchema,
|
||||
await api.get<{ group: string; permissions: PermissionItem[] }[]>(
|
||||
'/rbac/permissions/tree',
|
||||
),
|
||||
);
|
||||
} catch (e: unknown) {
|
||||
message.error(getErrorMessage(e, '加载权限失败'));
|
||||
return [];
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const groupNames: Record<string, string> = {
|
||||
dashboard: '数据面板',
|
||||
student: '学生管理',
|
||||
@@ -44,18 +63,6 @@ const PermissionsPage: React.FC = () => {
|
||||
'ai-chat': 'AI 助手',
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
api
|
||||
.get('/rbac/permissions/tree')
|
||||
.then((res: any) => setPermTree(res))
|
||||
.catch((e: unknown) => {
|
||||
const err = e as { message?: string };
|
||||
message.error(err?.message || '加载权限失败');
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
const filteredTree = search
|
||||
? permTree
|
||||
.map((g) => ({
|
||||
@@ -67,7 +74,7 @@ const PermissionsPage: React.FC = () => {
|
||||
.filter((g) => g.permissions.length > 0)
|
||||
: permTree;
|
||||
|
||||
if (loading) return <Spin style={{ display: 'block', margin: '40px auto' }} />;
|
||||
if (isLoading) return <Spin style={{ display: 'block', margin: '40px auto' }} />;
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import React, { useEffect, useState, useMemo, useCallback } from 'react';
|
||||
import React, { useState, useMemo, useCallback } from 'react';
|
||||
import { Table, Modal, Form, Input, Space, Tag, Popconfirm, Card, Checkbox, Empty } from 'antd';
|
||||
import { PlusOutlined, EditOutlined, StopOutlined } from '@ant-design/icons';
|
||||
import api from '../../api';
|
||||
import PermissionButton from '../../components/PermissionButton';
|
||||
import EditableCell from '../../components/EditableCell';
|
||||
import { message } from '../../ui/app-message';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useApiMutation } from '../../hooks/useApiMutation';
|
||||
import { validateResponse } from '../../utils/validate';
|
||||
import { permissionTreeSchema, rolesSchema } from '../../api/schemas';
|
||||
import { getErrorMessage } from '../../utils/error';
|
||||
|
||||
interface PermissionItem {
|
||||
id: number;
|
||||
@@ -23,36 +28,62 @@ interface RoleItem {
|
||||
}
|
||||
|
||||
const RolesPage: React.FC = () => {
|
||||
const [data, setData] = useState<RoleItem[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [editing, setEditing] = useState<RoleItem | null>(null);
|
||||
const [allPerms, setAllPerms] = useState<{ group: string; permissions: PermissionItem[] }[]>([]);
|
||||
const [form] = Form.useForm();
|
||||
const [selectedPermIds, setSelectedPermIds] = useState<number[]>([]);
|
||||
const [saving, setSaving] = useState(false);
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const [roles, permTree] = await Promise.all([
|
||||
api.get('/rbac/roles') as Promise<RoleItem[]>,
|
||||
api.get('/rbac/permissions/tree') as Promise<
|
||||
{ group: string; permissions: PermissionItem[] }[]
|
||||
>,
|
||||
]);
|
||||
setData(roles);
|
||||
setAllPerms(permTree);
|
||||
} catch (e: unknown) {
|
||||
const err = e as { message?: string };
|
||||
message.error(err?.message || '加载失败,请稍后重试');
|
||||
}
|
||||
setLoading(false);
|
||||
}, []);
|
||||
const {
|
||||
data: fetchResult = { roles: [], permTree: [] },
|
||||
isLoading,
|
||||
isFetching,
|
||||
} = useQuery<{
|
||||
roles: RoleItem[];
|
||||
permTree: { group: string; permissions: PermissionItem[] }[];
|
||||
}>({
|
||||
queryKey: ['rbac', 'roles', 'permission-tree'],
|
||||
queryFn: async () => {
|
||||
try {
|
||||
const [roles, permTree] = await Promise.all([
|
||||
api.get('/rbac/roles') as Promise<RoleItem[]>,
|
||||
api.get('/rbac/permissions/tree') as Promise<
|
||||
{ group: string; permissions: PermissionItem[] }[]
|
||||
>,
|
||||
]);
|
||||
return {
|
||||
roles: validateResponse<RoleItem[]>(rolesSchema, roles),
|
||||
permTree: validateResponse<{ group: string; permissions: PermissionItem[] }[]>(
|
||||
permissionTreeSchema,
|
||||
permTree,
|
||||
),
|
||||
};
|
||||
} catch (e: unknown) {
|
||||
message.error(getErrorMessage(e, '加载失败,请稍后重试'));
|
||||
return { roles: [], permTree: [] };
|
||||
}
|
||||
},
|
||||
});
|
||||
const data = fetchResult.roles;
|
||||
const allPerms = fetchResult.permTree;
|
||||
const loading = isLoading || isFetching;
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [fetchData]);
|
||||
const saveMutation = useApiMutation(
|
||||
async (values: { name: string; description?: string; permissionIds: number[] }) =>
|
||||
editing
|
||||
? api.put(`/rbac/roles/${editing.id}`, values)
|
||||
: api.post('/rbac/roles', values),
|
||||
{ invalidate: [['rbac', 'roles', 'permission-tree']] },
|
||||
);
|
||||
const disableMutation = useApiMutation(
|
||||
async (id: number) => api.delete(`/rbac/roles/${id}`),
|
||||
{ invalidate: [['rbac', 'roles', 'permission-tree']] },
|
||||
);
|
||||
const saveCellMutation = useApiMutation(
|
||||
async ({ record, field, value }: { record: RoleItem; field: string; value: unknown }) =>
|
||||
api.put(`/rbac/roles/${record.id}`, { [field]: value }),
|
||||
{ invalidate: [['rbac', 'roles', 'permission-tree']] },
|
||||
);
|
||||
|
||||
const handleAdd = () => {
|
||||
setEditing(null);
|
||||
@@ -72,25 +103,15 @@ const RolesPage: React.FC = () => {
|
||||
setSaving(true);
|
||||
const values = await form.validateFields();
|
||||
try {
|
||||
if (editing) {
|
||||
await api.put(`/rbac/roles/${editing.id}`, {
|
||||
name: values.name,
|
||||
description: values.description,
|
||||
permissionIds: selectedPermIds,
|
||||
});
|
||||
message.success('角色更新成功');
|
||||
} else {
|
||||
await api.post('/rbac/roles', {
|
||||
name: values.name,
|
||||
description: values.description,
|
||||
permissionIds: selectedPermIds,
|
||||
});
|
||||
message.success('角色创建成功');
|
||||
}
|
||||
await saveMutation.mutateAsync({
|
||||
name: values.name,
|
||||
description: values.description,
|
||||
permissionIds: selectedPermIds,
|
||||
});
|
||||
message.success(editing ? '角色更新成功' : '角色创建成功');
|
||||
setModalOpen(false);
|
||||
fetchData();
|
||||
} catch (e: any) {
|
||||
message.error(e.message || '操作失败');
|
||||
} catch {
|
||||
// 错误提示由 useApiMutation 统一处理
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -98,11 +119,10 @@ const RolesPage: React.FC = () => {
|
||||
|
||||
const handleDisable = async (id: number) => {
|
||||
try {
|
||||
await api.delete(`/rbac/roles/${id}`);
|
||||
await disableMutation.mutateAsync(id);
|
||||
message.success('角色已停用');
|
||||
fetchData();
|
||||
} catch (e: any) {
|
||||
message.error(e.message || '停用失败');
|
||||
} catch {
|
||||
// 错误提示由 useApiMutation 统一处理
|
||||
}
|
||||
};
|
||||
|
||||
@@ -144,11 +164,14 @@ const RolesPage: React.FC = () => {
|
||||
);
|
||||
const saveCell = useCallback(
|
||||
async (record: RoleItem, field: string, value: unknown) => {
|
||||
await api.put(`/rbac/roles/${record.id}`, { [field]: value });
|
||||
message.success('已保存');
|
||||
await fetchData();
|
||||
try {
|
||||
await saveCellMutation.mutateAsync({ record, field, value });
|
||||
message.success('已保存');
|
||||
} catch {
|
||||
// 错误提示由 useApiMutation 统一处理
|
||||
}
|
||||
},
|
||||
[fetchData],
|
||||
[saveCellMutation],
|
||||
);
|
||||
|
||||
const columns = useMemo(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState, useMemo, useCallback } from 'react';
|
||||
import { Table, Modal, Form, Input, Select, Switch, Space, Tag, Popconfirm } from 'antd';
|
||||
import React, { useState, useMemo, useCallback } from 'react';
|
||||
import { App, Button, Table, Modal, Form, Input, Select, Switch, Space, Tag, Popconfirm } from 'antd';
|
||||
import {
|
||||
PlusOutlined,
|
||||
EditOutlined,
|
||||
@@ -13,11 +13,25 @@ import PermissionButton from '../../components/PermissionButton';
|
||||
import EditableCell from '../../components/EditableCell';
|
||||
import { message } from '../../ui/app-message';
|
||||
import { userProfileResponseToFormValues, type UserProfileResponse } from './user-profile-form';
|
||||
import { usePermission } from '../../hooks/usePermission';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useApiMutation } from '../../hooks/useApiMutation';
|
||||
import { validateResponse } from '../../utils/validate';
|
||||
import { rolesSchema, usersSchema } from '../../api/schemas';
|
||||
import { getErrorMessage } from '../../utils/error';
|
||||
|
||||
const USER_FIELDS = {
|
||||
username: 'username',
|
||||
name: 'name',
|
||||
phone: 'phone',
|
||||
email: 'email',
|
||||
status: 'status',
|
||||
} as const;
|
||||
|
||||
const UsersPage: React.FC = () => {
|
||||
const [data, setData] = useState<any[]>([]);
|
||||
const [roles, setRoles] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { modal } = App.useApp();
|
||||
const { hasPermission } = usePermission();
|
||||
const canPurgeUser = hasPermission('user:purge');
|
||||
const [modalOpen, setModalOpen] = useState(false);
|
||||
const [pwdModalOpen, setPwdModalOpen] = useState(false);
|
||||
const [editing, setEditing] = useState<any>(null);
|
||||
@@ -45,36 +59,73 @@ const UsersPage: React.FC = () => {
|
||||
setSaving(true);
|
||||
const values = await profileForm.validateFields();
|
||||
try {
|
||||
await api.put(`/rbac/users/${profileUser.id}/profile`, values);
|
||||
await profileMutation.mutateAsync({ id: profileUser.id, values });
|
||||
message.success('档案更新成功');
|
||||
setProfileModalOpen(false);
|
||||
fetchData();
|
||||
} catch (e: any) {
|
||||
message.error(e.message || '操作失败');
|
||||
} catch {
|
||||
// 错误提示由 useApiMutation 统一处理
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const [users, rolesRes] = await Promise.all([
|
||||
api.get(`/rbac/users?isArchived=${showArchived}`) as Promise<any[]>,
|
||||
api.get('/rbac/roles') as Promise<any[]>,
|
||||
]);
|
||||
setData(users);
|
||||
setRoles(rolesRes);
|
||||
} catch (e: unknown) {
|
||||
const err = e as { message?: string };
|
||||
message.error(err?.message || '加载失败,请稍后重试');
|
||||
}
|
||||
setLoading(false);
|
||||
}, [showArchived]);
|
||||
const {
|
||||
data: fetchResult = { users: [], roles: [] },
|
||||
isLoading,
|
||||
isFetching,
|
||||
} = useQuery<{ users: any[]; roles: any[] }>({
|
||||
queryKey: ['rbac', 'users', showArchived],
|
||||
queryFn: async () => {
|
||||
try {
|
||||
const [users, rolesRes] = await Promise.all([
|
||||
api.get(`/rbac/users?isArchived=${showArchived}`) as Promise<any[]>,
|
||||
api.get('/rbac/roles') as Promise<any[]>,
|
||||
]);
|
||||
return {
|
||||
users: validateResponse<any[]>(usersSchema, users),
|
||||
roles: validateResponse<any[]>(rolesSchema, rolesRes),
|
||||
};
|
||||
} catch (e: unknown) {
|
||||
message.error(getErrorMessage(e, '加载失败,请稍后重试'));
|
||||
return { users: [], roles: [] };
|
||||
}
|
||||
},
|
||||
});
|
||||
const data = fetchResult.users;
|
||||
const roles = fetchResult.roles;
|
||||
const loading = isLoading || isFetching;
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [fetchData]);
|
||||
const profileMutation = useApiMutation(
|
||||
async ({ id, values }: { id: number; values: any }) =>
|
||||
api.put(`/rbac/users/${id}/profile`, values),
|
||||
{ invalidate: [['rbac', 'users']] },
|
||||
);
|
||||
const saveMutation = useApiMutation(
|
||||
async (values: { username: string; password?: string; name: string; roleIds: number[] }) =>
|
||||
editing
|
||||
? api.put(`/rbac/users/${editing.id}`, values)
|
||||
: api.post('/rbac/users', values),
|
||||
{ invalidate: [['rbac', 'users']] },
|
||||
);
|
||||
const archiveMutation = useApiMutation(
|
||||
async ({ id, archive }: { id: number; archive: boolean }) =>
|
||||
api.put(`/rbac/users/${id}/${archive ? 'archive' : 'restore'}`),
|
||||
{ invalidate: [['rbac', 'users']] },
|
||||
);
|
||||
const purgeMutation = useApiMutation(
|
||||
async (id: number) => api.delete(`/rbac/users/${id}/permanent`),
|
||||
{ invalidate: [['rbac', 'users']] },
|
||||
);
|
||||
const pwdMutation = useApiMutation(
|
||||
async ({ id, password }: { id: number; password: string }) =>
|
||||
api.put(`/rbac/users/${id}/password`, { password }),
|
||||
{ invalidate: [['rbac', 'users']] },
|
||||
);
|
||||
const saveCellMutation = useApiMutation(
|
||||
async ({ record, field, value }: { record: any; field: string; value: unknown }) =>
|
||||
api.put(`/rbac/users/${record.id}`, { [field]: value }),
|
||||
{ invalidate: [['rbac', 'users']] },
|
||||
);
|
||||
|
||||
const handleAdd = () => {
|
||||
setEditing(null);
|
||||
@@ -96,41 +147,48 @@ const UsersPage: React.FC = () => {
|
||||
setSaving(true);
|
||||
const values = await form.validateFields();
|
||||
try {
|
||||
if (editing) {
|
||||
await api.put(`/rbac/users/${editing.id}`, {
|
||||
username: values.username,
|
||||
name: values.name,
|
||||
roleIds: values.roleIds || [],
|
||||
});
|
||||
message.success('更新成功');
|
||||
} else {
|
||||
await api.post('/rbac/users', {
|
||||
username: values.username,
|
||||
password: values.password,
|
||||
name: values.name,
|
||||
roleIds: values.roleIds || [],
|
||||
});
|
||||
message.success('创建成功');
|
||||
}
|
||||
await saveMutation.mutateAsync({
|
||||
username: values.username,
|
||||
password: values.password,
|
||||
name: values.name,
|
||||
roleIds: values.roleIds || [],
|
||||
});
|
||||
message.success(editing ? '更新成功' : '创建成功');
|
||||
setModalOpen(false);
|
||||
fetchData();
|
||||
} catch (e: any) {
|
||||
message.error(e.message || '操作失败');
|
||||
} catch {
|
||||
// 错误提示由 useApiMutation 统一处理
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
};
|
||||
const handleArchive = async (id: number, archive: boolean) => {
|
||||
try {
|
||||
await api.put(`/rbac/users/${id}/${archive ? 'archive' : 'restore'}`);
|
||||
await archiveMutation.mutateAsync({ id, archive });
|
||||
message.success(archive ? '已归档' : '已恢复');
|
||||
fetchData();
|
||||
} catch (e: unknown) {
|
||||
const err = e as { message?: string };
|
||||
message.error(err?.message || '操作失败');
|
||||
} catch {
|
||||
// 错误提示由 useApiMutation 统一处理
|
||||
}
|
||||
};
|
||||
|
||||
const handlePurge = (record: any) => {
|
||||
modal.confirm({
|
||||
title: `永久删除账号「${record.name || record.username}」?`,
|
||||
content:
|
||||
'删除后不可恢复,关联学生、任教、排课或考勤操作时将无法删除;角色绑定、通知和 AI 会话将被清除,操作日志保留。确定继续?',
|
||||
okText: '永久删除',
|
||||
okButtonProps: { danger: true },
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
try {
|
||||
await purgeMutation.mutateAsync(record.id);
|
||||
message.success('已永久删除(不可恢复)');
|
||||
} catch {
|
||||
// 错误提示由 useApiMutation 统一处理
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleResetPwd = (record: any) => {
|
||||
setResetTarget(record);
|
||||
pwdForm.resetFields();
|
||||
@@ -141,12 +199,11 @@ const UsersPage: React.FC = () => {
|
||||
setSaving(true);
|
||||
const values = await pwdForm.validateFields();
|
||||
try {
|
||||
await api.put(`/rbac/users/${resetTarget.id}/password`, { password: values.password });
|
||||
await pwdMutation.mutateAsync({ id: resetTarget.id, password: values.password });
|
||||
message.success('密码已重置');
|
||||
setPwdModalOpen(false);
|
||||
} catch (e: unknown) {
|
||||
const err = e as { message?: string };
|
||||
message.error(err?.message || '操作失败');
|
||||
} catch {
|
||||
// 错误提示由 useApiMutation 统一处理
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -154,11 +211,14 @@ const UsersPage: React.FC = () => {
|
||||
|
||||
const saveCell = useCallback(
|
||||
async (record: any, field: string, value: unknown) => {
|
||||
await api.put(`/rbac/users/${record.id}`, { [field]: value });
|
||||
message.success('已保存');
|
||||
await fetchData();
|
||||
try {
|
||||
await saveCellMutation.mutateAsync({ record, field, value });
|
||||
message.success('已保存');
|
||||
} catch {
|
||||
// 错误提示由 useApiMutation 统一处理
|
||||
}
|
||||
},
|
||||
[fetchData],
|
||||
[saveCellMutation],
|
||||
);
|
||||
|
||||
const columns = useMemo(
|
||||
@@ -166,7 +226,7 @@ const UsersPage: React.FC = () => {
|
||||
{ title: 'ID', dataIndex: 'id', width: 60 },
|
||||
{
|
||||
title: '用户名',
|
||||
dataIndex: 'username',
|
||||
dataIndex: USER_FIELDS.username,
|
||||
width: 120,
|
||||
render: (v: string, r: any) => (
|
||||
<EditableCell
|
||||
@@ -174,7 +234,7 @@ const UsersPage: React.FC = () => {
|
||||
required
|
||||
permission="user:edit"
|
||||
disabled={r.isArchived}
|
||||
onSave={(next) => saveCell(r, 'username', next)}
|
||||
onSave={(next) => saveCell(r, USER_FIELDS.username, next)}
|
||||
>
|
||||
{v}
|
||||
</EditableCell>
|
||||
@@ -182,7 +242,7 @@ const UsersPage: React.FC = () => {
|
||||
},
|
||||
{
|
||||
title: '姓名',
|
||||
dataIndex: 'name',
|
||||
dataIndex: USER_FIELDS.name,
|
||||
width: 120,
|
||||
render: (v: string, r: any) => (
|
||||
<EditableCell
|
||||
@@ -190,7 +250,7 @@ const UsersPage: React.FC = () => {
|
||||
required
|
||||
permission="user:edit"
|
||||
disabled={r.isArchived}
|
||||
onSave={(next) => saveCell(r, 'name', next)}
|
||||
onSave={(next) => saveCell(r, USER_FIELDS.name, next)}
|
||||
>
|
||||
{v}
|
||||
</EditableCell>
|
||||
@@ -267,11 +327,18 @@ const UsersPage: React.FC = () => {
|
||||
重置密码
|
||||
</PermissionButton>
|
||||
{record.isArchived ? (
|
||||
<Popconfirm title="确认恢复?" onConfirm={() => handleArchive(record.id, false)}>
|
||||
<PermissionButton permission="user:edit" type="link" size="small">
|
||||
恢复
|
||||
</PermissionButton>
|
||||
</Popconfirm>
|
||||
<>
|
||||
<Popconfirm title="确认恢复?" onConfirm={() => handleArchive(record.id, false)}>
|
||||
<PermissionButton permission="user:edit" type="link" size="small">
|
||||
恢复
|
||||
</PermissionButton>
|
||||
</Popconfirm>
|
||||
{canPurgeUser ? (
|
||||
<Button size="small" danger type="link" onClick={() => handlePurge(record)}>
|
||||
删除
|
||||
</Button>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<Popconfirm
|
||||
title="归档后可恢复,确认归档?"
|
||||
@@ -286,7 +353,7 @@ const UsersPage: React.FC = () => {
|
||||
),
|
||||
},
|
||||
],
|
||||
[roles, saveCell],
|
||||
[roles, saveCell, canPurgeUser, handlePurge],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user