blob: 35ae4283e6ccfbecfeae35a30d5fb6a686d4fcea [file] [log] [blame]
22301080a93bebb2025-05-27 19:48:11 +08001import React, { useState, useEffect } from 'react';
2import { useNavigate } from 'react-router-dom';
3import './Administer.css';
4import {
5 getAllUsers,
6 searchUsers,
7 updateUserAuthority,
8 getAllDiscounts,
9 getCurrentDiscount,
10 addDiscount,
11 deleteDiscount
12} from '../api/administer';
13import DatePicker from 'react-datepicker';
14import 'react-datepicker/dist/react-datepicker.css';
15
16
17const Administer = () => {
18 const navigate = useNavigate();
19 const [users, setUsers] = useState([]);
20 const [discounts, setDiscounts] = useState([]);
21 const [currentDiscount, setCurrentDiscount] = useState(null);
22 const [searchKey, setSearchKey] = useState('');
23 const [loading, setLoading] = useState(false);
24 const [error, setError] = useState(null);
25 const [newDiscount, setNewDiscount] = useState({
26 name: '',
27 discountType: 'FREE'
28 });
29 const [startDate, setStartDate] = useState(new Date());
30 const [endDate, setEndDate] = useState(new Date());
31 const [activeTab, setActiveTab] = useState('users'); // 'users' 或 'discounts'
32
33const fetchAllUsers = async () => {
34 setLoading(true);
35 setError(null);
36 try {
37 const users = await getAllUsers();
38 console.log("API Data:", users); // 现在应该直接是用户数组
39
40 const formattedUsers = users.map(user => ({
41 username: user.username || '未知用户',
42 authority: user.authority || 'USER',
43 registTime: user.registTime || null,
44 lastLogin: user.lastLogin || null,
45 upload: Number(user.upload) || 0,
46 download: Number(user.download) || 0,
47 magicPoints: Number(user.magicPoints) || 0,
48 shareRate: Number(user.shareRate) || 0
49 }));
50
51 console.log("Formatted Users:", formattedUsers);
52 setUsers(formattedUsers);
53 } catch (err) {
54 console.error("Error details:", err);
55 setError(`获取用户列表失败: ${err.message}`);
56 } finally {
57 setLoading(false);
58 }
59};
60
61
62 const handleSearch = async () => {
63 if (!searchKey.trim()) {
64 fetchAllUsers();
65 return;
66 }
67
68 setLoading(true);
69 setError(null);
70 try {
71 const users = await searchUsers(searchKey);
72 console.log("Search Results:", users); // 打印搜索结果
73
74 // 格式化数据(确保数值字段正确解析)
75 const formattedUsers = users.map(user => ({
76 username: user.username || '未知用户',
77 authority: user.authority || 'USER',
78 registTime: user.registTime || null,
79 lastLogin: user.lastLogin || null,
80 upload: Number(user.upload) || 0, // 确保解析为数字
81 download: Number(user.download) || 0,
82 magicPoints: Number(user.magicPoints) || 0,
83 shareRate: Number(user.shareRate) || 0
84 }));
85
86 setUsers(formattedUsers);
87 } catch (err) {
88 setError('搜索用户失败,请重试');
89 console.error(err);
90 } finally {
91 setLoading(false);
92 }
93};
94
95 // 重置搜索
96 const handleReset = () => {
97 setSearchKey('');
98 fetchAllUsers();
99 };
100
101 // 修改用户权限
102 const handleChangeAuthority = async (username, newAuthority) => {
103 try {
104 await updateUserAuthority(username, newAuthority);
105 // 更新本地状态
106 setUsers(users.map(user =>
107 user.username === username ? { ...user, authority: newAuthority } : user
108 ));
109 } catch (err) {
110 setError('修改权限失败,请重试');
111 console.error(err);
112 }
113 };
114
115 // 获取所有折扣
116 const fetchAllDiscounts = async () => {
117 setLoading(true);
118 setError(null);
119 try {
120 const data = await getAllDiscounts();
121 setDiscounts(data);
122 } catch (err) {
123 setError('获取折扣列表失败: ' + err.message);
124 console.error(err);
125 } finally {
126 setLoading(false);
127 }
128 };
129
130 // 获取当前折扣
131 const fetchCurrentDiscount = async () => {
132 try {
133 const data = await getCurrentDiscount();
134 setCurrentDiscount(data);
135 } catch (err) {
136 console.error('获取当前折扣失败:', err);
137 }
138 };
139
140 const handleAddDiscount = async () => {
141 if (!newDiscount.name || !startDate || !endDate) {
142 setError('请填写所有必填字段');
143 return;
144 }
145
146 try {
147 // 验证时间
148 if (startDate >= endDate) {
149 setError('结束时间必须晚于开始时间');
150 return;
151 }
152
153 const payload = {
154 name: newDiscount.name,
155 startTime: formatDateToISO(startDate), // 例如: "2025-06-01T14:30:00"
156 endTime: formatDateToISO(endDate, true), // 例如: "2025-06-01T18:45:59"
157 discountType: newDiscount.discountType
158 };
159
160 console.log('提交数据:', payload); // 调试用
161
162 await addDiscount(payload);
163
164 // 重置表单
165 setNewDiscount({
166 name: '',
167 discountType: 'FREE'
168 });
169 setStartDate(new Date());
170 setEndDate(new Date());
171
172 fetchAllDiscounts();
173 setError(null);
174 } catch (err) {
175 setError('添加折扣失败: ' + err.message);
176 console.error(err);
177 }
178};
179
180const formatDateToISO = (date, isEndTime = false) => {
181 if (!date) return '';
182
183 const pad = (num) => num.toString().padStart(2, '0');
184
185 const year = date.getFullYear();
186 const month = pad(date.getMonth() + 1);
187 const day = pad(date.getDate());
188 const hours = pad(date.getHours());
189 const minutes = pad(date.getMinutes());
190
191 if (isEndTime) {
192 // 结束时间精确到用户选择的时间+59秒
193 return `${year}-${month}-${day}T${hours}:${minutes}:59`;
194 } else {
195 // 开始时间精确到用户选择的时间+00秒
196 return `${year}-${month}-${day}T${hours}:${minutes}:00`;
197 }
198};
199
200 // 删除折扣
201 const handleDeleteDiscount = async (id) => {
202 try {
203 await deleteDiscount(id);
204 fetchAllDiscounts();
205 } catch (err) {
206 setError('删除折扣失败: ' + err.message);
207 console.error(err);
208 }
209 };
210
211 // 初始化加载数据
212 useEffect(() => {
213 if (activeTab === 'users') {
214 fetchAllUsers();
215 } else {
216 fetchAllDiscounts();
217 fetchCurrentDiscount();
218 }
219 }, [activeTab]);
220
221 // 格式化分享率为百分比
222 const formatShareRate = (rate) => {
223 return (rate * 100).toFixed(2) + '%';
224 };
225
226 // 格式化日期
227 const formatDate = (date) => {
228 if (!date) return '-';
229 return new Date(date).toLocaleDateString();
230 };
231 // 格式化日期
232 const formatDateTime = (dateTime) => {
233 if (!dateTime) return '-';
234 return new Date(dateTime).toLocaleString();
235 };
236
237 // 折扣类型翻译
238 const translateDiscountType = (type) => {
239 switch (type) {
240 case 'FREE': return '全部免费';
241 case 'HALF': return '半价下载';
242 case 'DOUBLE': return '双倍上传';
243 default: return type;
244 }
245 };
246
247
248 return (
249 <div className="administer-container">
250 <h1>系统管理</h1>
251
252 {/* 选项卡切换 */}
253 <div className="tab-container">
254 <button
255 className={`tab-button ${activeTab === 'users' ? 'active' : ''}`}
256 onClick={() => setActiveTab('users')}
257 >
258 用户管理
259 </button>
260 <button
261 className={`tab-button ${activeTab === 'discounts' ? 'active' : ''}`}
262 onClick={() => setActiveTab('discounts')}
263 >
264 折扣管理
265 </button>
266 </div>
267
268 {activeTab === 'users' ? (
269 <>
270 {/* 搜索框 */}
271 <div className="search-container">
272 <input
273 type="text"
274 value={searchKey}
275 onChange={(e) => setSearchKey(e.target.value)}
276 placeholder="输入用户名搜索"
277 className="search-input"
278 />
279 <button onClick={handleSearch} className="search-button">
280 搜索
281 </button>
282 <button onClick={handleReset} className="reset-button">
283 重置
284 </button>
285 </div>
286
287 {/* 错误提示 */}
288 {error && <div className="error-message">{error}</div>}
289
290 {/* 加载状态 */}
291 {loading && <div className="loading-message">加载中...</div>}
292
293 {/* 用户列表 */}
294 <div className="user-list-container">
295 <table className="user-table">
296 <thead>
297 <tr>
298 <th>用户名</th>
299 <th>注册时间</th>
300 <th>最后登录</th>
301 <th>上传量</th>
302 <th>下载量</th>
303 <th>分享率</th>
304 <th>魔力值</th>
305 <th>权限</th>
306 <th>操作</th>
307 </tr>
308 </thead>
309 <tbody>
310 {Array.isArray(users) && users.map((user) => (
311 <tr key={user.username}>
312 <td>{user.username}</td>
313 <td>{formatDate(user.registTime)}</td>
314 <td>{formatDate(user.lastLogin)}</td>
315 <td>{user.upload}</td>
316 <td>{user.download}</td>
317 <td>{formatShareRate(user.shareRate)}</td>
318 <td>{user.magicPoints}</td>
319 <td>{user.authority}</td>
320 <td>
321 <select
322 value={user.authority}
323 onChange={(e) => handleChangeAuthority(user.username, e.target.value)}
324 className="authority-select"
325 >
326 <option value="USER">普通用户</option>
327 <option value="ADMIN">管理员</option>
328 <option value="LIMIT">受限用户</option>
329 <option value="BAN">封禁用户</option>
330 </select>
331 </td>
332 </tr>
333 ))}
334 </tbody>
335 </table>
336 </div>
337 </>
338 ) : (
339 /* 新增的折扣管理部分 */
340 <>
341 {/* 当前活动折扣 */}
342 <div className="current-discount-section">
343 <h3>当前活动折扣</h3>
344 {currentDiscount ? (
345 <div className="current-discount-card">
346 <p><strong>名称:</strong> {currentDiscount.name}</p>
347 <p><strong>类型:</strong> {translateDiscountType(currentDiscount.discountType)}</p>
348 <p><strong>时间:</strong> {formatDateTime(currentDiscount.startTime)} 至 {formatDateTime(currentDiscount.endTime)}</p>
349 <p><strong>状态:</strong> {currentDiscount.status}</p>
350 </div>
351 ) : (
352 <p>当前没有进行中的折扣</p>
353 )}
354 </div>
355
356 {/* 添加新折扣表单 */}
357 <div className="add-discount-form">
358 <h3>添加新折扣</h3>
359 <div className="form-group">
360 <label>折扣名称:</label>
361 <input
362 type="text"
363 value={newDiscount.name}
364 onChange={(e) => setNewDiscount({...newDiscount, name: e.target.value})}
365 />
366 </div>
367 <div className="form-group">
368 <label>开始时间:</label>
369 <DatePicker
370 selected={startDate}
371 onChange={(date) => setStartDate(date)}
372 showTimeSelect
373 timeFormat="HH:mm"
374 timeIntervals={1} // 1分钟间隔
375 dateFormat="yyyy-MM-dd HH:mm"
376 minDate={new Date()}
377 placeholderText="选择开始日期和时间"
378 />
379 </div>
380 <div className="form-group">
381 <label>结束时间:</label>
382 <DatePicker
383 selected={endDate}
384 onChange={(date) => setEndDate(date)}
385 showTimeSelect
386 timeFormat="HH:mm"
387 timeIntervals={1} // 1分钟间隔
388 dateFormat="yyyy-MM-dd HH:mm"
389 minDate={startDate}
390 placeholderText="选择结束日期和时间"
391 />
392 </div>
393 <div className="form-group">
394 <label>折扣类型:</label>
395 <select
396 value={newDiscount.discountType}
397 onChange={(e) => setNewDiscount({...newDiscount, discountType: e.target.value})}
398 >
399 <option value="FREE">全部免费</option>
400 <option value="HALF">半价下载</option>
401 <option value="DOUBLE">双倍上传</option>
402 </select>
403 </div>
404 <button
405 onClick={(e) => {
406 e.preventDefault(); // 确保没有阻止默认行为
407 handleAddDiscount();
408 }}
409 >
410 添加折扣
411 </button>
412 </div>
413
414 {/* 所有折扣列表 */}
415 <div className="discount-list-container">
416 <h3>所有折扣计划</h3>
417 <table className="discount-table">
418 <thead>
419 <tr>
420 <th>ID</th>
421 <th>名称</th>
422 <th>开始时间</th>
423 <th>结束时间</th>
424 <th>类型</th>
425 <th>创建时间</th>
426 <th>状态</th>
427 <th>操作</th>
428 </tr>
429 </thead>
430 <tbody>
431 {discounts.map(discount => (
432 <tr key={discount.id}>
433 <td>{discount.id}</td>
434 <td>{discount.name}</td>
435 <td>{formatDateTime(discount.startTime)}</td>
436 <td>{formatDateTime(discount.endTime)}</td>
437 <td>{translateDiscountType(discount.discountType)}</td>
438 <td>{formatDateTime(discount.createTime)}</td>
439 <td>{discount.status || '未知'}</td>
440 <td>
441 <button
442 onClick={() => handleDeleteDiscount(discount.id)}
443 className="delete-button"
444 >
445 删除
446 </button>
447 </td>
448 </tr>
449 ))}
450 </tbody>
451 </table>
452 </div>
453 </>
454 )}
455 </div>
456 );
457};
458
459export default Administer;