feat: 新增用户管理 & 论坛管理

This commit is contained in:
Shu Guang 2025-05-16 00:52:55 +08:00
parent 8ad4263e2a
commit 8926d28a72
3 changed files with 443 additions and 40 deletions

View File

@ -27,12 +27,13 @@ export default [
},
{
path: '/admin',
name: '管理',
name: '后台管理',
icon: 'crown',
access: 'canAdmin',
// access: 'canAdmin',
routes: [
{ path: '/admin', redirect: '/admin/sub-page' },
{ path: '/admin/sub-page', name: '二级管理页', component: './Admin' },
{ path: '/admin/sub-page', name: '论坛管理', component: './Admin' },
{ path: '/admin/user-manage', name: '用户管理', component: './Admin_UserManage' },
],
},
{ path: '/', redirect: '/welcome' },

View File

@ -1,44 +1,191 @@
import { HeartTwoTone, SmileTwoTone } from '@ant-design/icons';
import React, { useEffect, useState } from 'react';
import { PageContainer } from '@ant-design/pro-components';
import '@umijs/max';
import { Alert, Card, Typography } from 'antd';
import React from 'react';
const Admin: React.FC = () => {
import { Table, Button, Modal, Form, Input, Tag, Space, Popconfirm, message, Avatar } from 'antd';
import { listAllPostsUsingGet, updatePostUsingPost, deletePostUsingPost, addPostUsingPost } from '@/services/hebi/postController';
const ForumAdmin: React.FC = () => {
const [posts, setPosts] = useState<any[]>([]);
const [loading, setLoading] = useState(false);
const [editingPost, setEditingPost] = useState<any>(null);
const [modalVisible, setModalVisible] = useState(false);
const [form] = Form.useForm();
const [search, setSearch] = useState<string>('');
// 获取帖子列表
const fetchPosts = async () => {
setLoading(true);
const res = await listAllPostsUsingGet();
if (res && res.code === 0 && Array.isArray(res.data)) {
setPosts(res.data);
}
setLoading(false);
};
useEffect(() => {
fetchPosts();
}, []);
// 编辑
const handleEdit = (record: any) => {
setEditingPost(record);
setModalVisible(true);
form.setFieldsValue({
...record,
tags: record.tagList?.join(','),
});
};
// 新增
const handleAdd = () => {
setEditingPost(null);
setModalVisible(true);
form.resetFields();
};
// 删除
const handleDelete = async (id: number) => {
const res = await deletePostUsingPost({ id });
if (res && res.code === 0) {
message.success('删除成功');
fetchPosts();
} else {
message.error(res?.message || '删除失败');
}
};
// 提交表单
const handleOk = async () => {
const values = await form.validateFields();
const tags = values.tags ? values.tags.split(',').map((t: string) => t.trim()) : [];
if (editingPost) {
// 编辑
const res = await updatePostUsingPost({ ...editingPost, ...values, tags });
if (res && res.code === 0) {
message.success('更新成功');
setModalVisible(false);
fetchPosts();
} else {
message.error(res?.message || '更新失败');
}
} else {
// 新增
const res = await addPostUsingPost({ ...values, tags });
if (res && res.code === 0) {
message.success('新增成功');
setModalVisible(false);
fetchPosts();
} else {
message.error(res?.message || '新增失败');
}
}
};
const columns = [
{
title: 'ID',
dataIndex: 'id',
width: 60,
},
{
title: '标题',
dataIndex: 'title',
width: 200,
},
{
title: '标签',
dataIndex: 'tagList',
render: (tags: string[]) => (
<>
{tags?.map(tag => (
<Tag color="blue" key={tag}>{tag}</Tag>
))}
</>
),
},
{
title: '作者',
dataIndex: ['user', 'userName'],
width: 100,
render: (_: any, record: any) => (
<Space>
{record.user?.userName}
</Space>
),
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
},
{
title: '操作',
key: 'action',
width: 160,
render: (_: any, record: any) => (
<Space>
<Button type="link" onClick={() => handleEdit(record)}></Button>
<Popconfirm title="确定删除吗?" onConfirm={() => handleDelete(record.id)}>
<Button type="link" danger></Button>
</Popconfirm>
</Space>
),
},
];
// 前端搜索过滤
const filteredPosts = posts.filter(post => {
const keyword = search.trim().toLowerCase();
if (!keyword) return true;
const titleMatch = post.title?.toLowerCase().includes(keyword);
const tagsMatch = (post.tagList || []).some((tag: string) =>
tag.toLowerCase().includes(keyword)
);
return titleMatch || tagsMatch;
});
return (
<PageContainer content={' 这个页面只有 admin 权限才能查看'}>
<Card>
<Alert
message={'更快更强的重型组件,已经发布。'}
type="success"
showIcon
banner
style={{
margin: -12,
marginBottom: 48,
}}
<PageContainer title="论坛管理">
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
<Button type="primary" onClick={handleAdd}>
</Button>
<Input.Search
allowClear
placeholder="搜索标题或标签"
style={{ width: 300 }}
value={search}
onChange={e => setSearch(e.target.value)}
/>
<Typography.Title
level={2}
style={{
textAlign: 'center',
}}
>
<SmileTwoTone /> AIGC的智能数据分析系统 <HeartTwoTone twoToneColor="#eb2f96" />
</Typography.Title>
</Card>
<p
style={{
textAlign: 'center',
marginTop: 24,
}}
</div>
<Table
rowKey="id"
loading={loading}
columns={columns}
dataSource={filteredPosts}
bordered
pagination={{ pageSize: 8 }}
/>
<Modal
title={editingPost ? '编辑帖子' : '新增帖子'}
open={modalVisible}
onOk={handleOk}
onCancel={() => setModalVisible(false)}
destroyOnClose
>
Want to add more pages? Please refer to{' '}
<a href="https://pro.ant.design/docs/block-cn" target="_blank" rel="noopener noreferrer">
use block
</a>
</p>
<Form form={form} layout="vertical">
<Form.Item name="title" label="标题" rules={[{ required: true, message: '请输入标题' }]}>
<Input />
</Form.Item>
<Form.Item name="content" label="内容" rules={[{ required: true, message: '请输入内容' }]}>
<Input.TextArea rows={4} />
</Form.Item>
<Form.Item name="tags" label="标签(逗号分隔)">
<Input />
</Form.Item>
</Form>
</Modal>
</PageContainer>
);
};
export default Admin;
export default ForumAdmin;

View File

@ -0,0 +1,255 @@
import React, { useEffect, useState } from 'react';
import { PageContainer } from '@ant-design/pro-components';
import { Table, Button, Modal, Form, Input, Space, Popconfirm, message, Avatar, Select } from 'antd';
import { listUserByPageUsingPost, updateUserUsingPost, deleteUserUsingPost, addUserUsingPost } from '@/services/hebi/userController';
const { Option } = Select;
const UserManage: React.FC = () => {
const [allUsers, setAllUsers] = useState<any[]>([]);
const [users, setUsers] = useState<any[]>([]);
const [loading, setLoading] = useState(false);
const [editingUser, setEditingUser] = useState<any>(null);
const [modalVisible, setModalVisible] = useState(false);
const [form] = Form.useForm();
const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 });
const [search, setSearch] = useState<string>('');
const [isAdd, setIsAdd] = useState(false); // 新增/编辑标志
// 拉取所有用户
const fetchAllUsers = async () => {
setLoading(true);
const res = await listUserByPageUsingPost({
userQueryRequest: {
current: 1,
pageSize: 10000, // 假设不会超过1万用户
},
});
if (res && res.code === 0 && res.data) {
setAllUsers(res.data.records || []);
}
setLoading(false);
};
// 前端过滤和分页
const filterAndPaginate = (all: any[], keyword: string, page: number, pageSize: number) => {
let filtered = all;
if (keyword.trim()) {
filtered = all.filter(user =>
(user.userName || '').toLowerCase().includes(keyword.trim().toLowerCase())
);
}
const total = filtered.length;
const start = (page - 1) * pageSize;
const end = start + pageSize;
return {
users: filtered.slice(start, end),
total,
};
};
// 初始化和数据变动时处理
useEffect(() => {
fetchAllUsers();
}, []);
useEffect(() => {
const { users, total } = filterAndPaginate(allUsers, search, pagination.current, pagination.pageSize);
setUsers(users);
setPagination(prev => ({ ...prev, total }));
}, [allUsers, search, pagination.current, pagination.pageSize]);
// 搜索时重置到第一页
const handleSearch = (value: string) => {
setSearch(value);
setPagination(prev => ({ ...prev, current: 1 }));
};
// 新增
const handleAdd = () => {
setIsAdd(true);
setEditingUser(null);
setModalVisible(true);
form.resetFields();
};
// 编辑
const handleEdit = (record: any) => {
setIsAdd(false);
setEditingUser(record);
setModalVisible(true);
form.setFieldsValue({
...record,
});
};
// 提交表单
const handleOk = async () => {
const values = await form.validateFields();
if (isAdd) {
const res = await addUserUsingPost({ ...values });
if (res && res.code === 0) {
message.success('新增成功');
setModalVisible(false);
fetchAllUsers();
} else {
message.error(res?.message || '新增失败');
}
} else {
const res = await updateUserUsingPost({ ...editingUser, ...values });
if (res && res.code === 0) {
message.success('更新成功');
setModalVisible(false);
fetchAllUsers();
} else {
message.error(res?.message || '更新失败');
}
}
};
// 删除
const handleDelete = async (id: number) => {
const res = await deleteUserUsingPost({ id });
if (res && res.code === 0) {
message.success('删除成功');
fetchAllUsers();
} else {
message.error(res?.message || '删除失败');
}
};
const columns = [
{
title: 'ID',
dataIndex: 'id',
width: 60,
align: 'center',
},
{
title: '头像',
dataIndex: 'userAvatar',
width: 80,
align: 'center',
render: (avatar: string) => <Avatar src={avatar} />,
},
{
title: '用户名',
dataIndex: 'userName',
width: 160,
align: 'center',
},
{
title: '账号',
dataIndex: 'userAccount',
width: 160,
align: 'center',
},
{
title: '角色',
dataIndex: 'userRole',
width: 100,
align: 'center',
render: (role: string) => {
if (role === 'admin') return <span style={{ color: '#fa541c' }}></span>;
if (role === 'ban') return <span style={{ color: '#bfbfbf' }}></span>;
return <span></span>;
},
},
{
title: '简介',
dataIndex: 'userProfile',
width: 200,
align: 'center',
ellipsis: true,
},
{
title: '创建时间',
dataIndex: 'createTime',
width: 180,
align: 'center',
},
{
title: '操作',
key: 'action',
width: 160,
align: 'center',
render: (_: any, record: any) => (
<Space>
<Button type="link" onClick={() => handleEdit(record)}></Button>
<Popconfirm title="确定删除该用户吗?" onConfirm={() => handleDelete(record.id)}>
<Button type="link" danger></Button>
</Popconfirm>
</Space>
),
},
];
return (
<PageContainer title="用户管理">
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 16 }}>
<Button type="primary" onClick={handleAdd}></Button>
<Input.Search
allowClear
placeholder="搜索用户名"
style={{ width: 300 }}
value={search}
onChange={e => handleSearch(e.target.value)}
onSearch={handleSearch}
/>
</div>
<Table
rowKey="id"
loading={loading}
columns={columns}
dataSource={users}
bordered
pagination={{
current: pagination.current,
pageSize: pagination.pageSize,
total: pagination.total,
showSizeChanger: true,
onChange: (page, pageSize) => setPagination(prev => ({ ...prev, current: page, pageSize })),
}}
scroll={{ x: 1000 }}
/>
<Modal
title={isAdd ? "新增用户" : "编辑用户"}
open={modalVisible}
onOk={handleOk}
onCancel={() => setModalVisible(false)}
destroyOnClose
width={480}
bodyStyle={{ padding: 24 }}
>
<Form form={form} layout="vertical">
<Form.Item name="userName" label="用户名" rules={[{ required: true, message: '请输入用户名' }]}>
<Input />
</Form.Item>
<Form.Item name="userAccount" label="账号" rules={isAdd ? [{ required: true, message: '请输入账号' }] : []}>
<Input disabled={!isAdd} />
</Form.Item>
<Form.Item name="userAvatar" label="头像链接">
<Input />
</Form.Item>
<Form.Item name="userProfile" label="简介">
<Input.TextArea rows={3} />
</Form.Item>
<Form.Item name="userRole" label="角色" rules={[{ required: true, message: '请选择角色' }]}>
<Select>
<Option value="user"></Option>
<Option value="admin"></Option>
<Option value="ban"></Option>
</Select>
</Form.Item>
{isAdd && (
<Form.Item name="userPassword" label="密码" rules={[{ required: true, message: '请输入密码' }]}>
<Input.Password />
</Form.Item>
)}
</Form>
</Modal>
</PageContainer>
);
};
export default UserManage;