修改促销、优化页面布局
Change-Id: Iae813b5b6557efa7059fe6d94bc32e96c984e4ea
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}><</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}>></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}><</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}>></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}><</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}>></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}><</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}>></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;
+