修改促销、优化页面布局

Change-Id: Iae813b5b6557efa7059fe6d94bc32e96c984e4ea
diff --git a/src/pages/Forum/promotion-part/CategoryPromotionDialog.jsx b/src/pages/Forum/promotion-part/CategoryPromotionDialog.jsx
new file mode 100644
index 0000000..101b653
--- /dev/null
+++ b/src/pages/Forum/promotion-part/CategoryPromotionDialog.jsx
@@ -0,0 +1,79 @@
+import React from 'react';
+
+const CategoryPromotionDialog = ({
+  showCategoryDialog,
+  categoryFormData,
+  handleCategoryInputChange,
+  closeCategoryDialog,
+  handleCreateCategoryPromotion
+}) => {
+  return (
+    showCategoryDialog && (
+      <div className="dialog-overlay">
+        <div className="dialog">
+          <h3>创建特定分类促销</h3>
+          <div className="form-item">
+            <label>促销名称:</label>
+            <input
+              type="text"
+              name="name"
+              value={categoryFormData.name}
+              onChange={handleCategoryInputChange}
+              placeholder="请输入促销名称"
+            />
+          </div>
+          <div className="form-item">
+            <label>开始时间:</label>
+            <input
+              type="datetime-local"
+              name="startTime"
+              value={categoryFormData.startTime}
+              onChange={handleCategoryInputChange}
+            />
+          </div>
+          <div className="form-item">
+            <label>结束时间:</label>
+            <input
+              type="datetime-local"
+              name="endTime"
+              value={categoryFormData.endTime}
+              onChange={handleCategoryInputChange}
+            />
+          </div>
+          <div className="form-item">
+            <label>折扣百分比:</label>
+            <input
+              type="number"
+              name="discountPercentage"
+              value={categoryFormData.discountPercentage}
+              onChange={handleCategoryInputChange}
+              placeholder="正数表示上传加成,负数表示下载折扣"
+              step="0.1"
+            />
+          </div>
+          <div className="form-item">
+            <label>促销类别:</label>
+            <select
+              name="category"
+              value={categoryFormData.category}
+              onChange={handleCategoryInputChange}
+            >
+              <option value="movie">电影</option>
+              <option value="tv">剧集</option>
+              <option value="music">音乐</option>
+              <option value="game">游戏</option>
+              <option value="software">软件</option>
+              <option value="book">书籍</option>
+            </select>
+          </div>
+          <div className="dialog-buttons">
+            <button onClick={handleCreateCategoryPromotion}>确定</button>
+            <button onClick={closeCategoryDialog}>取消</button>
+          </div>
+        </div>
+      </div>
+    )
+  );
+};
+
+export default CategoryPromotionDialog;    
\ No newline at end of file
diff --git a/src/pages/Forum/promotion-part/ColdTorrentsDialog.jsx b/src/pages/Forum/promotion-part/ColdTorrentsDialog.jsx
new file mode 100644
index 0000000..79d2b4b
--- /dev/null
+++ b/src/pages/Forum/promotion-part/ColdTorrentsDialog.jsx
@@ -0,0 +1,69 @@
+import React from 'react';
+
+const ColdTorrentsDialog = ({
+  showColdDialog,
+  coldTorrents,
+  closeColdDialog,
+  fetchTorrentDetail
+}) => {
+  return (
+    showColdDialog && (
+      <div className="cold-dialog-overlay">
+        <div className="cold-dialog">
+          <h3 className="cold-dialog-title">冷门资源列表</h3>
+          <button 
+            className="close-btn" 
+            onClick={closeColdDialog}
+          >
+            &times;
+          </button>
+
+          {coldTorrents.length === 0 ? (
+            <div className="empty-state">暂无冷门资源</div>
+          ) : (
+            <div className="cold-table-container">
+              <table className="cold-torrent-table">
+                <thead>
+                  <tr>
+                    <th>序号</th>
+                    <th>资源名称</th>
+                    <th>资源ID</th>
+                    <th>分类</th>
+                    <th>描述</th>
+                    <th>下载用户数</th>
+                    <th>浏览次数</th>
+                    <th>创建时间</th>
+                  </tr>
+                </thead>
+                <tbody>
+                  {coldTorrents.map((torrent, index) => (
+                    <tr key={torrent.id}>
+                      <td>{index + 1}</td>
+                      <td>
+                        <button
+                          className="torrent-link"
+                          onClick={() => fetchTorrentDetail(torrent.id)}
+                          aria-label={`查看种子${torrent.title || torrent.id}的详情`}
+                        >
+                          {torrent.title}
+                        </button>
+                      </td>
+                      <td>{torrent.id}</td>
+                      <td>{torrent.category || '未分类'}</td>
+                      <td>{torrent.description || '无描述'}</td>
+                      <td>{torrent.leechers || 0}</td>
+                      <td>{torrent.views || 0}</td>
+                      <td>{new Date(torrent.createdTime).toLocaleDateString()}</td>
+                    </tr>
+                  ))}
+                </tbody>
+              </table>
+            </div>
+          )}
+        </div>
+      </div>
+    )
+  );
+};
+
+export default ColdTorrentsDialog;    
\ No newline at end of file
diff --git a/src/pages/Forum/promotion-part/CreatePromotionDialog.jsx b/src/pages/Forum/promotion-part/CreatePromotionDialog.jsx
new file mode 100644
index 0000000..6dcf236
--- /dev/null
+++ b/src/pages/Forum/promotion-part/CreatePromotionDialog.jsx
@@ -0,0 +1,115 @@
+import React from 'react';
+
+const CreatePromotionDialog = ({
+  showCreateDialog,
+  formData,
+  handleInputChange,
+  closeCreateDialog,
+  handleCreatePromotion,
+  fetchPromoColdTorrents,
+  showPromoColdTable,
+  coldTorrents,
+  handlePromoTorrentSelection
+}) => {
+  return (
+    showCreateDialog && (
+      <div className="dialog-overlay">
+        <div className="dialog">
+          <h3>创建冷门资源促销</h3>
+          <div className="form-item">
+            <label>促销名称:</label>
+            <input
+              type="text"
+              name="name"
+              value={formData.name}
+              onChange={handleInputChange}
+              placeholder="请输入促销名称"
+            />
+          </div>
+          <div className="form-item">
+            <label>开始时间:</label>
+            <input
+              type="datetime-local"
+              name="startTime"
+              value={formData.startTime}
+              onChange={handleInputChange}
+            />
+          </div>
+          <div className="form-item">
+            <label>结束时间:</label>
+            <input
+              type="datetime-local"
+              name="endTime"
+              value={formData.endTime}
+              onChange={handleInputChange}
+            />
+          </div>
+          <div className="form-item">
+            <label>折扣百分比:</label>
+            <input
+              type="number"
+              name="discountPercentage"
+              value={formData.discountPercentage}
+              onChange={handleInputChange}
+              placeholder="正数表示上传加成,负数表示下载折扣"
+              step="0.1"
+            />
+          </div>
+          <div className="form-item">
+            <label>适用种子:</label>
+            <button 
+              className="cold-btn small" 
+              onClick={fetchPromoColdTorrents}
+            >
+              选择冷门资源 <span>(点击加载列表)</span>
+            </button>
+            
+            {showPromoColdTable && (
+              <div className="torrent-table-container">
+                <table className="torrent-selection-table">
+                  <thead>
+                    <tr>
+                      <th>选择</th>
+                      <th>序号</th>
+                      <th>资源名称</th>
+                      <th>资源ID</th>
+                      <th>分类</th>
+                      <th>下载用户数</th>
+                      <th>浏览次数</th>
+                    </tr>
+                  </thead>
+                  <tbody>
+                    {coldTorrents.map((torrent, index) => (
+                      <tr key={torrent.id}>
+                        <td>
+                          <input
+                            type="checkbox"
+                            checked={formData.applicableTorrentIds.includes(torrent.id)}
+                            onChange={(e) => handlePromoTorrentSelection(torrent.id, e.target.checked)}
+                          />
+                        </td>
+                        <td>{index + 1}</td>
+                        <td>{torrent.title}</td>
+                        <td>{torrent.id}</td>
+                        <td>{torrent.category || '未分类'}</td>
+                        <td>{torrent.leechers || 0}</td>
+                        <td>{torrent.views || 0}</td>
+                      </tr>
+                    ))}
+                  </tbody>
+                </table>
+              </div>
+            )}
+          </div>
+
+          <div className="dialog-buttons">
+            <button onClick={handleCreatePromotion}>确定</button>
+            <button onClick={closeCreateDialog}>取消</button>
+          </div>
+        </div>
+      </div>
+    )
+  );
+};
+
+export default CreatePromotionDialog;    
\ No newline at end of file
diff --git a/src/pages/Forum/promotion-part/Promotion.css b/src/pages/Forum/promotion-part/Promotion.css
index 46e7a0d..656114a 100644
--- a/src/pages/Forum/promotion-part/Promotion.css
+++ b/src/pages/Forum/promotion-part/Promotion.css
@@ -1,83 +1,3 @@
-.promotion-container {
-  padding: 20px;
-  margin: 20px 0;
-  border-radius: 8px;
-}
-
-/* 并排两列 */
-.carousel-container {
-  display: flex;
-  gap: 20px;
-}
-
-.carousel-section {
-  flex: 1;
-}
-
-.carousel-section h2 {
-  font-size: 20px;
-  margin-bottom: 15px;
-}
-
-/* 轮播框架 */
-.carousel {
-  position: relative;
-  /* background: #a54747; */
-  /* background: linear-gradient(135deg, #4A3B34, #a54747); */
-  /* background: linear-gradient(135deg, #e38f77, #aa3e3e); */
-  /* 背景渐变 */
-  background: linear-gradient(135deg, #e1cab2, #b68791);
-  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
-  border-radius: 6px;
-  padding: 15px;
-  color: #fff;
-  min-height: 200px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-}
-
-/* 左右箭头 */
-.carousel .arrow {
-  background: rgba(0,0,0,0.2);
-  border: none;
-  color: #fff;
-  font-size: 24px;
-  width: 36px;
-  height: 36px;
-  border-radius: 50%;
-  cursor: pointer;
-  position: absolute;
-  top: 50%;
-  transform: translateY(-50%);
-}
-
-.carousel .arrow.left {
-  left: 10px;
-}
-
-.carousel .arrow.right {
-  right: 10px;
-}
-
-.carousel .arrow:hover {
-  background: rgba(0,0,0,0.4);
-}
-
-/* 每帧内容 */
-.carousel .slide {
-  width: calc(100% - 80px);
-  /* 留出箭头空间 */
-  text-align: left;
-}
-
-/* 冷门资源专用 slide */
-.cold-slide {
-  display: flex;
-  gap: 10px;
-  align-items: center;
-}
-
 /* 资源海报 */
 .resource-poster {
   width: 80px;
@@ -125,7 +45,7 @@
   background: #fff;
   padding: 20px;
   border-radius: 6px;
-  width: 400px;
+  width: 80%;
   max-width: 90%;
   box-shadow: 0 0 10px rgba(0,0,0,0.25);
 }
@@ -154,3 +74,266 @@
   cursor: pointer;
 }
 
