修改好友动态、发布动态、促销模块、创建帖子,Resolve review.
Change-Id: I84a2460dd1208bc703b0527d98225204d03e5efc
diff --git a/src/pages/Forum/promotion-part/Promotion.css b/src/pages/Forum/promotion-part/Promotion.css
new file mode 100644
index 0000000..2232ba5
--- /dev/null
+++ b/src/pages/Forum/promotion-part/Promotion.css
@@ -0,0 +1,110 @@
+.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;
+ height: 100px;
+ object-fit: cover;
+ border-radius: 4px;
+}
+
+/* 文本信息 */
+.resource-info div,
+.slide div {
+ margin-bottom: 6px;
+}
+
+/* 激励徽章 */
+.incentive-badge {
+ background-color: #17a2b8;
+ color: #d13c3c;
+ padding: 2px 8px;
+ margin-right: 6px;
+ font-size: 12px;
+ border-radius: 12px;
+}
+
+/* 空状态 */
+.empty-state {
+ color: #fff;
+ font-size: 16px;
+ text-align: center;
+}
diff --git a/src/pages/Forum/promotion-part/Promotion.jsx b/src/pages/Forum/promotion-part/Promotion.jsx
new file mode 100644
index 0000000..9af56bc
--- /dev/null
+++ b/src/pages/Forum/promotion-part/Promotion.jsx
@@ -0,0 +1,144 @@
+import React, { useEffect, useState, useRef } from 'react';
+import './Promotion.css';
+
+const API_BASE = process.env.REACT_APP_API_BASE;
+
+const Promotion = () => {
+ const [promotions, setPromotions] = useState([]);
+ const [coldResources, setColdResources] = useState([]);
+ const [loading, setLoading] = useState(true);
+
+ // 轮播索引
+ const [promoIndex, setPromoIndex] = useState(0);
+ const [coldIndex, setColdIndex] = useState(0);
+
+ // 计时器引用,用于清理
+ const promoTimerRef = useRef(null);
+ const coldTimerRef = useRef(null);
+
+ useEffect(() => {
+ fetchData();
+ }, []);
+
+ // 自动轮播:促销活动
+ useEffect(() => {
+ if (promotions.length === 0) return;
+ // 清理旧的计时器
+ clearInterval(promoTimerRef.current);
+ promoTimerRef.current = setInterval(() => {
+ setPromoIndex(prev => (prev + 1) % promotions.length);
+ }, 5000);
+ return () => clearInterval(promoTimerRef.current);
+ }, [promotions]);
+
+ // 自动轮播:冷门资源
+ useEffect(() => {
+ if (coldResources.length === 0) return;
+ clearInterval(coldTimerRef.current);
+ coldTimerRef.current = setInterval(() => {
+ setColdIndex(prev => (prev + 1) % coldResources.length);
+ }, 5000);
+ return () => clearInterval(coldTimerRef.current);
+ }, [coldResources]);
+
+ const fetchData = async () => {
+ try {
+ const promoResponse = await fetch(`${API_BASE}/echo/promotions/active`);
+ const promoData = await promoResponse.json();
+ setPromotions(promoData);
+
+ const coldResponse = await fetch(`${API_BASE}/echo/resources/cold`);
+ const coldData = await coldResponse.json();
+ setColdResources(coldData);
+ } catch (error) {
+ console.error('获取数据失败:', error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ if (loading) {
+ return <div className="promotion-container">加载中...</div>;
+ }
+
+ // 手动切换
+ const prevPromo = () => setPromoIndex((promoIndex - 1 + promotions.length) % promotions.length);
+ const nextPromo = () => setPromoIndex((promoIndex + 1) % promotions.length);
+ const prevCold = () => setColdIndex((coldIndex - 1 + coldResources.length) % coldResources.length);
+ const nextCold = () => setColdIndex((coldIndex + 1) % coldResources.length);
+
+ const currentPromo = promotions[promoIndex];
+ const currentCold = coldResources[coldIndex];
+
+ return (
+ <div className="promotion-container carousel-container">
+ {/* 促销活动轮播 */}
+ <section className="carousel-section">
+ <h2>当前促销活动</h2>
+ {promotions.length === 0 ? (
+ <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.category}</div>
+ <div><strong>促销时间:</strong>
+ {new Date(currentPromo.promotion_start_time).toLocaleString()} ~{' '}
+ {new Date(currentPromo.promotion_end_time).toLocaleString()}
+ </div>
+ <div><strong>下载折扣:</strong>{currentPromo.download_discount ?? '无'}</div>
+ <div><strong>上传奖励:</strong>{currentPromo.upload_reward ?? '无'}</div>
+ {currentPromo.description && (
+ <div><strong>详细描述:</strong>{currentPromo.description}</div>
+ )}
+ </div>
+ <button className="arrow right" onClick={nextPromo}>></button>
+ </div>
+ )}
+ </section>
+
+ {/* 冷门资源轮播 */}
+ <section className="carousel-section">
+ <h2>冷门资源推荐</h2>
+ {coldResources.length === 0 ? (
+ <div className="empty-state">暂无冷门资源推荐</div>
+ ) : (
+ <div
+ className="carousel"
+ onMouseEnter={() => clearInterval(coldTimerRef.current)}
+ onMouseLeave={() => {
+ coldTimerRef.current = setInterval(() => {
+ setColdIndex(prev => (prev + 1) % coldResources.length);
+ }, 3000);
+ }}
+ >
+ <button className="arrow left" onClick={prevCold}><</button>
+ <div className="slide cold-slide">
+ <img src={currentCold.poster} alt={currentCold.title} className="resource-poster" />
+ <div className="resource-info">
+ <div><strong>标题:</strong>{currentCold.title}</div>
+ <div><strong>下载量:</strong>{currentCold.download_count} | <strong>种子数:</strong>{currentCold.seed_count}</div>
+ <div><strong>激励:</strong>
+ {currentCold.incentives?.download_exempt && <span className="incentive-badge">免下载量</span>}
+ {currentCold.incentives?.extra_seed_bonus && <span className="incentive-badge">做种加成</span>}
+ {!(currentCold.incentives?.download_exempt || currentCold.incentives?.extra_seed_bonus) && '无'}
+ </div>
+ </div>
+ </div>
+ <button className="arrow right" onClick={nextCold}>></button>
+ </div>
+ )}
+ </section>
+ </div>
+ );
+};
+
+export default Promotion;