blob: bb50d72c048739d68f575ae8a81d523e45cebcfb [file] [log] [blame]
Krishya57cc17b2025-05-26 16:43:34 +08001import React, { useState, useEffect } from 'react';
2import './PromotionsPage.css';
3
4const API_BASE = process.env.REACT_APP_API_BASE;
5function PromotionsPage() {
6 const [promotions, setPromotions] = useState([]);
7 const [currentPromotion, setCurrentPromotion] = useState(null);
8 const [isAdmin, setIsAdmin] = useState(false);
9 const [isLoading, setIsLoading] = useState(false);
10 const [error, setError] = useState(null);
11 const [formData, setFormData] = useState({
12 name: '',
13 uploadCoeff: 1,
14 downloadCoeff: 1,
15 timeRange: 0,
16 criteria: 1,
17 pStartTime: '',
18 pEndTime: ''
19 });
20 const [isCreating, setIsCreating] = useState(false);
21 const [currentPage, setCurrentPage] = useState(1);
22 const [perPage] = useState(10);
23 const [totalPromotions, setTotalPromotions] = useState(0);
24
25 const getAuthHeaders = () => {
26 const token = localStorage.getItem('token');
27 return {
28 'Authorization': token ? `Bearer ${token}` : '',
29 'Content-Type': 'application/json'
30 };
31 };
32
33 const fetchPromotions = async (page = 1) => {
34 setIsLoading(true);
35 try {
36 const response = await fetch(`${API_BASE}/promotions/list?page=${page}&per_page=${perPage}`, {
37 headers: getAuthHeaders()
38 });
39
40 if (!response.ok) {
41 throw new Error('获取促销活动失败');
42 }
43
44 const data = await response.json();
45 if (data.code === 0 && data.result) {
46 setPromotions(data.rows || []);
47 setTotalPromotions(data.total || 0);
48 } else {
49 throw new Error(data.msg || '获取促销活动失败');
50 }
51 } catch (err) {
52 console.error('获取促销活动错误:', err);
53 setError(err.message);
54 } finally {
55 setIsLoading(false);
56 }
57 };
58
59 const fetchPromotionDetails = async (promoId) => {
60 setIsLoading(true);
61 try {
62 const response = await fetch(`${API_BASE}/promotions/${promoId}`, {
63 headers: getAuthHeaders()
64 });
65
66 if (!response.ok) {
67 throw new Error('获取促销详情失败');
68 }
69
70 const data = await response.json();
71 if (data.code === 0 && data.result) {
72 setCurrentPromotion(data.rows);
73 } else {
74 throw new Error(data.msg || '获取促销详情失败');
75 }
76 } catch (err) {
77 console.error('获取促销详情错误:', err);
78 setError(err.message);
79 } finally {
80 setIsLoading(false);
81 }
82 };
83
84 const createPromotion = async () => {
85 if (!formData.name || !formData.pStartTime || !formData.pEndTime) {
86 alert('请填写完整活动信息');
87 return;
88 }
89
90 if (new Date(formData.pStartTime) >= new Date(formData.pEndTime)) {
91 alert('活动时间设置不正确,请重新设定');
92 return;
93 }
94
95 setIsLoading(true);
96 try {
97 const response = await fetch('${API_BASE}/promotions/add', {
98 method: 'POST',
99 headers: getAuthHeaders(),
100 body: JSON.stringify(formData)
101 });
102
103 if (!response.ok) {
104 throw new Error('创建促销活动失败');
105 }
106
107 const data = await response.json();
108 if (data.code === 0 && data.result) {
109 alert(`活动创建成功!活动ID: ${data.msg}`);
110 setIsCreating(false);
111 setFormData({
112 name: '',
113 uploadCoeff: 1,
114 downloadCoeff: 1,
115 timeRange: 0,
116 criteria: 1,
117 pStartTime: '',
118 pEndTime: ''
119 });
120 fetchPromotions();
121 } else {
122 throw new Error(data.msg || '创建促销活动失败');
123 }
124 } catch (err) {
125 console.error('创建促销活动错误:', err);
126 alert(err.message);
127 } finally {
128 setIsLoading(false);
129 }
130 };
131
132 const deletePromotion = async (promoId) => {
133 if (!window.confirm('确定要删除这个促销活动吗?')) {
134 return;
135 }
136
137 setIsLoading(true);
138 try {
139 const response = await fetch(`${API_BASE}/promotions/delete/${promoId}`, {
140 method: 'DELETE',
141 headers: getAuthHeaders()
142 });
143
144 if (!response.ok) {
145 throw new Error('删除促销活动失败');
146 }
147
148 const data = await response.json();
149 if (data.code === 0 && data.result) {
150 alert('促销活动删除成功');
151 fetchPromotions();
152 if (currentPromotion && currentPromotion.promoId === promoId) {
153 setCurrentPromotion(null);
154 }
155 } else {
156 throw new Error(data.msg || '删除促销活动失败');
157 }
158 } catch (err) {
159 console.error('删除促销活动错误:', err);
160 alert(err.message);
161 } finally {
162 setIsLoading(false);
163 }
164 };
165
166 const checkAdminStatus = () => {
167 const role = localStorage.getItem('role');
168 setIsAdmin(role === 'admin');
169 };
170
171 useEffect(() => {
172 checkAdminStatus();
173 fetchPromotions();
174 }, []);
175
176 const handleInputChange = (e) => {
177 const { name, value } = e.target;
178 setFormData(prev => ({
179 ...prev,
180 [name]: name === 'uploadCoeff' || name === 'downloadCoeff' || name === 'timeRange' || name === 'criteria'
181 ? parseFloat(value)
182 : value
183 }));
184 };
185
186 const handlePageChange = (newPage) => {
187 setCurrentPage(newPage);
188 fetchPromotions(newPage);
189 };
190
191 const getPromotionType = (promo) => {
192 if (promo.downloadCoeff === 0 && promo.uploadCoeff > 1) {
193 return '免费';
194 } else if (promo.downloadCoeff < 1 && promo.uploadCoeff > 1) {
195 return '折扣+上传奖励';
196 } else if (promo.downloadCoeff < 1) {
197 return '折扣';
198 } else if (promo.uploadCoeff > 1) {
199 return '上传奖励';
200 }
201 return '普通';
202 };
203
204 return (
205 <div className="promotions-page">
206 <div className="promotions-container">
207 <h1>促销活动</h1>
208
209 {isAdmin && (
210 <div className="admin-actions">
211 <button
212 className="create-button"
213 onClick={() => setIsCreating(!isCreating)}
214 >
215 {isCreating ? '取消创建' : '创建新活动'}
216 </button>
217 </div>
218 )}
219
220 {isCreating && isAdmin && (
221 <div className="create-promotion-form">
222 <h2>创建新促销活动</h2>
223 <div className="form-group">
224 <label>活动名称</label>
225 <input
226 type="text"
227 name="name"
228 value={formData.name}
229 onChange={handleInputChange}
230 placeholder="例如: 春节特惠"
231 />
232 </div>
233
234 <div className="form-row">
235 <div className="form-group">
236 <label>上传量系数</label>
237 <input
238 type="number"
239 name="uploadCoeff"
240 min="0"
241 step="0.1"
242 value={formData.uploadCoeff}
243 onChange={handleInputChange}
244 />
245 </div>
246
247 <div className="form-group">
248 <label>下载量系数</label>
249 <input
250 type="number"
251 name="downloadCoeff"
252 min="0"
253 step="0.1"
254 value={formData.downloadCoeff}
255 onChange={handleInputChange}
256 />
257 </div>
258 </div>
259
260 <div className="form-row">
261 <div className="form-group">
262 <label>资源时间范围</label>
263 <select
264 name="timeRange"
265 value={formData.timeRange}
266 onChange={handleInputChange}
267 >
268 <option value="0">全站资源</option>
269 <option value="1">当天上传</option>
270 <option value="2">最近两天</option>
271 <option value="7">最近一周</option>
272 <option value="30">最近一个月</option>
273 </select>
274 </div>
275
276 <div className="form-group">
277 <label>最低用户等级</label>
278 <input
279 type="number"
280 name="criteria"
281 min="1"
282 value={formData.criteria}
283 onChange={handleInputChange}
284 />
285 </div>
286 </div>
287
288 <div className="form-row">
289 <div className="form-group">
290 <label>开始时间</label>
291 <input
292 type="datetime-local"
293 name="pStartTime"
294 value={formData.pStartTime}
295 onChange={handleInputChange}
296 />
297 </div>
298
299 <div className="form-group">
300 <label>结束时间</label>
301 <input
302 type="datetime-local"
303 name="pEndTime"
304 value={formData.pEndTime}
305 onChange={handleInputChange}
306 />
307 </div>
308 </div>
309
310 <button
311 className="submit-button"
312 onClick={createPromotion}
313 disabled={isLoading}
314 >
315 {isLoading ? '创建中...' : '提交创建'}
316 </button>
317 </div>
318 )}
319
320 {error && <div className="error-message">{error}</div>}
321
322 <div className="promotions-grid">
323 {/* 促销活动列表 */}
324 <div className="promotions-list">
325 <h2>当前促销活动</h2>
326 {isLoading && promotions.length === 0 ? (
327 <div className="loading">加载中...</div>
328 ) : promotions.length === 0 ? (
329 <div className="no-promotions">暂无促销活动</div>
330 ) : (
331 <div className="promotion-items">
332 {promotions.map(promo => (
333 <div
334 key={promo.promoId}
335 className={`promotion-item ${currentPromotion && currentPromotion.promoId === promo.promoId ? 'active' : ''}`}
336 onClick={() => fetchPromotionDetails(promo.promoId)}
337 >
338 <div className="promotion-header">
339 <h3>{promo.name}</h3>
340 <span className="promotion-type">{getPromotionType(promo)}</span>
341 </div>
342 <div className="promotion-dates">
343 {new Date(promo.pStartTime).toLocaleString()} - {new Date(promo.pEndTime).toLocaleString()}
344 </div>
345 <div className="promotion-coeffs">
346 <span>上传: {promo.uploadCoeff}x</span>
347 <span>下载: {promo.downloadCoeff}x</span>
348 </div>
349 {isAdmin && (
350 <button
351 className="delete-button"
352 onClick={(e) => {
353 e.stopPropagation();
354 deletePromotion(promo.promoId);
355 }}
356 disabled={isLoading}
357 >
358 删除
359 </button>
360 )}
361 </div>
362 ))}
363 </div>
364 )}
365
366 {totalPromotions > perPage && (
367 <div className="pagination">
368 <button
369 disabled={currentPage === 1}
370 onClick={() => handlePageChange(currentPage - 1)}
371 >
372 上一页
373 </button>
374 <span> {currentPage} 页</span>
375 <button
376 disabled={currentPage * perPage >= totalPromotions}
377 onClick={() => handlePageChange(currentPage + 1)}
378 >
379 下一页
380 </button>
381 </div>
382 )}
383 </div>
384
385 {/* 促销活动详情 */}
386 <div className="promotion-details">
387 {currentPromotion ? (
388 <>
389 <h2>{currentPromotion.name}</h2>
390 <div className="detail-item">
391 <label>活动ID:</label>
392 <span>{currentPromotion.promoId}</span>
393 </div>
394 <div className="detail-item">
395 <label>活动时间:</label>
396 <span>
397 {new Date(currentPromotion.pStartTime).toLocaleString()} - {new Date(currentPromotion.pEndTime).toLocaleString()}
398 </span>
399 </div>
400 <div className="detail-item">
401 <label>促销类型:</label>
402 <span>{getPromotionType(currentPromotion)}</span>
403 </div>
404 <div className="detail-item">
405 <label>上传量系数:</label>
406 <span>{currentPromotion.uploadCoeff}x</span>
407 </div>
408 <div className="detail-item">
409 <label>下载量系数:</label>
410 <span>{currentPromotion.downloadCoeff}x</span>
411 </div>
412 <div className="detail-item">
413 <label>适用资源:</label>
414 <span>
415 {currentPromotion.timeRange === 0
416 ? '全站资源'
417 : `最近${currentPromotion.timeRange}天内上传的资源`}
418 </span>
419 </div>
420 <div className="detail-item">
421 <label>参与条件:</label>
422 <span>用户等级 {currentPromotion.criteria}</span>
423 </div>
424 </>
425 ) : (
426 <div className="no-selection">请从左侧选择一个促销活动查看详情</div>
427 )}
428 </div>
429 </div>
430 </div>
431 </div>
432 );
433}
434
435export default PromotionsPage;