blob: 473703359ed1d49da801d9af544129c7ddfaf94a [file] [log] [blame]
kjWei53b79ae2025-06-05 09:18:35 +00001import React, { useState, useEffect } from "react";
956303669a32fc2c2025-06-02 19:45:53 +08002import AccountCircleIcon from "@mui/icons-material/AccountCircle";
3import { useNavigate } from "react-router-dom";
4import "./App.css";
5
6export default function UserProfile() {
7 const navigate = useNavigate();
8 const [userInfo, setUserInfo] = useState({
kjWei53b79ae2025-06-05 09:18:35 +00009 avatar_url: "",
956303669a32fc2c2025-06-02 19:45:53 +080010 username: "示例用户",
11 email: "user@example.com",
kjWei53b79ae2025-06-05 09:18:35 +000012 invite_left: "",
956303669a32fc2c2025-06-02 19:45:53 +080013 school: "",
kjWei53b79ae2025-06-05 09:18:35 +000014 account_status: "",
15 gender: "",
956303669a32fc2c2025-06-02 19:45:53 +080016 });
17 const [tempUserInfo, setTempUserInfo] = useState({ ...userInfo });
kjWei53b79ae2025-06-05 09:18:35 +000018 const [userSeeds, setUserSeeds] = useState([]);
19 const [userStats, setUserStats] = useState({
20 magic: 0,
21 upload: 0,
22 download: 0,
23 ratio: 0,
24 });
25
26 // 新增:根据userid从后端获取用户信息
27 useEffect(() => {
28 const fetchUserInfo = async () => {
29 // 假设userid存储在localStorage或其他地方
30 // const userid = localStorage.getItem("userid");
31 const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
32 if (!userid) return;
33 try {
34 const res = await fetch(`/api/user-profile?userid=${userid}`);
35 if (res.ok) {
36 const data = await res.json();
37 setUserInfo(data);
38 setTempUserInfo(data);
39 }
40 } catch (err) {
41 // 可以根据需要处理错误
42 console.error("获取用户信息失败", err);
43 }
44 };
45 fetchUserInfo();
46 }, []);
47
48 // 动态加载用户上传种子列表
49 useEffect(() => {
50 const fetchUserSeeds = async () => {
51 // const userid = localStorage.getItem("userid");
52 const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
53 if (!userid) return;
54 try {
55 const res = await fetch(`/api/user-seeds?userid=${userid}`);
56 if (res.ok) {
57 const data = await res.json();
58 setUserSeeds(data);
59 }
60 } catch (err) {
61 console.error("获取种子列表失败", err);
62 }
63 };
64 fetchUserSeeds();
65 }, []);
66
67 // 动态加载用户活跃度信息
68 useEffect(() => {
69 const fetchUserStats = async () => {
70 // const userid = localStorage.getItem("userid");
71 const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
72 if (!userid) return;
73 try {
74 const res = await fetch(`/api/user-stats?userid=${userid}`);
75 if (res.ok) {
76 const data = await res.json();
77 setUserStats(data);
78 }
79 } catch (err) {
80 console.error("获取活跃度信息失败", err);
81 }
82 };
83 fetchUserStats();
84 }, []);
956303669a32fc2c2025-06-02 19:45:53 +080085
86 const handleInputChange = (field, value) => {
87 setTempUserInfo({ ...tempUserInfo, [field]: value });
88 };
89
kjWei53b79ae2025-06-05 09:18:35 +000090 const handleSave = async () => {
956303669a32fc2c2025-06-02 19:45:53 +080091 setUserInfo({ ...tempUserInfo });
kjWei53b79ae2025-06-05 09:18:35 +000092 // 获取userid
93 // const userid = localStorage.getItem("userid");
94 const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
95 try {
96 const res = await fetch('/api/change-profile', {
97 method: 'POST',
98 headers: {
99 'Content-Type': 'application/json',
100 },
101 body: JSON.stringify({ userid, ...tempUserInfo }),
102 });
103 if (res.ok) {
104 alert("信息已保存!");
105 } else {
106 alert("保存失败,请重试。");
107 }
108 } catch (err) {
109 alert("保存失败,请检查网络连接。");
110 console.error("保存用户信息失败", err);
111 }
956303669a32fc2c2025-06-02 19:45:53 +0800112 };
113
114 const handleAvatarClick = () => {
115 const avatarUrl = prompt("请输入头像的URL:");
116 if (avatarUrl) {
117 setTempUserInfo({ ...tempUserInfo, avatar: avatarUrl });
118 }
119 };
120
121 return (
122 <div className="container" style={{ minHeight: '100vh', background: 'linear-gradient(135deg, #f0f4ff 0%, #e0e7ff 100%)', display: 'grid', gridTemplateColumns: '1fr 2fr', gridTemplateRows: 'auto 1fr', gap: '20px', padding: '40px' }}>
123 {/* 左侧:用户资料 */}
124 <div style={{ gridColumn: '1 / 2', gridRow: '1 / 3', display: 'flex', flexDirection: 'column', alignItems: 'center', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', padding: '20px' }}>
125 <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginBottom: 16 }}>
126 <div onClick={handleAvatarClick} style={{ cursor: 'pointer', position: 'relative' }}>
127 <AccountCircleIcon style={{ fontSize: 90, color: '#1a237e', marginBottom: 12 }} />
128 {tempUserInfo.avatar && (
129 <img
kjWei53b79ae2025-06-05 09:18:35 +0000130 src={tempUserInfo.avatar_url}
956303669a32fc2c2025-06-02 19:45:53 +0800131 alt="用户头像"
132 style={{
133 position: 'absolute',
134 top: 0,
135 left: 0,
136 width: 90,
137 height: 90,
138 borderRadius: '50%',
139 objectFit: 'cover',
140 }}
141 />
142 )}
143 </div>
144 <h2 style={{ color: '#1a237e', marginBottom: 0, fontSize: 24 }}>用户个人资料</h2>
145 </div>
146 <div className="card" style={{ padding: 28, width: '100%', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', flex: 1 }}>
147 <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
148 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>用户名:</b>
149 <input
150 type="text"
151 value={tempUserInfo.username}
152 onChange={(e) => handleInputChange("username", e.target.value)}
153 style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}
154 />
155 </div>
156 <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
157 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>邮箱:</b>
kjWei53b79ae2025-06-05 09:18:35 +0000158 <span
159 style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15, backgroundColor: '#f5f5f5', color: '#888' }}
160 >
161 {tempUserInfo.email}
162 </span>
956303669a32fc2c2025-06-02 19:45:53 +0800163 </div>
164 <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
kjWei53b79ae2025-06-05 09:18:35 +0000165 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>邀请剩余:</b>
166 <span
167 style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15, backgroundColor: '#f5f5f5', color: '#888' }}
168 >
169 {tempUserInfo.invite_left}
170 </span>
956303669a32fc2c2025-06-02 19:45:53 +0800171 </div>
172 <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
173 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>学校:</b>
174 <input
175 type="text"
176 value={tempUserInfo.school}
177 onChange={(e) => handleInputChange("school", e.target.value)}
178 style={{ flex: 1, padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15 }}
179 />
180 </div>
181 <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
kjWei53b79ae2025-06-05 09:18:35 +0000182 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>账号状态:</b>
183 <span
184 style={{ flex: 1, display: 'flex', alignItems: 'center', padding: '6px 10px', borderRadius: 7, border: '1px solid #b2b2b2', minWidth: 0, fontSize: 15, backgroundColor: '#f5f5f5', color: '#888' }}
185 >
186 {tempUserInfo.account_status === 1 || tempUserInfo.account_status === "1" ? "封禁" : "正常"}
187 <span style={{
188 display: 'inline-block',
189 width: 12,
190 height: 12,
191 borderRadius: '50%',
192 backgroundColor: tempUserInfo.account_status === 1 || tempUserInfo.account_status === "1" ? '#e53935' : '#43a047',
193 marginLeft: 10,
194 border: '1px solid #b2b2b2',
195 }} />
196 </span>
956303669a32fc2c2025-06-02 19:45:53 +0800197 </div>
198 <div style={{ marginBottom: 18, display: 'flex', alignItems: 'center' }}>
199 <b style={{ width: 72, textAlign: 'left', marginRight: 0, fontSize: 16 }}>性别:</b>
200 <div style={{ position: 'relative', flex: 1 }}>
201 <button
202 onClick={() => setTempUserInfo({ ...tempUserInfo, showGenderOptions: !tempUserInfo.showGenderOptions })}
203 style={{
204 width: '100%',
205 padding: '6px 10px',
206 borderRadius: 7,
207 border: '1px solid #b2b2b2',
208 textAlign: 'left',
209 backgroundColor: '#fff',
210 fontSize: 15,
211 cursor: 'pointer',
212 }}
213 >
214 {tempUserInfo.gender || "性别"}
215 </button>
216 {tempUserInfo.showGenderOptions && (
217 <ul
218 style={{
219 position: 'absolute',
220 top: '100%',
221 left: 0,
222 right: 0,
223 backgroundColor: '#fff',
224 border: '1px solid #b2b2b2',
225 borderRadius: 7,
226 listStyle: 'none',
227 margin: 0,
228 padding: 0,
229 zIndex: 10,
230 }}
231 >
232 {["男", "女", "跨性别男", "跨性别女", "无性别"].map((option) => (
233 <li
234 key={option}
235 onClick={() => {
236 setTempUserInfo({ ...tempUserInfo, gender: option, showGenderOptions: false });
237 }}
238 style={{
239 padding: '6px 10px',
240 cursor: 'pointer',
241 borderBottom: '1px solid #e0e0e0',
242 backgroundColor: option === tempUserInfo.gender ? '#f0f0f0' : '#fff',
243 }}
244 >
245 {option}
246 </li>
247 ))}
248 </ul>
249 )}
250 </div>
251 </div>
252 <button
253 onClick={handleSave}
254 style={{
255 marginTop: 20,
256 padding: '10px 20px',
257 backgroundColor: '#1a237e',
258 color: '#fff',
259 border: 'none',
260 borderRadius: 7,
261 cursor: 'pointer',
262 fontSize: 16,
263 alignSelf: 'center', // Center the button horizontally
264 }}
265 onMouseOver={(e) => (e.target.style.backgroundColor = '#0d1b5e')}
266 onMouseOut={(e) => (e.target.style.backgroundColor = '#1a237e')}
267 >
268 保存
269 </button>
270 </div>
271 </div>
272 {/* 上传种子列表 */}
273 <div style={{ gridColumn: '2 / 3', gridRow: '1 / 2', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', padding: '20px' }}>
274 <h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18 }}>个人上传种子列表</h3>
kjWei53b79ae2025-06-05 09:18:35 +0000275 <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, minHeight: 60, padding: 12 }}>
276 {userSeeds.length === 0 ? (
277 <div style={{ color: '#b2b2b2', fontSize: 18, textAlign: 'center' }}>(暂无上传种子)</div>
278 ) : (
279 <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
280 {userSeeds.map((seed, idx) => (
281 <li
282 key={seed.seed_id || idx}
283 style={{ display: 'flex', alignItems: 'center', padding: '10px 0', borderBottom: '1px solid #e0e7ff', cursor: 'pointer' }}
284 onClick={e => {
285 // 阻止点击删除按钮时跳转
286 if (e.target.classList.contains('delete-btn')) return;
287 navigate(`/torrent/${seed.seed_id}`);
288 }}
289 >
290 <span style={{ flex: 2, fontWeight: 500, color: '#1a237e', textDecoration: 'underline' }}>{seed.title}</span>
291 <span style={{ flex: 1, color: '#5c6bc0' }}>{seed.tags}</span>
292 <span style={{ flex: 1, color: '#ff9800', textAlign: 'right' }}>人气: {seed.popularity}</span>
293 <button
294 className="delete-btn"
295 style={{ marginLeft: 18, background: '#e53935', color: '#fff', border: 'none', borderRadius: 6, padding: '4px 14px', cursor: 'pointer', fontSize: 14 }}
296 onClick={async (e) => {
297 e.stopPropagation();
298 // const userid = localStorage.getItem("userid");
299 const userid = "550e8400-e29b-41d4-a716-446655440000"; // 示例userid
300 try {
301 const res = await fetch('/api/delete-seed', {
302 method: 'POST',
303 headers: { 'Content-Type': 'application/json' },
304 body: JSON.stringify({ seed_id: seed.seed_id, userid }),
305 });
306 if (res.ok) {
307 setUserSeeds(userSeeds.filter((s, i) => (s.seed_id || i) !== (seed.seed_id || idx)));
308 } else {
309 alert('删除失败,请重试');
310 }
311 } catch (err) {
312 alert('删除失败,请检查网络');
313 }
314 }}
315 >删除</button>
316 </li>
317 ))}
318 </ul>
319 )}
956303669a32fc2c2025-06-02 19:45:53 +0800320 </div>
321 </div>
322 {/* 活跃度模块 */}
323 <div style={{ gridColumn: '2 / 3', gridRow: '2 / 3', background: '#fff', borderRadius: 18, boxShadow: '0 4px 24px #e0e7ff', padding: '20px' }}>
324 <h3 style={{ color: '#1a237e', fontSize: 22, marginBottom: 18 }}>活跃度</h3>
kjWei53b79ae2025-06-05 09:18:35 +0000325 <div style={{ border: '1px dashed #b2b2b2', borderRadius: 12, minHeight: 60, padding: 18, display: 'flex', flexDirection: 'column', gap: 12, fontSize: 18 }}>
326 <div>魔力值:<b style={{ color: '#1976d2' }}>{userStats.magic}</b></div>
327 <div>上传量:<b style={{ color: '#43a047' }}>{userStats.upload} GB</b></div>
328 <div>下载量:<b style={{ color: '#e53935' }}>{userStats.download} GB</b></div>
329 <div>上传/下载值:<b style={{ color: '#ff9800' }}>{userStats.ratio}</b></div>
956303669a32fc2c2025-06-02 19:45:53 +0800330 </div>
331 </div>
332 </div>
333 );
334}