add reward manage

Change-Id: Ic0a25e1fb6442bbda2285b2cc9c736f8a9ed9713
diff --git a/src/app/user/page.tsx b/src/app/user/page.tsx
index 7a1b631..1172aeb 100644
--- a/src/app/user/page.tsx
+++ b/src/app/user/page.tsx
@@ -23,13 +23,16 @@
 import { Toast } from 'primereact/toast';
 // 页面跳转
 import { useRouter } from "next/navigation";
+// 类型转换
+import {toNumber} from "lodash";
+// 分页
+import { Paginator, type PaginatorPageChangeEvent } from 'primereact/paginator';
 
 // 接口传输
 import axios from "axios";
 
 // 样式
 import './user.scss';
-import {toNumber} from "lodash";
 
 // 用户信息
 interface UserInfo {
@@ -76,6 +79,42 @@
     records: Resource[];
 }
 
+// 帖子
+interface Thread {
+    threadId: number;
+    userId:  number;
+    threadPicture:   string;
+    title: string;
+    likes:  number;
+    createAt:  string;
+}
+
+// 发布帖子列表
+interface ThreadList {
+    records: Thread[];
+    total: number;
+    pages: number;
+    current: number;
+    size: number;
+}
+
+// 悬赏
+interface Reward {
+    rewardId: number;
+    userId: number;
+    rewardPicture: string;
+    rewardName: string;
+    createAt: string;
+    rewardDescription: string;
+    price: number;
+}
+
+// 我的悬赏列表
+interface RewardList {
+    rewardList: Reward[];
+    total: number; // 总记录数
+}
+
 // 资源标签
 interface GameplayOption {
     name: string;
@@ -112,8 +151,32 @@
     const toast = useRef<Toast>(null);
     // 资源标签
     const [selectedGameplay, setSelectedGameplay] = useState<GameplayOption[]>([]);
-    // 判断用户是否上传资源封面
-    const [isUploadPicture, setIsUploadPicture] = useState<boolean>(false);
+    // 资源封面路径
+    const [resourcePictureUrl, setResourcePictureUrl] = useState<string>('');
+    // 主页发布帖子列表
+    const [homePageThread, setHomePageThread] = useState<ThreadList>();
+    // 我的帖子列表
+    const [threadList, setThreadList] = useState<Thread[]>([]);
+    // 我的悬赏列表
+    const [rewardList, setRewardList] = useState<Reward[]>([]);
+    // 控制Tab切换
+    const [activeIndex, setActiveIndex] = useState(0);
+    // 帖子分页
+    const [threadFirst, setThreadFirst] = useState(0);
+    const [threadRows, setThreadRows] = useState(6);
+    const [totalThreads, setTotalThreads] = useState<number>(0);
+    const onThreadPageChange = (event: PaginatorPageChangeEvent) => {
+        setThreadFirst(event.first);
+        setThreadRows(event.rows);
+    };
+    // 悬赏分页
+    const [rewardFirst, setRewardFirst] = useState(0);
+    const [rewardRows, setRewardRows] = useState(5);
+    const [totalRewards, setTotalRewards] = useState<number>(0);
+    const onRewardPageChange = (event: PaginatorPageChangeEvent) => {
+        setRewardFirst(event.first);
+        setRewardRows(event.rows);
+    };
 
     useEffect(() => {
         fetchUserInfo();
@@ -175,15 +238,71 @@
             });
             console.log('获取发布资源列表:', response.data.records);
             setResourceList(response.data.records);
-
-            // const imgUrl = `${process.env.NEXT_PUBLIC_NGINX_URL}/${resourceList[0].resourcePicture}`;
-            // console.log("Image URL:", imgUrl);
         } catch (err) {
             console.error('获取发布资源失败', err);
             toast.current?.show({ severity: 'error', summary: 'error', detail: '获取发布资源失败' });
         }
     };
 
