blob: 0bfa18445ce333bd58026a2d9b4d7a8a04c8322a [file] [log] [blame]
LaoeGaoci85307e62025-05-30 23:28:42 +08001'use client';
LaoeGaocid0773912025-06-09 00:38:40 +08002import React, { useEffect, useRef, useState } from 'react';
lfmylbhf223a10f2025-06-08 20:50:31 +08003// import {TabView, TabPanel} from "primereact/tabview";
LaoeGaocid0773912025-06-09 00:38:40 +08004import { Card } from "primereact/card";
5import { Image } from "primereact/image";
6import { Button } from "primereact/button";
lfmylbhf223a10f2025-06-08 20:50:31 +08007// 弹窗
8import { Dialog } from 'primereact/dialog';
LaoeGaocid0773912025-06-09 00:38:40 +08009import { InputText } from "primereact/inputtext";
10import { InputTextarea } from "primereact/inputtextarea";
11import { FileUpload } from "primereact/fileupload";
lfmylbhf223a10f2025-06-08 20:50:31 +080012// 类型转换
LaoeGaocid0773912025-06-09 00:38:40 +080013import { toNumber } from "lodash";
lfmylbhf223a10f2025-06-08 20:50:31 +080014// 消息提醒
LaoeGaocid0773912025-06-09 00:38:40 +080015import { Toast } from 'primereact/toast';
lfmylbhf223a10f2025-06-08 20:50:31 +080016// 接口传输
17import axios from "axios";
18// 页面跳转
LaoeGaocid0773912025-06-09 00:38:40 +080019import { useRouter } from "next/navigation";
lfmylbhf223a10f2025-06-08 20:50:31 +080020// 分页
21import { Paginator, type PaginatorPageChangeEvent } from 'primereact/paginator';
22// 密码
23import { Password } from 'primereact/password';
LaoeGaocid0773912025-06-09 00:38:40 +080024import { useLocalStorage } from '../../../hook/useLocalStorage';
lfmylbhf223a10f2025-06-08 20:50:31 +080025// 样式
26import './resources.scss';
LaoeGaoci85307e62025-05-30 23:28:42 +080027
LaoeGaocid0773912025-06-09 00:38:40 +080028interface User {
29 Id: number;
30}
lfmylbhf223a10f2025-06-08 20:50:31 +080031
32// 用户发布的资源
33interface Resource {
34 resourceId: number;
35 resourceName: string;
36 resourcePicture: string;
37 resourceSummary: string; // 资源简介(一句话)
38 resourceDetail: string; // 资源介绍
39 uploadTime: string; // 上传时间
40 lastUpdateTime: string; // 最近更新时间
41 price: number;
42 downloads: number;
43 likes: number;
44 collections: number;
45 comments: number;
46 seeds: number; // 种子数
47 classify: string; // 资源分类(材质包:resourcePack,模组:mod,整合包:modPack ,地图:map
48}
49
50// 用户发布的资源列表
51interface ResourceList {
52 records: Resource[];
53 total: number; // 总记录数
54 pages: number; // 总页数
55 current: number; // 当前页码
56 sizes: number; // 每页大小
57}
58
59export default function UserManageResources() {
LaoeGaocid0773912025-06-09 00:38:40 +080060 const user = useLocalStorage<User>('user');
61 const userId: number = user?.Id ?? -1;
62
lfmylbhf223a10f2025-06-08 20:50:31 +080063 // 路由
64 const router = useRouter();
65 // 发布资源列表
66 const [resourceList, setResourceList] = useState<Resource[]>([]);
67 // 要删除资源的id
LaoeGaocid0773912025-06-09 00:38:40 +080068 const [deleteResourceId, setDeleteResourceId] = useState<number>(0);
lfmylbhf223a10f2025-06-08 20:50:31 +080069 // 资源封面路径
70 const [resourcePictureUrl, setResourcePictureUrl] = useState<string>('');
71 // 编辑资源的分类
72 const [resourceClassify, setResourceClassify] = useState<string>('');
73 // 消息提醒
74 const toast = useRef<Toast>(null);
75 // 分页
76 const [first, setFirst] = useState(0);
77 const [rows, setRows] = useState(5);
78 const [totalResources, setTotalResources] = useState<number>(0);
79 const onPageChange = (event: PaginatorPageChangeEvent) => {
80 setFirst(event.first);
81 setRows(event.rows);
82 };
83 // 删除资源弹窗
84 const [deleteVisible, setDeleteVisible] = useState(false);
85 const [deleteResourceFormData, setDeleteResourceFormData] = useState({
86 password: '',
87 });
88
89 // 编辑资源弹窗
90 const [editVisible, setEditVisible] = useState(false);
91 const [editResourceFormData, setEditResourceFormData] = useState({
92 resourceId: 0,
93 resourceName: '',
94 resourceSummary: '',
95 resourceDetail: '',
96 price: '',
97 });
98
99 useEffect(() => {
100 fetchResourceList();
101 }, [first, rows]);
102
103
104 // 获取用户发布资源
105 const fetchResourceList = async () => {
106 try {
107 const pageNumber = first / rows + 1;
108 const response = await axios.get<ResourceList>(process.env.PUBLIC_URL + `/user/upload`, {
LaoeGaocid0773912025-06-09 00:38:40 +0800109 params: { userId, pageNumber: pageNumber, rows: rows }
lfmylbhf223a10f2025-06-08 20:50:31 +0800110 });
111
112 console.log('获取发布资源列表:', response.data.records);
113 setResourceList(response.data.records);
114 setTotalResources(response.data.total);
115 } catch (err) {
116 console.error('获取发布资源失败', err);
LaoeGaocid0773912025-06-09 00:38:40 +0800117 toast.current?.show({ severity: 'error', summary: 'error', detail: '获取发布资源失败' });
lfmylbhf223a10f2025-06-08 20:50:31 +0800118 }
119 };
120
121 // 处理删除资源接口
122 const handleDeleteSubmit = async () => {
123 try {
124 const deleteData = {
125 resourceId: deleteResourceId,
LaoeGaocid0773912025-06-09 00:38:40 +0800126 userId,
lfmylbhf223a10f2025-06-08 20:50:31 +0800127 password: deleteResourceFormData.password,
128 };
129 // 发送DELETE请求
130 const response = await axios.delete(process.env.PUBLIC_URL + `/resource`, {
LaoeGaocid0773912025-06-09 00:38:40 +0800131 params: { resourceId: deleteData.resourceId, userId: deleteData.userId, password: deleteData.password },
lfmylbhf223a10f2025-06-08 20:50:31 +0800132 });
LaoeGaocid0773912025-06-09 00:38:40 +0800133 console.log("用户" + userId + "要删除" + deleteData.resourceId + "号资源");
lfmylbhf223a10f2025-06-08 20:50:31 +0800134 console.log(deleteData);
lfmylbhf223a10f2025-06-08 20:50:31 +0800135
136 if (response.status === 204) {
137 console.log("用户成功删除资源");
138 // setIsDeleteResource(true);
LaoeGaocid0773912025-06-09 00:38:40 +0800139 toast.current?.show({ severity: 'success', summary: 'Success', detail: '删除资源成功' });
lfmylbhf223a10f2025-06-08 20:50:31 +0800140 setDeleteVisible(false);
141 // 重置表单
142 setDeleteResourceFormData({
143 password: '',
144 });
145 // 重新拉取资源列表
146 fetchResourceList();
147 } else {
148 console.log('用户密码错误');
LaoeGaocid0773912025-06-09 00:38:40 +0800149 toast.current?.show({ severity: 'error', summary: 'Error', detail: '密码错误,删除资源失败' });
lfmylbhf223a10f2025-06-08 20:50:31 +0800150 }
151
152 } catch (error) {
153 console.error('资源删除失败:', error);
154 toast.current?.show({ severity: 'error', summary: 'error', detail: '密码错误,资源删除失败' });
155 }
156 };
157
158 // 处理编辑资源接口
159 const handleEditSubmit = async () => {
160 try {
161 const postData = {
162 resourceId: editResourceFormData.resourceId,
163 resourceName: editResourceFormData.resourceName,
164 resourcePicture: resourcePictureUrl,
165 resourceSummary: editResourceFormData.resourceSummary,
166 resourceDetail: editResourceFormData.resourceDetail,
167 price: toNumber(editResourceFormData.price),
168 classify: resourceClassify,
169 };
170 // 发送POST请求
171 const response = await axios.put(process.env.PUBLIC_URL + '/resource/info', postData);
172 console.log("编辑资源的信息:", postData);
173
174 if (response.status === 200) {
175 toast.current?.show({ severity: 'success', summary: 'Success', detail: '资源编辑成功' });
176 // 编辑成功
177 setEditVisible(false);
178 fetchResourceList();
179 }
180 } catch (error) {
181 console.error('资源上传失败:', error);
182 toast.current?.show({ severity: 'error', summary: 'error', detail: '资源上传失败' });
183 }
184 };
185
186 return (
187 <div className="resources-container">
188 <Toast ref={toast}></Toast>
189 <div className="resources-title">
190 <h1>我上传的资源</h1>
191 </div>
192
193 <div className="resource-list">
194 {resourceList.map((resourceList) => (
195 <Card key={resourceList.resourceId} className="resources-list-card"
LaoeGaocid0773912025-06-09 00:38:40 +0800196 onClick={() => router.push(`/resource/resource-detail/${resourceList.resourceId}`)}>
lfmylbhf223a10f2025-06-08 20:50:31 +0800197 <Image alt="avatar"
Seamherbb14ecb2025-06-09 22:37:20 +0800198 src={ "resource/" + resourceList.resourcePicture}
LaoeGaocid0773912025-06-09 00:38:40 +0800199 className="resource-avatar" width="250" height="140" />
lfmylbhf223a10f2025-06-08 20:50:31 +0800200 <div className="resource-header">
201 <div className="resource-content">
202 <h3>{resourceList.resourceName}</h3>
203 </div>
204
205 <div className="resource-operation">
206 <Button
207 label="编辑"
208 onClick={(e) => {
209 e.stopPropagation(); // 关键修复:阻止事件冒泡,避免触发Card的点击事件
210 setEditVisible(true);
LaoeGaocid0773912025-06-09 00:38:40 +0800211
lfmylbhf223a10f2025-06-08 20:50:31 +0800212 setResourceClassify(resourceList.classify);
213 setEditResourceFormData({
214 resourceId: resourceList.resourceId,
215 resourceName: resourceList.resourceName,
216 resourceSummary: resourceList.resourceSummary,
217 resourceDetail: resourceList.resourceDetail,
218 price: resourceList.price.toString(),
219 });
220 console.log("用户编辑资源");
221 }}
222 />
223 <Button
224 label="删除"
225 onClick={(e) => {
226 e.stopPropagation(); // 关键修复:阻止事件冒泡,避免触发Card的点击事件
227 setDeleteResourceId(resourceList.resourceId);
228 setDeleteVisible(true);
229 }}
LaoeGaocid0773912025-06-09 00:38:40 +0800230 style={{ backgroundColor: "rgba(255, 87, 51, 1)" }}
lfmylbhf223a10f2025-06-08 20:50:31 +0800231 />
232 </div>
233 </div>
234 </Card>
235 ))}
236 {totalResources > 5 && <Paginator
237 className="Paginator"
238 first={first}
239 rows={rows}
240 totalRecords={totalResources}
241 rowsPerPageOptions={[5, 10]}
242 onPageChange={onPageChange}
243 />}
244 </div>
245
246 {/*删除资源时,获取用户密码弹窗*/}
247 <Dialog
248 header="删除资源"
249 visible={deleteVisible}
250 onHide={() => setDeleteVisible(false)}
251 className="resource-delete-dialog"
252 modal
253 footer={
254 <div className="dialog-footer">
255 <Button label="确认" icon="pi pi-check" onClick={handleDeleteSubmit} autoFocus />
256 <Button label="取消" icon="pi pi-times" onClick={() => setDeleteVisible(false)} className="p-button-text" />
257 </div>
258 }
259 >
260 <div className="dialog-form">
261 <div className="form-field">
262 <div className="form-field-header">
263 <label htmlFor="name">密码</label>
264 </div>
265 <Password
266 id="passwrod"
267 value={deleteResourceFormData.password}
268 onChange={(e) => setDeleteResourceFormData(prev => ({
269 ...prev,
270 password: e.target.value
271 }))}
272 placeholder="请输入密码"
273 className="w-full"
274 toggleMask
275 />
276 </div>
277 </div>
278 </Dialog>
279
280 {/*编辑资源弹窗*/}
281 <Dialog
282 header="编辑资源"
283 visible={editVisible}
284 onHide={() => setEditVisible(false)}
285 className="resource-edit-dialog"
286 modal
287 footer={
288 <div className="dialog-footer">
289 <Button label="确认" icon="pi pi-check" onClick={handleEditSubmit} autoFocus />
290 <Button label="取消" icon="pi pi-times" onClick={() => setEditVisible(false)} className="p-button-text" />
291 </div>
292 }
293 >
294 <div className="dialog-form">
295 <div className="form-field">
296 <div className="form-field-header">
297 <label htmlFor="name">资源名称</label>
298 </div>
299 <InputText
300 id="name"
301 value={editResourceFormData.resourceName}
302 onChange={(e) => setEditResourceFormData(prev => ({
303 ...prev,
LaoeGaocid0773912025-06-09 00:38:40 +0800304 resourceName: e.target.value
lfmylbhf223a10f2025-06-08 20:50:31 +0800305 }))}
306 className="w-full"
307 />
308 </div>
309 <div className="form-field">
310 <div className="form-field-header">
311 <label htmlFor="summary">资源简介</label>
312 </div>
313 <InputText
314 id="summary"
315 value={editResourceFormData.resourceSummary}
316 onChange={(e) => setEditResourceFormData(prev => ({
317 ...prev,
LaoeGaocid0773912025-06-09 00:38:40 +0800318 resourceSummary: e.target.value
lfmylbhf223a10f2025-06-08 20:50:31 +0800319 }))}
320 className="w-full"
321 />
322 </div>
323 <div className="form-field">
324 <div className="form-field-header">
325 <label htmlFor="detail">资源介绍</label>
326 </div>
327 <InputTextarea
328 id="detail"
329 value={editResourceFormData.resourceDetail}
330 onChange={(e) => setEditResourceFormData(prev => ({
331 ...prev,
LaoeGaocid0773912025-06-09 00:38:40 +0800332 resourceDetail: e.target.value
lfmylbhf223a10f2025-06-08 20:50:31 +0800333 }))}
334 rows={5}
335 className="w-full"
336 />
337 </div>
338 <div className="form-field">
339 <div className="form-field-header">
340 <label htmlFor="price">价格</label>
341 </div>
342 <InputText
343 id="price"
344 value={editResourceFormData?.price.toString()}
345 onChange={(e) => setEditResourceFormData(prev => ({
346 ...prev,
LaoeGaocid0773912025-06-09 00:38:40 +0800347 price: e.target.value
lfmylbhf223a10f2025-06-08 20:50:31 +0800348 }))}
349 className="w-full"
350 />
351 </div>
352 <div className="form-field">
353 <div className="form-field-header">
354 <span className="form-field-sign">*</span>
355 <label>封面图片</label>
356 </div>
357
358 <FileUpload
359 mode="advanced"
360 name="resource-image"
361 customUpload
362
363 uploadHandler={async (e) => {
364 const formData = new FormData();
365 formData.append("file", e.files[0]);
366
367 try {
368 const res = await axios.post(`${process.env.PUBLIC_URL}/file`, formData);
369
370 const fileUrl = res.data.url;
371 console.log(fileUrl);
372 setResourcePictureUrl(fileUrl);
373 toast.current?.show({ severity: 'success', summary: '上传成功' });
374 } catch (error) {
375 console.log(error);
376 toast.current?.show({ severity: 'error', summary: '上传失败' });
377 }
378 }}
379 auto
380 accept="image/*"
381 chooseLabel="选择资源封面"
382 />
383 </div>
384 </div>
385 </Dialog>
386 </div>
387 )
LaoeGaoci85307e62025-05-30 23:28:42 +0800388};