我是人,我做了悬赏中心,可以进行悬赏哦
Change-Id: I845de799a633be286a6fadee2b7aa533238c3652
diff --git a/src/pages/Bounty/Detail.tsx b/src/pages/Bounty/Detail.tsx
new file mode 100644
index 0000000..41a25df
--- /dev/null
+++ b/src/pages/Bounty/Detail.tsx
@@ -0,0 +1,231 @@
+import React, { useState, useEffect } from 'react';
+import { Button, Descriptions, List, message, Tag } from 'antd';
+import axios from 'axios'; // 新增 axios 导入
+import { useParams } from 'umi';
+
+import { getBountyDetail, getProfile } from '@/services/bounty/bounty';
+import { downloadAttachment,adoptSubmission } from '@/services/bounty/bounty';
+
+
+
+const BountyDetail: React.FC = () => {
+ const [bounty, setBounty] = useState<API.BountyDetail>({} as API.BountyDetail);
+ const [loading, setLoading] = useState(false); // 新增加载状态
+ const [currentUserId, setCurrentUserId] = useState<number | null>(null);
+
+ console.log('当前用户id:',currentUserId);
+ console.log('悬赏发布用户id:',bounty.creator_id);
+
+ const handleAdoptSubmission = async (submissionId: number, currentStatus: number) => {
+ console.log('【采纳请求】', {
+ submissionId,
+ currentStatus,
+ bounty: bounty,
+ currentUserId
+ });
+
+ if (currentStatus === 1) return;
+
+ try {
+ const res = await adoptSubmission(submissionId);
+ console.log('【采纳响应】', {
+ status: res.status,
+ data: res.data,
+ headers: res.headers
+ });
+
+ if (res.code === 200) {
+ message.success('采纳成功');
+ setBounty(prev => {
+ console.log('【状态更新】', {
+ old: prev.submissions,
+ new: prev.submissions?.map(sub =>
+ sub.id === submissionId ? { ...sub, status: 1 } : sub
+ )
+ });
+
+ return {
+ ...prev,
+ submissions: prev.submissions?.map(sub =>
+ sub.id === submissionId ? { ...sub, status: 1 } : sub
+ )
+ };
+ });
+ } else {
+ message.error(`采纳失败: ${res.msg}`);
+ }
+ } catch (error: any) {
+ console.error('【采纳错误】', {
+ error: error,
+ response: error.response?.data,
+ status: error.response?.status
+ });
+
+ message.error(`网络异常: ${error.message}`);
+ }
+ };
+
+ const handleDownload = async (attachmentPath: string, submissionUserId: number) => {
+ try {
+
+ // ✅ 新增权限校验
+ if (!currentUserId || (currentUserId !== bounty.creator_id && currentUserId !== submissionUserId)) {
+ message.error('无权查看此附件');
+ return;
+ }
+
+ const blob = await downloadAttachment(attachmentPath);
+ const url = window.URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = attachmentPath.split('/').pop() || 'file';
+ document.body.appendChild(a);
+ a.click();
+ a.remove();
+ window.URL.revokeObjectURL(url);
+ } catch (err) {
+ message.error('下载附件失败,请重试');
+ console.error('下载附件失败:', err);
+ }
+ };
+
+
+
+ useEffect(() => {
+ const fetchProfile = async () => {
+ try {
+ const res = await getProfile(); // 调用后端接口
+ if (res && res.code === 200) {
+ setCurrentUserId(res.data.userId); // 从接口获取 userId
+ } else {
+ message.error('获取用户信息失败');
+ }
+ } catch (err) {
+ console.error('获取用户信息失败:', err);
+ }
+ };
+ fetchProfile();
+ }, []);
+
+
+ const { id } = useParams<{ id: string }>();
+
+ // 修改加载方法(适配统一请求)✅
+ useEffect(() => {
+ if (!id) return;
+
+ const loadBountyDetail = async () => {
+ try {
+ setLoading(true);
+ const res = await getBountyDetail(id);
+ console.log('【详情响应】原始数据:', res); // 👈 关键日志
+
+ if (res && res.code === 200) {
+ setBounty(res.data);
+ // 👇 新增:检查 submissions 数据结构
+ console.log('【submissions 数据】:', res.data.submissions);
+ } else {
+ throw new Error('响应结构异常');
+ }
+ } catch (err) {
+ console.error('【详情请求】错误:', err);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ loadBountyDetail();
+ }, [id]);
+
+ return (
+ <div className="page-container">
+ <h2>悬赏详情</h2>
+
+ {/* 基础信息 */}
+ <Descriptions title="悬赏信息" bordered>
+ <Descriptions.Item label="标题">{bounty.title}</Descriptions.Item>
+ <Descriptions.Item label="发布者ID">{bounty.creator_id}</Descriptions.Item> // ✅ 新增字段
+ <Descriptions.Item label="奖励">{bounty.reward}</Descriptions.Item>
+ <Descriptions.Item label="状态">
+ {bounty.status === 0 ? '进行中' : bounty.status === 1 ? '已完成' : '已关闭'}
+ </Descriptions.Item>
+ <Descriptions.Item label="截止时间">{bounty.deadline}</Descriptions.Item>
+ <Descriptions.Item label="描述" span={3}>
+ {bounty.description}
+ </Descriptions.Item>
+ </Descriptions>
+
+ {/* 回复列表 */}
+ {bounty.submissions && (
+ <div style={{ marginTop: 24 }}>
+ <h3>回复列表</h3>
+ <List
+ dataSource={bounty.submissions}
+ renderItem={(item) => (
+ <List.Item>
+ <List.Item.Meta
+ title={`回复人ID:${item.userId}`}
+ description={
+ <>
+ {item.content}
+ {/* 状态标签 */}
+ <span style={{ marginLeft: 16 }}>
+ {item.status === 1 ? (
+ <Tag color="green">已采纳</Tag>
+ ) : (
+ <Tag color="red">未被采纳</Tag>
+ )}
+ </span>
+ </>
+ }
+ />
+
+ {/* 发布者操作按钮 */}
+ {currentUserId === bounty.creator_id && (
+ <Button
+ type="primary"
+ size="small"
+ onClick={() => handleAdoptSubmission(item.id, item.status)}
+ disabled={item.status === 1}
+ >
+ {item.status === 1 ? '已采纳' : '采纳'}
+ </Button>
+ )}
+
+ {/* 附件下载 */}
+ {item.attachment && currentUserId === bounty.creator_id && (
+ <a onClick={(e) => handleDownload(item.attachment, item.userId)} style={{ marginLeft: 8 }}>
+ 查看附件
+ </a>
+ )}
+ </List.Item>
+ )}
+ />
+ </div>
+ )}
+ </div>
+ );
+};
+
+// 定义类型(建议单独放在src/types.d.ts中)
+declare namespace API {
+ export interface BountyDetail {
+ id: number;
+ title: string;
+ creator_id: number; // ✅ 新增:发布者ID
+ description: string;
+ reward: number;
+ deadline: string;
+ status: number;
+ submissions: Array<{
+ id: number;
+ userId: number; // ✅ 替换 id 为 userId
+ username: string;
+ content: string;
+ attachment: string;
+ status: number;
+ }>;
+ }
+}
+
+export default BountyDetail;