feat(auth): 实现登录注册功能并重构 App 组件

- 新增登录和注册页面组件
- 实现用户认证和权限管理逻辑
- 重构 App 组件,使用 Router 和 AuthProvider
- 添加管理员面板和论坛页面组件

Change-Id: Iaa4502616970e75e3268537f73c75dac8f60e24d
diff --git a/src/features/auth/pages/RegisterPage.jsx b/src/features/auth/pages/RegisterPage.jsx
new file mode 100644
index 0000000..c4fb06d
--- /dev/null
+++ b/src/features/auth/pages/RegisterPage.jsx
@@ -0,0 +1,130 @@
+// src/features/auth/pages/RegisterPage.jsx
+import React, { useState } from 'react';
+import { useNavigate, Link } from 'react-router-dom';
+import { Form, Input, Button, Card, Typography, Divider, message } from 'antd';
+import { UserOutlined, LockOutlined, MailOutlined } from '@ant-design/icons';
+import { useAuth } from '../contexts/AuthContext'; // 使用新的 AuthContext
+
+const { Title, Text } = Typography;
+
+const RegisterPage = () => {
+  const [loading, setLoading] = useState(false);
+  const navigate = useNavigate();
+  const { register, isAuthenticated, user } = useAuth(); // 从 Context 获取 register 方法
+
+  React.useEffect(() => {
+    if (isAuthenticated && user) {
+      navigate('/'); // 如果已登录,跳转到首页
+    }
+  }, [isAuthenticated, user, navigate]);
+
+  const onFinish = async (values) => {
+    setLoading(true);
+    try {
+      // 从表单值中移除 'confirm' 字段,因为它不需要发送到后端
+      const { confirm, ...registrationData } = values;
+      await register(registrationData); // 使用 context 中的 register 方法
+      message.success('注册成功!将跳转到登录页...');
+      setTimeout(() => {
+        navigate('/login');
+      }, 1500); // 延迟跳转,让用户看到成功消息
+    } catch (error) {
+      // 错误消息由 AuthContext 中的 register 方法处理或 request 拦截器处理
+      console.error('Registration page error:', error);
+      // message.error(error.message || '注册失败,请重试'); // 如果 Context 未处理错误提示
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  return (
+    <div className="flex justify-center items-center min-h-screen bg-slate-100 p-4">
+      <Card className="w-full max-w-md shadow-lg rounded-lg">
+        <div className="text-center mb-8">
+          <Title level={2} className="!mb-2 text-slate-700">创建您的账户</Title>
+          <Text type="secondary">加入我们的PT社区</Text>
+        </div>
+        
+        <Form
+          name="register_form"
+          onFinish={onFinish}
+          size="large"
+          layout="vertical"
+          className="space-y-4" // 调整表单项间距
+        >
+          <Form.Item
+            name="username"
+            rules={[
+              { required: true, message: '请输入您的用户名!' },
+              { min: 3, message: '用户名至少需要3个字符' },
+              { max: 20, message: '用户名不能超过20个字符' },
+              { pattern: /^[a-zA-Z0-9_]+$/, message: '用户名只能包含字母、数字和下划线' }
+            ]}
+            hasFeedback // 显示校验状态图标
+          >
+            <Input prefix={<UserOutlined />} placeholder="用户名" />
+          </Form.Item>
+          
+          <Form.Item
+            name="email"
+            rules={[
+              { required: true, message: '请输入您的邮箱地址!' },
+              { type: 'email', message: '请输入一个有效的邮箱地址!' }
+            ]}
+            hasFeedback
+          >
+            <Input prefix={<MailOutlined />} placeholder="邮箱" />
+          </Form.Item>
+          
+          <Form.Item
+            name="password"
+            rules={[
+              { required: true, message: '请输入您的密码!' },
+              { min: 6, message: '密码至少需要6个字符' }
+              // 可以添加更复杂的密码强度校验规则
+            ]}
+            hasFeedback
+          >
+            <Input.Password prefix={<LockOutlined />} placeholder="密码" />
+          </Form.Item>
+          
+          <Form.Item
+            name="confirm"
+            dependencies={['password']}
+            hasFeedback
+            rules={[
+              { required: true, message: '请再次输入您的密码!' },
+              ({ getFieldValue }) => ({
+                validator(_, value) {
+                  if (!value || getFieldValue('password') === value) {
+                    return Promise.resolve();
+                  }
+                  return Promise.reject(new Error('两次输入的密码不一致!'));
+                },
+              }),
+            ]}
+          >
+            <Input.Password prefix={<LockOutlined />} placeholder="确认密码" />
+          </Form.Item>
+
+          <Form.Item className="!mt-6"> {/* 增加注册按钮的上边距 */}
+            <Button type="primary" htmlType="submit" className="w-full !text-base" loading={loading}>
+              注 册
+            </Button>
+          </Form.Item>
+          
+          <Divider plain><span className="text-slate-500">或</span></Divider>
+          
+          <div className="text-center">
+            <Text type="secondary" className="mr-1">已经有账户了?</Text>
+            <Link to="/login" className="font-medium text-blue-600 hover:text-blue-700 hover:underline">
+              前往登录
+            </Link>
+          </div>
+        </Form>
+      </Card>
+    </div>
+  );
+};
+
+export default RegisterPage;
\ No newline at end of file