+
+/* 冷门资源模态框样式 */
+.cold-dialog-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(255, 228, 230, 0.3); /* 清透粉半透明背景 */
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 1000;
+}
+
+.cold-dialog {
+  background-color: #F8F8F0; /* 米白色背景 */
+  padding: 20px;
+  border-radius: 10px;
+  box-shadow: 0 4px 12px rgba(255, 192, 203, 0.3); /* 粉调阴影 */
+  max-width: 800px;
+  width: 100%;
+}
+
+.cold-dialog-title {
+  color: #FF6B81; /* 亮粉标题 */
+  text-align: center;
+  margin-bottom: 15px;
+}
+
+.close-btn {
+  position: absolute;
+  top: 10px;
+  right: 15px;
+  font-size: 20px;
+  background: none;
+  border: none;
+  cursor: pointer;
+  color: #FF4E50; /* 深粉关闭按钮 */
+}
+
+.cold-table-container {
+  overflow-x: auto; /* 长表格横向滚动 */
+}
+
+.cold-torrent-table {
+  width: 100%;
+  border-collapse: collapse;
+  margin-top: 15px;
+  background-color: white; /* 表格白色背景 */
+}
+
+.cold-torrent-table th,
+.cold-torrent-table td {
+  padding: 12px 15px;
+  text-align: left;
+  border-bottom: 1px solid #FFE4E6; /* 清透粉分隔线 */
+}
+
+.cold-torrent-table th {
+  background-color: #FFF0F5; /* 淡粉表头背景 */
+  color: #FF69B4; /* 粉紫表头文字 */
+  font-weight: 500;
+}
+
+.cold-torrent-table tr:hover {
+  background-color: #FFF5EB; /* 米白悬停效果 */
+}
+
+.empty-state {
+  text-align: center;
+  padding: 20px;
+  color: #666;
+}
+
+/* 适配小屏幕 */
+@media (max-width: 600px) {
+  .cold-dialog {
+    margin: 20px;
+    max-width: calc(100% - 40px);
+  }
+}
+
+.cold-btn.small {
+  font-size: 0.9em;
+  padding: 5px 10px;
+  margin-top: 5px;
+}
+
+.torrent-table-container {
+  margin-top: 10px;
+  max-height: 300px;
+  overflow-y: auto;
+}
+
+.torrent-selection-table {
+  width: 100%;
+  border-collapse: collapse;
+}
+
+.torrent-selection-table th,
+.torrent-selection-table td {
+  padding: 8px 12px;
+  border: 1px solid #ddd;
+  text-align: left;
+}
+
+.torrent-selection-table th {
+  background-color: #f5f5f5;
+}
+
+.detail-dialog-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0, 0, 0, 0.5);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 100;
+}
+
+.detail-dialog {
+  background-color: white;
+  padding: 20px;
+  border-radius: 8px;
+  width: 90%;
+  max-width: 600px;
+  max-height: 80vh;
+  overflow-y: auto;
+}
+
+.detail-content {
+  margin-top: 20px;
+}
+
+.detail-item {
+  margin-bottom: 15px;
+  display: flex;
+}
+
+.detail-label {
+  font-weight: bold;
+  min-width: 120px;
+}
+
+.detail-value {
+  flex: 1;
+}
+
+.torrent-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+
+.torrent-link {
+  background: none;
+  border: none;
+  color: #0066cc;
+  text-decoration: underline;
+  cursor: pointer;
+  padding: 0;
+  font-size: inherit;
+}
+
+.torrent-link:hover,
+.torrent-link:focus {
+  text-decoration: none;
+  outline: none;
+  color: #004499;
+}
+
+.torrent-detail-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0, 0, 0, 0.5);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 200; /* 确保在促销详情对话框之上 */
+}
+
+.torrent-detail-dialog {
+  background-color: white;
+  padding: 20px;
+  border-radius: 8px;
+  width: 90%;
+  max-width: 700px;
+  max-height: 85vh;
+  overflow-y: auto;
+}
+
+.torrent-detail-content {
+  margin-top: 20px;
+}
+
+.torrent-detail-item {
+  margin-bottom: 15px;
+  display: flex;
+}
+
+.torrent-detail-label {
+  font-weight: bold;
+  min-width: 120px;
+}
+
+.torrent-detail-value {
+  flex: 1;
+}
+
+.description {
+  white-space: pre-wrap;
+}
+
+.torrent-cover-container {
+  display: flex;
+  justify-content: center;
+  margin-bottom: 20px;
+}
+
+.torrent-cover {
+  max-width: 100%;
+  max-height: 300px;
+  object-fit: contain;
+  border-radius: 4px;
+  box-shadow: 0 2px 8px rgba(0,0,0,0.15);
+}
+
+.status-badge {
+  padding: 2px 8px;
+  border-radius: 4px;
+  font-size: 0.9em;
+  color: white;
+}
+
+.status-badge.hot {
+  background-color: #e53935;
+}
+
+.status-badge.cold {
+  background-color: #1e88e5;
+}
+
+.status-badge.normal {
+  background-color: #757575;
+}
+
+.download-link {
+  color: #0066cc;
+  text-decoration: none;
+  display: inline-flex;
+  align-items: center;
+}
+
+.download-link:hover {
+  text-decoration: underline;
+}
\ No newline at end of file
diff --git a/src/pages/Forum/promotion-part/Promotion.jsx b/src/pages/Forum/promotion-part/Promotion.jsx
index 953a37f..37730f3 100644
--- a/src/pages/Forum/promotion-part/Promotion.jsx
+++ b/src/pages/Forum/promotion-part/Promotion.jsx
@@ -1,832 +1,13 @@
-// import React, { useEffect, useState, useRef } from 'react';
-// import './Promotion.css';
-// import { useUser } from '../../../context/UserContext';
-
-// const Promotion = () => {
-//   const { user } = useUser();
-//   const [promotions, setPromotions] = useState([]);
-//   const [torrents, setTorrents] = useState([]);
-//   const [loading, setLoading] = useState(true);
-//   const [promoIndex, setPromoIndex] = useState(0);
-//   const promoTimerRef = useRef(null);
-
-//   // 新增:控制创建对话框显示
-//   const [showCreateDialog, setShowCreateDialog] = useState(false);
-
-//   // 创建促销活动表单状态
-//   const [formData, setFormData] = useState({
-//     name: '',
-//     startTime: '',
-//     endTime: '',
-//     discountPercentage: '',
-//     uploadCoeff: '',
-//     downloadCoeff: '',
-//     description: ''
-//   });
-
-//   useEffect(() => {
-//     fetchData();
-//     fetchTorrentList();
-//   }, []);
-
-//   useEffect(() => {
-//     if (promotions.length === 0) return;
-//     clearInterval(promoTimerRef.current);
-//     promoTimerRef.current = setInterval(() => {
-//       setPromoIndex(prev => (prev + 1) % promotions.length);
-//     }, 5000);
-//     return () => clearInterval(promoTimerRef.current);
-//   }, [promotions]);
-
-//   const fetchData = async () => {
-//     try {
-//       const response = await fetch('/seeds/promotions');
-//       const json = await response.json();
-//       const promoData = Array.isArray(json?.data) ? json.data : [];
-//       setPromotions(promoData);
-//     } catch (error) {
-//       console.error('获取促销活动失败:', error);
-//     } finally {
-//       setLoading(false);
-//     }
-//   };
-
-//   const fetchTorrentList = async () => {
-//     try {
-//       const response = await fetch('/seeds/list');
-//       const json = await response.json();
-//       const torrentList = Array.isArray(json?.data) ? json.data : [];
-//       setTorrents(torrentList);
-//     } catch (error) {
-//       console.error('获取种子列表失败:', error);
-//     }
-//   };
-
-//   // 打开创建促销活动弹窗
-//   const openCreateDialog = () => {
-//     // 重置表单数据
-//     setFormData({
-//       name: '',
-//       startTime: '',
-//       endTime: '',
-//       discountPercentage: '',
-//       uploadCoeff: '',
-//       downloadCoeff: '',
-//       description: ''
-//     });
-//     setShowCreateDialog(true);
-//   };
-
-//   // 关闭弹窗
-//   const closeCreateDialog = () => {
-//     setShowCreateDialog(false);
-//   };
-
-//   // 处理表单输入变化
-//   const handleInputChange = (e) => {
-//     const { name, value } = e.target;
-//     setFormData(prev => ({
-//       ...prev,
-//       [name]: value
-//     }));
-//   };
-
-//   // 提交创建促销活动
-//   const handleCreatePromotion = async () => {
-//     if (torrents.length === 0) {
-//       alert('没有可用的种子,请先上传种子');
-//       return;
-//     }
-//     if (!formData.name.trim()) {
-//       alert('促销名称不能为空');
-//       return;
-//     }
-//     if (!formData.startTime || !formData.endTime) {
-//       alert('促销开始时间和结束时间不能为空');
-//       return;
-//     }
-//     if (new Date(formData.startTime) >= new Date(formData.endTime)) {
-//       alert('促销结束时间必须晚于开始时间');
-//       return;
-//     }
-//     if (!formData.discountPercentage || isNaN(formData.discountPercentage)) {
-//       alert('折扣百分比必须是数字');
-//       return;
-//     }
-
-//     const applicableTorrentIds = torrents.map(t => t.id);
-
-//     const newPromo = {
-//       name: formData.name,
-//       startTime: new Date(formData.startTime).toISOString(),
-//       endTime: new Date(formData.endTime).toISOString(),
-//       discountPercentage: Number(formData.discountPercentage),
-//       uploadCoeff: formData.uploadCoeff ? Number(formData.uploadCoeff) : undefined,
-//       downloadCoeff: formData.downloadCoeff ? Number(formData.downloadCoeff) : undefined,
-//       applicableTorrentIds: JSON.stringify(applicableTorrentIds), // ✅ 关键修改
-//       description: formData.description
-//     };
-
-
-//     try {
-//       const res = await fetch('/seeds/promotions', {
-//         method: 'POST',
-//         headers: { 'Content-Type': 'application/json' },
-//         body: JSON.stringify(newPromo)
-//       });
-//       const json = await res.json();
-//       if (json.code === 200) {
-//         alert('促销活动创建成功');
-//         fetchData();
-//         setShowCreateDialog(false);
-//       } else {
-//         alert('创建失败: ' + (json.msg || '未知错误'));
-//       }
-//     } catch (err) {
-//       console.error('创建促销失败:', err);
-//       alert('创建促销失败');
-//     }
-//   };
-
-//   const handleDeletePromotion = async (promotionId) => {
-//     if (!window.confirm('确认删除该促销活动吗?')) return;
-
-//     try {
-//       const res = await fetch(`/seeds/promotions/${promotionId}`, { method: 'DELETE' });
-//       const json = await res.json();
-//       if (json.success) {
-//         alert('删除成功');
-//         fetchData();
-//       } else {
-//         alert('删除失败: ' + json.message);
-//       }
-//     } catch (err) {
-//       console.error('删除失败:', err);
-//     }
-//   };
-
-//   const isAdmin = user?.role === 'admin';
-//   const prevPromo = () => setPromoIndex((promoIndex - 1 + promotions.length) % promotions.length);
-//     const nextPromo = () => setPromoIndex((promoIndex + 1) % promotions.length);
-//   const currentPromo = promotions[promoIndex];
-
-//   if (loading) {
-//     return <div className="promotion-container">加载中...</div>;
-//   }
-
-//   return (
-//     <div className="promotion-container carousel-container">
-//       <section className="carousel-section">
-//         <h2>当前促销活动</h2>
-
-//         {isAdmin && (
-//           <button className="create-btn" onClick={openCreateDialog}>
-//             创建促销活动
-//           </button>
-//         )}
-
-//         {promotions.length === 0 || !currentPromo ? (
-//           <div className="empty-state">暂无促销活动</div>
-//         ) : (
-//           <div
-//             className="carousel"
-//             onMouseEnter={() => clearInterval(promoTimerRef.current)}
-//             onMouseLeave={() => {
-//               promoTimerRef.current = setInterval(() => {
-//                 setPromoIndex(prev => (prev + 1) % promotions.length);
-//               }, 3000);
-//             }}
-//           >
-//             <button className="arrow left" onClick={prevPromo}>&lt;</button>
-//             <div className="slide">
-//               <div><strong>促销名称:</strong>{currentPromo?.name ?? '未知'}</div>
-//               <div><strong>促销时间:</strong>
-//                 {currentPromo?.pStartTime && currentPromo?.pEndTime
-//                   ? `${new Date(currentPromo.pStartTime).toLocaleString()} ~ ${new Date(currentPromo.pEndTime).toLocaleString()}`
-//                   : '未知'}
-//               </div>
-//               <div><strong>上传奖励系数:</strong>{currentPromo?.uploadCoeff ?? '无'}</div>
-//               <div><strong>下载折扣系数:</strong>{currentPromo?.downloadCoeff ?? '无'}</div>
-//               {currentPromo?.description && (
-//                 <div><strong>描述:</strong>{currentPromo.description}</div>
-//               )}
-//               {isAdmin && (
-//                 <button className="delete-btn" onClick={() => handleDeletePromotion(currentPromo.id)}>
-//                   删除该活动
-//                 </button>
-//               )}
-//             </div>
-//             <button className="arrow right" onClick={nextPromo}>&gt;</button>
-//           </div>
-//         )}
-//       </section>
-
-//       {/* 创建促销活动弹窗 */}
-//       {showCreateDialog && (
-//         <div className="dialog-overlay">
-//           <div className="dialog">
-//             <h3>创建促销活动</h3>
-//             <div className="form-item">
-//               <label>促销名称:</label>
-//               <input
-//                 type="text"
-//                 name="name"
-//                 value={formData.name}
-//                 onChange={handleInputChange}
-//                 placeholder="请输入促销名称"
-//               />
-//             </div>
-//             <div className="form-item">
-//               <label>开始时间:</label>
-//               <input
-//                 type="datetime-local"
-//                 name="startTime"
-//                 value={formData.startTime}
-//                 onChange={handleInputChange}
-//               />
-//             </div>
-//             <div className="form-item">
-//               <label>结束时间:</label>
-//               <input
-//                 type="datetime-local"
-//                 name="endTime"
-//                 value={formData.endTime}
-//                 onChange={handleInputChange}
-//               />
-//             </div>
-//             <div className="form-item">
-//               <label>折扣百分比(数字):</label>
-//               <input
-//                 type="number"
-//                 name="discountPercentage"
-//                 value={formData.discountPercentage}
-//                 onChange={handleInputChange}
-//                 placeholder="例如:20 表示 20% 折扣"
-//                 min="0"
-//                 max="100"
-//               />
-//             </div>
-//             <div className="form-item">
-//               <label>上传奖励系数(可选):</label>
-//               <input
-//                 type="number"
-//                 name="uploadCoeff"
-//                 value={formData.uploadCoeff}
-//                 onChange={handleInputChange}
-//                 placeholder="例如:1.5"
-//                 step="0.1"
-//               />
-//             </div>
-//             <div className="form-item">
-//               <label>下载折扣系数(可选):</label>
-//               <input
-//                 type="number"
-//                 name="downloadCoeff"
-//                 value={formData.downloadCoeff}
-//                 onChange={handleInputChange}
-//                 placeholder="例如:0.8"
-//                 step="0.1"
-//               />
-//             </div>
-//             <div className="form-item">
-//               <label>描述(可选):</label>
-//               <textarea
-//                 name="description"
-//                 value={formData.description}
-//                 onChange={handleInputChange}
-//                 placeholder="促销活动描述"
-//                 rows={3}
-//               />
-//             </div>
-//             <div className="dialog-buttons">
-//               <button onClick={handleCreatePromotion}>确定</button>
-//               <button onClick={closeCreateDialog}>取消</button>
-//             </div>
-//           </div>
-//         </div>
-//       )}
-//     </div>
-//   );
-// };
-
-// export default Promotion;
-
-
-// // import React, { useEffect, useState, useRef } from 'react';
-// // import './Promotion.css';
-// // import { useUser } from '../../../context/UserContext';
-
-// // const Promotion = () => {
-// //   const { user } = useUser();
-// //   const [promotions, setPromotions] = useState([]);
-// //   const [torrents, setTorrents] = useState([]);
-// //   const [loading, setLoading] = useState(true);
-// //   const [promoIndex, setPromoIndex] = useState(0);
-// //   const promoTimerRef = useRef(null);
-
-// //   // 新增:控制模态框显示与表单状态
-// //   const [showCreateModal, setShowCreateModal] = useState(false);
-// //   const [formData, setFormData] = useState({
-// //     name: '',
-// //     description: '',
-// //     discountPercentage: 0,
-// //     startTime: '',
-// //     endTime: '',
-// //     applicableTorrentIds: [],
-// //   });
-
-// //   useEffect(() => {
-// //     fetchData();
-// //     fetchTorrentList();
-// //   }, []);
-
-// //   useEffect(() => {
-// //     if (promotions.length === 0) return;
-// //     clearInterval(promoTimerRef.current);
-// //     promoTimerRef.current = setInterval(() => {
-// //       setPromoIndex(prev => (prev + 1) % promotions.length);
-// //     }, 5000);
-// //     return () => clearInterval(promoTimerRef.current);
-// //   }, [promotions]);
-
-// //   const fetchData = async () => {
-// //     try {
-// //       const response = await fetch('/seeds/promotions');
-// //       const json = await response.json();
-// //       const promoData = Array.isArray(json?.data) ? json.data : [];
-// //       setPromotions(promoData);
-// //     } catch (error) {
-// //       console.error('获取促销活动失败:', error);
-// //     } finally {
-// //       setLoading(false);
-// //     }
-// //   };
-
-// //   const fetchTorrentList = async () => {
-// //     try {
-// //       const response = await fetch('/seeds/list');
-// //       const json = await response.json();
-// //       const torrentList = Array.isArray(json?.data) ? json.data : [];
-// //       setTorrents(torrentList);
-// //     } catch (error) {
-// //       console.error('获取种子列表失败:', error);
-// //     }
-// //   };
-
-// //   // 打开模态框时,重置表单数据,默认设置时间并填入所有种子ID
-// //   const openCreateModal = () => {
-// //     if (torrents.length === 0) {
-// //       alert('没有可用的种子,请先上传种子');
-// //       return;
-// //     }
-// //     setFormData({
-// //       name: '',
-// //       description: '',
-// //       discountPercentage: 20,
-// //       startTime: new Date().toISOString().slice(0, 16), // 用于datetime-local输入框,格式 YYYY-MM-DDTHH:mm
-// //       endTime: new Date(Date.now() + 7 * 86400000).toISOString().slice(0, 16),
-// //       applicableTorrentIds: torrents.map(t => t.id),
-// //     });
-// //     setShowCreateModal(true);
-// //   };
-
-// //   // 表单输入处理
-// //   const handleInputChange = (e) => {
-// //     const { name, value } = e.target;
-// //     setFormData(prev => ({
-// //       ...prev,
-// //       [name]: name === 'discountPercentage' ? Number(value) : value,
-// //     }));
-// //   };
-
-// //   // 点击确定提交创建
-// //   const handleCreateConfirm = async () => {
-// //     if (!formData.name) {
-// //       alert('促销名称不能为空');
-// //       return;
-// //     }
-// //     if (!formData.startTime || !formData.endTime) {
-// //       alert('请选择开始时间和结束时间');
-// //       return;
-// //     }
-// //     if (formData.discountPercentage <= 0 || formData.discountPercentage >= 100) {
-// //       alert('折扣百分比应在1-99之间');
-// //       return;
-// //     }
-// //     if (!formData.applicableTorrentIds.length) {
-// //       alert('请选择适用的种子');
-// //       return;
-// //     }
-
-// //     // 准备发送数据,适配后端字段名
-// //     const newPromo = {
-// //       name: formData.name,
-// //       description: formData.description,
-// //       discountPercentage: formData.discountPercentage,
-// //       startTime: new Date(formData.startTime).toISOString(),
-// //       endTime: new Date(formData.endTime).toISOString(),
-// //       applicableTorrentIds: formData.applicableTorrentIds,
-// //     };
-
-// //     try {
-// //       const res = await fetch('/seeds/promotions', {
-// //         method: 'POST',
-// //         headers: { 'Content-Type': 'application/json' },
-// //         body: JSON.stringify(newPromo),
-// //       });
-// //       const json = await res.json();
-// //       if (json.code === 200) {
-// //         alert('促销活动创建成功');
-// //         setShowCreateModal(false);
-// //         fetchData();
-// //       } else {
-// //         alert('创建失败: ' + (json.msg || '未知错误'));
-// //       }
-// //     } catch (err) {
-// //       console.error('创建促销失败:', err);
-// //       alert('创建促销失败');
-// //     }
-// //   };
-
-// //   const handleCancel = () => {
-// //     setShowCreateModal(false);
-// //   };
-
-// //   const handleDeletePromotion = async (promotionId) => {
-// //     if (!window.confirm('确认删除该促销活动吗?')) return;
-
-// //     try {
-// //       const res = await fetch(`/seeds/promotions/${promotionId}`, { method: 'DELETE' });
-// //       const json = await res.json();
-// //       if (json.success) {
-// //         alert('删除成功');
-// //         fetchData();
-// //       } else {
-// //         alert('删除失败: ' + json.message);
-// //       }
-// //     } catch (err) {
-// //       console.error('删除失败:', err);
-// //     }
-// //   };
-
-// //   const isAdmin = user?.role === 'admin';
-// //   const prevPromo = () => setPromoIndex((promoIndex - 1 + promotions.length) % promotions.length);
-// //   const nextPromo = () => setPromoIndex((promoIndex + 1) % promotions.length);
-// //   const currentPromo = promotions[promoIndex];
-
-// //   if (loading) {
-// //     return <div className="promotion-container">加载中...</div>;
-// //   }
-
-// //   return (
-// //     <div className="promotion-container carousel-container">
-// //       <section className="carousel-section">
-// //         <h2>当前促销活动</h2>
-
-// //         {isAdmin && (
-// //           <button className="create-btn" onClick={openCreateModal}>
-// //             创建促销活动
-// //           </button>
-// //         )}
-
-// //         {promotions.length === 0 || !currentPromo ? (
-// //           <div className="empty-state">暂无促销活动</div>
-// //         ) : (
-// //           <div
-// //             className="carousel"
-// //             onMouseEnter={() => clearInterval(promoTimerRef.current)}
-// //             onMouseLeave={() => {
-// //               promoTimerRef.current = setInterval(() => {
-// //                 setPromoIndex(prev => (prev + 1) % promotions.length);
-// //               }, 3000);
-// //             }}
-// //           >
-// //             <button className="arrow left" onClick={prevPromo}>&lt;</button>
-// //             <div className="slide">
-// //               <div><strong>促销名称:</strong>{currentPromo?.name ?? '未知'}</div>
-// //               <div><strong>促销时间:</strong>
-// //                 {currentPromo?.startTime && currentPromo?.endTime
-// //                   ? `${new Date(currentPromo.startTime).toLocaleString()} ~ ${new Date(currentPromo.endTime).toLocaleString()}`
-// //                   : '未知'}
-// //               </div>
-// //               <div><strong>折扣百分比:</strong>{currentPromo?.discountPercentage ?? '无'}</div>
-// //               {currentPromo?.description && (
-// //                 <div><strong>描述:</strong>{currentPromo.description}</div>
-// //               )}
-// //               {isAdmin && (
-// //                 <button className="delete-btn" onClick={() => handleDeletePromotion(currentPromo.id)}>
-// //                   删除该活动
-// //                 </button>
-// //               )}
-// //             </div>
-// //             <button className="arrow right" onClick={nextPromo}>&gt;</button>
-// //           </div>
-// //         )}
-// //       </section>
-
-// //       {/* 创建促销模态框 */}
-// //       {showCreateModal && (
-// //         <div className="modal-overlay">
-// //           <div className="modal-content">
-// //             <h3>创建促销活动</h3>
-// //             <label>
-// //               促销名称:
-// //               <input
-// //                 type="text"
-// //                 name="name"
-// //                 value={formData.name}
-// //                 onChange={handleInputChange}
-// //               />
-// //             </label>
-// //             <label>
-// //               描述:
-// //               <textarea
-// //                 name="description"
-// //                 value={formData.description}
-// //                 onChange={handleInputChange}
-// //                 rows={3}
-// //               />
-// //             </label>
-// //             <label>
-// //               折扣百分比:
-// //               <input
-// //                 type="number"
-// //                 name="discountPercentage"
-// //                 value={formData.discountPercentage}
-// //                 min={1}
-// //                 max={99}
-// //                 onChange={handleInputChange}
-// //               />
-// //             </label>
-// //             <label>
-// //               开始时间:
-// //               <input
-// //                 type="datetime-local"
-// //                 name="startTime"
-// //                 value={formData.startTime}
-// //                 onChange={handleInputChange}
-// //               />
-// //             </label>
-// //             <label>
-// //               结束时间:
-// //               <input
-// //                 type="datetime-local"
-// //                 name="endTime"
-// //                 value={formData.endTime}
-// //                 onChange={handleInputChange}
-// //               />
-// //             </label>
-// //             <label>
-// //               适用种子ID(逗号分隔,可留空默认所有):
-// //               <input
-// //                 type="text"
-// //                 name="applicableTorrentIds"
-// //                 value={formData.applicableTorrentIds.join(',')}
-// //                 onChange={(e) => {
-// //                   const ids = e.target.value
-// //                     .split(',')
-// //                     .map(id => id.trim())
-// //                     .filter(id => id !== '')
-// //                     .map(id => Number(id))
-// //                     .filter(id => !isNaN(id));
-// //                   setFormData(prev => ({ ...prev, applicableTorrentIds: ids }));
-// //                 }}
-// //               />
-// //             </label>
-
-// //             <div className="modal-buttons">
-// //               <button onClick={handleCreateConfirm}>确定</button>
-// //               <button onClick={handleCancel}>取消</button>
-// //             </div>
-// //           </div>
-// //         </div>
-// //       )}
-
-// //       {/* 模态框简单样式 */}
-// //       <style>{`
-// //         .modal-overlay {
-// //           position: fixed;
-// //           top: 0; left: 0; right: 0; bottom: 0;
-// //           background: rgba(0,0,0,0.4);
-// //           display: flex;
-// //           justify-content: center;
-// //           align-items: center;
-// //           z-index: 999;
-// //         }
-// //         .modal-content {
-// //           background: white;
-// //           padding: 20px;
-// //           border-radius: 6px;
-// //           width: 320px;
-// //           max-width: 90%;
-// //         }
-// //         .modal-content label {
-// //           display: block;
-// //           margin-bottom: 10px;
-// //           font-size: 14px;
-// //         }
-// //         .modal-content input[type="text"],
-// //         .modal-content input[type="number"],
-// //         .modal-content input[type="datetime-local"],
-// //         .modal-content textarea {
-// //           width: 100%;
-// //           box-sizing: border-box;
-// //           padding: 5px;
-// //           font-size: 14px;
-// //           margin-top: 4px;
-// //         }
-// //         .modal-buttons {
-// //           margin-top: 15px;
-// //           text-align: right;
-// //         }
-// //         .modal-buttons button {
-// //           margin-left: 10px;
-// //           padding: 6px 12px;
-// //           font-size: 14px;
-// //         }
-// //       `}</style>
-// //     </div>
-// //   );
-// // };
-
-// // export default Promotion;
-
-
-// // import React, { useEffect, useState, useRef } from 'react';
-// // import './Promotion.css';
-// // import { useUser } from '../../../context/UserContext';
-
-// // const Promotion = () => {
-// //   const { user } = useUser();
-// //   const [promotions, setPromotions] = useState([]);
-// //   const [torrents, setTorrents] = useState([]); // 新增,存放种子列表
-// //   const [loading, setLoading] = useState(true);
-// //   const [promoIndex, setPromoIndex] = useState(0);
-// //   const promoTimerRef = useRef(null);
-
-// //   useEffect(() => {
-// //     fetchData();
-// //     fetchTorrentList();  // 新增,获取种子列表
-// //   }, []);
-
-// //   useEffect(() => {
-// //     if (promotions.length === 0) return;
-// //     clearInterval(promoTimerRef.current);
-// //     promoTimerRef.current = setInterval(() => {
-// //       setPromoIndex(prev => (prev + 1) % promotions.length);
-// //     }, 5000);
-// //     return () => clearInterval(promoTimerRef.current);
-// //   }, [promotions]);
-
-// //   // 获取促销数据
-// //   const fetchData = async () => {
-// //     try {
-// //       const response = await fetch('/seeds/promotions');
-// //       const json = await response.json();
-// //       const promoData = Array.isArray(json?.data) ? json.data : [];
-// //       setPromotions(promoData);
-// //     } catch (error) {
-// //       console.error('获取促销活动失败:', error);
-// //     } finally {
-// //       setLoading(false);
-// //     }
-// //   };
-
-// //   // 获取种子列表,赋值给torrents
-// //   const fetchTorrentList = async () => {
-// //     try {
-// //       const response = await fetch('/seeds/list');
-// //       const json = await response.json();
-// //       const torrentList = Array.isArray(json?.data) ? json.data : [];
-// //       setTorrents(torrentList);
-// //     } catch (error) {
-// //       console.error('获取种子列表失败:', error);
-// //     }
-// //   };
-
-// //   // 创建促销时,自动使用当前种子的id列表,而不是写死
-// //   const handleCreatePromotion = async () => {
-// //     if (torrents.length === 0) {
-// //       alert('没有可用的种子,请先上传种子');
-// //       return;
-// //     }
-
-// //     const applicableTorrentIds = torrents.map(t => t.id); // 获取所有种子id数组
-
-// //     const newPromo = {
-// //       name: '测试促销活动',
-// //       startTime: new Date().toISOString(),
-// //       endTime: new Date(Date.now() + 7 * 86400000).toISOString(),
-// //       discountPercentage: 20,
-// //       applicableTorrentIds: applicableTorrentIds, // 动态传入种子ID数组
-// //       description: '这是一个测试促销活动'
-// //     };
-
-// //     try {
-// //       const res = await fetch('/seeds/promotions', {
-// //         method: 'POST',
-// //         headers: {
-// //           'Content-Type': 'application/json'
-// //         },
-// //         body: JSON.stringify(newPromo)
-// //       });
-// //       const json = await res.json();
-// //       if (json.code === 200) {
-// //         alert('促销活动创建成功');
-// //         fetchData();
-// //       } else {
-// //         alert('创建失败: ' + (json.msg || '未知错误'));
-// //       }
-// //     } catch (err) {
-// //       console.error('创建促销失败:', err);
-// //       alert('创建促销失败');
-// //     }
-// //   };
-
-// //   const handleDeletePromotion = async (promotionId) => {
-// //     if (!window.confirm('确认删除该促销活动吗?')) return;
-
-// //     try {
-// //       const res = await fetch(`/seeds/promotions/${promotionId}`, {
-// //         method: 'DELETE'
-// //       });
-// //       const json = await res.json();
-// //       if (json.success) {
-// //         alert('删除成功');
-// //         fetchData();
-// //       } else {
-// //         alert('删除失败: ' + json.message);
-// //       }
-// //     } catch (err) {
-// //       console.error('删除失败:', err);
-// //     }
-// //   };
-
-// //   const isAdmin = user?.role === 'admin';
-// //   const prevPromo = () => setPromoIndex((promoIndex - 1 + promotions.length) % promotions.length);
-// //   const nextPromo = () => setPromoIndex((promoIndex + 1) % promotions.length);
-// //   const currentPromo = promotions[promoIndex];
-
-// //   if (loading) {
-// //     return <div className="promotion-container">加载中...</div>;
-// //   }
-
-// //   return (
-// //     <div className="promotion-container carousel-container">
-// //       <section className="carousel-section">
-// //         <h2>当前促销活动</h2>
-
-// //         {isAdmin && (
-// //           <button className="create-btn" onClick={handleCreatePromotion}>
-// //             创建促销活动
-// //           </button>
-// //         )}
-
-// //         {promotions.length === 0 || !currentPromo ? (
-// //           <div className="empty-state">暂无促销活动</div>
-// //         ) : (
-// //           <div
-// //             className="carousel"
-// //             onMouseEnter={() => clearInterval(promoTimerRef.current)}
-// //             onMouseLeave={() => {
-// //               promoTimerRef.current = setInterval(() => {
-// //                 setPromoIndex(prev => (prev + 1) % promotions.length);
-// //               }, 3000);
-// //             }}
-// //           >
-// //             <button className="arrow left" onClick={prevPromo}>&lt;</button>
-// //             <div className="slide">
-// //               <div><strong>促销名称:</strong>{currentPromo?.name ?? '未知'}</div>
-// //               <div><strong>促销时间:</strong>
-// //                 {currentPromo?.pStartTime && currentPromo?.pEndTime
-// //                   ? `${new Date(currentPromo.pStartTime).toLocaleString()} ~ ${new Date(currentPromo.pEndTime).toLocaleString()}`
-// //                   : '未知'}
-// //               </div>
-// //               <div><strong>上传奖励系数:</strong>{currentPromo?.uploadCoeff ?? '无'}</div>
-// //               <div><strong>下载折扣系数:</strong>{currentPromo?.downloadCoeff ?? '无'}</div>
-// //               {currentPromo?.description && (
-// //                 <div><strong>描述:</strong>{currentPromo.description}</div>
-// //               )}
-// //               {isAdmin && (
-// //                 <button className="delete-btn" onClick={() => handleDeletePromotion(currentPromo.id)}>
-// //                   删除该活动
-// //                 </button>
-// //               )}
-// //             </div>
-// //             <button className="arrow right" onClick={nextPromo}>&gt;</button>
-// //           </div>
-// //         )}
-// //       </section>
-// //     </div>
-// //   );
-// // };
-
-// // export default Promotion;
-
-
 import React, { useEffect, useState, useRef } from 'react';
 import './Promotion.css';
 import { useUser } from '../../../context/UserContext';
