Twilioapp/src/pages/Users/UserList.tsx

391 lines
10 KiB
TypeScript

import { useState, useEffect } from 'react';
import {
Table,
Card,
Button,
Space,
Tag,
Modal,
Form,
Input,
Select,
message,
Typography,
Row,
Col,
Statistic
} from 'antd';
import type { ColumnsType } from 'antd/es/table';
import {
PlusOutlined,
EditOutlined,
DeleteOutlined,
SearchOutlined,
UserOutlined,
TeamOutlined,
CrownOutlined
} from '@ant-design/icons';
import { useLoading } from '@/store';
import { formatDate } from '@/utils';
const { Title } = Typography;
const { Option } = Select;
interface User {
id: string;
name: string;
email: string;
phone: string;
role: 'admin' | 'translator' | 'user';
status: 'active' | 'inactive' | 'suspended';
registeredAt: Date;
lastLoginAt: Date;
}
const UserList = () => {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(false);
const [modalVisible, setModalVisible] = useState(false);
const [editingUser, setEditingUser] = useState<User | null>(null);
const [searchText, setSearchText] = useState('');
const [form] = Form.useForm();
const { setLoading: setGlobalLoading } = useLoading();
// 模拟数据
const mockUsers: User[] = [
{
id: '1',
name: '张三',
email: 'zhangsan@example.com',
phone: '13800138001',
role: 'admin',
status: 'active',
registeredAt: new Date('2023-01-15'),
lastLoginAt: new Date('2024-01-15'),
},
{
id: '2',
name: '李四',
email: 'lisi@example.com',
phone: '13800138002',
role: 'translator',
status: 'active',
registeredAt: new Date('2023-02-20'),
lastLoginAt: new Date('2024-01-14'),
},
{
id: '3',
name: '王五',
email: 'wangwu@example.com',
phone: '13800138003',
role: 'user',
status: 'inactive',
registeredAt: new Date('2023-03-10'),
lastLoginAt: new Date('2024-01-10'),
},
];
const columns: ColumnsType<User> = [
{
title: '姓名',
dataIndex: 'name',
key: 'name',
filteredValue: searchText ? [searchText] : null,
onFilter: (value, record) =>
record.name.toLowerCase().includes(String(value).toLowerCase()) ||
record.email.toLowerCase().includes(String(value).toLowerCase()),
},
{
title: '邮箱',
dataIndex: 'email',
key: 'email',
},
{
title: '电话',
dataIndex: 'phone',
key: 'phone',
},
{
title: '角色',
dataIndex: 'role',
key: 'role',
render: (role: string) => {
const roleMap = {
admin: { color: 'red', text: '管理员', icon: <CrownOutlined /> },
translator: { color: 'blue', text: '译员', icon: <TeamOutlined /> },
user: { color: 'green', text: '用户', icon: <UserOutlined /> },
};
const roleInfo = roleMap[role as keyof typeof roleMap];
return (
<Tag color={roleInfo.color} icon={roleInfo.icon}>
{roleInfo.text}
</Tag>
);
},
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
render: (status: string) => {
const statusMap = {
active: { color: 'green', text: '活跃' },
inactive: { color: 'orange', text: '不活跃' },
suspended: { color: 'red', text: '已暂停' },
};
const statusInfo = statusMap[status as keyof typeof statusMap];
return <Tag color={statusInfo.color}>{statusInfo.text}</Tag>;
},
},
{
title: '注册时间',
dataIndex: 'registeredAt',
key: 'registeredAt',
render: (date: Date) => formatDate(date),
},
{
title: '最后登录',
dataIndex: 'lastLoginAt',
key: 'lastLoginAt',
render: (date: Date) => formatDate(date),
},
{
title: '操作',
key: 'action',
render: (_, record) => (
<Space size="middle">
<Button
type="link"
icon={<EditOutlined />}
onClick={() => handleEdit(record)}
>
</Button>
<Button
type="link"
danger
icon={<DeleteOutlined />}
onClick={() => handleDelete(record.id)}
>
</Button>
</Space>
),
},
];
const handleEdit = (user: User) => {
setEditingUser(user);
form.setFieldsValue(user);
setModalVisible(true);
};
const handleDelete = (userId: string) => {
Modal.confirm({
title: '确认删除',
content: '确定要删除这个用户吗?',
onOk: () => {
setUsers(users.filter(user => user.id !== userId));
message.success('删除成功');
},
});
};
const handleSubmit = async (values: any) => {
try {
setLoading(true);
if (editingUser) {
// 编辑用户
setUsers(users.map(user =>
user.id === editingUser.id ? { ...user, ...values } : user
));
message.success('更新成功');
} else {
// 新增用户
const newUser: User = {
id: Date.now().toString(),
...values,
registeredAt: new Date(),
lastLoginAt: new Date(),
};
setUsers([...users, newUser]);
message.success('添加成功');
}
setModalVisible(false);
form.resetFields();
setEditingUser(null);
} catch (error) {
message.error('操作失败');
} finally {
setLoading(false);
}
};
const handleCancel = () => {
setModalVisible(false);
form.resetFields();
setEditingUser(null);
};
useEffect(() => {
setGlobalLoading(true);
// 模拟数据加载
setTimeout(() => {
setUsers(mockUsers);
setGlobalLoading(false);
}, 1000);
}, [setGlobalLoading]);
const stats = {
total: users.length,
active: users.filter(u => u.status === 'active').length,
translators: users.filter(u => u.role === 'translator').length,
admins: users.filter(u => u.role === 'admin').length,
};
return (
<div style={{ padding: '24px' }}>
<Title level={2}></Title>
{/* 统计卡片 */}
<Row gutter={16} style={{ marginBottom: '24px' }}>
<Col span={6}>
<Card>
<Statistic
title="总用户数"
value={stats.total}
prefix={<UserOutlined />}
/>
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic
title="活跃用户"
value={stats.active}
prefix={<TeamOutlined />}
valueStyle={{ color: '#3f8600' }}
/>
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic
title="译员数量"
value={stats.translators}
prefix={<TeamOutlined />}
valueStyle={{ color: '#1890ff' }}
/>
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic
title="管理员"
value={stats.admins}
prefix={<CrownOutlined />}
valueStyle={{ color: '#cf1322' }}
/>
</Card>
</Col>
</Row>
<Card>
<div style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between' }}>
<Space>
<Input
placeholder="搜索用户..."
prefix={<SearchOutlined />}
value={searchText}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSearchText(e.target.value)}
style={{ width: 300 }}
/>
</Space>
<Button
type="primary"
icon={<PlusOutlined />}
onClick={() => setModalVisible(true)}
>
</Button>
</div>
<Table
columns={columns}
dataSource={users}
rowKey="id"
loading={loading}
pagination={{
pageSize: 10,
showSizeChanger: true,
showQuickJumper: true,
showTotal: (total) => `${total} 条记录`,
}}
/>
</Card>
<Modal
title={editingUser ? '编辑用户' : '添加用户'}
open={modalVisible}
onOk={() => form.submit()}
onCancel={handleCancel}
confirmLoading={loading}
>
<Form
form={form}
layout="vertical"
onFinish={handleSubmit}
>
<Form.Item
label="姓名"
name="name"
rules={[{ required: true, message: '请输入姓名' }]}
>
<Input />
</Form.Item>
<Form.Item
label="邮箱"
name="email"
rules={[
{ required: true, message: '请输入邮箱' },
{ type: 'email', message: '请输入有效邮箱' },
]}
>
<Input />
</Form.Item>
<Form.Item
label="电话"
name="phone"
rules={[{ required: true, message: '请输入电话' }]}
>
<Input />
</Form.Item>
<Form.Item
label="角色"
name="role"
rules={[{ required: true, message: '请选择角色' }]}
>
<Select>
<Option value="user"></Option>
<Option value="translator"></Option>
<Option value="admin"></Option>
</Select>
</Form.Item>
<Form.Item
label="状态"
name="status"
rules={[{ required: true, message: '请选择状态' }]}
>
<Select>
<Option value="active"></Option>
<Option value="inactive"></Option>
<Option value="suspended"></Option>
</Select>
</Form.Item>
</Form>
</Modal>
</div>
);
};
export default UserList;