cosmo/frontend/src/pages/admin/Users.tsx

150 lines
3.8 KiB
TypeScript

/**
* User Management Page
*/
import { useState, useEffect } from 'react';
import { message, Modal, Button, Popconfirm } from 'antd';
import type { ColumnsType } from 'antd/es/table';
import { DataTable } from '../../components/admin/DataTable';
import { request } from '../../utils/request';
import { ReloadOutlined } from '@ant-design/icons';
interface UserItem {
id: number;
username: string;
full_name: string;
email: string;
is_active: boolean;
roles: string[];
last_login_at: string;
created_at: string;
}
export function Users() {
const [loading, setLoading] = useState(false);
const [data, setData] = useState<UserItem[]>([]);
const [filteredData, setFilteredData] = useState<UserItem[]>([]);
useEffect(() => {
loadData();
}, []);
const loadData = async () => {
setLoading(true);
try {
const { data: result } = await request.get('/users/list');
setData(result.users || []);
setFilteredData(result.users || []);
} catch (error) {
message.error('加载用户数据失败');
} finally {
setLoading(false);
}
};
const handleSearch = (keyword: string) => {
const lowerKeyword = keyword.toLowerCase();
const filtered = data.filter(
(item) =>
item.username.toLowerCase().includes(lowerKeyword) ||
item.full_name?.toLowerCase().includes(lowerKeyword) ||
item.email?.toLowerCase().includes(lowerKeyword)
);
setFilteredData(filtered);
};
const handleStatusChange = async (record: UserItem, checked: boolean) => {
try {
await request.put(`/users/${record.id}/status`, { is_active: checked });
message.success(`用户 ${record.username} 状态更新成功`);
const newData = data.map(item =>
item.id === record.id ? { ...item, is_active: checked } : item
);
setData(newData);
setFilteredData(newData);
} catch (error) {
message.error('状态更新失败');
}
};
const handleResetPassword = async (record: UserItem) => {
try {
await request.post(`/users/${record.id}/reset-password`);
message.success(`用户 ${record.username} 密码已重置`);
} catch (error) {
message.error('密码重置失败');
}
};
const columns: ColumnsType<UserItem> = [
{
title: 'ID',
dataIndex: 'id',
width: 80,
sorter: (a, b) => a.id - b.id,
},
{
title: '用户名',
dataIndex: 'username',
sorter: (a, b) => a.username.localeCompare(b.username),
},
{
title: '姓名',
dataIndex: 'full_name',
},
{
title: '邮箱',
dataIndex: 'email',
},
{
title: '角色',
dataIndex: 'roles',
render: (roles: string[]) => roles.join(', '),
},
{
title: '最近登录',
dataIndex: 'last_login_at',
render: (text) => text ? new Date(text).toLocaleString() : '从未',
},
{
title: '注册时间',
dataIndex: 'created_at',
render: (text) => new Date(text).toLocaleDateString(),
},
{
title: '操作',
key: 'action',
width: 120,
render: (_, record) => (
<Popconfirm
title="确认重置密码?"
description="密码将被重置为默认密码"
onConfirm={() => handleResetPassword(record)}
okText="确认"
cancelText="取消"
>
<Button type="link" icon={<ReloadOutlined />} size="small">
</Button>
</Popconfirm>
),
},
];
return (
<DataTable
title="用户管理"
columns={columns}
dataSource={filteredData}
loading={loading}
total={filteredData.length}
onSearch={handleSearch}
onStatusChange={handleStatusChange}
statusField="is_active"
rowKey="id"
pageSize={10}
// No onAdd, No onDelete, No onEdit
/>
);
}