mirror of
https://github.com/whyour/qinglong.git
synced 2025-12-15 08:25:38 +08:00
Add user management frontend interface for admins
Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>
This commit is contained in:
parent
15d94f469b
commit
5c798a0e93
|
|
@ -97,6 +97,7 @@ export default (app: Router) => {
|
|||
username: authInfo.username,
|
||||
avatar: authInfo.avatar,
|
||||
twoFactorActivated: authInfo.twoFactorActivated,
|
||||
role: req.user?.role,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
|
|
|
|||
|
|
@ -530,5 +530,20 @@
|
|||
"请输入自定义日志文件夹名称或绝对路径": "Please enter a custom log folder name or absolute path",
|
||||
"请输入自定义日志文件夹名称或 /dev/null": "Please enter a custom log folder name or /dev/null",
|
||||
"日志名称只能包含字母、数字、下划线和连字符": "Log name can only contain letters, numbers, underscores and hyphens",
|
||||
"日志名称不能超过100个字符": "Log name cannot exceed 100 characters"
|
||||
"日志名称不能超过100个字符": "Log name cannot exceed 100 characters",
|
||||
"用户管理": "User Management",
|
||||
"用户名": "Username",
|
||||
"密码": "Password",
|
||||
"角色": "Role",
|
||||
"管理员": "Admin",
|
||||
"普通用户": "User",
|
||||
"启用": "Enabled",
|
||||
"禁用": "Disabled",
|
||||
"创建时间": "Created At",
|
||||
"确认删除选中的用户吗": "Are you sure to delete selected users?",
|
||||
"请输入用户名": "Please enter username",
|
||||
"请输入密码": "Please enter password",
|
||||
"密码长度至少为6位": "Password must be at least 6 characters",
|
||||
"新增用户": "Add User",
|
||||
"编辑用户": "Edit User"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -530,5 +530,20 @@
|
|||
"请输入自定义日志文件夹名称或绝对路径": "请输入自定义日志文件夹名称或绝对路径",
|
||||
"请输入自定义日志文件夹名称或 /dev/null": "请输入自定义日志文件夹名称或 /dev/null",
|
||||
"日志名称只能包含字母、数字、下划线和连字符": "日志名称只能包含字母、数字、下划线和连字符",
|
||||
"日志名称不能超过100个字符": "日志名称不能超过100个字符"
|
||||
"日志名称不能超过100个字符": "日志名称不能超过100个字符",
|
||||
"用户管理": "用户管理",
|
||||
"用户名": "用户名",
|
||||
"密码": "密码",
|
||||
"角色": "角色",
|
||||
"管理员": "管理员",
|
||||
"普通用户": "普通用户",
|
||||
"启用": "启用",
|
||||
"禁用": "禁用",
|
||||
"创建时间": "创建时间",
|
||||
"确认删除选中的用户吗": "确认删除选中的用户吗",
|
||||
"请输入用户名": "请输入用户名",
|
||||
"请输入密码": "请输入密码",
|
||||
"密码长度至少为6位": "密码长度至少为6位",
|
||||
"新增用户": "新增用户",
|
||||
"编辑用户": "编辑用户"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import './index.less';
|
|||
import useResizeObserver from '@react-hook/resize-observer';
|
||||
import SystemLog from './systemLog';
|
||||
import Dependence from './dependence';
|
||||
import UserManagement from './userManagement';
|
||||
|
||||
const { Text } = Typography;
|
||||
const isDemoEnv = window.__ENV__DeployEnv === 'demo';
|
||||
|
|
@ -343,6 +344,15 @@ const Setting = () => {
|
|||
label: intl.get('登录日志'),
|
||||
children: <LoginLog height={height} data={loginLogData} />,
|
||||
},
|
||||
...(user?.role === 0 && !isDemoEnv
|
||||
? [
|
||||
{
|
||||
key: 'user-management',
|
||||
label: intl.get('用户管理'),
|
||||
children: <UserManagement height={height} />,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
key: 'dependence',
|
||||
label: intl.get('依赖设置'),
|
||||
|
|
|
|||
264
src/pages/setting/userManagement.tsx
Normal file
264
src/pages/setting/userManagement.tsx
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
import intl from 'react-intl-universal';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Table,
|
||||
Space,
|
||||
Modal,
|
||||
Form,
|
||||
Input,
|
||||
Select,
|
||||
message,
|
||||
Tag,
|
||||
} from 'antd';
|
||||
import {
|
||||
EditOutlined,
|
||||
DeleteOutlined,
|
||||
PlusOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { request } from '@/utils/http';
|
||||
import config from '@/utils/config';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
interface User {
|
||||
id: number;
|
||||
username: string;
|
||||
password?: string;
|
||||
role: number;
|
||||
status: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
const UserManagement: React.FC<{ height: number }> = ({ height }) => {
|
||||
const [users, setUsers] = useState<User[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [editingUser, setEditingUser] = useState<User | null>(null);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: intl.get('用户名'),
|
||||
dataIndex: 'username',
|
||||
key: 'username',
|
||||
},
|
||||
{
|
||||
title: intl.get('角色'),
|
||||
dataIndex: 'role',
|
||||
key: 'role',
|
||||
render: (role: number) => (
|
||||
<Tag color={role === 0 ? 'red' : 'blue'}>
|
||||
{role === 0 ? intl.get('管理员') : intl.get('普通用户')}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: intl.get('状态'),
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
render: (status: number) => (
|
||||
<Tag color={status === 0 ? 'green' : 'default'}>
|
||||
{status === 0 ? intl.get('启用') : intl.get('禁用')}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: intl.get('创建时间'),
|
||||
dataIndex: 'createdAt',
|
||||
key: 'createdAt',
|
||||
render: (text: string) => text ? new Date(text).toLocaleString() : '-',
|
||||
},
|
||||
{
|
||||
title: intl.get('操作'),
|
||||
key: 'action',
|
||||
render: (_: any, record: User) => (
|
||||
<Space size="middle">
|
||||
<Button
|
||||
type="link"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => handleEdit(record)}
|
||||
>
|
||||
{intl.get('编辑')}
|
||||
</Button>
|
||||
<Button
|
||||
type="link"
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={() => handleDelete([record.id])}
|
||||
>
|
||||
{intl.get('删除')}
|
||||
</Button>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const fetchUsers = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { code, data } = await request.get(
|
||||
`${config.apiPrefix}user-management`
|
||||
);
|
||||
if (code === 200) {
|
||||
setUsers(data);
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('Failed to fetch users');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchUsers();
|
||||
}, []);
|
||||
|
||||
const handleAdd = () => {
|
||||
setEditingUser(null);
|
||||
form.resetFields();
|
||||
setIsModalVisible(true);
|
||||
};
|
||||
|
||||
const handleEdit = (record: User) => {
|
||||
setEditingUser(record);
|
||||
form.setFieldsValue({
|
||||
username: record.username,
|
||||
role: record.role,
|
||||
status: record.status,
|
||||
});
|
||||
setIsModalVisible(true);
|
||||
};
|
||||
|
||||
const handleDelete = (ids: number[]) => {
|
||||
Modal.confirm({
|
||||
title: intl.get('确认删除'),
|
||||
content: intl.get('确认删除选中的用户吗'),
|
||||
onOk: async () => {
|
||||
try {
|
||||
const { code, message: msg } = await request.delete(
|
||||
`${config.apiPrefix}user-management`,
|
||||
{ data: ids }
|
||||
);
|
||||
if (code === 200) {
|
||||
message.success(msg || intl.get('删除成功'));
|
||||
fetchUsers();
|
||||
} else {
|
||||
message.error(msg || intl.get('删除失败'));
|
||||
}
|
||||
} catch (error: any) {
|
||||
message.error(error.message || intl.get('删除失败'));
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
|
||||
if (editingUser) {
|
||||
// Update user
|
||||
const { code, message: msg } = await request.put(
|
||||
`${config.apiPrefix}user-management`,
|
||||
{ ...values, id: editingUser.id }
|
||||
);
|
||||
if (code === 200) {
|
||||
message.success(msg || intl.get('更新成功'));
|
||||
setIsModalVisible(false);
|
||||
fetchUsers();
|
||||
} else {
|
||||
message.error(msg || intl.get('更新失败'));
|
||||
}
|
||||
} else {
|
||||
// Create user
|
||||
const { code, message: msg } = await request.post(
|
||||
`${config.apiPrefix}user-management`,
|
||||
values
|
||||
);
|
||||
if (code === 200) {
|
||||
message.success(msg || intl.get('创建成功'));
|
||||
setIsModalVisible(false);
|
||||
fetchUsers();
|
||||
} else {
|
||||
message.error(msg || intl.get('创建失败'));
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
message.error(error.message || intl.get('操作失败'));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={handleAdd}
|
||||
>
|
||||
{intl.get('新增用户')}
|
||||
</Button>
|
||||
</div>
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={users}
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
scroll={{ y: height - 120 }}
|
||||
pagination={false}
|
||||
/>
|
||||
<Modal
|
||||
title={editingUser ? intl.get('编辑用户') : intl.get('新增用户')}
|
||||
open={isModalVisible}
|
||||
onOk={handleSubmit}
|
||||
onCancel={() => setIsModalVisible(false)}
|
||||
>
|
||||
<Form form={form} layout="vertical">
|
||||
<Form.Item
|
||||
name="username"
|
||||
label={intl.get('用户名')}
|
||||
rules={[{ required: true, message: intl.get('请输入用户名') }]}
|
||||
>
|
||||
<Input placeholder={intl.get('请输入用户名')} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="password"
|
||||
label={intl.get('密码')}
|
||||
rules={[
|
||||
{ required: !editingUser, message: intl.get('请输入密码') },
|
||||
{ min: 6, message: intl.get('密码长度至少为6位') },
|
||||
]}
|
||||
>
|
||||
<Input.Password placeholder={intl.get('请输入密码')} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="role"
|
||||
label={intl.get('角色')}
|
||||
rules={[{ required: true, message: intl.get('请选择角色') }]}
|
||||
initialValue={1}
|
||||
>
|
||||
<Select>
|
||||
<Option value={0}>{intl.get('管理员')}</Option>
|
||||
<Option value={1}>{intl.get('普通用户')}</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="status"
|
||||
label={intl.get('状态')}
|
||||
rules={[{ required: true, message: intl.get('请选择状态') }]}
|
||||
initialValue={0}
|
||||
>
|
||||
<Select>
|
||||
<Option value={0}>{intl.get('启用')}</Option>
|
||||
<Option value={1}>{intl.get('禁用')}</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserManagement;
|
||||
Loading…
Reference in New Issue
Block a user