+    useEffect(() => {
+        fetchHomePageThread();
+    }, []);
+
+    // 获取主页部分的发布帖子
+    const fetchHomePageThread = async () => {
+        try {
+            const response = await axios.get<ThreadList>(process.env.PUBLIC_URL + `/user/thread`, {
+                params: { userId: 22301010,  pageNumber: 1, rows: 3 }
+            })
+            console.log('获取主页发布帖子:', response.data);
+            setHomePageThread(response.data);
+
+        } catch (err) {
+            console.error('获取主页发布帖子失败', err);
+            toast.current?.show({ severity: 'error', summary: 'error', detail: '获取主页发布帖子失败' });
+        }
+    }
+
+    useEffect(() => {
+        fetchThreadList();
+    }, [threadFirst, threadRows]);
+
+    // 获取用户发布的所有帖子
+    const fetchThreadList = async () => {
+        try {
+            const pageNumber = threadFirst / threadRows + 1;
+            const response = await axios.get<ThreadList>(process.env.PUBLIC_URL + `/user/thread`, {
+                params: { userId: 22301010,  pageNumber: pageNumber, rows: threadRows }
+            })
+            console.log('获取我的帖子:', response.data);
+            setThreadList(response.data.records);
+            setTotalThreads(response.data.total)
+        }catch (err) {
+            console.error('获取我的帖子失败', err);
+            toast.current?.show({ severity: 'error', summary: 'error', detail: '获取我的帖子失败' });
+        }
+    }
+
+    useEffect(() => {
+        fetchRewardList();
+    }, [rewardFirst, rewardRows]);
+
+    // 获取用户发布的所有悬赏
+    const fetchRewardList = async () => {
+        try {
+            const pageNumber = rewardFirst / rewardRows + 1;
+            const response = await axios.get<RewardList>(process.env.PUBLIC_URL + `/user/reward`, {
+                params: { userId: 22301010,  pageNumber: pageNumber, rows: rewardRows }
+            })
+            console.log('获取我的悬赏:', response.data);
+            setRewardList(response.data.rewardList);
+            setTotalRewards(response.data.total)
+        }catch (err) {
+            console.error('获取我的悬赏失败', err);
+            toast.current?.show({ severity: 'error', summary: 'error', detail: '获取我的悬赏失败' });
+        }
+    }
+
     // 浮动按钮的子模块
     const actions = [
         {
@@ -227,11 +346,68 @@
     });
     const [ingredient, setIngredient] = useState<string>('');
 
+    // 删除悬赏弹窗
+    const [deleteVisible, setDeleteVisible] = useState(false);
+    // 要删除悬赏的id
+    const [deleteRewardId, setDeleteResourceId] =  useState<number>(0);
+    // 处理删除悬赏接口
+    const handleDeleteSubmit = async () => {
+        try {
+            // 发送DELETE请求
+            const response = await axios.delete(process.env.PUBLIC_URL + `/reward`, {
+                params: {rewardId: deleteRewardId},
+            });
+            console.log("用户" + 22301010 + "要删除" + deleteRewardId + "号悬赏");
 
-    // 图片上传消息通知
-    const onUpload = () => {
-        setIsUploadPicture(true);
-        toast.current?.show({ severity: 'info', summary: 'Success', detail: 'File Uploaded' });
+            if (response.status === 204) {
+                console.log("用户成功删除悬赏");
+                toast.current?.show({severity: 'success', summary: 'Success', detail: '删除悬赏成功'});
+                setDeleteVisible(false);
+                // 重新拉取资源列表
+                fetchRewardList();
+            }
+        } catch (error) {
+            console.error('资源删除失败:', error);
+            toast.current?.show({ severity: 'error', summary: 'error', detail: '密码错误,资源删除失败' });
+        }
+    };
+
+
+    // 编辑悬赏弹窗
+    const [editVisible, setEditVisible] = useState(false);
+    // 悬赏封面路径
+    const [rewardPictureUrl, setRewardPictureUrl] = useState<string>('');
+    const [editRewardFormData, setEditRewardFormData] = useState({
+        rewardId: 0,
+        rewardName: '',
+        price: '',
+        rewardDescription: '',
+    });
+
+    // 处理编辑资源接口
+    const handleEditSubmit = async () => {
+        try {
+            const postData = {
+                rewardId: editRewardFormData.rewardId,
+                rewardName: editRewardFormData.rewardName,
+                rewardPicture: rewardPictureUrl,
+                price: toNumber(editRewardFormData.price),
+                rewardDescription: editRewardFormData.rewardDescription,
+            };
+            // 发送POST请求
+            const response = await axios.put(process.env.PUBLIC_URL + '/reward/info', postData);
+            console.log("编辑悬赏的信息:", postData);
+
+            if (response.status === 200) {
+                toast.current?.show({ severity: 'success', summary: 'Success', detail: '悬赏编辑成功' });
+                // 编辑成功
+                setEditVisible(false);
+                fetchRewardList();
+            }
+        } catch (error) {
+            console.error('悬赏编辑失败:', error);
+            toast.current?.show({ severity: 'error', summary: 'error', detail: '悬赏编辑失败' });
+        }
     };
 
     // 上传资源接口
@@ -261,7 +437,7 @@
                 toast.current?.show({ severity: 'info', summary: 'error', detail: '缺少资源标签' });
                 return;
             }
