blob: 40200f78f4d46e61c127a617e0020861d84250ae [file] [log] [blame]
LaoeGaociee7c5772025-05-28 12:34:47 +08001'use client';
2
3import React, { useEffect, useState, useRef } from "react";
4import { InputText } from 'primereact/inputtext';
5import { Button } from 'primereact/button';
6import { Card } from 'primereact/card';
7import { Image } from 'primereact/image';
8import { Dropdown } from 'primereact/dropdown';
9// 页面跳转
10import { useParams } from 'next/navigation'
11import { useRouter } from 'next/navigation';
12// 分页
13import { Paginator, type PaginatorPageChangeEvent } from 'primereact/paginator';
14// 评分图标
15import { Fire } from '@icon-park/react';
16// 消息提醒
17import { Toast } from 'primereact/toast';
18// 发布帖子
19import { Dialog } from 'primereact/dialog';
20import { FileUpload } from 'primereact/fileupload';
21import { InputTextarea } from 'primereact/inputtextarea';
22// 接口传输
23import axios from 'axios';
24// 防抖函数
25import { debounce } from 'lodash';
26// 样式
27import './resource-community.scss';
28
29// 帖子列表数据
30interface Thread {
31 threadId: number;
32 userId: number;
33 threadPicture: string;
34 title: string;
35 createdAt: string;
36 communityId: string;
37 likes: number;
38}
39interface ThreadList {
40 records: Thread[];
41}
42// 社区信息
43interface CommunityInfo {
44 communityId: string;
45 communityName: string;
46 communityPicture: string;
47 description: string;
48 hot: number;
49 type: string;
50 threadNumber: number;
51}
52
53
54// 社区详情页面
55export default function CommunityDetailPage() {
56 // 获取URL参数,页面跳转
57 const params = useParams<{ communityId: string }>()
58 const communityId = decodeURIComponent(params.communityId); // 防止中文路径乱码
59 const router = useRouter();
60 // 社区数据
61 const [communityInfo, setCommunityInfo] = useState<CommunityInfo | null>(null);
62 // 帖子列表数据
63 const [threads, setThreads] = useState<Thread[]>([]);
64 const [totalThreads, setTotalThreads] = useState<number>(0);
65 // 搜索框
66 const [searchValue, setSearchValue] = useState("");
67 const debouncedSearch = useRef(
68 debounce((value: string) => {
69 setSearchValue(value);
70 }, 600)
71 ).current;
72 // 消息提醒
73 const toast = useRef<Toast>(null);
74 // 筛选器选项数据
75 const [selectedOption, setSelectedOption] = useState({ name: '热度最高' });
76 const options = [
77 { name: '来自好友' },
78 { name: '热度最高' }
79 ];
80 // 分页
81 const [first, setFirst] = useState(0);
82 const [rows, setRows] = useState(6);
83 const onPageChange = (event: PaginatorPageChangeEvent) => {
84 setFirst(event.first);
85 setRows(event.rows);
86 };
87 // 获取社区信息
88 useEffect(() => {
89 const fetchThreadInfo = async () => {
90 try {
91 const { data } = await axios.get(`http://127.0.0.1:4523/m1/6387307-6083949-default/community/info?communityId=${communityId}`);
92 setCommunityInfo(data);
93 setTotalThreads(data.threadNumber);
94 } catch (err) {
95 console.error(err);
96 toast.current?.show({ severity: 'error', summary: 'error', detail: '获取社区信息失败' });
97 }
98 };
99 fetchThreadInfo();
100 }, [communityId]);
101
102 // 获取帖子列表
103 useEffect(() => {
104 fetchThreads();
105 }, [communityId, first, rows, selectedOption, searchValue]);
106
107 const fetchThreads = async () => {
108 try {
109 const page = first / rows + 1;
110 console.log("当前页" + page + "size" + rows + "搜索内容" + searchValue);
111 const option = selectedOption.name // 添加排序参数
112 const response = await axios.get<ThreadList>(
113 `http://127.0.0.1:4523/m1/6387307-6083949-default/community/threads`, {
114 params: { communityId, page, rows, option, searchValue }
115 }
116 );
117 console.log('获取帖子列表:', response.data.records);
118 setThreads(response.data.records);
119 } catch (err) {
120 console.error('获取帖子失败', err);
121 toast.current?.show({ severity: 'error', summary: 'error', detail: '获取帖子失败' });
122 }
123 };
124
125 // 发布帖子弹窗
126 const [visible, setVisible] = useState(false);
127 const [formData, setFormData] = useState({
128 title: '',
129 content: '',
130 threadPicture: ''
131 });
132 // 图片上传消息通知
133 const onUpload = () => {
134 toast.current?.show({ severity: 'info', summary: 'Success', detail: 'File Uploaded' });
135 };
136
137 // 发帖接口
138 const handleSubmit = async () => {
139 try {
140 const currentDate = new Date().toISOString();
141 const postData = {
142 userId: 22301145, // 记得用户登录状态获取
143 threadPicture: formData.threadPicture,
144 title: formData.title,
145 content: formData.content,
146 createdAt: currentDate,
147 communityId: communityId // 从URL参数获取的社区ID
148 };
149 // 发送POST请求
150 const response = await axios.post('http://127.0.0.1:4523/m1/6387307-6083949-default/thread', postData);
151
152 if (response.status === 200) {
153 toast.current?.show({ severity: 'success', summary: 'Success', detail: '帖子发布成功' });
154 // 发帖成功
155 setVisible(false);
156 // 重置表单
157 setFormData({
158 title: '',
159 content: '',
160 threadPicture: ''
161 });
162 // 可以刷新帖子列表
163 fetchThreads();
164 }
165 } catch (error) {
166 console.error('发帖失败:', error);
167 toast.current?.show({ severity: 'error', summary: 'error', detail: '帖子发布失败' });
168 }
169 };
170 return (
171 <div className="resource-community">
172 <Toast ref={toast}></Toast>
173 {/* 社区标题和介绍 */}
174 <div className="community-header">
175 <div className="title-section">
176 <h1>{communityInfo?.communityName}</h1>
177 <p className="subtitle">{communityInfo?.description}</p>
178 <div className="community-states">
179 <div className="state-item">
180 <Fire theme="outline" size="16" fill="#FF8D1A" />
181 <span>热度: {communityInfo?.hot}</span>
182 </div>
183 <div className="state-item">
184 <i className="pi pi-book" />
185 <span>帖子数: {totalThreads}</span>
186 </div>
187 </div>
188 </div>
189 <div className="input">
190 <Button label="返回列表" link onClick={() => router.push(`/community/resource-community-list/${communityInfo?.type}`)} />
191 <div className="action-section">
192 <div className="communities-searchBar">
193 <i className="pi pi-search" />
194 <InputText type="search" className="search-helper" placeholder="搜索你感兴趣的帖子" onChange={(e) => { const target = e.target as HTMLInputElement; debouncedSearch(target.value); }} />
195 </div>
196 <Dropdown
197 value={selectedOption}
198 onChange={(e) => setSelectedOption(e.value)}
199 options={options}
200 optionLabel="name"
201 className="select-dropdown"
202 />
203 </div>
204 </div>
205 </div>
206
207 {/* 帖子列表 */}
208 <div className="thread-list">
209 <div className="resource-grid">
210 {threads.map((thread) => (
211 <Card key={thread.threadId} className="resource-card" onClick={() => router.push(`/community/thread-detail/${thread.threadId}`)}>
212 <Image
213 src={process.env.NEXT_PUBLIC_NGINX_URL + thread.threadPicture}
214 alt={thread.title}
215 width="368"
216 height="200"
217 />
218 <div className="card-content">
219 <h3>{thread.title}</h3>
220 <div className="view-count">
221 <i className="pi pi-face-smile" />
222 <span>{thread.likes}</span>
223 </div>
224 </div>
225 </Card>
226 ))}
227 </div>
228 {totalThreads > 6 && (<Paginator className="Paginator" first={first} rows={rows} totalRecords={totalThreads} rowsPerPageOptions={[6, 12]} onPageChange={onPageChange} />)}
229 </div>
230
231 {/* 添加按钮 */}
232 <Button className="add-resource-button" icon="pi pi-plus" rounded aria-label="add-thread" onClick={() => setVisible(true)} />
233
234 {/* 发布帖子弹窗 */}
235 <Dialog
236 header="发布新帖子"
237 visible={visible}
238 onHide={() => setVisible(false)}
239 className="publish-dialog"
240 modal
241 footer={
242 <div className="dialog-footer">
243 <Button label="发布" icon="pi pi-check" onClick={handleSubmit} autoFocus />
244 <Button label="取消" icon="pi pi-times" onClick={() => setVisible(false)} className="p-button-text" />
245 </div>
246 }
247 >
248 <div className="publish-form">
249 <div className="form-field">
250 <label htmlFor="title">标题</label>
251 <InputText
252 id="title"
253 value={formData.title}
254 onChange={(e) => setFormData(prev => ({ ...prev, title: e.target.value }))}
255 placeholder="请输入帖子标题"
256 className="w-full"
257 />
258 </div>
259
260 <div className="form-field">
261 <label htmlFor="content">内容</label>
262 <InputTextarea
263 id="content"
264 value={formData.content}
265 onChange={(e) => setFormData(prev => ({ ...prev, content: e.target.value }))}
266 rows={5}
267 placeholder="请输入帖子内容"
268 className="w-full"
269 />
270 </div>
271
272 <div className="form-field">
273 <label>封面图片</label>
274 <FileUpload
275 mode="basic"
276 name="thread-image"
277 url="/file" // 与后端交互的URL
278 accept="image/*"
279 maxFileSize={10000000000}
280 chooseLabel="选择图片"
281 className="w-full"
282 onUpload={onUpload}
283 />
284 </div>
285 </div>
286 </Dialog>
287 </div>
288 );
289}