返回 AiToEarn
UserProfile.tsx
根目录 / project / aitoearn-electron / src / views / user / mine / UserProfile.tsx
1 import { useEffect, useState } from 'react';
2 import { Card, Descriptions, Spin, Typography, Space, Avatar, Switch, Slider, Button, Alert, InputNumber, Form, Tooltip } from 'antd';
3 import { userApi } from '@/api/user';
4 import styles from './userProfile.module.scss';
5 import { UserOutlined, PhoneOutlined, IdcardOutlined, QrcodeOutlined, ClockCircleOutlined, SafetyOutlined, WechatOutlined, QuestionCircleOutlined } from '@ant-design/icons';
6 import { message } from 'antd';
7
8 const { Title, Text } = Typography;
9
10 interface UserInfo {
11 createTime: string;
12 id: string;
13 inviteCode: string;
14 name: string;
15 phone: string;
16 status: number;
17 updateTime: string;
18 wxOpenid: string;
19 _id: string;
20 earnInfo?: {
21 status: number;
22 cycleInterval: number;
23 };
24 }
25
26 const UserProfile = () => {
27 const [loading, setLoading] = useState(false);
28 const [userInfo, setUserInfo] = useState<UserInfo | null>(null);
29 const [popularizeCode, setPopularizeCode] = useState<string>('');
30 const [earnStatus, setEarnStatus] = useState<boolean>(false);
31 const [cycleInterval, setCycleInterval] = useState<number>(30);
32 const [earnLoading, setEarnLoading] = useState<boolean>(false);
33 const [form] = Form.useForm();
34
35 const fetchUserInfo = async () => {
36 try {
37 setLoading(true);
38 const res:any = await userApi.getUserInfo();
39 setUserInfo(res);
40 // 如果没有邀请码,则获取
41 if (!res.popularizeCode) {
42 const code = await userApi.getMinePopularizeCode();
43 setPopularizeCode(code);
44 } else {
45 setPopularizeCode(res.popularizeCode);
46 }
47 if (res.earnInfo) {
48 setEarnStatus(res.earnInfo.status === 1);
49 setCycleInterval(res.earnInfo.cycleInterval || 30);
50 form.setFieldsValue({
51 cycleInterval: res.earnInfo.cycleInterval || 30
52 });
53 } else {
54 setEarnStatus(false);
55 setCycleInterval(30);
56 }
57 } catch (error) {
58 console.error('获取用户信息失败:', error);
59 } finally {
60 setLoading(false);
61 }
62 };
63
64 useEffect(() => {
65 fetchUserInfo();
66 }, []);
67
68 const handleSaveEarn = async () => {
69 setEarnLoading(true);
70 try {
71 await userApi.updateUserInfo({
72 earnInfo: {
73 status: earnStatus ? 1 : 0,
74 cycleInterval,
75 },
76 });
77 message.success('设置已保存');
78 fetchUserInfo();
79 } catch (e) {
80 message.error('保存失败');
81 } finally {
82 setEarnLoading(false);
83 }
84 };
85
86 const handleEarnStatusChange = async (checked: boolean) => {
87 setLoading(true);
88 try {
89 await userApi.setUserEarnInfo({
90 status: checked ? 1 : 0,
91 cycleInterval: cycleInterval
92 });
93 setEarnStatus(checked);
94 message.success(`赚钱模式已${checked ? '开启' : '关闭'}`);
95 } catch (error) {
96 console.error('更新赚钱模式状态失败:', error);
97 message.error('操作失败,请稍后重试');
98 // 恢复原状态
99 setEarnStatus(!checked);
100 } finally {
101 setLoading(false);
102 }
103 };
104
105 const handleCycleIntervalChange = async (value: number) => {
106 setCycleInterval(value);
107 };
108
109 const handleAfterChangeInterval = async (value: number) => {
110 if (!value || value < 5) return;
111
112 setLoading(true);
113 try {
114 await userApi.setUserEarnInfo({
115 status: earnStatus ? 1 : 0,
116 cycleInterval: value
117 });
118 message.success('检查频率已更新');
119 } catch (error) {
120 console.error('更新检查频率失败:', error);
121 message.error('操作失败,请稍后重试');
122 // 恢复原值
123 setCycleInterval(cycleInterval);
124 } finally {
125 setLoading(false);
126 }
127 };
128
129 const getIntervalLabel = (val: number) => {
130 if (val < 60) return `${val} 分钟`;
131 if (val % 60 === 0) return `${val / 60} 小时`;
132 return `${Math.floor(val / 60)} 小时${val % 60} 分钟`;
133 };
134
135 return (
136 <div className={styles.profileContainer}>
137 <Spin spinning={loading}>
138 <div className={styles.profileHeader}>
139 <div className={styles.profileAvatar}>
140 <Avatar size={120} icon={<UserOutlined />} />
141 <Title level={3} className={styles.profileName}>
142 {userInfo?.name || '未设置用户名'}
143 </Title>
144 </div>
145 </div>
146
147 <div className={styles.profileContent}>
148
149 <Card className={styles.profileCard}>
150 <Space direction="vertical" size="large" className={styles.infoSection}>
151 <div className={styles.infoGroup}>
152 <div className={styles.infoItem}>
153 <IdcardOutlined className={styles.infoIcon} />
154 <div className={styles.infoContent}>
155 <span className={styles.infoLabel}>用户ID</span>
156 <span className={styles.infoValue}>{userInfo?.id || '-'}</span>
157 </div>
158 </div>
159
160 <div className={styles.infoItem}>
161 <PhoneOutlined className={styles.infoIcon} />
162 <div className={styles.infoContent}>
163 <span className={styles.infoLabel}>手机号</span>
164 <span className={styles.infoValue}>{userInfo?.phone || '-'}</span>
165 </div>
166 </div>
167
168
169
170 <div className={styles.infoItem}>
171 <QrcodeOutlined className={styles.infoIcon} />
172 <div className={styles.infoContent}>
173 <span className={styles.infoLabel}>我的邀请码</span>
174 <span className={styles.infoValue}>{popularizeCode || '-'}</span>
175 </div>
176 </div>
177
178 {userInfo?.inviteCode && (
179 <div className={styles.infoItem}>
180 <QrcodeOutlined className={styles.infoIcon} />
181 <div className={styles.infoContent}>
182 <span className={styles.infoLabel}>邀请者邀请码</span>
183 <span className={styles.infoValue}>{userInfo.inviteCode}</span>
184 </div>
185 </div>
186 )}
187
188 {/* <div className={styles.infoItem}>
189 <ClockCircleOutlined className={styles.infoIcon} />
190 <div className={styles.infoContent}>
191 <span className={styles.infoLabel}>创建时间</span>
192 <span className={styles.infoValue}>
193 {userInfo?.createTime ? new Date(userInfo.createTime).toLocaleString() : '-'}
194 </span>
195 </div>
196 </div> */}
197
198 {/* <div className={styles.infoItem}>
199 <ClockCircleOutlined className={styles.infoIcon} />
200 <div className={styles.infoContent}>
201 <span className={styles.infoLabel}>更新时间</span>
202 <span className={styles.infoValue}>
203 {userInfo?.updateTime ? new Date(userInfo.updateTime).toLocaleString() : '-'}
204 </span>
205 </div>
206 </div> */}
207
208 {/* <div className={styles.infoItem}>
209 <WechatOutlined className={styles.infoIcon} />
210 <div className={styles.infoContent}>
211 <span className={styles.infoLabel}>微信OpenID</span>
212 <span className={styles.infoValue}>{userInfo?.wxOpenid || '-'}</span>
213 </div>
214 </div> */}
215
216 {/* <div className={styles.infoItem}>
217 <SafetyOutlined className={styles.infoIcon} />
218 <div className={styles.infoContent}>
219 <span className={styles.infoLabel}>账号状态</span>
220 <span className={`${styles.infoValue} ${userInfo?.status === 1 ? styles.statusNormal : styles.statusAbnormal}`}>
221 {userInfo?.status === 1 ? '正常' : '异常'}
222 </span>
223 </div>
224 </div> */}
225 </div>
226 </Space>
227 </Card>
228
229 <Card className={styles.profileCard} style={{ marginTop: 24 }}>
230 <Title level={5} style={{ marginBottom: 16 }}>赚钱模式设置</Title>
231 <div style={{ marginBottom: 16 }}>
232 <div style={{ marginBottom: 16, display: 'flex', alignItems: 'center', gap: 16 }}>
233 <span>赚钱模式</span>
234 <Switch
235 checked={earnStatus}
236 onChange={handleEarnStatusChange}
237 loading={loading}
238 />
239 <Tooltip title="开启后系统将自动为您寻找并完成任务">
240 <QuestionCircleOutlined style={{ color: '#999' }} />
241 </Tooltip>
242 </div>
243
244 <div style={{ marginBottom: 8, display: 'flex', alignItems: 'center', gap: 16 }}>
245 <span>检查频率</span>
246 <Slider
247 min={5}
248 max={120}
249 step={1}
250 value={cycleInterval}
251 onChange={handleCycleIntervalChange}
252 onAfterChange={handleAfterChangeInterval}
253 style={{ flex: 1, marginRight: 16 }}
254 disabled={!earnStatus || loading}
255 />
256 <span style={{ minWidth: 70, textAlign: 'right' }}>{getIntervalLabel(cycleInterval)}</span>
257 </div>
258 </div>
259 <div style={{ color: '#999', fontSize: 12, marginBottom: 12 }}>
260 可设置 5分钟~24小时 区间
261 </div>
262 <Alert
263 type="info"
264 showIcon
265 style={{ background: '#fafaff', border: 'none', marginBottom: 16 }}
266 message={
267 <span>
268 开启赚钱模式后,系统将自动接取并完成互动需求,确保您能获得最大收益。
269 </span>
270 }
271 />
272 </Card>
273
274 </div>
275 </Spin>
276 </div>
277 );
278 };
279
280 export default UserProfile;
280 lines Plain Text