“实现帖子与评论上传图片,删除评论,评论计数,管理员界面”

Change-Id: I33d5331e41de0411f2d6f1913f3a939db61f665d
diff --git a/src/components/Administer.jsx b/src/components/Administer.jsx
new file mode 100644
index 0000000..35ae428
--- /dev/null
+++ b/src/components/Administer.jsx
@@ -0,0 +1,459 @@
+import React, { useState, useEffect } from 'react';
+import { useNavigate } from 'react-router-dom';
+import './Administer.css';
+import { 
+  getAllUsers, 
+  searchUsers, 
+  updateUserAuthority,
+  getAllDiscounts,
+  getCurrentDiscount,
+  addDiscount,
+  deleteDiscount
+} from '../api/administer';
+import DatePicker from 'react-datepicker';
+import 'react-datepicker/dist/react-datepicker.css';
+
+
+const Administer = () => {
+  const navigate = useNavigate();
+  const [users, setUsers] = useState([]);
+  const [discounts, setDiscounts] = useState([]);
+  const [currentDiscount, setCurrentDiscount] = useState(null);
+  const [searchKey, setSearchKey] = useState('');
+  const [loading, setLoading] = useState(false);
+  const [error, setError] = useState(null);
+  const [newDiscount, setNewDiscount] = useState({
+    name: '',
+    discountType: 'FREE'
+  });
+  const [startDate, setStartDate] = useState(new Date());
+  const [endDate, setEndDate] = useState(new Date());
+  const [activeTab, setActiveTab] = useState('users'); // 'users' 或 'discounts'
+
+const fetchAllUsers = async () => {
+  setLoading(true);
+  setError(null);
+  try {
+    const users = await getAllUsers();
+    console.log("API Data:", users); // 现在应该直接是用户数组
+    
+    const formattedUsers = users.map(user => ({
+      username: user.username || '未知用户',
+      authority: user.authority || 'USER',
+      registTime: user.registTime || null,
+      lastLogin: user.lastLogin || null,
+      upload: Number(user.upload) || 0,
+      download: Number(user.download) || 0,
+      magicPoints: Number(user.magicPoints) || 0,
+      shareRate: Number(user.shareRate) || 0
+    }));
+    
+    console.log("Formatted Users:", formattedUsers);
+    setUsers(formattedUsers);
+  } catch (err) {
+    console.error("Error details:", err);
+    setError(`获取用户列表失败: ${err.message}`);
+  } finally {
+    setLoading(false);
+  }
+};
+
+
+ const handleSearch = async () => {
+  if (!searchKey.trim()) {
+    fetchAllUsers();
+    return;
+  }
+
+  setLoading(true);
+  setError(null);
+  try {
+    const users = await searchUsers(searchKey);
+    console.log("Search Results:", users); // 打印搜索结果
+
+    // 格式化数据(确保数值字段正确解析)
+    const formattedUsers = users.map(user => ({
+      username: user.username || '未知用户',
+      authority: user.authority || 'USER',
+      registTime: user.registTime || null,
+      lastLogin: user.lastLogin || null,
+      upload: Number(user.upload) || 0,    // 确保解析为数字
+      download: Number(user.download) || 0,
+      magicPoints: Number(user.magicPoints) || 0,
+      shareRate: Number(user.shareRate) || 0
+    }));
+
+    setUsers(formattedUsers);
+  } catch (err) {
+    setError('搜索用户失败,请重试');
+    console.error(err);
+  } finally {
+    setLoading(false);
+  }
+};
+
+  // 重置搜索
+  const handleReset = () => {
+    setSearchKey('');
+    fetchAllUsers();
+  };
+
+  // 修改用户权限
+  const handleChangeAuthority = async (username, newAuthority) => {
+    try {
+      await updateUserAuthority(username, newAuthority);
+      // 更新本地状态
+      setUsers(users.map(user => 
+        user.username === username ? { ...user, authority: newAuthority } : user
+      ));
+    } catch (err) {
+      setError('修改权限失败,请重试');
+      console.error(err);
+    }
+  };
+
+   // 获取所有折扣
+  const fetchAllDiscounts = async () => {
+    setLoading(true);
+    setError(null);
+    try {
+      const data = await getAllDiscounts();
+      setDiscounts(data);
+    } catch (err) {
+      setError('获取折扣列表失败: ' + err.message);
+      console.error(err);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  // 获取当前折扣
+  const fetchCurrentDiscount = async () => {
+    try {
+      const data = await getCurrentDiscount();
+      setCurrentDiscount(data);
+    } catch (err) {
+      console.error('获取当前折扣失败:', err);
+    }
+  };
+
+    const handleAddDiscount = async () => {
+  if (!newDiscount.name || !startDate || !endDate) {
+    setError('请填写所有必填字段');
+    return;
+  }
+
+  try {
+    // 验证时间
+    if (startDate >= endDate) {
+      setError('结束时间必须晚于开始时间');
+      return;
+    }
+
+    const payload = {
+      name: newDiscount.name,
+      startTime: formatDateToISO(startDate), // 例如: "2025-06-01T14:30:00"
+      endTime: formatDateToISO(endDate, true), // 例如: "2025-06-01T18:45:59"
+      discountType: newDiscount.discountType
+    };
+
+    console.log('提交数据:', payload); // 调试用
+
+    await addDiscount(payload);
+    
+    // 重置表单
+    setNewDiscount({
+      name: '',
+      discountType: 'FREE'
+    });
+    setStartDate(new Date());
+    setEndDate(new Date());
+    
+    fetchAllDiscounts();
+    setError(null);
+  } catch (err) {
+    setError('添加折扣失败: ' + err.message);
+    console.error(err);
+  }
+};
+
+const formatDateToISO = (date, isEndTime = false) => {
+  if (!date) return '';
+  
+  const pad = (num) => num.toString().padStart(2, '0');
+  
+  const year = date.getFullYear();
+  const month = pad(date.getMonth() + 1);
+  const day = pad(date.getDate());
+  const hours = pad(date.getHours());
+  const minutes = pad(date.getMinutes());
+  
+  if (isEndTime) {
+    // 结束时间精确到用户选择的时间+59秒
+    return `${year}-${month}-${day}T${hours}:${minutes}:59`;
+  } else {
+    // 开始时间精确到用户选择的时间+00秒
+    return `${year}-${month}-${day}T${hours}:${minutes}:00`;
+  }
+};
+
+  // 删除折扣
+  const handleDeleteDiscount = async (id) => {
+    try {
+      await deleteDiscount(id);
+      fetchAllDiscounts();
+    } catch (err) {
+      setError('删除折扣失败: ' + err.message);
+      console.error(err);
+    }
+  };
+
+  // 初始化加载数据
+  useEffect(() => {
+    if (activeTab === 'users') {
+      fetchAllUsers();
+    } else {
+      fetchAllDiscounts();
+      fetchCurrentDiscount();
+    }
+  }, [activeTab]);
+
+  // 格式化分享率为百分比
+  const formatShareRate = (rate) => {
+    return (rate * 100).toFixed(2) + '%';
+  };
+
+  // 格式化日期
+  const formatDate = (date) => {
+    if (!date) return '-';
+    return new Date(date).toLocaleDateString();
+  };
+    // 格式化日期
+  const formatDateTime = (dateTime) => {
+    if (!dateTime) return '-';
+    return new Date(dateTime).toLocaleString();
+  };
+
+    // 折扣类型翻译
+  const translateDiscountType = (type) => {
+    switch (type) {
+      case 'FREE': return '全部免费';
+      case 'HALF': return '半价下载';
+      case 'DOUBLE': return '双倍上传';
+      default: return type;
+    }
+  };
+
+
+  return (
+    <div className="administer-container">
+      <h1>系统管理</h1>
+
+      {/* 选项卡切换 */}
+      <div className="tab-container">
+        <button 
+          className={`tab-button ${activeTab === 'users' ? 'active' : ''}`}
+          onClick={() => setActiveTab('users')}
+        >
+          用户管理
+        </button>
+        <button 
+          className={`tab-button ${activeTab === 'discounts' ? 'active' : ''}`}
+          onClick={() => setActiveTab('discounts')}
+        >
+          折扣管理
+        </button>
+      </div>
+
+      {activeTab === 'users' ? (
+        <>
+            {/* 搜索框 */}
+            <div className="search-container">
+                <input
+                type="text"
+                value={searchKey}
+                onChange={(e) => setSearchKey(e.target.value)}
+                placeholder="输入用户名搜索"
+                className="search-input"
+                />
+                <button onClick={handleSearch} className="search-button">
+                搜索
+                </button>
+                <button onClick={handleReset} className="reset-button">
+                重置
+                </button>
+            </div>
+
+            {/* 错误提示 */}
+            {error && <div className="error-message">{error}</div>}
+
+            {/* 加载状态 */}
+            {loading && <div className="loading-message">加载中...</div>}
+
+            {/* 用户列表 */}
+            <div className="user-list-container">
+                <table className="user-table">
+                <thead>
+                    <tr>
+                    <th>用户名</th>
+                    <th>注册时间</th>
+                    <th>最后登录</th>
+                    <th>上传量</th>
+                    <th>下载量</th>
+                    <th>分享率</th>
+                    <th>魔力值</th>
+                    <th>权限</th>
+                    <th>操作</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    {Array.isArray(users) && users.map((user) => (
+                    <tr key={user.username}>
+                        <td>{user.username}</td>
+                        <td>{formatDate(user.registTime)}</td>
+                        <td>{formatDate(user.lastLogin)}</td>
+                        <td>{user.upload}</td>
+                        <td>{user.download}</td>
+                        <td>{formatShareRate(user.shareRate)}</td>
+                        <td>{user.magicPoints}</td>
+                        <td>{user.authority}</td>
+                        <td>
+                        <select
+                            value={user.authority}
+                            onChange={(e) => handleChangeAuthority(user.username, e.target.value)}
+                            className="authority-select"
+                        >
+                            <option value="USER">普通用户</option>
+                            <option value="ADMIN">管理员</option>
+                            <option value="LIMIT">受限用户</option>
+                            <option value="BAN">封禁用户</option>
+                        </select>
+                        </td>
+                    </tr>
+                    ))}
+                </tbody>
+                </table>
+            </div>
+        </>
+    ) : (
+        /* 新增的折扣管理部分 */
+        <>
+          {/* 当前活动折扣 */}
+          <div className="current-discount-section">
+            <h3>当前活动折扣</h3>
+            {currentDiscount ? (
+              <div className="current-discount-card">
+                <p><strong>名称:</strong> {currentDiscount.name}</p>
+                <p><strong>类型:</strong> {translateDiscountType(currentDiscount.discountType)}</p>
+                <p><strong>时间:</strong> {formatDateTime(currentDiscount.startTime)} 至 {formatDateTime(currentDiscount.endTime)}</p>
+                <p><strong>状态:</strong> {currentDiscount.status}</p>
+              </div>
+            ) : (
+              <p>当前没有进行中的折扣</p>
+            )}
+          </div>
+
+          {/* 添加新折扣表单 */}
+          <div className="add-discount-form">
+            <h3>添加新折扣</h3>
+            <div className="form-group">
+              <label>折扣名称:</label>
+              <input
+                type="text"
+                value={newDiscount.name}
+                onChange={(e) => setNewDiscount({...newDiscount, name: e.target.value})}
+              />
+            </div>
+            <div className="form-group">
+                <label>开始时间:</label>
+                <DatePicker
+                  selected={startDate}
+                  onChange={(date) => setStartDate(date)}
+                  showTimeSelect
+                  timeFormat="HH:mm"
+                  timeIntervals={1} // 1分钟间隔
+                  dateFormat="yyyy-MM-dd HH:mm"
+                  minDate={new Date()}
+                  placeholderText="选择开始日期和时间"
+                />
+            </div>
+            <div className="form-group">
+                  <label>结束时间:</label>
+                  <DatePicker
+                    selected={endDate}
+                    onChange={(date) => setEndDate(date)}
+                    showTimeSelect
+                    timeFormat="HH:mm"
+                    timeIntervals={1} // 1分钟间隔
+                    dateFormat="yyyy-MM-dd HH:mm"
+                    minDate={startDate}
+                    placeholderText="选择结束日期和时间"
+                  />
+            </div>
+            <div className="form-group">
+              <label>折扣类型:</label>
+              <select
+                value={newDiscount.discountType}
+                onChange={(e) => setNewDiscount({...newDiscount, discountType: e.target.value})}
+              >
+                <option value="FREE">全部免费</option>
+                <option value="HALF">半价下载</option>
+                <option value="DOUBLE">双倍上传</option>
+              </select>
+            </div>
+            <button 
+                onClick={(e) => {
+                    e.preventDefault(); // 确保没有阻止默认行为
+                    handleAddDiscount();
+                }}
+                >
+                添加折扣
+            </button>
+          </div>
+
+          {/* 所有折扣列表 */}
+          <div className="discount-list-container">
+            <h3>所有折扣计划</h3>
+            <table className="discount-table">
+              <thead>
+                <tr>
+                  <th>ID</th>
+                  <th>名称</th>
+                  <th>开始时间</th>
+                  <th>结束时间</th>
+                  <th>类型</th>
+                  <th>创建时间</th>
+                  <th>状态</th>
+                  <th>操作</th>
+                </tr>
+              </thead>
+              <tbody>
+                {discounts.map(discount => (
+                  <tr key={discount.id}>
+                    <td>{discount.id}</td>
+                    <td>{discount.name}</td>
+                    <td>{formatDateTime(discount.startTime)}</td>
+                    <td>{formatDateTime(discount.endTime)}</td>
+                    <td>{translateDiscountType(discount.discountType)}</td>
+                    <td>{formatDateTime(discount.createTime)}</td>
+                    <td>{discount.status || '未知'}</td>
+                    <td>
+                      <button 
+                        onClick={() => handleDeleteDiscount(discount.id)}
+                        className="delete-button"
+                      >
+                        删除
+                      </button>
+                    </td>
+                  </tr>
+                ))}
+              </tbody>
+            </table>
+          </div>
+        </>
+      )}
+    </div>
+  );
+};
+
+export default Administer;
\ No newline at end of file