| "use client"; |
| |
| import React, { useEffect, useState, useRef } from "react"; |
| import { InputText } from "primereact/inputtext"; |
| import { Button } from "primereact/button"; |
| import { Card } from "primereact/card"; |
| import { Image } from "primereact/image"; |
| // 页面跳转 |
| import { useRouter } from "next/navigation"; |
| // 分页 |
| import { Paginator, type PaginatorPageChangeEvent } from "primereact/paginator"; |
| // 消息提醒 |
| import { Toast } from "primereact/toast"; |
| // 发布帖子 |
| import { Dialog } from "primereact/dialog"; |
| import { FileUpload } from "primereact/fileupload"; |
| import { InputTextarea } from "primereact/inputtextarea"; |
| // 接口传输 |
| import axios from "axios"; |
| // 防抖函数 |
| import { debounce } from "lodash"; |
| import { TabView, TabPanel } from "primereact/tabview"; |
| import { useLocalStorage } from "../hook/useLocalStorage"; |
| // 样式 |
| import "./reward.scss"; |
| interface User { |
| Id: number; |
| } |
| // 悬赏列表数据 |
| interface Reward { |
| rewardId: number; |
| userId: number; |
| rewardPicture: string; |
| rewardName: string; |
| createAt: string; |
| rewardDescription: string; |
| price: number; |
| } |
| interface RewardList { |
| total: number; // 总记录数 |
| records: Reward[]; |
| } |
| |
| // 社区详情页面 |
| export default function RewardDetailPage() { |
| const user = useLocalStorage<User>("user"); |
| const userId: number = user?.Id ?? -1; |
| // 页面跳转 |
| const router = useRouter(); |
| // 帖子列表数据 |
| const [rewards, setRewards] = useState<Reward[]>([]); |
| const [totalRewards, setTotalRewards] = useState<number>(0); |
| const [activeOption, setActiveOption] = useState<number>(0); // 0表示"赏金最高",1表示"最新发布" |
| const options = ["赏金最高", "最新发布"]; |
| // 搜索框 |
| const [searchValue, setSearchValue] = useState(""); |
| const debouncedSearch = useRef( |
| debounce((value: string) => { |
| setSearchValue(value); |
| }, 600) |
| ).current; |
| // 消息提醒 |
| const toast = useRef<Toast>(null); |
| // 分页 |
| const [first, setFirst] = useState(0); |
| const [rows, setRows] = useState(5); |
| const onPageChange = (event: PaginatorPageChangeEvent) => { |
| setFirst(event.first); |
| setRows(event.rows); |
| }; |
| |
| // 获取悬赏列表 |
| useEffect(() => { |
| fetchRewards(); |
| }, [first, rows, searchValue, activeOption]); |
| |
| const fetchRewards = async () => { |
| try { |
| const pageNumber = first / rows + 1; |
| console.log( |
| "当前页" + |
| pageNumber + |
| "size" + |
| rows + |
| "搜索内容" + |
| searchValue + |
| "排序方式" + |
| activeOption |
| ); |
| const response = await axios.get<RewardList>( |
| process.env.PUBLIC_URL + `/reward`, |
| { |
| params: { |
| pageNumber, |
| rows, |
| searchValue, |
| option: options[activeOption], |
| }, |
| } |
| ); |
| console.log("获取悬赏列表:", response.data.records); |
| setRewards(response.data.records); |
| setTotalRewards(response.data.total); // 假设返回的总数 |
| } catch (err) { |
| console.error("获取悬赏失败", err); |
| toast.current?.show({ |
| severity: "error", |
| summary: "error", |
| detail: "获取悬赏失败", |
| }); |
| } |
| }; |
| |
| // 发布悬赏弹窗 |
| const [visible, setVisible] = useState(false); |
| const [formData, setFormData] = useState({ |
| rewardName: "", |
| rewardDescription: "", |
| rewardPicture: "", |
| price: "", |
| }); |
| |
| // 发布悬赏接口 |
| const handleSubmit = async () => { |
| try { |
| const currentDate = new Date().toISOString(); |
| const postData = { |
| userId, // 记得用户登录状态获取 |
| rewardPicture: formData.rewardPicture, |
| rewardName: formData.rewardName, |
| rewardDescription: formData.rewardDescription, |
| createAt: currentDate.slice(0, 10), |
| lastUpdateAt: currentDate.slice(0, 10), |
| price: Number(formData.price), |
| }; |
| // 发送POST请求 |
| const response = await axios.post( |
| process.env.PUBLIC_URL + "/reward", |
| postData |
| ); |
| |
| if (response.status === 200) { |
| toast.current?.show({ |
| severity: "success", |
| summary: "Success", |
| detail: "悬赏发布成功", |
| }); |
| // 发帖成功 |
| setVisible(false); |
| // 重置表单 |
| setFormData({ |
| rewardName: "", |
| rewardDescription: "", |
| rewardPicture: "", |
| price: "", |
| }); |
| // 可以刷新帖子列表 |
| fetchRewards(); |
| } |
| } catch (error) { |
| console.error("发布悬赏失败:", error); |
| toast.current?.show({ |
| severity: "error", |
| summary: "error", |
| detail: "悬赏发布失败", |
| }); |
| } |
| }; |
| return ( |
| <div className="reward"> |
| <Toast ref={toast}></Toast> |
| {/* 悬赏标题和介绍 */} |
| <div className="reward-header"> |
| <div className="title-section"> |
| <h1>悬赏排行</h1> |
| </div> |
| <div className="input"> |
| <div className="searchBar"> |
| <i className="pi pi-search" /> |
| <InputText |
| type="search" |
| className="search-helper" |
| placeholder="搜索你的目标悬赏" |
| onChange={(e) => { |
| const target = e.target as HTMLInputElement; |
| debouncedSearch(target.value); |
| }} |
| /> |
| </div> |
| <div className="reward-buttons"> |
| <Button |
| label="我的悬赏" |
| onClick={() => router.push(`/user/悬赏`)} |
| /> |
| <Button label="发布悬赏" onClick={() => setVisible(true)} /> |
| </div> |
| </div> |
| </div> |
| |
| {/* 悬赏列表 */} |
| <TabView |
| activeIndex={activeOption} |
| onTabChange={(e) => setActiveOption(e.index)} |
| > |
| <TabPanel header={options[0]}> |
| <div className="rewards-list"> |
| {rewards.map((reward) => ( |
| <Card |
| key={reward.rewardId} |
| className="rewards-list-card" |
| onClick={() => |
| router.push(`/reward/reward-detail/${reward.rewardId}`) |
| } |
| > |
| <Image |
| alt="avatar" |
| src={reward.rewardPicture} |
| className="reward-avatar" |
| width="250" |
| height="140" |
| /> |
| <div className="reward-header"> |
| <div className="reward-content"> |
| <h3>{reward.rewardName}</h3> |
| </div> |
| <div className="reward-states"> |
| <span className="price">$: {reward.price}</span> |
| <Button label="提交悬赏" /> |
| </div> |
| </div> |
| </Card> |
| ))} |
| {totalRewards > 5 && ( |
| <Paginator |
| className="Paginator" |
| first={first} |
| rows={rows} |
| totalRecords={totalRewards} |
| rowsPerPageOptions={[5, 10]} |
| onPageChange={onPageChange} |
| /> |
| )} |
| </div> |
| </TabPanel> |
| <TabPanel header={options[1]}> |
| <div className="rewards-list"> |
| {rewards.map((reward) => ( |
| <Card |
| key={reward.rewardId} |
| className="rewards-list-card" |
| onClick={() => |
| router.push(`/reward/reward-detail/${reward.rewardId}`) |
| } |
| > |
| <Image |
| alt="avatar" |
| src={reward.rewardPicture} |
| className="reward-avatar" |
| width="250" |
| height="140" |
| /> |
| <div className="reward-header"> |
| <div className="reward-content"> |
| <h3>{reward.rewardName}</h3> |
| </div> |
| <div className="reward-states"> |
| <span className="price">$: {reward.price}</span> |
| <Button label="提交悬赏" /> |
| </div> |
| </div> |
| </Card> |
| ))} |
| {totalRewards > 5 && ( |
| <Paginator |
| className="Paginator" |
| first={first} |
| rows={rows} |
| totalRecords={totalRewards} |
| rowsPerPageOptions={[5, 10]} |
| onPageChange={onPageChange} |
| /> |
| )} |
| </div> |
| </TabPanel> |
| </TabView> |
| {/* 发布悬赏弹窗 */} |
| <Dialog |
| header="发布新悬赏" |
| visible={visible} |
| onHide={() => setVisible(false)} |
| className="publish-dialog" |
| modal |
| footer={ |
| <div className="dialog-footer"> |
| <Button |
| label="发布" |
| icon="pi pi-check" |
| onClick={handleSubmit} |
| autoFocus |
| /> |
| <Button |
| label="取消" |
| icon="pi pi-times" |
| onClick={() => setVisible(false)} |
| className="p-button-text" |
| /> |
| </div> |
| } |
| > |
| <div className="publish-form"> |
| <div className="form-field"> |
| <label htmlFor="title">标题</label> |
| <InputText |
| id="title" |
| value={formData.rewardName} |
| onChange={(e) => |
| setFormData((prev) => ({ ...prev, rewardName: e.target.value })) |
| } |
| placeholder="请输入悬赏标题" |
| className="w-full" |
| /> |
| </div> |
| |
| <div className="form-field"> |
| <label htmlFor="content">内容</label> |
| <InputTextarea |
| id="content" |
| value={formData.rewardDescription} |
| onChange={(e) => |
| setFormData((prev) => ({ |
| ...prev, |
| rewardDescription: e.target.value, |
| })) |
| } |
| rows={5} |
| placeholder="请输入悬赏需求" |
| className="w-full" |
| /> |
| </div> |
| <div className="form-field"> |
| <label htmlFor="price">赏金</label> |
| <InputText |
| id="price" |
| value={formData.price} |
| onChange={(e) => |
| setFormData((prev) => ({ ...prev, price: e.target.value })) |
| } |
| placeholder="请输入赏金金额" |
| className="w-full" |
| /> |
| </div> |
| <div className="form-field"> |
| <label>封面图片</label> |
| <FileUpload |
| mode="advanced" |
| name="file" |
| customUpload |
| uploadHandler={async (e) => { |
| const formData = new FormData(); |
| formData.append("file", e.files[0]); |
| |
| try { |
| const res = await axios.post( |
| `${process.env.PUBLIC_URL}/file`, |
| formData |
| ); |
| |
| const fileUrl = res.data; |
| console.log(fileUrl); |
| setFormData((prev) => ({ ...prev, rewardPicture: fileUrl })); |
| toast.current?.show({ |
| severity: "success", |
| summary: "上传成功", |
| }); |
| } catch (error) { |
| console.log(error); |
| toast.current?.show({ |
| severity: "error", |
| summary: "上传失败", |
| }); |
| } |
| }} |
| auto |
| accept="image/*" |
| chooseLabel="上传帖子封面" |
| /> |
| </div> |
| </div> |
| </Dialog> |
| </div> |
| ); |
| } |