blob: 99d00ec2e100b82cae9c9775d1e26ddbd304ba7e [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 onUpload = () => {
toast.current?.show({ severity: 'info', summary: 'Success', detail: 'File Uploaded' });
};
// 发布悬赏接口
const handleSubmit = async () => {
try {
const currentDate = new Date().toISOString();
const postData = {
userId, // 记得用户登录状态获取
rewardPicture: formData.rewardPicture,
rewardName: formData.rewardName,
rewardDescription: formData.rewardDescription,
createdAt: currentDate,
price: 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={ "rewards/" + 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, title: 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, content: 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="basic"
name="thread-image"
url={process.env.PUBLIC_URL + "/file"} // 与后端交互的URL
accept="image/*"
maxFileSize={10000000000}
chooseLabel="选择悬赏封面"
className="w-full"
onUpload={onUpload}
/>
</div>
</div>
</Dialog>
</div>
);
}