blob: 33011eb7903d80b7e726f40eadb012021ac54968 [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';
LaoeGaocid0773912025-06-09 00:38:40 +080026import { useLocalStorage } from '../../../hook/useLocalStorage';
LaoeGaociee7c5772025-05-28 12:34:47 +080027// 样式
28import './resource-community.scss';
29
LaoeGaocid0773912025-06-09 00:38:40 +080030interface User {
LaoeGaoci36b5bc82025-06-09 20:52:39 +080031 Id: number;
LaoeGaocid0773912025-06-09 00:38:40 +080032}
LaoeGaociee7c5772025-05-28 12:34:47 +080033// 帖子列表数据
34interface Thread {
35 threadId: number;
36 userId: number;
37 threadPicture: string;
38 title: string;
39 createdAt: string;
40 communityId: string;
41 likes: number;
42}
43interface ThreadList {
44 records: Thread[];
45}
46// 社区信息
47interface CommunityInfo {
48 communityId: string;
49 communityName: string;
50 communityPicture: string;
51 description: string;
52 hot: number;
53 type: string;
54 threadNumber: number;
55}
56
57
58// 社区详情页面
59export default function CommunityDetailPage() {
LaoeGaocid0773912025-06-09 00:38:40 +080060 const user = useLocalStorage<User>('user');
61 const userId: number = user?.Id ?? -1;
LaoeGaociee7c5772025-05-28 12:34:47 +080062 // 获取URL参数,页面跳转
63 const params = useParams<{ communityId: string }>()
64 const communityId = decodeURIComponent(params.communityId); // 防止中文路径乱码
65 const router = useRouter();
66 // 社区数据
67 const [communityInfo, setCommunityInfo] = useState<CommunityInfo | null>(null);
68 // 帖子列表数据
69 const [threads, setThreads] = useState<Thread[]>([]);
70 const [totalThreads, setTotalThreads] = useState<number>(0);
71 // 搜索框
72 const [searchValue, setSearchValue] = useState("");
73 const debouncedSearch = useRef(
74 debounce((value: string) => {
75 setSearchValue(value);
76 }, 600)
77 ).current;
78 // 消息提醒
79 const toast = useRef<Toast>(null);
80 // 筛选器选项数据
81 const [selectedOption, setSelectedOption] = useState({ name: '热度最高' });
82 const options = [
83 { name: '来自好友' },
84 { name: '热度最高' }
85 ];
86 // 分页
87 const [first, setFirst] = useState(0);
88 const [rows, setRows] = useState(6);
89 const onPageChange = (event: PaginatorPageChangeEvent) => {
90 setFirst(event.first);
91 setRows(event.rows);
92 };
93 // 获取社区信息
94 useEffect(() => {
95 const fetchThreadInfo = async () => {
96 try {
LaoeGaoci388f7762025-05-29 22:24:35 +080097 const { data } = await axios.get(process.env.PUBLIC_URL + `/community/info?communityId=${communityId}`);
LaoeGaociee7c5772025-05-28 12:34:47 +080098 setCommunityInfo(data);
99 setTotalThreads(data.threadNumber);
100 } catch (err) {
101 console.error(err);
102 toast.current?.show({ severity: 'error', summary: 'error', detail: '获取社区信息失败' });
103 }
104 };
105 fetchThreadInfo();
106 }, [communityId]);
107
108 // 获取帖子列表
109 useEffect(() => {
110 fetchThreads();
111 }, [communityId, first, rows, selectedOption, searchValue]);
112
113 const fetchThreads = async () => {
114 try {
LaoeGaoci388f7762025-05-29 22:24:35 +0800115 const pageNumber = first / rows + 1;
116 console.log("当前页" + pageNumber + "size" + rows + "搜索内容" + searchValue);
LaoeGaociee7c5772025-05-28 12:34:47 +0800117 const option = selectedOption.name // 添加排序参数
118 const response = await axios.get<ThreadList>(
LaoeGaoci388f7762025-05-29 22:24:35 +0800119 process.env.PUBLIC_URL + `/community/threads`, {
LaoeGaocid0773912025-06-09 00:38:40 +0800120 params: { userId, communityId, pageNumber, rows, option, searchValue }
LaoeGaociee7c5772025-05-28 12:34:47 +0800121 }
122 );
123 console.log('获取帖子列表:', response.data.records);
124 setThreads(response.data.records);
125 } catch (err) {
126 console.error('获取帖子失败', err);
127 toast.current?.show({ severity: 'error', summary: 'error', detail: '获取帖子失败' });
128 }
129 };
130
131 // 发布帖子弹窗
132 const [visible, setVisible] = useState(false);
133 const [formData, setFormData] = useState({
134 title: '',
135 content: '',
136 threadPicture: ''
137 });
138 // 图片上传消息通知
LaoeGaoci36b5bc82025-06-09 20:52:39 +0800139
LaoeGaociee7c5772025-05-28 12:34:47 +0800140
141 // 发帖接口
142 const handleSubmit = async () => {
143 try {
144 const currentDate = new Date().toISOString();
Seamherbb14ecb2025-06-09 22:37:20 +0800145 console.log(formData);
LaoeGaociee7c5772025-05-28 12:34:47 +0800146 const postData = {
LaoeGaocid0773912025-06-09 00:38:40 +0800147 userId, // 记得用户登录状态获取
LaoeGaociee7c5772025-05-28 12:34:47 +0800148 threadPicture: formData.threadPicture,
149 title: formData.title,
150 content: formData.content,
Seamherbb14ecb2025-06-09 22:37:20 +0800151 createAt: currentDate.slice(0, 10),
LaoeGaociee7c5772025-05-28 12:34:47 +0800152 communityId: communityId // 从URL参数获取的社区ID
153 };
154 // 发送POST请求
LaoeGaoci388f7762025-05-29 22:24:35 +0800155 const response = await axios.post(process.env.PUBLIC_URL + '/thread', postData);
LaoeGaociee7c5772025-05-28 12:34:47 +0800156
157 if (response.status === 200) {
158 toast.current?.show({ severity: 'success', summary: 'Success', detail: '帖子发布成功' });
159 // 发帖成功
160 setVisible(false);
161 // 重置表单
162 setFormData({
163 title: '',
164 content: '',
165 threadPicture: ''
166 });
167 // 可以刷新帖子列表
168 fetchThreads();
169 }
170 } catch (error) {
171 console.error('发帖失败:', error);
172 toast.current?.show({ severity: 'error', summary: 'error', detail: '帖子发布失败' });
173 }
174 };
175 return (
176 <div className="resource-community">
177 <Toast ref={toast}></Toast>
178 {/* 社区标题和介绍 */}
179 <div className="community-header">
180 <div className="title-section">
181 <h1>{communityInfo?.communityName}</h1>
182 <p className="subtitle">{communityInfo?.description}</p>
183 <div className="community-states">
184 <div className="state-item">
185 <Fire theme="outline" size="16" fill="#FF8D1A" />
186 <span>热度: {communityInfo?.hot}</span>
187 </div>
188 <div className="state-item">
189 <i className="pi pi-book" />
190 <span>帖子数: {totalThreads}</span>
191 </div>
192 </div>
193 </div>
194 <div className="input">
LaoeGaociee7c5772025-05-28 12:34:47 +0800195 <div className="action-section">
LaoeGaoci388f7762025-05-29 22:24:35 +0800196 <div className="searchBar">
LaoeGaociee7c5772025-05-28 12:34:47 +0800197 <i className="pi pi-search" />
198 <InputText type="search" className="search-helper" placeholder="搜索你感兴趣的帖子" onChange={(e) => { const target = e.target as HTMLInputElement; debouncedSearch(target.value); }} />
199 </div>
200 <Dropdown
201 value={selectedOption}
202 onChange={(e) => setSelectedOption(e.value)}
203 options={options}
204 optionLabel="name"
205 className="select-dropdown"
206 />
207 </div>
208 </div>
209 </div>
210
211 {/* 帖子列表 */}
212 <div className="thread-list">
213 <div className="resource-grid">
214 {threads.map((thread) => (
215 <Card key={thread.threadId} className="resource-card" onClick={() => router.push(`/community/thread-detail/${thread.threadId}`)}>
216 <Image
Seamherbb14ecb2025-06-09 22:37:20 +0800217 src={ thread.threadPicture}
LaoeGaociee7c5772025-05-28 12:34:47 +0800218 alt={thread.title}
219 width="368"
220 height="200"
221 />
222 <div className="card-content">
223 <h3>{thread.title}</h3>
224 <div className="view-count">
225 <i className="pi pi-face-smile" />
226 <span>{thread.likes}</span>
227 </div>
228 </div>
229 </Card>
230 ))}
231 </div>
232 {totalThreads > 6 && (<Paginator className="Paginator" first={first} rows={rows} totalRecords={totalThreads} rowsPerPageOptions={[6, 12]} onPageChange={onPageChange} />)}
233 </div>
234
235 {/* 添加按钮 */}
236 <Button className="add-resource-button" icon="pi pi-plus" rounded aria-label="add-thread" onClick={() => setVisible(true)} />
237
238 {/* 发布帖子弹窗 */}
239 <Dialog
240 header="发布新帖子"
241 visible={visible}
242 onHide={() => setVisible(false)}
243 className="publish-dialog"
244 modal
245 footer={
246 <div className="dialog-footer">
247 <Button label="发布" icon="pi pi-check" onClick={handleSubmit} autoFocus />
248 <Button label="取消" icon="pi pi-times" onClick={() => setVisible(false)} className="p-button-text" />
249 </div>
250 }
251 >
252 <div className="publish-form">
253 <div className="form-field">
254 <label htmlFor="title">标题</label>
255 <InputText
256 id="title"
257 value={formData.title}
258 onChange={(e) => setFormData(prev => ({ ...prev, title: e.target.value }))}
259 placeholder="请输入帖子标题"
260 className="w-full"
261 />
262 </div>
263
264 <div className="form-field">
265 <label htmlFor="content">内容</label>
266 <InputTextarea
267 id="content"
268 value={formData.content}
269 onChange={(e) => setFormData(prev => ({ ...prev, content: e.target.value }))}
270 rows={5}
271 placeholder="请输入帖子内容"
272 className="w-full"
273 />
274 </div>
275
276 <div className="form-field">
277 <label>封面图片</label>
278 <FileUpload
LaoeGaoci36b5bc82025-06-09 20:52:39 +0800279 mode="advanced"
280 name="file"
281 customUpload
282 uploadHandler={async (e) => {
283 const formData = new FormData();
284 formData.append("file", e.files[0]);
285
286 try {
287 const res = await axios.post(`${process.env.PUBLIC_URL}/file`, formData);
288
Seamherbb14ecb2025-06-09 22:37:20 +0800289 const fileUrl = res.data;
LaoeGaoci36b5bc82025-06-09 20:52:39 +0800290 console.log(fileUrl);
Seamherbb14ecb2025-06-09 22:37:20 +0800291 setFormData(prev => ({ ...prev, threadPicture: fileUrl }))
LaoeGaoci36b5bc82025-06-09 20:52:39 +0800292 toast.current?.show({ severity: 'success', summary: '上传成功' });
293 } catch (error) {
294 console.log(error);
295 toast.current?.show({ severity: 'error', summary: '上传失败' });
296 }
297 }}
298 auto
LaoeGaociee7c5772025-05-28 12:34:47 +0800299 accept="image/*"
LaoeGaoci36b5bc82025-06-09 20:52:39 +0800300 chooseLabel="上传帖子封面"
LaoeGaociee7c5772025-05-28 12:34:47 +0800301 />
302 </div>
303 </div>
304 </Dialog>
305 </div>
306 );
307}