| import React, { useState, useEffect } from 'react'; |
| import { |
| createRequest, |
| deleteRequest, |
| updateMoney, |
| findByUserid, |
| findByName, |
| getTotalMoneyByName, |
| updateLoaduserByName, |
| getAllRequests |
| } from '../api/request'; |
| import { useSetState } from '@mantine/hooks'; |
| import { useNavigate } from 'react-router-dom'; |
| import './RequestBoard.css'; |
| |
| const RequestBoard = ({ currentUserId, onBack }) => { |
| const storedUser = localStorage.getItem('user'); |
| //let currentUserId = null; // 初始化为 null |
| |
| if (storedUser) { |
| try { |
| const parsedUser = JSON.parse(storedUser); |
| currentUserId = parsedUser.userid; // 直接赋值 |
| } catch (error) { |
| console.error('解析用户数据失败:', error); |
| // 可以在这里处理 JSON 解析错误(如数据损坏) |
| } |
| } else { |
| console.log('用户未登录'); |
| } |
| |
| // 现在 currentUserId 可以在后续逻辑中使用 |
| console.log('当前用户ID:', currentUserId); |
| |
| const [requests, setRequests] = useState([]); |
| const [allRequests, setAllRequests] = useState([]); |
| const [searchedRequests, setSearchedRequests] = useState([]); |
| const [userInfos, setUserInfos] = useState({}); // 存储所有用户信息:{ userid: { username, avatar } } |
| const navigate = useNavigate(); |
| const [formData, setFormData] = useState({ |
| userid: currentUserId, |
| name: '', |
| plot: '', |
| money: '', |
| year: '', |
| country: '', |
| photo: null, |
| }); |
| const [searchName, setSearchName] = useState(''); |
| const [totalMoney, setTotalMoney] = useState(null); |
| const [viewMode, setViewMode] = useState('my'); |
| const [showForm, setShowForm] = useState(false); // 控制表单折叠 |
| |
| // 获取用户信息的函数 |
| const fetchUserInfo = async (userId) => { |
| try { |
| // 如果已经获取过该用户的信息,直接返回 |
| if (userInfos[userId]) return; |
| |
| const response = await fetch(`http://localhost:8080/user/getDecoration?userid=${userId}`); |
| if (response.ok) { |
| const data = await response.json(); |
| if (data.success) { |
| // 更新用户信息状态 |
| setUserInfos(prev => ({ |
| ...prev, |
| [userId]: { |
| username: data.data.username, |
| avatar: data.data.image, |
| decoration: data.data.decoration |
| } |
| })); |
| } else { |
| console.error('获取用户信息失败:', data.message); |
| } |
| } else { |
| console.error('获取用户信息失败:', response.statusText); |
| } |
| } catch (error) { |
| console.error('获取用户信息出错:', error); |
| } |
| }; |
| |
| useEffect(() => { |
| loadUserRequests(); |
| loadAllRequests(); |
| }, [currentUserId]); |
| |
| const loadUserRequests = async () => { |
| const data = await findByUserid(currentUserId); |
| setRequests(data); |
| // 为每个需求贴获取用户信息 |
| data.forEach(request => { |
| fetchUserInfo(request.userid); // 获取创建者信息 |
| if (request.loaduser) { |
| fetchUserInfo(request.loaduser); // 获取接管者信息 |
| } |
| }); |
| }; |
| |
| const loadAllRequests = async () => { |
| const data = await getAllRequests(); |
| setAllRequests(data); |
| // 为每个需求贴获取用户信息 |
| data.forEach(request => { |
| fetchUserInfo(request.userid); // 获取创建者信息 |
| if (request.loaduser) { |
| fetchUserInfo(request.loaduser); // 获取接管者信息 |
| } |
| }); |
| }; |
| |
| // 处理搜索的需求贴 |
| const processSearchedRequests = (data) => { |
| setSearchedRequests(data); |
| // 为每个需求贴获取用户信息 |
| data.forEach(request => { |
| fetchUserInfo(request.userid); // 获取创建者信息 |
| if (request.loaduser) { |
| fetchUserInfo(request.loaduser); // 获取接管者信息 |
| } |
| }); |
| }; |
| |
| const handleChange = (e) => { |
| const { name, value, files } = e.target; |
| setFormData((prev) => ({ |
| ...prev, |
| [name]: files ? files[0] : value, |
| })); |
| }; |
| |
| const handleCreate = async (e) => { |
| e.preventDefault(); |
| const fd = new FormData(); |
| Object.entries(formData).forEach(([key, value]) => { |
| if (value !== '' && value !== null) fd.append(key, value); |
| }); |
| |
| const res = await createRequest(fd); |
| if (res.data === true) { |
| alert('创建成功'); |
| setFormData(prev => ({ |
| ...prev, |
| name: '', |
| plot: '', |
| money: '', |
| year: '', |
| country: '', |
| photo: null, |
| })); |
| setShowForm(false); |
| loadUserRequests(); |
| loadAllRequests(); |
| } else { |
| alert('创建失败'); |
| } |
| }; |
| |
| const handleDelete = async (id) => { |
| if (window.confirm('确定要删除这个需求贴吗?')) { |
| await deleteRequest(id); |
| loadUserRequests(); |
| loadAllRequests(); |
| } |
| }; |
| |
| const handleUpdateMoney = async (id, newMoney) => { |
| if (!newMoney) return; |
| await updateMoney(id, newMoney); |
| loadUserRequests(); |
| }; |
| |
| const handleSearch = async () => { |
| if (!searchName.trim()) { |
| alert('请输入搜索关键词'); |
| return; |
| } |
| const data = await findByName(searchName); |
| const total = await getTotalMoneyByName(searchName); |
| setTotalMoney(total); |
| setViewMode('search'); |
| processSearchedRequests(data); |
| }; |
| |
| const handleProcess = (request) => { |
| navigate(`/uploadfull/${request.requestid}`, { |
| state: { |
| requestid: request.requestid |
| }, |
| }); |
| }; |
| |
| const handleUploadLoaduser = async (name, requestid) => { |
| if (window.confirm(`确定要接管 "${name}" 的所有需求贴吗?`)) { |
| try { |
| await updateLoaduserByName(name, currentUserId); |
| |
| alert('接管成功'); |
| handleProcess({ requestid: requestid }); |
| console.log(requestid); |
| loadUserRequests(); |
| loadAllRequests(); |
| } catch (error) { |
| alert('接管失败,请稍后重试'); |
| console.error(error); |
| } |
| } |
| }; |
| |
| // 渲染用户信息展示 |
| const renderUserInfo = (userId, prefix = '') => { |
| if (!userId) return null; |
| |
| const userInfo = userInfos[userId]; |
| |
| return ( |
| <div className="request-user-info"> |
| <div className="flex items-center gap-2"> |
| <div className="request-user-avatar"> |
| {userInfo?.avatar ? ( |
| <img |
| src={userInfo.avatar} |
| alt={`${prefix}头像`} |
| className="w-8 h-8 rounded-full object-cover border-2 border-orange-200 sm:w-6 sm:h-6" |
| /> |
| ) : ( |
| <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"> |
| {prefix.charAt(0)} |
| </div> |
| )} |
| </div> |
| <div className="request-user-details"> |
| <div className="text-xs sm:text-[11px] font-medium text-gray-600"> |
| {prefix}: |
| </div> |
| <div className="text-sm sm:text-xs font-medium text-orange-600 truncate max-w-[100px]"> |
| {userInfo?.username || `用户${userId}`} |
| </div> |
| </div> |
| </div> |
| </div> |
| ); |
| }; |
| |
| const renderActions = (request) => { |
| if (request.userid === currentUserId) { |
| return ( |
| <div className="flex flex-col sm:flex-row gap-2 w-full"> |
| <div className="flex gap-2 flex-1"> |
| <input |
| type="number" |
| placeholder="新金额" |
| onChange={(e) => handleUpdateMoney(request.requestid, e.target.value)} |
| className="request-input flex-1 text-center py-1 sm:py-0" |
| /> |
| <button |
| onClick={() => handleDelete(request.requestid)} |
| className="request-button request-button-delete" |
| > |
| 删除 |
| </button> |
| </div> |
| </div> |
| ); |
| } |
| return ( |
| <button |
| onClick={() => handleUploadLoaduser(request.name, request.requestid)} |
| className="request-button request-button-takeover" |
| > |
| 接管任务 |
| </button> |
| ); |
| }; |
| |
| return ( |
| <div className="request-container"> |
| <div className="request-header"> |
| <div className="flex flex-col lg:flex-row justify-between gap-4 mb-6"> |
| <h1 className="text-2xl font-bold text-orange-700 sm:text-xl">需求贴发布区</h1> |
| <div className="flex flex-wrap gap-2"> |
| <button |
| className={`request-tab ${viewMode === 'my' ? 'request-tab-active' : ''}`} |
| onClick={() => setViewMode('my')} |
| > |
| 我的需求贴 |
| </button> |
| <button |
| className={`request-tab ${viewMode === 'all' ? 'request-tab-active' : ''}`} |
| onClick={() => setViewMode('all')} |
| > |
| 所有需求贴 |
| </button> |
| <button |
| className="request-button request-button-back" |
| onClick={onBack} |
| > |
| 返回主页面 |
| </button> |
| <button |
| onClick={() => setShowForm(!showForm)} |
| className="request-button request-button-create" |
| > |
| {showForm ? '收起表单' : '创建新需求贴'} |
| </button> |
| </div> |
| </div> |
| |
| {/* 折叠表单 */} |
| {showForm && ( |
| <form className="request-form" onSubmit={handleCreate}> |
| <h2 className="text-xl font-semibold text-orange-700 mb-4 sm:text-lg">发布新需求</h2> |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> |
| <div> |
| <label className="request-label">需求标题 *</label> |
| <input |
| name="name" |
| placeholder="请输入需求标题" |
| className="request-input" |
| value={formData.name} |
| onChange={handleChange} |
| required |
| /> |
| </div> |
| <div> |
| <label className="request-label">所需金额 (¥) *</label> |
| <input |
| name="money" |
| type="number" |
| placeholder="请输入金额" |
| className="request-input" |
| value={formData.money} |
| onChange={handleChange} |
| min="1" |
| required |
| /> |
| </div> |
| </div> |
| |
| <div className="mt-4"> |
| <label className="request-label">详细描述 *</label> |
| <textarea |
| name="plot" |
| placeholder="请详细描述您的需求..." |
| className="request-textarea" |
| value={formData.plot} |
| onChange={handleChange} |
| required |
| /> |
| </div> |
| |
| <div className="grid grid-cols-1 md:grid-cols-3 gap-4 mt-4"> |
| <div> |
| <label className="request-label">年份</label> |
| <input |
| name="year" |
| type="number" |
| placeholder="如:2023" |
| className="request-input" |
| value={formData.year} |
| onChange={handleChange} |
| /> |
| </div> |
| <div> |
| <label className="request-label">国家/地区</label> |
| <input |
| name="country" |
| placeholder="如:中国" |
| className="request-input" |
| value={formData.country} |
| onChange={handleChange} |
| /> |
| </div> |
| <div> |
| <label className="request-label">相关图片</label> |
| <input |
| name="photo" |
| type="file" |
| className="request-file" |
| onChange={handleChange} |
| /> |
| </div> |
| </div> |
| |
| <button |
| type="submit" |
| className="request-button request-button-submit mt-6" |
| > |
| 发布需求贴 |
| </button> |
| </form> |
| )} |
| </div> |
| |
| {/* 搜索区域 */} |
| <div className="request-search"> |
| <h2 className="text-xl font-semibold text-orange-700 mb-4 sm:text-lg">搜索需求贴</h2> |
| <div className="flex flex-col sm:flex-row gap-3"> |
| <input |
| type="text" |
| placeholder="输入需求标题关键词..." |
| value={searchName} |
| onChange={(e) => setSearchName(e.target.value)} |
| className="request-input flex-1" |
| /> |
| <button |
| onClick={handleSearch} |
| className="request-button request-button-search" |
| > |
| 搜索需求 |
| </button> |
| </div> |
| |
| {totalMoney !== null && viewMode === 'search' && ( |
| <div className="request-money-total mt-4"> |
| <span className="font-medium">"{searchName}" 需求总金额:</span> |
| <span className="font-bold text-red-600 ml-2">¥{totalMoney}</span> |
| </div> |
| )} |
| </div> |
| |
| {/* 搜索结果 */} |
| {viewMode === 'search' && searchedRequests.length > 0 && ( |
| <div className="request-results"> |
| <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-6 gap-3"> |
| <h2 className="text-xl font-semibold text-orange-700 sm:text-lg"> |
| 搜索结果:<span className="text-orange-500">{searchedRequests.length}</span> 条需求 |
| </h2> |
| <button |
| onClick={() => setViewMode('my')} |
| className="request-button request-button-back" |
| > |
| 返回我的需求 |
| </button> |
| </div> |
| |
| <div className="request-cards"> |
| {searchedRequests.map((request) => ( |
| <div key={request.requestid} className="request-card"> |
| <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3"> |
| <div className="flex-1 min-w-0"> |
| <h3 className="request-card-title">{request.name}</h3> |
| <div className="request-card-money">¥{request.money}</div> |
| </div> |
| <div className="request-user-info-container"> |
| {renderUserInfo(request.userid, '创建者')} |
| {request.loaduser && ( |
| <> |
| <div className="request-user-divider"></div> |
| {renderUserInfo(request.loaduser, '接管者')} |
| </> |
| )} |
| </div> |
| </div> |
| |
| <div className="request-card-content"> |
| {request.plot} |
| </div> |
| |
| <div className="request-card-meta"> |
| {request.year && ( |
| <div className="request-meta-item"> |
| <span className="request-meta-label">年份:</span> |
| <span>{request.year}</span> |
| </div> |
| )} |
| {request.country && ( |
| <div className="request-meta-item"> |
| <span className="request-meta-label">地区:</span> |
| <span>{request.country}</span> |
| </div> |
| )} |
| <div className="request-meta-item"> |
| <span className="request-meta-label">发布时间:</span> |
| <span>{new Date(request.requestTime).toLocaleString()}</span> |
| </div> |
| </div> |
| |
| {request.photo && ( |
| <div className="request-card-image-container"> |
| <img |
| src={`http://localhost:8080${request.photo}`} |
| alt="需求贴配图" |
| className="request-card-image" |
| /> |
| </div> |
| )} |
| |
| <div className="request-card-actions"> |
| {renderActions(request)} |
| </div> |
| </div> |
| ))} |
| </div> |
| </div> |
| )} |
| |
| {/* 我的需求贴 */} |
| {viewMode === 'my' && ( |
| <div className="request-my"> |
| <h2 className="text-xl font-semibold text-orange-700 mb-6 sm:text-lg">我的需求贴</h2> |
| |
| {requests.length === 0 ? ( |
| <div className="request-empty"> |
| <div className="request-empty-icon">📋</div> |
| <p className="request-empty-text">您还没有发布任何需求贴</p> |
| <button |
| className="request-button request-button-create mt-4" |
| onClick={() => { |
| setShowForm(true); |
| document.querySelector('.request-form')?.scrollIntoView({ behavior: 'smooth' }); |
| }} |
| > |
| 立即发布需求 |
| </button> |
| </div> |
| ) : ( |
| <div className="request-cards"> |
| {requests.map((request) => ( |
| <div key={request.requestid} className="request-card"> |
| <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3"> |
| <div className="flex-1 min-w-0"> |
| <h3 className="request-card-title">{request.name}</h3> |
| <div className="request-card-money">¥{request.money}</div> |
| </div> |
| <div className="request-user-info-container"> |
| {renderUserInfo(request.userid, '创建者')} |
| {request.loaduser && ( |
| <> |
| <div className="request-user-divider"></div> |
| {renderUserInfo(request.loaduser, '接管者')} |
| </> |
| )} |
| </div> |
| </div> |
| |
| <div className="request-card-content"> |
| {request.plot} |
| </div> |
| |
| <div className="request-card-meta"> |
| {request.year && ( |
| <div className="request-meta-item"> |
| <span className="request-meta-label">年份:</span> |
| <span>{request.year}</span> |
| </div> |
| )} |
| {request.country && ( |
| <div className="request-meta-item"> |
| <span className="request-meta-label">地区:</span> |
| <span>{request.country}</span> |
| </div> |
| )} |
| <div className="request-meta-item"> |
| <span className="request-meta-label">发布时间:</span> |
| <span>{new Date(request.requestTime).toLocaleString()}</span> |
| </div> |
| </div> |
| |
| {request.photo && ( |
| <div className="request-card-image-container"> |
| <img |
| src={`http://localhost:8080${request.photo}`} |
| alt="需求贴配图" |
| className="request-card-image" |
| /> |
| </div> |
| )} |
| |
| <div className="request-card-actions"> |
| {renderActions(request)} |
| </div> |
| </div> |
| ))} |
| </div> |
| )} |
| </div> |
| )} |
| |
| {/* 所有需求贴 */} |
| {viewMode === 'all' && ( |
| <div className="request-all"> |
| <div className="flex justify-between items-center mb-6"> |
| <h2 className="text-xl font-semibold text-orange-700 sm:text-lg">所有需求贴</h2> |
| <span className="text-gray-600">共 {allRequests.length} 条需求</span> |
| </div> |
| |
| {allRequests.length === 0 ? ( |
| <div className="request-empty"> |
| <div className="request-empty-icon">📭</div> |
| <p className="request-empty-text">当前平台暂无需求贴</p> |
| </div> |
| ) : ( |
| <div className="request-cards"> |
| {allRequests.map((request) => ( |
| <div key={request.requestid} className="request-card"> |
| <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3"> |
| <div className="flex-1 min-w-0"> |
| <h3 className="request-card-title">{request.name}</h3> |
| <div className="request-card-money">¥{request.money}</div> |
| </div> |
| <div className="request-user-info-container"> |
| {renderUserInfo(request.userid, '创建者')} |
| {request.loaduser && ( |
| <> |
| <div className="request-user-divider"></div> |
| {renderUserInfo(request.loaduser, '接管者')} |
| </> |
| )} |
| </div> |
| </div> |
| |
| <div className="request-card-content"> |
| {request.plot} |
| </div> |
| |
| <div className="request-card-meta"> |
| {request.year && ( |
| <div className="request-meta-item"> |
| <span className="request-meta-label">年份:</span> |
| <span>{request.year}</span> |
| </div> |
| )} |
| {request.country && ( |
| <div className="request-meta-item"> |
| <span className="request-meta-label">地区:</span> |
| <span>{request.country}</span> |
| </div> |
| )} |
| <div className="request-meta-item"> |
| <span className="request-meta-label">发布时间:</span> |
| <span>{new Date(request.requestTime).toLocaleString()}</span> |
| </div> |
| </div> |
| |
| {request.photo && ( |
| <div className="request-card-image-container"> |
| <img |
| src={`http://localhost:8080${request.photo}`} |
| alt="需求贴配图" |
| className="request-card-image" |
| /> |
| </div> |
| )} |
| |
| <div className="request-card-actions"> |
| {renderActions(request)} |
| </div> |
| </div> |
| ))} |
| </div> |
| )} |
| </div> |
| )} |
| </div> |
| ); |
| }; |
| |
| export default RequestBoard; |