blob: 0af92a4370d6512dc8797fde7b28d542637374ac [file] [log] [blame]
lfmylbhf789e7172025-06-06 18:37:21 +08001'use client';
2
3import React, { useEffect, useState, useRef } from 'react';
4import { TabView, TabPanel } from 'primereact/tabview';
5import { Avatar } from 'primereact/avatar';
6import { Button } from 'primereact/button';
7import { Card } from 'primereact/card';
8import {Image} from "primereact/image";
9// 浮动按钮
10import { SpeedDial } from 'primereact/speeddial';
11// 评分图标
12import { Fire } from '@icon-park/react';
13// 消息提醒
14import { Toast } from 'primereact/toast';
15// 页面跳转
16import { useRouter } from "next/navigation";
17// import Link from 'next/link';
18
19// 接口传输
20import axios from "axios";
21
22// 样式
23import './user.scss';
24
25
26
27// 用户信息
28interface UserInfo {
29 userId: number;
30 username: string;
31 password: string;
32 avatar: string;
33 followerCount: number;// 粉丝数
34 subscriberCount: number;// 关注数
35 signature: string;// 个性签名
36 uploadAmount: number;
37 purchaseAmount: number;
38 credits: number;
39}
40
41// 用户数据
42interface UserData {
43 subscriberCount: number; // 关注数
44 uploadAmount: number; // 上传量(资源个数)
45 beDownloadedAmount: number; // 上传资源被下载量
46 seedPercentageList: number[]; // 上传资源类型百分比列表,按材质包、模组、整合包、地图的顺序返回
47}
48
49// 用户发布的资源
50interface Resource {
51 resourceId: number;
52 resourceName: string;
53 resourcePicture: string;
54 resourceSummary: string; // 资源简介(一句话)
55 resourceDetail: string; // 资源介绍
56 uploadTime: string; // 上传时间
57 lastUpdateTime: string; // 最近更新时间
58 price: number;
59 downloads: number;
60 likes: number;
61 collections: number;
62 comments: number;
63 seeds: number; // 种子数
64 classify: string; // 资源分类(材质包:resourcePack,模组:mod,整合包:modPack ,地图:map
65}
66
67// 用户发布的资源列表
68interface ResourceList {
69 records: Resource[];
70}
71
72export default function UserPage() {
73 // 路由
74 const router = useRouter();
75 // 发布资源列表
76 const [resourceList, setResourceList] = useState<Resource[]>([]);
77 // 用户信息
78 const [userInfo, setUserInfo] = useState<UserInfo>();
79 // 用户数据
80 const [userData, setUserData] = useState<UserData>();
81 // 消息提醒
82 const toast = useRef<Toast>(null);
83
84
85 useEffect(() => {
86 fetchUserInfo();
87 fetchUserData();
88 fetchResourceList();
89 }, []);
90
91 // 获取用户信息
92 const fetchUserInfo = async () => {
93 try {
94 const response = await axios.get<UserInfo>(process.env.PUBLIC_URL + `/user/info`, {
95 params: { userId: 22301010 }
96 });
97 console.log('获取用户信息:', response.data);
98 setUserInfo(response.data);
99 } catch (err) {
100 console.error('获取用户信息失败', err);
101 toast.current?.show({ severity: 'error', summary: 'error', detail: '获取用户信息失败' });
102 }
103 };
104
105 // 获取用户数据
106 const fetchUserData = async () => {
107 try {
108 const response = await axios.get<UserData>(process.env.PUBLIC_URL + `/user/data`, {
109 params: { userId: 22301010 }
110 });
111 console.log('获取用户数据:', response.data);
112 setUserData(response.data);
113 } catch (err) {
114 console.error('获取用户数据失败', err);
115 toast.current?.show({ severity: 'error', summary: 'error', detail: '获取用户数据失败' });
116 }
117 };
118
119 // 格式化数字显示 (3000 -> 3k)
120 const formatCount = (count?: number): string => {
121 if (count == null) return "0"; // 同时处理 undefined/null
122
123 const absCount = Math.abs(count); // 处理负数
124
125 const format = (num: number, suffix: string) => {
126 const fixed = num.toFixed(1);
127 return fixed.endsWith('.0')
128 ? `${Math.floor(num)}${suffix}`
129 : `${fixed}${suffix}`;
130 };
131
132 if (absCount >= 1e6) return format(count / 1e6, "m");
133 if (absCount >= 1e3) return format(count / 1e3, "k");
134 return count.toString();
135 };
136
137 // 获取发布资源
138 const fetchResourceList = async () => {
139 try {
140 const response = await axios.get<ResourceList>(process.env.PUBLIC_URL +`/user/upload`, {
141 params: { userId: 22301010, pageNumber: 1, rows: 3 }
142 });
143 console.log('获取发布资源列表:', response.data.records);
144 setResourceList(response.data.records);
145
146 // const imgUrl = `${process.env.NEXT_PUBLIC_NGINX_URL}/${resourceList[0].resourcePicture}`;
147 // console.log("Image URL:", imgUrl);
148 } catch (err) {
149 console.error('获取发布资源失败', err);
150 toast.current?.show({ severity: 'error', summary: 'error', detail: '获取发布资源失败' });
151 }
152 };
153
154 // 浮动按钮的子模块
155 const actions = [
156 {
157 template: () => (
158 <Button label="管理资源" onClick={() => router.push(`/user/manage/resources/`)}/>
159 )
160 },
161 {
162 template: () => (
163 <Button label="已购资源" onClick={() => router.push(`/user/purchased-resources/`)}/>
164 )
165 },
166 {
167 template: () => (
168 <Button label="发布资源" onClick={() => router.push(`/user/manage/resources/`)}/>
169 )
170 },
171 {
172 template: () => (
173 <Button label="编辑悬赏" onClick={() => router.push(`/user/manage/resources/`)}/>
174 )
175 }
176 ];
177
178
179
180
181 return (
182 <div className="user-container">
183
184 {/*个人信息*/}
185 <div className="user-profile-card">
186 <Avatar
187 image={`${process.env.NEXT_PUBLIC_NGINX_URL}/users/${userInfo?.avatar}`}
188 className="user-avatar"
189 shape="circle"
190 />
191 <div className="user-info">
192 <div className="user-detail-info">
193 <div className="name-container">
194 <h2 className="name">{userInfo?.username}</h2>
195 <span className="signature">{userInfo?.signature}</span>
196 </div>
197
198 <div className="stats-container">
199 <div className="stats">
200 <span className="stats-label">粉丝:</span>
201 <span className="stats-value">{userInfo?.followerCount}</span>
202 </div>
203 <div className="stats">
204 <span className="stats-label">累计上传量:</span>
205 <span className="stats-value">{formatCount(userData?.uploadAmount)}</span>
206 </div>
207 <div className="stats">
208 <span className="stats-label">关注:</span>
209 <span className="stats-value">{userInfo?.subscriberCount}</span>
210 </div>
211 <div className="stats">
212 <span className="stats-label">累计被下载量:</span>
213 <span className="stats-value">{formatCount(userData?.beDownloadedAmount)}</span>
214 </div>
215 </div>
216 </div>
217
218 <Button label="关注" className="action-button"/>
219 </div>
220 </div>
221
222 {/*个人内容*/}
223 <TabView>
224 <TabPanel header="主页">
225 {/*推荐资源*/}
226 <div className="homepage-item">
227 <div className="section-header">
228 <h1>推荐资源</h1>
229 <Button
230 label="显示更多"
231 link
232 onClick={() => router.push('/resource/recommend/模组')}
233 />
234 </div>
235 <div className="resource-grid">
236 {/*{mods.map((mod) => (*/}
237 {/* <Card key={mod.resourceId} className="resource-card" onClick={() => router.push(`/resource/resource-detail/${mod.resourceId}`)}>*/}
238 {/* <Image*/}
239 {/* src={process.env.NEXT_PUBLIC_NGINX_URL + mod.resourcePicture}*/}
240 {/* alt={mod.resourceName}*/}
241 {/* width="368"*/}
242 {/* height="200"*/}
243 {/* />*/}
244 {/* <div className="card-content">*/}
245 {/* <h3>{mod.resourceName}</h3>*/}
246 {/* <div className="view-count">*/}
247 {/* <Fire theme="outline" size="16" fill="#FF8D1A" />*/}
248 {/* <span>{mod.likes}</span>*/}
249 {/* </div>*/}
250 {/* </div>*/}
251 {/* </Card>*/}
252 {/*))}*/}
253 </div>
254 </div>
255
256 {/*发布资源*/}
257 <div className="homepage-item">
258 <div className="section-header">
259 <h1>发布资源</h1>
260 <Button
261 label="显示更多"
262 link
263 onClick={() => router.push('/user/manage/resources/')}
264 />
265 </div>
266 <div className="resource-grid">
267 {resourceList.map((resourceList) => (
268 <Card key={resourceList.resourceId} className="resource-card" onClick={() => router.push(`/resource/resource-detail/${resourceList.resourceId}`)}>
269 <Image
270 src={process.env.NEXT_PUBLIC_NGINX_URL + resourceList.resourcePicture}
271 alt={resourceList.resourceName}
272 width="368"
273 height="200"
274 />
275 <div className="card-content">
276 <h3>{resourceList.resourceName}</h3>
277 <div className="view-count">
278 <Fire theme="outline" size="16" fill="#FF8D1A" />
279 <span>{resourceList.likes}</span>
280 </div>
281 </div>
282 </Card>
283 ))}
284 </div>
285 </div>
286
287 {/*发布帖子*/}
288 <div className="homepage-item">
289 <div className="section-header">
290 <h1>发布帖子</h1>
291 <Button
292 label="显示更多"
293 link
294 onClick={() => router.push('/resource/recommend/模组')}
295 />
296 </div>
297 <div className="resource-grid">
298
299 {/*{mods.map((mod) => (*/}
300 {/* <Card key={mod.resourceId} className="resource-card" onClick={() => router.push(`/resource/resource-detail/${mod.resourceId}`)}>*/}
301 {/* <Image*/}
302 {/* src={process.env.NEXT_PUBLIC_NGINX_URL + mod.resourcePicture}*/}
303 {/* alt={mod.resourceName}*/}
304 {/* width="368"*/}
305 {/* height="200"*/}
306 {/* />*/}
307 {/* <div className="card-content">*/}
308 {/* <h3>{mod.resourceName}</h3>*/}
309 {/* <div className="view-count">*/}
310 {/* <Fire theme="outline" size="16" fill="#FF8D1A" />*/}
311 {/* <span>{mod.likes}</span>*/}
312 {/* </div>*/}
313 {/* </div>*/}
314 {/* </Card>*/}
315 {/*))}*/}
316 </div>
317 </div>
318 </TabPanel>
319
320 <TabPanel header="发布">
321
322 </TabPanel>
323
324 <TabPanel header="帖子">
325
326 </TabPanel>
327
328 <TabPanel header="收藏">
329
330 </TabPanel>
331
332 <TabPanel header="数据">
333
334 </TabPanel>
335
336 <TabPanel header="悬赏">
337
338 </TabPanel>
339 </TabView>
340
341 {/*浮动按钮*/}
342 <div className="card">
343 <SpeedDial
344 model={actions}
345 direction="up"
346 style={{ position: 'fixed', bottom: '2rem', right: '2rem' }}
347 showIcon="pi pi-plus"
348 hideIcon="pi pi-times"
349 buttonClassName="custom-speeddial-button"
350 />
351 </div>
352
353 </div>
354 );
355};
356