feat(auth): 实现登录注册功能并重构 App 组件
- 新增登录和注册页面组件
- 实现用户认证和权限管理逻辑
- 重构 App 组件,使用 Router 和 AuthProvider
- 添加管理员面板和论坛页面组件
Change-Id: Iaa4502616970e75e3268537f73c75dac8f60e24d
diff --git a/src/features/forum/pages/ForumPage.jsx b/src/features/forum/pages/ForumPage.jsx
new file mode 100644
index 0000000..7e6e78f
--- /dev/null
+++ b/src/features/forum/pages/ForumPage.jsx
@@ -0,0 +1,143 @@
+import React, { useState, useEffect } from 'react';
+import { List, Avatar, Space, Tag, Typography, Button, message, Modal, Form, Input, Spin } from 'antd';
+import { getPosts, createPost } from '../services/forumApi';
+
+const { Title, Paragraph, Text } = Typography;
+const { TextArea } = Input;
+
+const ForumPage = () => {
+ const [posts, setPosts] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [isModalOpen, setIsModalOpen] = useState(false);
+ const [form] = Form.useForm();
+
+ // 获取用户信息
+ const user = JSON.parse(localStorage.getItem('user') || '{}');
+
+ // 加载帖子数据
+ useEffect(() => {
+ fetchPosts();
+ }, []);
+
+ // 获取帖子列表
+ const fetchPosts = async () => {
+ try {
+ setLoading(true);
+ const response = await getPosts({ username: user.username });
+ if (response.success) {
+ setPosts(response.data.posts || []);
+ }
+ } catch (error) {
+ message.error(error.message || '获取帖子列表失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ // 显示新建帖子对话框
+ const showModal = () => {
+ setIsModalOpen(true);
+ };
+
+ // 关闭对话框
+ const handleCancel = () => {
+ setIsModalOpen(false);
+ form.resetFields();
+ };
+
+ // 提交新帖子
+ const handleSubmit = async () => {
+ try {
+ const values = await form.validateFields();
+
+ // 添加作者信息
+ values.author = user.username;
+
+ const response = await createPost(values);
+ if (response.success) {
+ message.success('帖子发布成功');
+ setIsModalOpen(false);
+ form.resetFields();
+ fetchPosts(); // 重新加载帖子列表
+ }
+ } catch (error) {
+ message.error(error.message || '发布帖子失败');
+ }
+ };
+
+ return (
+ <div className="space-y-6">
+ <Title level={2}>社区论坛</Title>
+ <Paragraph className="text-slate-500">
+ 欢迎来到我们的社区论坛,这里是会员交流分享的地方。
+ </Paragraph>
+
+ {loading ? (
+ <div className="flex justify-center py-8">
+ <Spin size="large" tip="加载中..." />
+ </div>
+ ) : (
+ <List
+ itemLayout="vertical"
+ size="large"
+ dataSource={posts}
+ renderItem={(item) => (
+ <List.Item
+ key={item.id}
+ extra={
+ <Space>
+ <Tag color="green">浏览: {item.views || 0}</Tag>
+ <Tag color="blue">点赞: {item.likes || 0}</Tag>
+ <Text type="secondary">{item.createTime}</Text>
+ </Space>
+ }
+ >
+ <List.Item.Meta
+ avatar={<Avatar src={`https://api.dicebear.com/7.x/avataaars/svg?seed=${item.author}`} />}
+ title={<a href={`/post/${item.id}`}>{item.title}</a>}
+ description={<Text type="secondary">作者: {item.author}</Text>}
+ />
+ <Paragraph ellipsis={{ rows: 2 }}>{item.content}</Paragraph>
+ </List.Item>
+ )}
+ />
+ )}
+
+ <div className="text-center mt-4">
+ <Button type="primary" onClick={showModal}>发布新主题</Button>
+ </div>
+
+ {/* 新建帖子对话框 */}
+ <Modal
+ title="发布新主题"
+ open={isModalOpen}
+ onOk={handleSubmit}
+ onCancel={handleCancel}
+ okText="发布"
+ cancelText="取消"
+ >
+ <Form
+ form={form}
+ layout="vertical"
+ >
+ <Form.Item
+ name="title"
+ label="标题"
+ rules={[{ required: true, message: '请输入标题' }]}
+ >
+ <Input placeholder="请输入标题" />
+ </Form.Item>
+ <Form.Item
+ name="content"
+ label="内容"
+ rules={[{ required: true, message: '请输入帖子内容' }]}
+ >
+ <TextArea rows={6} placeholder="请输入帖子内容" />
+ </Form.Item>
+ </Form>
+ </Modal>
+ </div>
+ );
+};
+
+export default ForumPage;
\ No newline at end of file
diff --git a/src/features/forum/services/forumApi.js b/src/features/forum/services/forumApi.js
new file mode 100644
index 0000000..68feca1
--- /dev/null
+++ b/src/features/forum/services/forumApi.js
@@ -0,0 +1,117 @@
+import request from "../../../services/request";
+
+// 帖子相关API
+export const createPost = (postData) => {
+ const token = localStorage.getItem("token");
+ return request
+ .post(
+ "/posts/create",
+ postData,
+ { headers: { token } }
+ )
+ .then((response) => {
+ if (response.data && response.data.success) {
+ return response.data;
+ } else {
+ return Promise.reject(new Error(response.data.message || "创建帖子失败"));
+ }
+ });
+};
+
+export const getPosts = (filters) => {
+ const token = localStorage.getItem("token");
+
+ // 构建查询参数
+ let queryParams = new URLSearchParams();
+ if (filters) {
+ if (filters.username) queryParams.append("username", filters.username);
+ if (filters.title) queryParams.append("title", filters.title);
+ if (filters.author) queryParams.append("author", filters.author);
+ if (filters.date) queryParams.append("date", filters.date);
+ }
+
+ return request
+ .get(
+ `/posts/list?${queryParams.toString()}`,
+ { headers: { token } }
+ )
+ .then((response) => {
+ if (response.data && response.data.success) {
+ return response.data;
+ } else {
+ return Promise.reject(new Error(response.data.message || "获取帖子列表失败"));
+ }
+ });
+};
+
+export const deletePost = (username, pid) => {
+ const token = localStorage.getItem("token");
+ return request
+ .delete(
+ "/posts/delete",
+ {
+ headers: { token },
+ data: { username, pid }
+ }
+ )
+ .then((response) => {
+ if (response.data && response.data.success) {
+ return response.data;
+ } else {
+ return Promise.reject(new Error(response.data.message || "删除帖子失败"));
+ }
+ });
+};
+
+// 评论相关API
+export const addComment = (commentData) => {
+ const token = localStorage.getItem("token");
+ return request
+ .post(
+ "/comment/add",
+ commentData,
+ { headers: { token } }
+ )
+ .then((response) => {
+ if (response.data && response.data.success) {
+ return response.data;
+ } else {
+ return Promise.reject(new Error(response.data.message || "添加评论失败"));
+ }
+ });
+};
+
+export const getComments = (postId, username) => {
+ const token = localStorage.getItem("token");
+ return request
+ .get(
+ `/comment/get?postId=${postId}&username=${username}`,
+ { headers: { token } }
+ )
+ .then((response) => {
+ if (response.data && response.data.success) {
+ return response.data;
+ } else {
+ return Promise.reject(new Error(response.data.message || "获取评论失败"));
+ }
+ });
+};
+
+export const deleteComment = (username, commentId) => {
+ const token = localStorage.getItem("token");
+ return request
+ .delete(
+ "/comment/delete",
+ {
+ headers: { token },
+ data: { username, commentId }
+ }
+ )
+ .then((response) => {
+ if (response.data && response.data.success) {
+ return response.data;
+ } else {
+ return Promise.reject(new Error(response.data.message || "删除评论失败"));
+ }
+ });
+};
\ No newline at end of file