-            if (!isUploadPicture) {
+            if (resourcePictureUrl === '') {
                 toast.current?.show({ severity: 'info', summary: 'error', detail: '缺少资源封面' });
                 return;
             }
@@ -270,7 +446,7 @@
             const postData = {
                 resource: {
                     resourceName: resourceFormData.resource.resourceName,
-                    resourcePicture: resourceFormData.resource.resourcePicture,
+                    resourcePicture: resourcePictureUrl,
                     resourceSummary: resourceFormData.resource.resourceSummary,
                     resourceDetail: resourceFormData.resource.resourceDetail,
                     uploadTime: currentDate,
@@ -310,8 +486,8 @@
                 setIngredient("");
                 // 重置资源标签
                 setSelectedGameplay([]);
-                // 重置上传封面状态
-                setIsUploadPicture(false);
+                // 重置资源封面
+                setResourcePictureUrl('');
                 // 可以刷新资源列表
                 // fetchResourceList();
             }
@@ -321,9 +497,6 @@
         }
     };
 
-
-
-
     return (
         <div className="user-container">
             <Toast ref={toast}></Toast>
@@ -366,39 +539,8 @@
             </div>
 
             {/*个人内容*/}
-            <TabView>
-                <TabPanel header="主页">
-                    {/*推荐资源*/}
-                    <div className="homepage-item">
-                        <div className="section-header">
-                            <h1>推荐资源</h1>
-                            <Button
-                                label="显示更多"
-                                link
-                                onClick={() => router.push('/resource/recommend/模组')}
-                            />
-                        </div>
-                        <div className="resource-grid">
-                            {/*{mods.map((mod) => (*/}
-                            {/*    <Card key={mod.resourceId} className="resource-card" onClick={() => router.push(`/resource/resource-detail/${mod.resourceId}`)}>*/}
-                            {/*        <Image*/}
-                            {/*            src={process.env.NEXT_PUBLIC_NGINX_URL + mod.resourcePicture}*/}
-                            {/*            alt={mod.resourceName}*/}
-                            {/*            width="368"*/}
-                            {/*            height="200"*/}
-                            {/*        />*/}
-                            {/*        <div className="card-content">*/}
-                            {/*            <h3>{mod.resourceName}</h3>*/}
-                            {/*            <div className="view-count">*/}
-                            {/*                <Fire theme="outline" size="16" fill="#FF8D1A" />*/}
-                            {/*                <span>{mod.likes}</span>*/}
-                            {/*            </div>*/}
-                            {/*        </div>*/}
-                            {/*    </Card>*/}
-                            {/*))}*/}
-                        </div>
-                    </div>
-
+            <TabView activeIndex={activeIndex} onTabChange={(e) => setActiveIndex(e.index)}>
+                <TabPanel header="主页" >
                     {/*发布资源*/}
                     <div className="homepage-item">
                         <div className="section-header">
@@ -437,38 +579,69 @@
                             <Button
                                 label="显示更多"
                                 link
-                                onClick={() => router.push('/resource/recommend/模组')}
+                                onClick={() => setActiveIndex(1)}
                             />
                         </div>
                         <div className="resource-grid">
-
-                            {/*{mods.map((mod) => (*/}
-                            {/*    <Card key={mod.resourceId} className="resource-card" onClick={() => router.push(`/resource/resource-detail/${mod.resourceId}`)}>*/}
-                            {/*        <Image*/}
-                            {/*            src={process.env.NEXT_PUBLIC_NGINX_URL + mod.resourcePicture}*/}
-                            {/*            alt={mod.resourceName}*/}
-                            {/*            width="368"*/}
-                            {/*            height="200"*/}
-                            {/*        />*/}
-                            {/*        <div className="card-content">*/}
-                            {/*            <h3>{mod.resourceName}</h3>*/}
-                            {/*            <div className="view-count">*/}
-                            {/*                <Fire theme="outline" size="16" fill="#FF8D1A" />*/}
-                            {/*                <span>{mod.likes}</span>*/}
-                            {/*            </div>*/}
-                            {/*        </div>*/}
-                            {/*    </Card>*/}
-                            {/*))}*/}
+                            {homePageThread?.records.map((homePageThread) => (
+                                <Card key={homePageThread.threadId} className="resource-card" onClick={() => router.push(`/community/thread-detail/${homePageThread.threadId}`)}>
+                                    <Image
+                                        src={process.env.NEXT_PUBLIC_NGINX_URL + homePageThread.threadPicture}
+                                        alt={homePageThread.title}
+                                        width="368"
+                                        height="200"
+                                    />
+                                    <div className="card-content">
+                                        <h3>{homePageThread.title}</h3>
+                                        <div className="view-count">
+                                            <Fire theme="outline" size="16" fill="#FF8D1A" />
+                                            <span>{homePageThread.likes}</span>
+                                        </div>
+                                    </div>
+                                </Card>
+                            ))}
                         </div>
                     </div>
                 </TabPanel>
 
-                <TabPanel header="发布">
+                {/*<TabPanel header="发布">*/}
 
-                </TabPanel>
+                {/*</TabPanel>*/}
 
                 <TabPanel header="帖子">
-
+                    {/*我的帖子*/}
+                    <div className="homepage-item">
+                        <div className="section-header">
+                            <h1>我的帖子</h1>
+                        </div>
+                        <div className="resource-grid">
+                            {threadList.map((threadList) => (
+                                <Card key={threadList.threadId} className="resource-card" onClick={() => router.push(`/community/thread-detail/${threadList.threadId}`)}>
+                                    <Image
+                                        src={process.env.NEXT_PUBLIC_NGINX_URL + threadList.threadPicture}
+                                        alt={threadList.title}
+                                        width="368"
+                                        height="200"
+                                    />
+                                    <div className="card-content">
+                                        <h3>{threadList.title}</h3>
+                                        <div className="view-count">
+                                            <Fire theme="outline" size="16" fill="#FF8D1A" />
+                                            <span>{threadList.likes}</span>
+                                        </div>
+                                    </div>
+                                </Card>
+                            ))}
+                        </div>
+                        {totalThreads > 6 && <Paginator
+                            className="Paginator"
+                            first={threadFirst}
+                            rows={threadRows}
+                            totalRecords={totalThreads}
+                            rowsPerPageOptions={[6, 12]}
+                            onPageChange={onThreadPageChange}
+                        />}
+                    </div>
                 </TabPanel>
 
                 <TabPanel header="收藏">
@@ -480,7 +653,57 @@
                 </TabPanel>
 
                 <TabPanel header="悬赏">
+                    <div className="resource-list">
+                        {rewardList.map((rewardItem) => (
+                            <Card key={rewardItem.rewardId} className="resources-list-card"
+                                  onClick={() => router.push(`/reward/reward-detail/${rewardItem.rewardId}`)}>
+                                <Image alt="avatar"
+                                       src={process.env.NEXT_PUBLIC_NGINX_URL + "rewards/" + rewardItem.rewardPicture}
+                                       className="resource-avatar" width="250" height="140"/>
+                                <div className="resource-header">
+                                    <div className="resource-content">
+                                        <h3>{rewardItem.rewardName}</h3>
+                                    </div>
 
+                                    <div className="resource-operation">
+                                        <Button
+                                            label="编辑"
+                                            onClick={(e) => {
+                                                e.stopPropagation(); // 关键修复:阻止事件冒泡,避免触发Card的点击事件
+                                                setEditVisible(true);
+
+                                                setEditRewardFormData({
+                                                    rewardId: rewardItem.rewardId,
+                                                    rewardName: rewardItem.rewardName,
+                                                    price: rewardItem.price.toString(),
+                                                    rewardDescription: rewardItem.rewardDescription,
+                                                });
+                                                console.log("用户编辑悬赏");
+                                            }}
+                                        />
+                                        <Button
+                                            label="删除"
+                                            onClick={(e) => {
+                                                e.stopPropagation(); // 关键修复:阻止事件冒泡,避免触发Card的点击事件
+                                                setDeleteResourceId(rewardItem.rewardId);
+                                                setDeleteVisible(true);
+                                            }}
+                                            style={{backgroundColor: "rgba(255, 87, 51, 1)"}}
+                                        />
+                                    </div>
+                                </div>
+                            </Card>
+                        ))}
+
+                        {totalRewards > 5 && <Paginator
+                            className="Paginator"
+                            first={rewardFirst}
+                            rows={rewardRows}
+                            totalRecords={totalRewards}
+                            rowsPerPageOptions={[5, 10]}
+                            onPageChange={onRewardPageChange}
+                        />}
+                    </div>
                 </TabPanel>
             </TabView>
 
@@ -528,7 +751,6 @@
                             }))}
                             placeholder="请输入资源名称"
                             className="w-full"
