新增邀请码页面,新增添加好友功能
Change-Id: Ifa0a5c355ab3693eecfe919de06fa9bef8171695
diff --git a/react-ui/src/pages/Invite/data.d.ts b/react-ui/src/pages/Invite/data.d.ts
new file mode 100644
index 0000000..2a07e7a
--- /dev/null
+++ b/react-ui/src/pages/Invite/data.d.ts
@@ -0,0 +1,12 @@
+// data.ts 建议的类型定义
+export interface GetInviteCodeResponse {
+ code: number;
+ msg: string;
+ data: string; // 邀请码字符串
+}
+
+export interface GetUserByInviteCodeResponse {
+ code: number;
+ msg: string;
+ data: number; // 用户ID
+}
\ No newline at end of file
diff --git a/react-ui/src/pages/Invite/index.tsx b/react-ui/src/pages/Invite/index.tsx
new file mode 100644
index 0000000..56989ff
--- /dev/null
+++ b/react-ui/src/pages/Invite/index.tsx
@@ -0,0 +1,300 @@
+import React, { useState, useEffect } from 'react';
+import { useModel } from 'umi';
+import { Card, Input, Button, Space, Typography, Row, Col, message } from 'antd';
+import { CopyOutlined, UserAddOutlined, GiftOutlined, ShareAltOutlined } from '@ant-design/icons';
+import { getUserInviteCode, getUserByInviteCode } from './service';
+
+const { Title, Text, Paragraph } = Typography;
+
+const InvitePage: React.FC = () => {
+ const [myInviteCode, setMyInviteCode] = useState<string>('');
+ const [inputInviteCode, setInputInviteCode] = useState<string>('');
+ const [loading, setLoading] = useState<boolean>(false);
+ const [submitting, setSubmitting] = useState<boolean>(false);
+
+ // 获取当前用户信息
+ const { initialState } = useModel('@@initialState');
+ const userId = initialState?.currentUser?.userId || '';
+
+ // 获取我的邀请码
+ const fetchMyInviteCode = async () => {
+ if (!userId) {
+ message.error('用户信息获取失败');
+ return;
+ }
+
+ setLoading(true);
+ try {
+ const response = await getUserInviteCode();
+ if (response.code === 200) {
+ // 根据后端返回的数据结构调整
+ setMyInviteCode(response.msg);
+ } else {
+ message.error(response.msg || '获取邀请码失败');
+ }
+ } catch (error) {
+ message.error('获取邀请码失败');
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ // 复制邀请码
+ const copyInviteCode = () => {
+ if (myInviteCode) {
+ navigator.clipboard.writeText(myInviteCode).then(() => {
+ message.success('邀请码已复制到剪贴板');
+ }).catch(() => {
+ message.error('复制失败,请手动复制');
+ });
+ }
+ };
+
+ // 查询邀请码对应的用户
+ const handleSubmitInviteCode = async () => {
+ if (!inputInviteCode.trim()) {
+ message.warning('请输入邀请码');
+ return;
+ }
+
+ setSubmitting(true);
+ try {
+ const response = await getUserByInviteCode(inputInviteCode.trim());
+
+ if (response.code === 200) {
+ message.success(`邀请码有效,对应用户ID: ${response.data}`);
+ setInputInviteCode('');
+ // 这里可以根据业务需求进行后续操作,比如关注用户等
+ } else {
+ message.error(response.msg || '邀请码无效');
+ }
+ } catch (error) {
+ message.error('查询邀请码失败');
+ } finally {
+ setSubmitting(false);
+ }
+ };
+
+ useEffect(() => {
+ fetchMyInviteCode();
+ }, []);
+
+ return (
+ <div
+ style={{
+ padding: '32px 24px',
+ maxWidth: '800px',
+ margin: '0 auto',
+ minHeight: '100vh',
+ background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
+ }}
+ >
+ {/* 头部标题区域 */}
+ <div style={{ textAlign: 'center', marginBottom: '40px' }}>
+ <GiftOutlined
+ style={{
+ fontSize: '48px',
+ color: '#fff',
+ marginBottom: '16px',
+ filter: 'drop-shadow(0 4px 8px rgba(0,0,0,0.2))'
+ }}
+ />
+ <Title
+ level={1}
+ style={{
+ color: '#fff',
+ margin: 0,
+ fontSize: '32px',
+ fontWeight: 600,
+ textShadow: '0 2px 4px rgba(0,0,0,0.3)'
+ }}
+ >
+ 邀请码管理
+ </Title>
+ <Paragraph
+ style={{
+ color: 'rgba(255,255,255,0.8)',
+ fontSize: '16px',
+ margin: '8px 0 0 0'
+ }}
+ >
+ 邀请好友,共享美好体验
+ </Paragraph>
+ </div>
+
+ <Row gutter={[24, 24]}>
+ {/* 我的邀请码 */}
+ <Col xs={24} lg={12}>
+ <Card
+ title={
+ <Space>
+ <ShareAltOutlined style={{ color: '#1890ff' }} />
+ <span style={{ color: '#1890ff', fontWeight: 600 }}>我的邀请码</span>
+ </Space>
+ }
+ style={{
+ height: '100%',
+ borderRadius: '16px',
+ boxShadow: '0 8px 32px rgba(0,0,0,0.12)',
+ border: 'none',
+ background: 'rgba(255,255,255,0.95)',
+ backdropFilter: 'blur(10px)'
+ }}
+ bodyStyle={{ padding: '24px' }}
+ >
+ <Space direction="vertical" style={{ width: '100%' }} size="large">
+ <div style={{
+ padding: '20px',
+ background: 'linear-gradient(135deg, #e3f2fd 0%, #f3e5f5 100%)',
+ borderRadius: '12px',
+ textAlign: 'center'
+ }}>
+ <Text
+ type="secondary"
+ style={{
+ fontSize: '14px',
+ display: 'block',
+ marginBottom: '16px'
+ }}
+ >
+ 分享下方邀请码给好友,邀请他们加入
+ </Text>
+ <div style={{
+ background: '#fff',
+ padding: '16px',
+ borderRadius: '8px',
+ border: '2px dashed #d9d9d9',
+ marginBottom: '16px'
+ }}>
+ <Text
+ copyable={false}
+ style={{
+ fontSize: '24px',
+ fontWeight: 'bold',
+ color: '#1890ff',
+ fontFamily: 'Monaco, monospace',
+ letterSpacing: '2px'
+ }}
+ >
+ {loading ? '加载中...' : (myInviteCode || '暂无邀请码')}
+ </Text>
+ </div>
+ <Button
+ type="primary"
+ size="large"
+ icon={<CopyOutlined />}
+ onClick={copyInviteCode}
+ disabled={!myInviteCode}
+ style={{
+ borderRadius: '8px',
+ height: '44px',
+ fontSize: '16px',
+ fontWeight: 500,
+ boxShadow: '0 4px 12px rgba(24, 144, 255, 0.3)'
+ }}
+ block
+ >
+ 复制邀请码
+ </Button>
+ </div>
+ </Space>
+ </Card>
+ </Col>
+
+ {/* 输入邀请码 */}
+ <Col xs={24} lg={12}>
+ <Card
+ title={
+ <Space>
+ <UserAddOutlined style={{ color: '#52c41a' }} />
+ <span style={{ color: '#52c41a', fontWeight: 600 }}>查询邀请码</span>
+ </Space>
+ }
+ style={{
+ height: '100%',
+ borderRadius: '16px',
+ boxShadow: '0 8px 32px rgba(0,0,0,0.12)',
+ border: 'none',
+ background: 'rgba(255,255,255,0.95)',
+ backdropFilter: 'blur(10px)'
+ }}
+ bodyStyle={{ padding: '24px' }}
+ >
+ <Space direction="vertical" style={{ width: '100%' }} size="large">
+ <div style={{
+ padding: '20px',
+ background: 'linear-gradient(135deg, #f6ffed 0%, #e6f7ff 100%)',
+ borderRadius: '12px'
+ }}>
+ <Text
+ type="secondary"
+ style={{
+ fontSize: '14px',
+ display: 'block',
+ marginBottom: '20px',
+ textAlign: 'center'
+ }}
+ >
+ 输入邀请码
+ </Text>
+ <Space direction="vertical" style={{ width: '100%' }} size="middle">
+ <Input
+ value={inputInviteCode}
+ onChange={(e) => setInputInviteCode(e.target.value)}
+ placeholder="请输入邀请码"
+ size="large"
+ style={{
+ borderRadius: '8px',
+ fontSize: '16px',
+ height: '48px',
+ textAlign: 'center',
+ fontFamily: 'Monaco, monospace',
+ letterSpacing: '1px'
+ }}
+ onPressEnter={handleSubmitInviteCode}
+ />
+ <Button
+ type="primary"
+ size="large"
+ icon={<UserAddOutlined />}
+ onClick={handleSubmitInviteCode}
+ loading={submitting}
+ disabled={!inputInviteCode.trim()}
+ style={{
+ borderRadius: '8px',
+ height: '44px',
+ fontSize: '16px',
+ fontWeight: 500,
+ background: '#52c41a',
+ borderColor: '#52c41a',
+ boxShadow: '0 4px 12px rgba(82, 196, 26, 0.3)'
+ }}
+ block
+ >
+ 邀请码
+ </Button>
+ </Space>
+ </div>
+ </Space>
+ </Card>
+ </Col>
+ </Row>
+
+ {/* 底部装饰 */}
+ <div style={{
+ textAlign: 'center',
+ marginTop: '40px',
+ padding: '20px',
+ background: 'rgba(255,255,255,0.1)',
+ borderRadius: '12px',
+ backdropFilter: 'blur(10px)'
+ }}>
+ <Text style={{ color: 'rgba(255,255,255,0.7)', fontSize: '14px' }}>
+ 通过邀请码连接更多朋友,一起享受精彩体验
+ </Text>
+ </div>
+ </div>
+ );
+};
+
+export default InvitePage;
\ No newline at end of file
diff --git a/react-ui/src/pages/Invite/service.ts b/react-ui/src/pages/Invite/service.ts
new file mode 100644
index 0000000..e6cf9af
--- /dev/null
+++ b/react-ui/src/pages/Invite/service.ts
@@ -0,0 +1,19 @@
+import { request } from '@umijs/max';
+import type {
+ GetInviteCodeResponse,
+ GetUserByInviteCodeResponse
+} from './data';
+
+// 获取用户邀请码
+export async function getUserInviteCode(): Promise<GetInviteCodeResponse> {
+ return request('/api/system/user/invite/code', {
+ method: 'GET', // 修改为 GET 请求
+ });
+}
+
+// 根据邀请码获取用户信息
+export async function getUserByInviteCode(code: string): Promise<GetUserByInviteCodeResponse> {
+ return request(`/api/system/user/invite/user/${code}`, {
+ method: 'GET', // 修改为 GET 请求,并使用路径参数
+ });
+}
\ No newline at end of file