blob: 77d11a6fdf3c746611cc5b011083cdb0df681a9e [file] [log] [blame]
"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>
);
}