-                            required
                         />
                     </div>
                     <div className="form-field">
@@ -548,7 +770,6 @@
                             }))}
                             placeholder="请输入资源简介(一句话)"
                             className="w-full"
-                            required
                         />
                     </div>
                     <div className="form-field">
@@ -568,7 +789,6 @@
                             rows={5}
                             placeholder="请输入资源介绍"
                             className="w-full"
-                            required
                         />
                     </div>
                     <div className="form-field">
@@ -711,19 +931,166 @@
                             <label>封面图片</label>
                         </div>
                         <FileUpload
-                            mode="basic"
+                            mode="advanced"
                             name="resource-image"
-                            url={process.env.PUBLIC_URL +"/file"} // 与后端交互的URL
+                            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.url;
+                                    console.log(fileUrl);
+                                    setResourcePictureUrl(fileUrl);
+                                    toast.current?.show({ severity: 'success', summary: '上传成功' });
+                                } catch (error) {
+                                    console.log(error);
+                                    toast.current?.show({ severity: 'error', summary: '上传失败' });
+                                }
+                            }}
+                            auto
                             accept="image/*"
-                            maxFileSize={10000000000}
-                            chooseLabel="选择资源封面"
-                            className="w-full"
-                            onUpload={onUpload}
+                            chooseLabel="上传资源封面"
+
+                            // accept="image/*"
+                            // maxFileSize={10000000000}
+                            // chooseLabel="选择资源封面"
+                            // className="w-full"
+                            // onUpload={onUpload}
                         />
                     </div>
                 </div>
             </Dialog>
 
