blob: 3204d6ac5945b47252bc3b35d42182dcb0d765f7 [file] [log] [blame]
LaoeGaoci85307e62025-05-30 23:28:42 +08001'use client';
LaoeGaoci85307e62025-05-30 23:28:42 +08002
LaoeGaoci8f6d0db2025-06-03 22:57:04 +08003import React, { useEffect, useState, useRef } from "react";
4import { Card } from 'primereact/card';
5import { Image } from 'primereact/image';
6import { Carousel } from 'primereact/carousel';
7// 页面跳转
8import { useRouter } from 'next/navigation';
9// 消息提醒
10import { Toast } from 'primereact/toast';
11// 分页
12import { Paginator, type PaginatorPageChangeEvent } from 'primereact/paginator';
13// 评分图标
14import { Fire } from '@icon-park/react';
15// 接口传输
16import axios from 'axios';
17// 标签
18import { Tag } from 'primereact/tag';
19
20import { Chart } from 'primereact/chart';
21
22import { TabView, TabPanel } from 'primereact/tabview';
23// 样式
24import './hot-resource.scss';
25
26// 热门资源数据
27
28interface HotResourceSlide {
29 resourceId: number;
30 resourceName: string;
31 resourcePicture: string;
32}
33
34interface HotResourceSlideList {
35 records: HotResourceSlide[];
36}
37interface HotResource {
38 resourceId: number;
39 resourceName: string;
40 resourcePicture: string;
41 resourceSummary: string;
42 lastUpdateTime: string;
43 hot: number;
44 gamePlayList: { gameplayName: string }[];
45}
46interface HotEntry {
47 hot: number;
48}
49interface HotInfo {
50 resourceId: number;
51 resourceName: string;
52 hotList: HotEntry[];
53}
54interface HotInfoResponse {
55 hotInfoList: HotInfo[];
56}
57interface HotResourceList {
58 total: number;
59 records: HotResource[];
60}
61// 主页
62export default function HotResource() {
63 // 热门资源列表
64 const [hotResources, setHotResources] = useState<HotResource[]>([]);
65 const [totalHotResource, setTotalHotResource] = useState(0);
66 const [hotResourceSlide, setHotResourceSlide] = useState<HotResourceSlide[]>([]);
67 // 图表数据
68 const [chartData, setChartData] = useState({});
69 const [chartOptions, setChartOptions] = useState({});
70
71 // 消息提醒
72 const toast = useRef<Toast>(null);
73 const router = useRouter();
74 const [activeTabIndex, setActiveTabIndex] = useState(0);
75 const resourceTabs = [
76 { title: '模组' },
77 { title: '地图' },
78 { title: '整合包' },
79 { title: '材质包' }
80 ];
81 // 当前选中的 classify
82 const [classify, setClassify] = useState<string>(resourceTabs[0].title);
83
84 // 分页
85 const [first, setFirst] = useState(0);
86 const [rows, setRows] = useState(6);
87 const onPageChange = (event: PaginatorPageChangeEvent) => {
88 setFirst(event.first);
89 setRows(event.rows);
90 };
91 // 获取帖子列表
92 useEffect(() => {
93 fetchHotResources(classify);
94 }, [first, rows, classify]);
95
96 useEffect(() => {
97 fetchHotInfo();
98 // 组件加载时获取热门资源幻灯片
99 fetchHotResourcesSlide();
100 }, []);
101
102 const generateColor = (index: number): string => {
103 const colors = [
104 '#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0',
105 '#9966FF', '#FF9F40', '#C9CBCF', '#009688'
106 ];
107 return colors[index % colors.length];
108 };
109
110 const fetchHotInfo = async () => {
111 try {
112 const response = await axios.get<HotInfoResponse>(process.env.PUBLIC_URL + `/resource/hot-info`);
113 const hotInfoList = response.data.hotInfoList;
114
115 // 获取最近七天的日期标签(格式:MM-DD)
116 const labels = Array.from({ length: 7 }, (_, i) => {
117 const date = new Date();
118 date.setDate(date.getDate() - (6 - i)); // 向前推6~0天,按时间顺序排列
119 return `${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
120 });
121
122 const datasets = hotInfoList.map((info, idx) => ({
123 label: info.resourceName,
124 data: info.hotList.map(h => h.hot),
125 fill: false,
126 tension: 0.4,
127 borderColor: generateColor(idx),
128 backgroundColor: generateColor(idx)
129 }));
130
131 setChartData({
132 labels,
133 datasets
134 });
135
136 setChartOptions({
137 responsive: true,
138 plugins: {
139 legend: {
140 position: 'right'
141 },
142 title: {
143 display: true,
144 text: '资源热度变化趋势图'
145 }
146 },
147 scales: {
148 x: {
149 title: {
150 display: true,
151 text: '日期'
152 }
153 },
154 y: {
155 title: {
156 display: true,
157 text: '热度'
158 },
159 beginAtZero: true,
160 min: 4,
161 max: 10,
162 ticks: {
LaoeGaocia8f7d6c2025-06-04 15:39:49 +0800163 stepSize: 2,
LaoeGaoci8f6d0db2025-06-03 22:57:04 +0800164 font: {
165 size: 12
166 }
167 }
168 }
169 }
170 });
171 } catch (err) {
172 console.error('获取资源热度信息失败', err);
173 toast.current?.show({ severity: 'error', summary: 'error', detail: '获取热度信息失败' });
174 }
175 };
176
177 // 获取热门资源幻灯片
178 const fetchHotResourcesSlide = async () => {
179 try {
180 const response = await axios.get<HotResourceSlideList>(process.env.PUBLIC_URL + `/resource/hot/slide`);
181 console.log('获取热门社区幻灯片:', response.data.records);
182 setHotResourceSlide(response.data.records);
183 } catch (err) {
184 console.error('获取Mod失败', err);
185 toast.current?.show({ severity: 'error', summary: 'error', detail: '获取Mod失败' });
186 }
187 };
188 // 获取热门资源
189 const fetchHotResources = async (classify: string) => {
190 try {
191 const pageNumber = first / rows + 1;
192 console.log("当前页" + pageNumber + "size" + rows + "分类" + classify);
193 const response = await axios.get<HotResourceList>(process.env.PUBLIC_URL + `/resource/hot`, {
194 params: { pageNumber, rows, classify }
195 });
196 console.log('获取热门社区:', response.data.records);
197
198 setHotResources(response.data.records);
199 setTotalHotResource(response.data.total);
200 } catch (err) {
201 console.error('获取Mod失败', err);
202 toast.current?.show({ severity: 'error', summary: 'error', detail: '获取Mod失败' });
203 }
204 };
LaoeGaoci85307e62025-05-30 23:28:42 +0800205 return (
LaoeGaoci8f6d0db2025-06-03 22:57:04 +0800206 <div className="HotResource">
207 {/* 轮播图部分 */}
208 <div className="main-header">
209 <div className="carousel-wrapper">
210 <Carousel
211 value={hotResourceSlide}
212 numVisible={1}
213 numScroll={1}
214 showIndicators={false}
215 showNavigators={true}
216 className="custom-carousel"
217 itemTemplate={(hotResource) => (
218 <div className="carousel-item" onClick={() => router.push(`/resource/resource-detail/${hotResource.resourceId}`)}>
219 <Image alt="slide" src={process.env.NEXT_PUBLIC_NGINX_URL + hotResource.resourcePicture} className="carousel-avatar" width="480" height="350" />
220 <h3>{hotResource.resourceName}</h3>
221 </div>
222 )}
223 />
224 </div>
225 <div className="chart-wrapper">
226 <Chart type="line" data={chartData} options={chartOptions} />
227 </div>
228 </div>
229 {/* 全部社区 */}
230 <TabView scrollable className="all-resources" activeIndex={activeTabIndex} onTabChange={(e) => {
231 setActiveTabIndex(e.index);
232 const newClassify = resourceTabs[e.index].title;
233 setClassify(newClassify);
234 }}>
235 {resourceTabs.map((tab) => {
236 return (
237 <TabPanel key={tab.title} header={tab.title}>
238 <div className="all-resources-list">
239 {hotResources.map((hotResource) => (
240 <Card key={hotResource.resourceId} className="all-resources-card" onClick={() => router.push(`/resource/resource-detail/${hotResource.resourceId}`)}>
241 <Image alt="avatar" src={process.env.NEXT_PUBLIC_NGINX_URL + "hotResource/" + hotResource.resourcePicture} className="resource-avatar" width="250" height="140" />
242 <div className="resource-header">
243 <div className="resource-content">
244 <h3>{hotResource.resourceName}</h3>
245 <div className="tags">
246 {hotResource.gamePlayList.map((tag, index) => (
247 <Tag key={index} value={tag.gameplayName} />
248 ))}
249 </div>
250 </div>
251 <div className="resources-states">
252 <div className="state-item">
253 <Fire theme="outline" size="16" fill="#FF8D1A" />
254 <span>热度: {hotResource.hot}</span>
255 </div>
256 <div className="state-item">
257 <span>最新更新时间: {hotResource.lastUpdateTime}</span>
258 </div>
259 </div>
260 </div>
261 </Card>
262 ))}
263 </div>
264 </TabPanel>
265 );
266 })}
267 {totalHotResource > 6 && (<Paginator className="Paginator" first={first} rows={rows} totalRecords={totalHotResource} rowsPerPageOptions={[6, 12]} onPageChange={onPageChange} />)}
268 </TabView>
LaoeGaoci85307e62025-05-30 23:28:42 +0800269 </div>
270 );
LaoeGaoci8f6d0db2025-06-03 22:57:04 +0800271}