blob: 3e634825b7645ace4e62bc07f1be7a2e8ecfa32d [file] [log] [blame]
ym923f0485a52025-06-09 20:18:10 +08001import React, { useState, useEffect } from 'react';
2import {
3 createRequest,
4 deleteRequest,
5 updateMoney,
6 findByUserid,
7 findByName,
8 getTotalMoneyByName,
9 updateLoaduserByName,
10 getAllRequests
11} from '../api/request';
12import { useSetState } from '@mantine/hooks';
13import { useNavigate } from 'react-router-dom';
14import './RequestBoard.css';
15
16const RequestBoard = ({ currentUserId, onBack }) => {
17 const storedUser = localStorage.getItem('user');
18 //let currentUserId = null; // 初始化为 null
19
20 if (storedUser) {
21 try {
22 const parsedUser = JSON.parse(storedUser);
23 currentUserId = parsedUser.userid; // 直接赋值
24 } catch (error) {
25 console.error('解析用户数据失败:', error);
26 // 可以在这里处理 JSON 解析错误(如数据损坏)
27 }
28 } else {
29 console.log('用户未登录');
30 }
31
32 // 现在 currentUserId 可以在后续逻辑中使用
33 console.log('当前用户ID:', currentUserId);
34
35 const [requests, setRequests] = useState([]);
36 const [allRequests, setAllRequests] = useState([]);
37 const [searchedRequests, setSearchedRequests] = useState([]);
38 const [userInfos, setUserInfos] = useState({}); // 存储所有用户信息:{ userid: { username, avatar } }
39 const navigate = useNavigate();
40 const [formData, setFormData] = useState({
41 userid: currentUserId,
42 name: '',
43 plot: '',
44 money: '',
45 year: '',
46 country: '',
47 photo: null,
48 });
49 const [searchName, setSearchName] = useState('');
50 const [totalMoney, setTotalMoney] = useState(null);
51 const [viewMode, setViewMode] = useState('my');
52 const [showForm, setShowForm] = useState(false); // 控制表单折叠
53
54 // 获取用户信息的函数
55 const fetchUserInfo = async (userId) => {
56 try {
57 // 如果已经获取过该用户的信息,直接返回
58 if (userInfos[userId]) return;
59
60 const response = await fetch(`http://localhost:8080/user/getDecoration?userid=${userId}`);
61 if (response.ok) {
62 const data = await response.json();
63 if (data.success) {
64 // 更新用户信息状态
65 setUserInfos(prev => ({
66 ...prev,
67 [userId]: {
68 username: data.data.username,
69 avatar: data.data.image,
70 decoration: data.data.decoration
71 }
72 }));
73 } else {
74 console.error('获取用户信息失败:', data.message);
75 }
76 } else {
77 console.error('获取用户信息失败:', response.statusText);
78 }
79 } catch (error) {
80 console.error('获取用户信息出错:', error);
81 }
82 };
83
84 useEffect(() => {
85 loadUserRequests();
86 loadAllRequests();
87 }, [currentUserId]);
88
89 const loadUserRequests = async () => {
90 const data = await findByUserid(currentUserId);
91 setRequests(data);
92 // 为每个需求贴获取用户信息
93 data.forEach(request => {
94 fetchUserInfo(request.userid); // 获取创建者信息
95 if (request.loaduser) {
96 fetchUserInfo(request.loaduser); // 获取接管者信息
97 }
98 });
99 };
100
101 const loadAllRequests = async () => {
102 const data = await getAllRequests();
103 setAllRequests(data);
104 // 为每个需求贴获取用户信息
105 data.forEach(request => {
106 fetchUserInfo(request.userid); // 获取创建者信息
107 if (request.loaduser) {
108 fetchUserInfo(request.loaduser); // 获取接管者信息
109 }
110 });
111 };
112
113 // 处理搜索的需求贴
114 const processSearchedRequests = (data) => {
115 setSearchedRequests(data);
116 // 为每个需求贴获取用户信息
117 data.forEach(request => {
118 fetchUserInfo(request.userid); // 获取创建者信息
119 if (request.loaduser) {
120 fetchUserInfo(request.loaduser); // 获取接管者信息
121 }
122 });
123 };
124
125 const handleChange = (e) => {
126 const { name, value, files } = e.target;
127 setFormData((prev) => ({
128 ...prev,
129 [name]: files ? files[0] : value,
130 }));
131 };
132
133 const handleCreate = async (e) => {
134 e.preventDefault();
135 const fd = new FormData();
136 Object.entries(formData).forEach(([key, value]) => {
137 if (value !== '' && value !== null) fd.append(key, value);
138 });
139
140 const res = await createRequest(fd);
141 if (res.data === true) {
142 alert('创建成功');
143 setFormData(prev => ({
144 ...prev,
145 name: '',
146 plot: '',
147 money: '',
148 year: '',
149 country: '',
150 photo: null,
151 }));
152 setShowForm(false);
153 loadUserRequests();
154 loadAllRequests();
155 } else {
156 alert('创建失败');
157 }
158 };
159
160 const handleDelete = async (id) => {
161 if (window.confirm('确定要删除这个需求贴吗?')) {
162 await deleteRequest(id);
163 loadUserRequests();
164 loadAllRequests();
165 }
166 };
167
168 const handleUpdateMoney = async (id, newMoney) => {
169 if (!newMoney) return;
170 await updateMoney(id, newMoney);
171 loadUserRequests();
172 };
173
174 const handleSearch = async () => {
175 if (!searchName.trim()) {
176 alert('请输入搜索关键词');
177 return;
178 }
179 const data = await findByName(searchName);
180 const total = await getTotalMoneyByName(searchName);
181 setTotalMoney(total);
182 setViewMode('search');
183 processSearchedRequests(data);
184 };
185
186 const handleProcess = (request) => {
187 navigate(`/uploadfull/${request.requestid}`, {
188 state: {
189 requestid: request.requestid
190 },
191 });
192 };
193
194 const handleUploadLoaduser = async (name, requestid) => {
195 if (window.confirm(`确定要接管 "${name}" 的所有需求贴吗?`)) {
196 try {
197 await updateLoaduserByName(name, currentUserId);
198
199 alert('接管成功');
200 handleProcess({ requestid: requestid });
201 console.log(requestid);
202 loadUserRequests();
203 loadAllRequests();
204 } catch (error) {
205 alert('接管失败,请稍后重试');
206 console.error(error);
207 }
208 }
209 };
210
211 // 渲染用户信息展示
212 const renderUserInfo = (userId, prefix = '') => {
213 if (!userId) return null;
214
215 const userInfo = userInfos[userId];
216
217 return (
218 <div className="request-user-info">
219 <div className="flex items-center gap-2">
220 <div className="request-user-avatar">
221 {userInfo?.avatar ? (
222 <img
223 src={userInfo.avatar}
224 alt={`${prefix}头像`}
225 className="w-8 h-8 rounded-full object-cover border-2 border-orange-200 sm:w-6 sm:h-6"
226 />
227 ) : (
228 <div className="w-8 h-8 bg-gray-100 rounded-full flex items-center justify-center text-gray-500 text-xs border-2 border-orange-200 sm:w-6 sm:h-6">
229 {prefix.charAt(0)}
230 </div>
231 )}
232 </div>
233 <div className="request-user-details">
234 <div className="text-xs sm:text-[11px] font-medium text-gray-600">
235 {prefix}:
236 </div>
237 <div className="text-sm sm:text-xs font-medium text-orange-600 truncate max-w-[100px]">
238 {userInfo?.username || `用户${userId}`}
239 </div>
240 </div>
241 </div>
242 </div>
243 );
244 };
245
246 const renderActions = (request) => {
247 if (request.userid === currentUserId) {
248 return (
249 <div className="flex flex-col sm:flex-row gap-2 w-full">
250 <div className="flex gap-2 flex-1">
251 <input
252 type="number"
253 placeholder="新金额"
254 onChange={(e) => handleUpdateMoney(request.requestid, e.target.value)}
255 className="request-input flex-1 text-center py-1 sm:py-0"
256 />
257 <button
258 onClick={() => handleDelete(request.requestid)}
259 className="request-button request-button-delete"
260 >
261 删除
262 </button>
263 </div>
264 </div>
265 );
266 }
267 return (
268 <button
269 onClick={() => handleUploadLoaduser(request.name, request.requestid)}
270 className="request-button request-button-takeover"
271 >
272 接管任务
273 </button>
274 );
275 };
276
277 return (
278 <div className="request-container">
279 <div className="request-header">
280 <div className="flex flex-col lg:flex-row justify-between gap-4 mb-6">
281 <h1 className="text-2xl font-bold text-orange-700 sm:text-xl">需求贴发布区</h1>
282 <div className="flex flex-wrap gap-2">
283 <button
284 className={`request-tab ${viewMode === 'my' ? 'request-tab-active' : ''}`}
285 onClick={() => setViewMode('my')}
286 >
287 我的需求贴
288 </button>
289 <button
290 className={`request-tab ${viewMode === 'all' ? 'request-tab-active' : ''}`}
291 onClick={() => setViewMode('all')}
292 >
293 所有需求贴
294 </button>
295 <button
296 className="request-button request-button-back"
297 onClick={onBack}
298 >
299 返回主页面
300 </button>
301 <button
302 onClick={() => setShowForm(!showForm)}
303 className="request-button request-button-create"
304 >
305 {showForm ? '收起表单' : '创建新需求贴'}
306 </button>
307 </div>
308 </div>
309
310 {/* 折叠表单 */}
311 {showForm && (
312 <form className="request-form" onSubmit={handleCreate}>
313 <h2 className="text-xl font-semibold text-orange-700 mb-4 sm:text-lg">发布新需求</h2>
314 <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
315 <div>
316 <label className="request-label">需求标题 *</label>
317 <input
318 name="name"
319 placeholder="请输入需求标题"
320 className="request-input"
321 value={formData.name}
322 onChange={handleChange}
323 required
324 />
325 </div>
326 <div>
327 <label className="request-label">所需金额 (¥) *</label>
328 <input
329 name="money"
330 type="number"
331 placeholder="请输入金额"
332 className="request-input"
333 value={formData.money}
334 onChange={handleChange}
335 min="1"
336 required
337 />
338 </div>
339 </div>
340
341 <div className="mt-4">
342 <label className="request-label">详细描述 *</label>
343 <textarea
344 name="plot"
345 placeholder="请详细描述您的需求..."
346 className="request-textarea"
347 value={formData.plot}
348 onChange={handleChange}
349 required
350 />
351 </div>
352
353 <div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-4">
354 <div>
355 <label className="request-label">年份</label>
356 <input
357 name="year"
358 type="number"
359 placeholder="如:2023"
360 className="request-input"
361 value={formData.year}
362 onChange={handleChange}
363 />
364 </div>
365 <div>
366 <label className="request-label">国家/地区</label>
367 <input
368 name="country"
369 placeholder="如:中国"
370 className="request-input"
371 value={formData.country}
372 onChange={handleChange}
373 />
374 </div>
375 <div>
376 <label className="request-label">相关图片</label>
377 <input
378 name="photo"
379 type="file"
380 className="request-file"
381 onChange={handleChange}
382 />
383 </div>
384 </div>
385
386 <button
387 type="submit"
388 className="request-button request-button-submit mt-6"
389 >
390 发布需求贴
391 </button>
392 </form>
393 )}
394 </div>
395
396 {/* 搜索区域 */}
397 <div className="request-search">
398 <h2 className="text-xl font-semibold text-orange-700 mb-4 sm:text-lg">搜索需求贴</h2>
399 <div className="flex flex-col sm:flex-row gap-3">
400 <input
401 type="text"
402 placeholder="输入需求标题关键词..."
403 value={searchName}
404 onChange={(e) => setSearchName(e.target.value)}
405 className="request-input flex-1"
406 />
407 <button
408 onClick={handleSearch}
409 className="request-button request-button-search"
410 >
411 搜索需求
412 </button>
413 </div>
414
415 {totalMoney !== null && viewMode === 'search' && (
416 <div className="request-money-total mt-4">
417 <span className="font-medium">"{searchName}" 需求总金额:</span>
418 <span className="font-bold text-red-600 ml-2">¥{totalMoney}</span>
419 </div>
420 )}
421 </div>
422
423 {/* 搜索结果 */}
424 {viewMode === 'search' && searchedRequests.length > 0 && (
425 <div className="request-results">
426 <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-6 gap-3">
427 <h2 className="text-xl font-semibold text-orange-700 sm:text-lg">
428 搜索结果:<span className="text-orange-500">{searchedRequests.length}</span> 条需求
429 </h2>
430 <button
431 onClick={() => setViewMode('my')}
432 className="request-button request-button-back"
433 >
434 返回我的需求
435 </button>
436 </div>
437
438 <div className="request-cards">
439 {searchedRequests.map((request) => (
440 <div key={request.requestid} className="request-card">
441 <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3">
442 <div className="flex-1 min-w-0">
443 <h3 className="request-card-title">{request.name}</h3>
444 <div className="request-card-money">¥{request.money}</div>
445 </div>
446 <div className="request-user-info-container">
447 {renderUserInfo(request.userid, '创建者')}
448 {request.loaduser && (
449 <>
450 <div className="request-user-divider"></div>
451 {renderUserInfo(request.loaduser, '接管者')}
452 </>
453 )}
454 </div>
455 </div>
456
457 <div className="request-card-content">
458 {request.plot}
459 </div>
460
461 <div className="request-card-meta">
462 {request.year && (
463 <div className="request-meta-item">
464 <span className="request-meta-label">年份:</span>
465 <span>{request.year}</span>
466 </div>
467 )}
468 {request.country && (
469 <div className="request-meta-item">
470 <span className="request-meta-label">地区:</span>
471 <span>{request.country}</span>
472 </div>
473 )}
474 <div className="request-meta-item">
475 <span className="request-meta-label">发布时间:</span>
476 <span>{new Date(request.requestTime).toLocaleString()}</span>
477 </div>
478 </div>
479
480 {request.photo && (
481 <div className="request-card-image-container">
482 <img
483 src={`http://localhost:8080${request.photo}`}
484 alt="需求贴配图"
485 className="request-card-image"
486 />
487 </div>
488 )}
489
490 <div className="request-card-actions">
491 {renderActions(request)}
492 </div>
493 </div>
494 ))}
495 </div>
496 </div>
497 )}
498
499 {/* 我的需求贴 */}
500 {viewMode === 'my' && (
501 <div className="request-my">
502 <h2 className="text-xl font-semibold text-orange-700 mb-6 sm:text-lg">我的需求贴</h2>
503
504 {requests.length === 0 ? (
505 <div className="request-empty">
506 <div className="request-empty-icon">📋</div>
507 <p className="request-empty-text">您还没有发布任何需求贴</p>
508 <button
509 className="request-button request-button-create mt-4"
510 onClick={() => {
511 setShowForm(true);
512 document.querySelector('.request-form')?.scrollIntoView({ behavior: 'smooth' });
513 }}
514 >
515 立即发布需求
516 </button>
517 </div>
518 ) : (
519 <div className="request-cards">
520 {requests.map((request) => (
521 <div key={request.requestid} className="request-card">
522 <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3">
523 <div className="flex-1 min-w-0">
524 <h3 className="request-card-title">{request.name}</h3>
525 <div className="request-card-money">¥{request.money}</div>
526 </div>
527 <div className="request-user-info-container">
528 {renderUserInfo(request.userid, '创建者')}
529 {request.loaduser && (
530 <>
531 <div className="request-user-divider"></div>
532 {renderUserInfo(request.loaduser, '接管者')}
533 </>
534 )}
535 </div>
536 </div>
537
538 <div className="request-card-content">
539 {request.plot}
540 </div>
541
542 <div className="request-card-meta">
543 {request.year && (
544 <div className="request-meta-item">
545 <span className="request-meta-label">年份:</span>
546 <span>{request.year}</span>
547 </div>
548 )}
549 {request.country && (
550 <div className="request-meta-item">
551 <span className="request-meta-label">地区:</span>
552 <span>{request.country}</span>
553 </div>
554 )}
555 <div className="request-meta-item">
556 <span className="request-meta-label">发布时间:</span>
557 <span>{new Date(request.requestTime).toLocaleString()}</span>
558 </div>
559 </div>
560
561 {request.photo && (
562 <div className="request-card-image-container">
563 <img
564 src={`http://localhost:8080${request.photo}`}
565 alt="需求贴配图"
566 className="request-card-image"
567 />
568 </div>
569 )}
570
571 <div className="request-card-actions">
572 {renderActions(request)}
573 </div>
574 </div>
575 ))}
576 </div>
577 )}
578 </div>
579 )}
580
581 {/* 所有需求贴 */}
582 {viewMode === 'all' && (
583 <div className="request-all">
584 <div className="flex justify-between items-center mb-6">
585 <h2 className="text-xl font-semibold text-orange-700 sm:text-lg">所有需求贴</h2>
586 <span className="text-gray-600">共 {allRequests.length} 条需求</span>
587 </div>
588
589 {allRequests.length === 0 ? (
590 <div className="request-empty">
591 <div className="request-empty-icon">📭</div>
592 <p className="request-empty-text">当前平台暂无需求贴</p>
593 </div>
594 ) : (
595 <div className="request-cards">
596 {allRequests.map((request) => (
597 <div key={request.requestid} className="request-card">
598 <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3">
599 <div className="flex-1 min-w-0">
600 <h3 className="request-card-title">{request.name}</h3>
601 <div className="request-card-money">¥{request.money}</div>
602 </div>
603 <div className="request-user-info-container">
604 {renderUserInfo(request.userid, '创建者')}
605 {request.loaduser && (
606 <>
607 <div className="request-user-divider"></div>
608 {renderUserInfo(request.loaduser, '接管者')}
609 </>
610 )}
611 </div>
612 </div>
613
614 <div className="request-card-content">
615 {request.plot}
616 </div>
617
618 <div className="request-card-meta">
619 {request.year && (
620 <div className="request-meta-item">
621 <span className="request-meta-label">年份:</span>
622 <span>{request.year}</span>
623 </div>
624 )}
625 {request.country && (
626 <div className="request-meta-item">
627 <span className="request-meta-label">地区:</span>
628 <span>{request.country}</span>
629 </div>
630 )}
631 <div className="request-meta-item">
632 <span className="request-meta-label">发布时间:</span>
633 <span>{new Date(request.requestTime).toLocaleString()}</span>
634 </div>
635 </div>
636
637 {request.photo && (
638 <div className="request-card-image-container">
639 <img
640 src={`http://localhost:8080${request.photo}`}
641 alt="需求贴配图"
642 className="request-card-image"
643 />
644 </div>
645 )}
646
647 <div className="request-card-actions">
648 {renderActions(request)}
649 </div>
650 </div>
651 ))}
652 </div>
653 )}
654 </div>
655 )}
656 </div>
657 );
658};
659
660export default RequestBoard;