+            {/*删除悬赏弹窗*/}
+            <Dialog
+                header="删除悬赏"
+                visible={deleteVisible}
+                onHide={() => setDeleteVisible(false)}
+                className="resource-delete-dialog"
+                modal
+                footer={
+                    <div className="dialog-footer">
+                        <Button label="确认" icon="pi pi-check" onClick={handleDeleteSubmit} autoFocus />
+                        <Button label="取消" icon="pi pi-times" onClick={() => setDeleteVisible(false)} className="p-button-text" />
+                    </div>
+                }
+            >
+                <div className="dialog-form">
+                    <span>
+                        确认是否删除该悬赏?
+                    </span>
+                    {/*<div className="form-field">*/}
+                    {/*    <div className="form-field-header">*/}
+                    {/*        <label htmlFor="name">密码</label>*/}
+                    {/*    </div>*/}
+                    {/*    <Password*/}
+                    {/*        id="passwrod"*/}
+                    {/*        value={deleteResourceFormData.password}*/}
+                    {/*        onChange={(e) => setDeleteResourceFormData(prev => ({*/}
+                    {/*            ...prev,*/}
+                    {/*            password: e.target.value*/}
+                    {/*        }))}*/}
+                    {/*        placeholder="请输入密码"*/}
+                    {/*        className="w-full"*/}
+                    {/*        toggleMask*/}
+                    {/*    />*/}
+                    {/*</div>*/}
+                </div>
+            </Dialog>
+
+            {/*编辑资源弹窗*/}
+            <Dialog
+                header="编辑资源"
+                visible={editVisible}
+                onHide={() => setEditVisible(false)}
+                className="resource-edit-dialog"
+                modal
+                footer={
+                    <div className="dialog-footer">
+                        <Button label="确认" icon="pi pi-check" onClick={handleEditSubmit} autoFocus />
+                        <Button label="取消" icon="pi pi-times" onClick={() => setEditVisible(false)} className="p-button-text" />
+                    </div>
+                }
+            >
+                <div className="dialog-form">
+                    <div className="form-field">
+                        <div className="form-field-header">
+                            <label htmlFor="name">更改标题</label>
+                        </div>
+                        <InputText
+                            id="name"
+                            value={editRewardFormData.rewardName}
+                            onChange={(e) => setEditRewardFormData(prev => ({
+                                ...prev,
+                                rewardName:  e.target.value
+                            }))}
+                            className="w-full"
+                        />
+                    </div>
+                    <div className="form-field">
+                        <div className="form-field-header">
+                            <label htmlFor="summary">更改定价</label>
+                        </div>
+                        <InputText
+                            id="summary"
+                            value={editRewardFormData.price}
+                            onChange={(e) => setEditRewardFormData(prev => ({
+                                ...prev,
+                                price:  e.target.value
+                            }))}
+                            className="w-full"
+                        />
+                    </div>
+                    <div className="form-field">
+                        <div className="form-field-header">
+                            <label htmlFor="detail">更改需求</label>
+                        </div>
+                        <InputTextarea
+                            id="detail"
+                            value={editRewardFormData.rewardDescription}
+                            onChange={(e) => setEditRewardFormData(prev => ({
+                                ...prev,
+                                rewardDescription:  e.target.value
+                            }))}
+                            rows={5}
+                            className="w-full"
+                        />
+                    </div>
+                    <div className="form-field">
+                        <div className="form-field-header">
+                            <span className="form-field-sign">*</span>
+                            <label>封面图片</label>
+                        </div>
+                        <FileUpload
+                            mode="advanced"
+                            name="resource-image"
+                            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.url;
+                                    console.log(fileUrl);
+                                    setRewardPictureUrl(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>
     );
 };