+import axios from 'axios';
+import PromotionCarousel from './PromotionCarousel';
+import PromotionDetailDialog from './PromotionDetailDialog';
+import TorrentDetailDialog from './TorrentDetailDialog';
+import CreatePromotionDialog from './CreatePromotionDialog';
+import CategoryPromotionDialog from './CategoryPromotionDialog';
+import ColdTorrentsDialog from './ColdTorrentsDialog';
 
 const Promotion = () => {
   const { user } = useUser();
@@ -835,20 +16,36 @@
   const [loading, setLoading] = useState(true);
   const [promoIndex, setPromoIndex] = useState(0);
   const promoTimerRef = useRef(null);
-
-  // 新增:控制创建对话框显示
   const [showCreateDialog, setShowCreateDialog] = useState(false);
-
-  // 创建促销活动表单状态
+  const [showCategoryDialog, setShowCategoryDialog] = useState(false);
   const [formData, setFormData] = useState({
     name: '',
     startTime: '',
     endTime: '',
     discountPercentage: '',
-    uploadCoeff: '',
-    downloadCoeff: '',
-    description: ''
+    applicableTorrentIds: []
   });
+  const [categoryFormData, setCategoryFormData] = useState({
+    name: '',
+    startTime: '',
+    endTime: '',
+    discountPercentage: '',
+    category: 'movie'
+  });
+
+  // 冷门资源列表状态
+  const [coldTorrents, setColdTorrents] = useState([]);
+  const [showColdDialog, setShowColdDialog] = useState(false);
+  // 新增状态:控制促销对话框中冷门资源表格的显示
+  const [showPromoColdTable, setShowPromoColdTable] = useState(false);
+
+  // 新增状态:促销详情数据和详情对话框显示控制
+  const [promotionDetail, setPromotionDetail] = useState(null);
+  const [showDetailDialog, setShowDetailDialog] = useState(false);
+
+  // 新增状态:种子详情数据和种子详情对话框显示控制
+  const [torrentDetail, setTorrentDetail] = useState(null);
+  const [showTorrentDialog, setShowTorrentDialog] = useState(false);
 
   useEffect(() => {
     fetchData();
@@ -868,8 +65,11 @@
     try {
       const response = await fetch('/seeds/promotions');
       const json = await response.json();
-      const promoData = Array.isArray(json?.data) ? json.data : [];
-      setPromotions(promoData);
+      if (json.code === 0 || json.code === 200) {
+        setPromotions(json.data || []);
+      } else {
+        console.error('获取促销活动失败:', json.msg);
+      }
     } catch (error) {
       console.error('获取促销活动失败:', error);
     } finally {
@@ -877,38 +77,191 @@
     }
   };
 
+  const formatImageUrl = (url) => {
+    if (!url) return '';
+    const filename = url.split('/').pop();
+    return `http://localhost:5011/uploads/torrents/${filename}`;
+  };
+
+  // 修正后的获取种子详情函数
+  const fetchTorrentDetail = async (torrentId) => {
+  try {
+    // 修正参数名称为torrentId
+    const res = await axios.post(`/seeds/info/${torrentId}`);
+    if (res.data.code === 0) {
+      const seedData = res.data.data;
+      
+      // 处理封面图片
+      let cover = seedData.imageUrl;
+      if (!cover && seedData.imgUrl) {
+        const imgs = seedData.imgUrl
+          .split(',')
+          .map((i) => i.trim())
+          .filter(Boolean);
+        cover = imgs.length > 0 ? formatImageUrl(imgs[0]) : null;
+      }
+      
+      setTorrentDetail({...seedData, coverImage: cover});
+      setShowTorrentDialog(true);
+    } else {
+      alert(`获取种子详情失败: ${res.data.msg || '未知错误'}`);
+    }
+  } catch (err) {
+    console.error('获取种子详情失败:', err);
+    alert('获取种子详情失败');
+  }
+};
+
+
+const fetchPromotionDetail = async (promotionId) => {
+  try {
+    const response = await fetch(`/seeds/promotions/${promotionId}`);
+    const json = await response.json();
+    if (json.code === 0 || json.code === 200) {
+      // 正确解析applicableTorrentIds
+      const data = {
+        ...json.data,
+        applicableTorrentIds: parseTorrentIds(json.data.applicableTorrentIds)
+      };
+      setPromotionDetail(data);
+      setShowDetailDialog(true);
+    } else {
+      alert(`获取促销详情失败: ${json.msg || '未知错误'}`);
+    }
+  } catch (error) {
+    console.error('获取促销详情失败:', error);
+    alert('获取促销详情失败');
+  }
+};
+
+// 解析种子ID字符串为数组
+const parseTorrentIds = (idString) => {
+  try {
+    // 处理类似 "[69, 49, 6]" 的字符串
+    return JSON.parse(idString);
+  } catch (e) {
+    console.error('解析种子ID失败:', e);
+    return [];
+  }
+};
+
+  // 关闭详情对话框
+  const closeDetailDialog = () => {
+    setShowDetailDialog(false);
+    setPromotionDetail(null);
+  };
+
+  // 关闭种子详情对话框
+  const closeTorrentDialog = () => {
+    setShowTorrentDialog(false);
+    setTorrentDetail(null);
+  };
+
   const fetchTorrentList = async () => {
     try {
       const response = await fetch('/seeds/list');
       const json = await response.json();
-      const torrentList = Array.isArray(json?.data) ? json.data : [];
-      setTorrents(torrentList);
+      if (json.code === 0 || json.code === 200) {
+        // 为每个种子添加selected状态
+        const torrentsWithSelection = (json.data || []).map(torrent => ({
+          ...torrent,
+          selected: false
+        }));
+        setTorrents(torrentsWithSelection);
+      } else {
+        console.error('获取种子列表失败:', json.msg);
+      }
     } catch (error) {
       console.error('获取种子列表失败:', error);
     }
   };
 
-  // 打开创建促销活动弹窗
+  const fetchColdTorrents = async () => {
+    try {
+      const response = await fetch('/seeds/cold');
+      const json = await response.json();
+      if (json.code === 0 || json.code === 200) {
+        setColdTorrents(json.data || []); // 存储冷门资源数据
+        setShowColdDialog(true); // 打开模态框
+      } else {
+        alert(`获取冷门资源失败: ${json.msg || '未知错误'}`);
+      }
+    } catch (error) {
+      console.error('获取冷门资源失败:', error);
+      alert('获取冷门资源失败');
+    }
+  };
+
+  // 从服务器获取冷门资源并显示在促销对话框中
+  const fetchPromoColdTorrents = async () => {
+    try {
+      const response = await fetch('/seeds/cold');
+      const json = await response.json();
+      if (json.code === 0 || json.code === 200) {
+        setColdTorrents(json.data || []);
+        setShowPromoColdTable(true);
+      } else {
+        alert(`获取冷门资源失败: ${json.msg || '未知错误'}`);
+      }
+    } catch (error) {
+      console.error('获取冷门资源失败:', error);
+      alert('获取冷门资源失败');
+    }
+  };
+
+  // 处理促销对话框中种子的选择
+  const handlePromoTorrentSelection = (torrentId, isChecked) => {
+    setFormData(prev => {
+      const ids = [...prev.applicableTorrentIds];
+      if (isChecked) {
+        ids.push(torrentId);
+      } else {
+        const index = ids.indexOf(torrentId);
+        if (index !== -1) ids.splice(index, 1);
+      }
+      return {
+        ...prev,
+        applicableTorrentIds: ids
+      };
+    });
+  };
+
+  // 关闭冷门资源模态框
+  const closeColdDialog = () => {
+    setShowColdDialog(false);
+    setColdTorrents([]);
+  };
+
   const openCreateDialog = () => {
-    // 重置表单数据
     setFormData({
       name: '',
       startTime: '',
       endTime: '',
       discountPercentage: '',
-      uploadCoeff: '',
-      downloadCoeff: '',
-      description: ''
+      applicableTorrentIds: []
     });
     setShowCreateDialog(true);
   };
 
-  // 关闭弹窗
   const closeCreateDialog = () => {
     setShowCreateDialog(false);
   };
 
-  // 处理表单输入变化
+  const openCategoryDialog = () => {
+    setCategoryFormData({
+      name: '',
+      startTime: '',
+      endTime: '',
+      discountPercentage: '',
+      category: 'movie'
+    });
+    setShowCategoryDialog(true);
+  };
+
+  const closeCategoryDialog = () => {
+    setShowCategoryDialog(false);
+  };
+
   const handleInputChange = (e) => {
     const { name, value } = e.target;
     setFormData(prev => ({
@@ -917,12 +270,46 @@
     }));
   };
 
-  // 提交创建促销活动
+  const handleCategoryInputChange = (e) => {
+    const { name, value } = e.target;
+    setCategoryFormData(prev => ({
+      ...prev,
+      [name]: value
+    }));
+  };
+
+  const formatSize = (bytes) => {
+    if (bytes === 0) return '0 B';
+    
+    const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
+    const i = Math.floor(Math.log(bytes) / Math.log(1024));
+    
+    return parseFloat((bytes / Math.pow(1024, i)).toFixed(2)) + ' ' + sizes[i];
+  };
+
+  const handleTorrentSelection = (torrentId, isChecked) => {
+    setTorrents(prev => prev.map(torrent => 
+      torrent.id === torrentId 
+        ? {...torrent, selected: isChecked} 
+        : torrent
+    ));
+    
+    setFormData(prev => {
+      const ids = [...prev.applicableTorrentIds];
+      if (isChecked) {
+        ids.push(torrentId);
+      } else {
+        const index = ids.indexOf(torrentId);
+        if (index !== -1) ids.splice(index, 1);
+      }
+      return {
+        ...prev,
+        applicableTorrentIds: ids
+      };
+    });
+  };
+
   const handleCreatePromotion = async () => {
-    if (torrents.length === 0) {
-      alert('没有可用的种子,请先上传种子');
-      return;
-    }
     if (!formData.name.trim()) {
       alert('促销名称不能为空');
       return;
@@ -939,21 +326,19 @@
       alert('折扣百分比必须是数字');
       return;
     }
-
-    const applicableTorrentIds = torrents.map(t => t.id);
+    if (formData.applicableTorrentIds.length === 0) {
+      alert('请至少选择一个适用的种子');
+      return;
+    }
 
     const newPromo = {
       name: formData.name,
       startTime: new Date(formData.startTime).toISOString(),
       endTime: new Date(formData.endTime).toISOString(),
       discountPercentage: Number(formData.discountPercentage),
-      uploadCoeff: formData.uploadCoeff ? Number(formData.uploadCoeff) : undefined,
-      downloadCoeff: formData.downloadCoeff ? Number(formData.downloadCoeff) : undefined,
-      applicableTorrentIds: applicableTorrentIds,
-      description: formData.description
+      applicableTorrentIds: formData.applicableTorrentIds // 使用formData中的种子ID
     };
 
-
     try {
       const res = await fetch('/seeds/promotions', {
         method: 'POST',
@@ -961,12 +346,12 @@
         body: JSON.stringify(newPromo)
       });
       const json = await res.json();
-      if (json.code === 200 || json.code === 0) {
+      if (json.code === 0 || json.code === 200) {
         alert('促销活动创建成功');
         fetchData();
         setShowCreateDialog(false);
       } else {
-        alert('创建失败: ' + (json.msg || '未知错误'));
+        alert(`创建失败: ${json.msg || '未知错误'}`);
       }
     } catch (err) {
       console.error('创建促销失败:', err);
@@ -974,6 +359,51 @@
     }
   };
 
+  const handleCreateCategoryPromotion = async () => {
+    if (!categoryFormData.name.trim()) {
+      alert('促销名称不能为空');
+      return;
+    }
+    if (!categoryFormData.startTime || !categoryFormData.endTime) {
+      alert('促销开始时间和结束时间不能为空');
+      return;
+    }
+    if (new Date(categoryFormData.startTime) >= new Date(categoryFormData.endTime)) {
+      alert('促销结束时间必须晚于开始时间');
+      return;
+    }
+    if (!categoryFormData.discountPercentage || isNaN(categoryFormData.discountPercentage)) {
+      alert('折扣百分比必须是数字');
+      return;
+    }
+
+    const newPromo = {
+      name: categoryFormData.name,
+      startTime: new Date(categoryFormData.startTime).toISOString(),
+      endTime: new Date(categoryFormData.endTime).toISOString(),
+      discountPercentage: Number(categoryFormData.discountPercentage)
+    };
+
+    try {
+      const res = await fetch(`/seeds/promotions/category?category=${categoryFormData.category}`, {
+        method: 'POST',
+        headers: { 'Content-Type': 'application/json' },
+        body: JSON.stringify(newPromo)
+      });
+      const json = await res.json();
+      if (json.code === 0 || json.code === 200) {
+        alert('分类促销活动创建成功');
+        fetchData();
+        setShowCategoryDialog(false);
+      } else {
+        alert(`创建失败: ${json.msg || '未知错误'}`);
+      }
+    } catch (err) {
+      console.error('创建分类促销失败:', err);
+      alert('创建分类促销失败');
+    }
+  };
+
   const handleDeletePromotion = async (promotionId) => {
     if (!window.confirm('确认删除该促销活动吗?')) return;
 
@@ -984,7 +414,7 @@
         alert('删除成功');
         fetchData();
       } else {
-        alert('删除失败: ' + (json.msg || '未知错误'));
+        alert(`删除失败: ${json.msg || '未知错误'}`);
       }
     } catch (err) {
       console.error('删除失败:', err);
@@ -993,146 +423,93 @@
 
   const isAdmin = user?.role === 'admin';
   const prevPromo = () => setPromoIndex((promoIndex - 1 + promotions.length) % promotions.length);
-    const nextPromo = () => setPromoIndex((promoIndex + 1) % promotions.length);
+  const nextPromo = () => setPromoIndex((promoIndex + 1) % promotions.length);
   const currentPromo = promotions[promoIndex];
 
+  const formatDateTime = (dateTime) => {
+    if (!dateTime) return '未知';
+    return new Date(dateTime).toLocaleString();
+  };
+
+  const getUploadBonusDisplay = (promo) => {
+    if (!promo || !promo.discountPercentage) return '无';
+    const bonus = promo.discountPercentage;
+    return bonus > 0 ? `+${bonus}%` : `-${Math.abs(bonus)}%`;
+  };
+
+  const getDownloadDiscountDisplay = (promo) => {
+    if (!promo || !promo.downloadDiscount) return '无';
+    const discount = (1 - promo.downloadDiscount) * 100;
+    return discount > 0 ? `-${discount.toFixed(1)}%` : '无';
+  };
+
   if (loading) {
     return <div className="promotion-container">加载中...</div>;
-  }
+  };
 
   return (
     <div className="promotion-container carousel-container">
-      <section className="carousel-section">
-        <h2>当前促销活动</h2>
+      <PromotionCarousel
+        promotions={promotions}
+        currentPromo={currentPromo}
+        prevPromo={prevPromo}
+        nextPromo={nextPromo}
+        isAdmin={isAdmin}
+        openCreateDialog={openCreateDialog}
+        openCategoryDialog={openCategoryDialog}
+        fetchColdTorrents={fetchColdTorrents}
+        handleDeletePromotion={handleDeletePromotion}
+        fetchPromotionDetail={fetchPromotionDetail}
+      />
 
-        {isAdmin && (
-          <button className="create-btn" onClick={openCreateDialog}>
-            创建促销活动
-          </button>
-        )}
+      {/* 新增:冷门资源模态框 */}
+      <ColdTorrentsDialog
+        showColdDialog={showColdDialog}
+        coldTorrents={coldTorrents}
+        closeColdDialog={closeColdDialog}
+        fetchTorrentDetail={fetchTorrentDetail}
+      />
 
-        {promotions.length === 0 || !currentPromo ? (
-          <div className="empty-state">暂无促销活动</div>
-        ) : (
-          <div
-            className="carousel"
-            onMouseEnter={() => clearInterval(promoTimerRef.current)}
-            onMouseLeave={() => {
-              promoTimerRef.current = setInterval(() => {
-                setPromoIndex(prev => (prev + 1) % promotions.length);
-              }, 3000);
-            }}
-          >
-            <button className="arrow left" onClick={prevPromo}>&lt;</button>
-            <div className="slide">
-              <div><strong>促销名称:</strong>{currentPromo?.name ?? '未知'}</div>
-              <div><strong>促销时间:</strong>
-                {currentPromo?.pStartTime && currentPromo?.pEndTime
-                  ? `${new Date(currentPromo.pStartTime).toLocaleString()} ~ ${new Date(currentPromo.pEndTime).toLocaleString()}`
-                  : '未知'}
-              </div>
-              <div><strong>上传奖励系数:</strong>{currentPromo?.uploadCoeff ?? '无'}</div>
-              <div><strong>下载折扣系数:</strong>{currentPromo?.downloadCoeff ?? '无'}</div>
-              {currentPromo?.description && (
-                <div><strong>描述:</strong>{currentPromo.description}</div>
-              )}
-              {isAdmin && (
-                <button className="delete-btn" onClick={() => handleDeletePromotion(currentPromo.id)}>
-                  删除该活动
-                </button>
-              )}
-            </div>
-            <button className="arrow right" onClick={nextPromo}>&gt;</button>
-          </div>
-        )}
-      </section>
+      {/* 促销详情对话框 */}
+      <PromotionDetailDialog
+        showDetailDialog={showDetailDialog}
+        promotionDetail={promotionDetail}
+        closeDetailDialog={closeDetailDialog}
+        torrents={torrents}
+        fetchTorrentDetail={fetchTorrentDetail}
+      />
 
-      {/* 创建促销活动弹窗 */}
-      {showCreateDialog && (
-        <div className="dialog-overlay">
-          <div className="dialog">
-            <h3>创建促销活动</h3>
-            <div className="form-item">
-              <label>促销名称:</label>
-              <input
-                type="text"
-                name="name"
-                value={formData.name}
-                onChange={handleInputChange}
-                placeholder="请输入促销名称"
-              />
-            </div>
-            <div className="form-item">
-              <label>开始时间:</label>
-              <input
-                type="datetime-local"
-                name="startTime"
-                value={formData.startTime}
-                onChange={handleInputChange}
-              />
-            </div>
-            <div className="form-item">
-              <label>结束时间:</label>
-              <input
-                type="datetime-local"
-                name="endTime"
-                value={formData.endTime}
-                onChange={handleInputChange}
-              />
-            </div>
-            <div className="form-item">
-              <label>折扣百分比(数字):</label>
-              <input
-                type="number"
-                name="discountPercentage"
-                value={formData.discountPercentage}
-                onChange={handleInputChange}
-                placeholder="例如:20 表示 20% 折扣"
-                min="0"
-                max="100"
-              />
-            </div>
-            <div className="form-item">
-              <label>上传奖励系数(可选):</label>
-              <input
-                type="number"
-                name="uploadCoeff"
-                value={formData.uploadCoeff}
-                onChange={handleInputChange}
-                placeholder="例如:1.5"
-                step="0.1"
-              />
-            </div>
-            <div className="form-item">
-              <label>下载折扣系数(可选):</label>
-              <input
-                type="number"
-                name="downloadCoeff"
-                value={formData.downloadCoeff}
-                onChange={handleInputChange}
-                placeholder="例如:0.8"
-                step="0.1"
-              />
-            </div>
-            <div className="form-item">
-              <label>描述(可选):</label>
-              <textarea
-                name="description"
-                value={formData.description}
-                onChange={handleInputChange}
-                placeholder="促销活动描述"
-                rows={3}
-              />
-            </div>
-            <div className="dialog-buttons">
-              <button onClick={handleCreatePromotion}>确定</button>
-              <button onClick={closeCreateDialog}>取消</button>
-            </div>
-          </div>
-        </div>
-      )}
+      {/* 种子详情对话框 */}
+      <TorrentDetailDialog
+        showTorrentDialog={showTorrentDialog}
+        torrentDetail={torrentDetail}
+        closeTorrentDialog={closeTorrentDialog}
+      />
+
+      {/* 创建冷门资源促销弹窗 */}
+      <CreatePromotionDialog
+        showCreateDialog={showCreateDialog}
+        formData={formData}
+        handleInputChange={handleInputChange}
+        closeCreateDialog={closeCreateDialog}
+        handleCreatePromotion={handleCreatePromotion}
+        fetchPromoColdTorrents={fetchPromoColdTorrents}
+        showPromoColdTable={showPromoColdTable}
+        coldTorrents={coldTorrents}
+        handlePromoTorrentSelection={handlePromoTorrentSelection}
+      />
+
+      {/* 创建特定分类促销弹窗 */}
+      <CategoryPromotionDialog
+        showCategoryDialog={showCategoryDialog}
+        categoryFormData={categoryFormData}
+        handleCategoryInputChange={handleCategoryInputChange}
+        closeCategoryDialog={closeCategoryDialog}
+        handleCreateCategoryPromotion={handleCreateCategoryPromotion}
+      />
     </div>
   );
 };
 
-export default Promotion;
+export default Promotion;    
+
diff --git a/src/pages/Forum/promotion-part/PromotionCarousel.css b/src/pages/Forum/promotion-part/PromotionCarousel.css
new file mode 100644
index 0000000..330c886
--- /dev/null
+++ b/src/pages/Forum/promotion-part/PromotionCarousel.css
@@ -0,0 +1,150 @@
+/* 优化后的轮播样式 */
+.carousel-container {
+  display: flex;
+  margin-top: 20px;
+  margin-left: 2%;
+  margin-right: 2%;
+}
+
+.carousel-section {
+  flex: 1;
+}
+
+.carousel-section h2 {
+  font-size: 20px;
+  margin-bottom: 15px;
+}
+
+.carousel {
+  position: relative;
+  background: linear-gradient(135deg, #e1cab2, #b68791);
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
+  border-radius: 12px;
+  padding: 24px 48px;
+  color: #fff;
+  min-height: 220px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  overflow: hidden;
+}
+
+.promotion-name {
+  font-size: 24px;
+  font-weight: bold;
+  margin-top: 12px;
+  margin-bottom: 14px;
+  color: #135c69;
+  text-align: center;
+}
+
+.carousel .arrow {
+  background: rgba(0, 0, 0, 0.3);
+  border: none;
+  color: #fff;
+  font-size: 24px;
+  width: 40px;
+  height: 40px;
+  border-radius: 50%;
+  cursor: pointer;
+  position: absolute;
+  top: 50%;
+  transform: translateY(-50%);
+  transition: background 0.3s;
+  z-index: 10;
+}
+
+.carousel .arrow.left {
+  left: 16px;
+}
+
+.carousel .arrow.right {
+  right: 16px;
+}
+
+.carousel .arrow:hover {
+  background: rgba(0, 0, 0, 0.5);
+}
+
+.carousel .slide {
+  width: 100%;
+  max-width: 720px;
+  text-align: left;
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+  line-height: 1.6;
+}
+
+.carousel .slide strong {
+  font-weight: 600;
+}
+
+.carousel .action-buttons {
+  display: flex;
+  gap: 12px;
+  margin-top: 12px;
+}
+
+.carousel .action-buttons button {
+  padding: 6px 12px;
+  font-size: 14px;
+  border: none;
+  border-radius: 6px;
+  cursor: pointer;
+  transition: background-color 0.3s;
+}
+
+.carousel .delete-btn {
+  background-color: #d9534f;
+  color: white;
+}
+
+.carousel .delete-btn:hover {
+  background-color: #c9302c;
+}
+
+.carousel .view-btn {
+  background-color: #5bc0de;
+  color: white;
+}
+
+.carousel .view-btn:hover {
+  background-color: #31b0d5;
+}
+
+/* 轮播指示器容器 */
+.carousel-indicators {
+  margin-top: 5%;
+  position: absolute; 
+  bottom: 16px;       /* 距离轮播底部的间距,可调整 */
+  left: 50%;         
+  transform: translateX(-50%); /* 水平居中 */
+  display: flex;     
+  align-items: center;
+  gap: 8px;          /* 圆点间距 */
+  z-index: 10;       /* 确保在内容上层 */
+}
+
+/* 单个圆点 */
+.carousel-indicators .indicator {
+  width: 12px;       
+  height: 12px;      
+  border-radius: 50%; /* 圆形 */
+  background-color: rgba(255, 255, 255, 0.4); /* 未选中状态颜色 */
+  cursor: pointer;   
+  transition: background-color 0.3s ease; /* 渐变过渡 */
+}
+
+/* 选中状态的圆点 */
+.carousel-indicators .indicator.active {
+  background-color: #fff; /* 选中时的高亮颜色 */
+}
+
+
+/* 冷门资源专用 slide */
+.cold-slide {
+  display: flex;
+  gap: 10px;
+  align-items: center;
+}
diff --git a/src/pages/Forum/promotion-part/PromotionCarousel.jsx b/src/pages/Forum/promotion-part/PromotionCarousel.jsx
new file mode 100644
index 0000000..6408c54
--- /dev/null
+++ b/src/pages/Forum/promotion-part/PromotionCarousel.jsx
@@ -0,0 +1,143 @@
+import React, { useState } from 'react';
+import './PromotionCarousel.css'; 
+
+const PromotionCarousel = ({
+  promotions,
+  isAdmin,
+  openCreateDialog,
+  openCategoryDialog,
+  fetchColdTorrents,
+  handleDeletePromotion,
+  fetchPromotionDetail
+}) => {
+  // 1. 新增状态:跟踪当前轮播项的索引
+  const [currentIndex, setCurrentIndex] = useState(0);
+
+  // 2. 上一个/下一个轮播的逻辑
+  const prevPromo = () => {
+    if (promotions.length === 0) return;
+    setCurrentIndex(prev => 
+      prev === 0 ? promotions.length - 1 : prev - 1
+    );
+  };
+
+  const nextPromo = () => {
+    if (promotions.length === 0) return;
+    setCurrentIndex(prev => 
+      prev === promotions.length - 1 ? 0 : prev + 1
+    );
+  };
+
+  // 3. 当前显示的促销项(从数组中取)
+  const currentPromo = promotions[currentIndex];
+
+  // 原有工具函数保持不变
+  const formatDateTime = (dateTime) => {
+    if (!dateTime) return '未知';
+    // dateTime [2025,1,3,12,10]
+    return new Date(...dateTime).toLocaleString();
+
+  };
+
+  // 修改为一个统一的处理函数,避免混淆
+const getUploadBonusDisplay = (promo) => {
+  if (!promo || typeof promo.discountPercentage !== 'number') return '无';
+  const bonus = promo.discountPercentage;
+  return bonus > 0 ? `${(bonus * 100).toFixed(0)}%` : '无';
+};
+
+const getDownloadDiscountDisplay = (promo) => {
+  if (!promo || typeof promo.discountPercentage !== 'number') return '无';
+  const discount = promo.discountPercentage;
+  return discount < 0 ? `${Math.abs(discount * 100).toFixed(0)}%` : '无';
+};
+
+
+  if (!Array.isArray(promotions) || promotions.length === 0 || !promotions[currentIndex]) {
+  return (
+    <section className="carousel-section">
+      <h2 style={{ marginLeft: '1.6rem' }}>当前促销活动</h2>
+      {isAdmin && (
+        <div style={{ display: 'flex', gap: '0px', marginBottom: '10px' }}>
+          <button className="create-btn" onClick={openCreateDialog}>
+            +创建冷门资源促销
+          </button>
+          <button className="create-btn" onClick={openCategoryDialog}>
+            +创建特定分类促销
+          </button>
+        </div>
+      )}
+      <div className="empty-state">暂无促销活动</div>
+    </section>
+  );
+}
+
+
+  return (
+    <section className="carousel-section">
+      <h2 style={{ marginLeft: '1.6rem' }}>当前促销活动</h2>
+
+      {isAdmin && (
+        <div style={{ display: 'flex', gap: '0px', marginBottom: '10px' }}>
+          <button className="create-btn" onClick={openCreateDialog}>
+            +创建冷门资源促销
+          </button>
+          <button className="create-btn" onClick={openCategoryDialog}>
+            +创建特定分类促销
+          </button>
+        </div>
+      )}
+
+      <div
+        className="carousel"
+        onMouseEnter={() => {}}
+        onMouseLeave={() => {}}
+      >
+
+        <button className="arrow left" onClick={prevPromo}>&lt;</button>
+        <div className="slide">
+          <div className='promotion-name'><strong>{currentPromo?.name ?? '未知'}</strong></div>
+          <div style={{ color: '#135c69' }}><strong>促销时间:</strong>
+            {currentPromo?.startTime && currentPromo?.endTime
+              ? `${formatDateTime(currentPromo.startTime)} ~ ${formatDateTime(currentPromo.endTime)}`
+              : '未知'}
+          </div>
+          <div style={{ color: '#135c69' }}><strong>上传奖励:</strong>{getUploadBonusDisplay(currentPromo)}</div>
+          <div style={{ color: '#135c69' }}><strong>下载折扣:</strong>{getDownloadDiscountDisplay(currentPromo)}</div>
+          {currentPromo?.description && (
+            <div><strong>描述:</strong>{currentPromo.description}</div>
+          )}            
+
+          <div className="action-buttons" style={{ display: 'flex', gap: '10px' }}>
+            {isAdmin && (
+              <button className="delete-btn" onClick={() => handleDeletePromotion(currentPromo.id)}>
+                删除活动
+              </button>
+            )}
+            <button
+              className="view-btn"
+              onClick={() => fetchPromotionDetail(currentPromo.id)}
+              aria-label={`查看${currentPromo.name}的详情`}
+            >
+              查看详情
+            </button>
+          </div>
+        </div>
+        <button className="arrow right" onClick={nextPromo}>&gt;</button>
+
+        {/* 4. 新增:底部指示器 */}
+        <div className="carousel-indicators">
+          {promotions.map((_, index) => (
+            <div
+              key={index}
+              className={`indicator ${currentIndex === index ? 'active' : ''}`}
+              onClick={() => setCurrentIndex(index)} // 点击切换轮播项
+            />
+          ))}
+        </div>
+      </div>
+    </section>
+  );
+};
+
+export default PromotionCarousel;
\ No newline at end of file
diff --git a/src/pages/Forum/promotion-part/PromotionDetailDialog.jsx b/src/pages/Forum/promotion-part/PromotionDetailDialog.jsx
new file mode 100644
index 0000000..5310c79
--- /dev/null
+++ b/src/pages/Forum/promotion-part/PromotionDetailDialog.jsx
@@ -0,0 +1,166 @@
+import { type } from '@testing-library/user-event/dist/type';
+import React from 'react';
+import { useNavigate } from 'react-router-dom';
+
+const PromotionDetailDialog = ({
+  showDetailDialog,
+  promotionDetail,
+  closeDetailDialog,
+  torrents,
+  fetchTorrentDetail
+}) => {
+  const navigate = useNavigate();
+
+  // 处理API返回的日期数组格式
+  const parseApiDate = (dateArray) => {
+    if (!Array.isArray(dateArray) || dateArray.length < 3) return null;
+    
+    // 注意:JavaScript的Date月份是从0开始的,所以需要减1
+    return new Date(
+      dateArray[0],        // 年
+      dateArray[1] - 1,    // 月 (减1)
+      dateArray[2],        // 日
+      dateArray[3] || 0,   // 时 (默认0)
+      dateArray[4] || 0,   // 分 (默认0)
+      dateArray[5] || 0    // 秒 (默认0)
+    );
+  };
+
+  const formatDateTime = (dateArray) => {
+    if (!dateArray) return '未知';
+    
+    try {
+      const date = parseApiDate(dateArray);
+      return date ? date.toLocaleString() : '格式错误';
+    } catch (e) {
+      return '格式错误';
+    }
+  };
+
+  const getUploadBonusDisplay = (promo) => {
+    if (!promo || !promo.discountPercentage) return '无';
+    const bonus = promo.discountPercentage;
+    return bonus > 0 ? `+${bonus}%` : `-${Math.abs(bonus)}%`;
+  };
+
+  const getDownloadDiscountDisplay = (promo) => {
+    if (!promo || !promo.downloadDiscount) return '无';
+    const discount = (1 - promo.downloadDiscount) * 100;
+    return discount > 0 ? `-${discount.toFixed(1)}%` : '无';
+  };
+
+  const redirectToTorrentDetail = (torrentId) => {
+    if (fetchTorrentDetail) {
+      navigate(`/seed/${torrentId}`);
+    }
+  };
+
+  return (
+    showDetailDialog && promotionDetail && (
+      <div className="detail-dialog-overlay">
+        <div className="detail-dialog">
+          <h3 className="detail-dialog-title">{promotionDetail.name}</h3>
+          <button 
+            className="close-btn" 
+            onClick={closeDetailDialog}
+          >
+            &times;
+          </button>
+          
+          <div className="detail-content">
+            {/* <div className="detail-item"> */}
+              {/* <span className="detail-label">促销ID:</span> */}
+              {/* <span className="detail-value">{promotionDetail.id}</span>
+            </div> */}
+            {/* <div className="detail-item">
+              <span className="detail-label">促销名称:</span>
+              <span className="detail-value">{promotionDetail.name}</span>
+            </div> */}
+            <div className="detail-item">
+              <span className="detail-label">开始时间:</span>
+              <span className="detail-value">{formatDateTime(promotionDetail.startTime)}</span>
+            </div>
+            <div className="detail-item">
+              <span className="detail-label">结束时间:</span>
+              <span className="detail-value">{formatDateTime(promotionDetail.endTime)}</span>
+            </div>
+            <div className="detail-item">
+              <span className="detail-label">上传奖励:</span>
+              <span className="detail-value">{getUploadBonusDisplay(promotionDetail)}</span>
+            </div>
+            <div className="detail-item">
+              <span className="detail-label">下载折扣:</span>
+              <span className="detail-value">{getDownloadDiscountDisplay(promotionDetail)}</span>
+            </div>
+            {/* <div className="detail-item">
+              <span className="detail-label">创建时间:</span>
+              <span className="detail-value">{formatDateTime(promotionDetail.createTime)}</span>
+            </div> */}
+            {/* <div className="detail-item">
+              <span className="detail-label">创建者:</span>
+              <span className="detail-value">{promotionDetail.creator || '未知'}</span>
+            </div> */}
+            
+            {promotionDetail.description && (
+              <div className="detail-item">
+                <span className="detail-label">描述:</span>
+                <span className="detail-value">{promotionDetail.description}</span>
+              </div>
+            )}
+            
+            {/* 适用种子列表 */}
+            <div className="detail-item">
+              <span className="detail-label">适用种子:</span>
+              <div className="detail-value">
+                {promotionDetail.applicableTorrentIds ? (
+                  <div className="torrent-list">
+                    {/* 解析字符串形式的数组 */}
+                    {parseTorrentIds(promotionDetail.applicableTorrentIds).map(torrentId => {
+                      const matchedTorrent = torrents.find(t => t.id === torrentId) || {};
+
+                      return (
+                        <button
+                          key={torrentId}
+                          className="torrent-link"
+                          onClick={() => redirectToTorrentDetail(torrentId)}
+                          aria-label={`查看种子${matchedTorrent.name || torrentId}的详情`}
+                        >
+                          {matchedTorrent.name || `种子${torrentId}`}
+                          {/* {matchedTorrent.seeders > 10 && <span className="status-indicator hot">热门</span>}
+                          {matchedTorrent.seeders === 0 && <span className="status-indicator cold">冷门</span>} */}
+                        </button>
+                      );
+                    })}
+                  </div>
+                ) : (
+                  <span className="empty-list">暂无适用种子</span>
+                )}
+              </div>
+            </div>
+          </div>
+          
+          <div className="dialog-buttons">
+            <button onClick={closeDetailDialog}>关闭</button>
+          </div>
+        </div>
+      </div>
+    )
+  );
+};
+
+// 解析种子ID字符串为数组
+const parseTorrentIds = (idString) => {
+  if (typeof idString === 'number') {
+    return [idString];
+  }
+
+  if (Array.isArray(idString)) {
+    return idString;
+  }
+
+  let items = typeof idString === 'string' && !idString.startsWith('[') ? idString.split(',') : JSON.parse(idString || '[]');
+
+  return items;
+};
+
+export default PromotionDetailDialog;
\ No newline at end of file
diff --git a/src/pages/Forum/promotion-part/TorrentDetailDialog.jsx b/src/pages/Forum/promotion-part/TorrentDetailDialog.jsx
new file mode 100644
index 0000000..f038b90
--- /dev/null
+++ b/src/pages/Forum/promotion-part/TorrentDetailDialog.jsx
@@ -0,0 +1,130 @@
+import React from 'react';
+
+const TorrentDetailDialog = ({
+  showTorrentDialog,
+  torrentDetail,
+  closeTorrentDialog
+}) => {
+  const formatSize = (bytes) => {
+    if (bytes === 0) return '0 B';
+    
+    const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
+    const i = Math.floor(Math.log(bytes) / Math.log(1024));
+    
+    return parseFloat((bytes / Math.pow(1024, i)).toFixed(2)) + ' ' + sizes[i];
+  };
+
+  const formatDateTime = (dateTime) => {
+    if (!dateTime) return '未知';
+    return new Date(dateTime).toLocaleString();
+  };
+
+  return (
+    showTorrentDialog && torrentDetail && (
+      <div className="torrent-detail-overlay">
+        <div className="torrent-detail-dialog">
+          <h3 className="torrent-detail-title">种子详情 - {torrentDetail.title}</h3>
+          <button 
+            className="close-btn" 
+            onClick={closeTorrentDialog}
+          >
+            &times;
+          </button>
+          
+          {/* 封面图片 */}
+          {torrentDetail.coverImage && (
+            <div className="torrent-cover-container">
+              <img 
+                src={torrentDetail.coverImage} 
+                alt={`${torrentDetail.name}封面`} 
+                className="torrent-cover"
+              />
+            </div>
+          )}
+          
+          <div className="torrent-detail-content">
+            <div className="torrent-detail-item">
+              <span className="torrent-detail-label">种子ID:</span>
+              <span className="torrent-detail-value">{torrentDetail.id}</span>
+            </div>
+            <div className="torrent-detail-item">
+              <span className="torrent-detail-label">种子名称:</span>
+              <span className="torrent-detail-value">{torrentDetail.title}</span>
+            </div>
+            <div className="torrent-detail-item">
+              <span className="torrent-detail-label">分类:</span>
+              <span className="torrent-detail-value">{torrentDetail.category || '未分类'}</span>
+            </div>
+            <div className="torrent-detail-item">
+              <span className="torrent-detail-label">大小:</span>
+              <span className="torrent-detail-value">{formatSize(torrentDetail.size)}</span>
+            </div>
+            <div className="torrent-detail-item">
+              <span className="torrent-detail-label">上传者:</span>
+              <span className="torrent-detail-value">{torrentDetail.username || '匿名'}</span>
+            </div>
+            <div className="torrent-detail-item">
+              <span className="torrent-detail-label">上传时间:</span>
+              <span className="torrent-detail-value">{formatDateTime(torrentDetail.createTime)}</span>
+            </div>
+            <div className="torrent-detail-item">
+              <span className="torrent-detail-label">做种人数:</span>
+              <span className="torrent-detail-value">{torrentDetail.seeders || 0}</span>
+            </div>
+            <div className="torrent-detail-item">
+              <span className="torrent-detail-label">下载人数:</span>
+              <span className="torrent-detail-value">{torrentDetail.leechers || 0}</span>
+            </div>
+            <div className="torrent-detail-item">
+              <span className="torrent-detail-label">完成次数:</span>
+              <span className="torrent-detail-value">{torrentDetail.completed || 0}</span>
+            </div>
+            
+            {/* 种子状态 */}
+            <div className="torrent-detail-item">
+              <span className="torrent-detail-label">状态:</span>
+              <span className="torrent-detail-value">
+                {torrentDetail.seeders > 10 ? (
+                  <span className="status-badge hot">热门</span>
+                ) : torrentDetail.seeders === 0 ? (
+                  <span className="status-badge cold">冷门</span>
+                ) : (
+                  <span className="status-badge normal">普通</span>
+                )}
+              </span>
+            </div>
+            
+            {/* 种子描述 */}
+            {torrentDetail.description && (
+              <div className="torrent-detail-item">
+                <span className="torrent-detail-label">描述:</span>
+                <div className="torrent-detail-value description">{torrentDetail.description}</div>
+              </div>
+            )}
+            
+            {/* 下载链接 */}
+            {torrentDetail.downloadUrl && (
+              <div className="torrent-detail-item">
+                <span className="torrent-detail-label">下载:</span>
+                <a 
+                  href={torrentDetail.downloadUrl} 
+                  target="_blank" 
+                  rel="noopener noreferrer"
+                  className="download-link"
+                >
+                  <i className="fa fa-download"></i> 下载种子
+                </a>
+              </div>
+            )}
+          </div>
+          
+          <div className="dialog-buttons">
+            <button onClick={closeTorrentDialog}>关闭</button>
+          </div>
+        </div>
+      </div>
+    )
+  );
+};
+
+export default TorrentDetailDialog;    
\ No newline at end of file