修改前端页面
Change-Id: I344849fc3dfca5725e0ef2a107e2c89a14fce8da
diff --git a/front/src/SeedPromotionPage.js b/front/src/SeedPromotionPage.js
index 249533c..6e09f62 100644
--- a/front/src/SeedPromotionPage.js
+++ b/front/src/SeedPromotionPage.js
@@ -1,45 +1,73 @@
import React, { useEffect, useState } from "react";
import { API_BASE_URL } from "./config";
-// 示例数据,实际应从后端获取
-const mockSeeds = [
- {
- seed_id: "seed001",
- title: "三体 1080P 蓝光",
- tags: "科幻,电影",
- popularity: 123,
- promotion: {
- start_time: "",
- end_time: "",
- discount: 1,
- },
- },
- {
- seed_id: "seed002",
- title: "灌篮高手 国语配音",
- tags: "动画,体育",
- popularity: 88,
- promotion: {
- start_time: "",
- end_time: "",
- discount: 1,
- },
- },
-];
-
export default function SeedPromotionPage() {
const [seeds, setSeeds] = useState([]);
const [currentTime, setCurrentTime] = useState("");
+ const [loading, setLoading] = useState(true);
+ // 时间戳转datetime-local字符串
+ const tsToDatetimeLocal = (ts) => {
+ if (!ts) return "";
+ const d = new Date(ts);
+ const pad = (n) => n.toString().padStart(2, "0");
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
+ };
+
+ // 加载所有种子和促销信息
useEffect(() => {
- // 获取当前时间,格式为 yyyy-MM-ddTHH:mm
const now = new Date();
const pad = (n) => n.toString().padStart(2, "0");
const localISOTime = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}T${pad(now.getHours())}:${pad(now.getMinutes())}`;
setCurrentTime(localISOTime);
- // 实际应从后端获取种子及促销信息
- setSeeds(mockSeeds);
+ async function fetchData() {
+ setLoading(true);
+ try {
+ // 获取所有种子
+ const seedsRes = await fetch(`${API_BASE_URL}/api/all-seeds`);
+ if (!seedsRes.ok) throw new Error("获取种子列表失败");
+ const seedsData = await seedsRes.json();
+
+ // 获取所有促销信息
+ const promoRes = await fetch(`${API_BASE_URL}/api/all-seed-promotions`);
+ if (!promoRes.ok) throw new Error("获取促销信息失败");
+ const promoData = await promoRes.json();
+
+ // 构建促销信息映射
+ const promoMap = {};
+ promoData.forEach(p => {
+ const seedid = p.seed?.seedid;
+ promoMap[seedid] = {
+ start_time: p.startTime ? tsToDatetimeLocal(p.startTime) : "",
+ end_time: p.endTime ? tsToDatetimeLocal(p.endTime) : "",
+ discount: typeof p.discount === "number" ? p.discount : 1,
+ };
+ });
+
+ // 合并数据
+ const mergedSeeds = seedsData.map(seed => ({
+ seed_id: seed.seedid,
+ title: seed.title,
+ tags: seed.seedtag,
+ popularity: seed.downloadtimes,
+ promotion: promoMap[seed.seedid] || {
+ start_time: "",
+ end_time: "",
+ discount: 1,
+ },
+ }));
+
+ setSeeds(mergedSeeds);
+ } catch (err) {
+ setSeeds([]);
+ alert(err.message || "加载失败");
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ fetchData();
}, []);
// 输入框变更处理
@@ -64,101 +92,126 @@
return start && end && end < start;
};
+ // 提交促销设置
+ const handleStartPromotion = async (seed) => {
+ const { start_time, end_time, discount } = seed.promotion;
+ try {
+ const res = await fetch(`${API_BASE_URL}/api/set-seed-promotion`, {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ seed_id: seed.seed_id,
+ start_time,
+ end_time,
+ discount,
+ }),
+ });
+ if (!res.ok) {
+ const errData = await res.json();
+ throw new Error(errData.message || "促销设置失败");
+ }
+ alert(`已为「${seed.title}」开启促销!`);
+ } catch (err) {
+ alert(err.message || "促销设置失败");
+ }
+ };
+
return (
<div style={{ padding: 40, maxWidth: 900, margin: "0 auto" }}>
<h1 style={{ textAlign: "center", marginBottom: 32 }}>种子促销管理</h1>
- <table style={{ width: "100%", background: "#fff", borderRadius: 10, boxShadow: "0 2px 8px #e0e7ff" }}>
- <thead>
- <tr style={{ background: "#f5f5f5" }}>
- <th>标题</th>
- <th>标签</th>
- <th>热度</th>
- <th>促销开始时间</th>
- <th>促销结束时间</th>
- <th>促销倍率</th>
- <th>操作</th>
- </tr>
- </thead>
- <tbody>
- {seeds.map((seed) => {
- const { start_time, end_time, discount } = seed.promotion;
- const endTimeInvalid = isEndTimeInvalid(start_time, end_time);
- const canStartPromotion = start_time && end_time && !endTimeInvalid && discount >= 1;
- return (
- <tr key={seed.seed_id}>
- <td>{seed.title}</td>
- <td>{seed.tags}</td>
- <td>{seed.popularity}</td>
- <td>
- <input
- type="datetime-local"
- value={start_time}
- min={currentTime}
- onChange={(e) =>
- handlePromotionChange(seed.seed_id, "start_time", e.target.value)
- }
- />
- </td>
- <td>
- <input
- type="datetime-local"
- value={end_time}
- min={start_time || currentTime}
- onChange={(e) =>
- handlePromotionChange(seed.seed_id, "end_time", e.target.value)
- }
- style={endTimeInvalid ? { border: "1.5px solid #e53935" } : {}}
- />
- {endTimeInvalid && (
- <div style={{ color: "#e53935", fontSize: 12 }}>
- 结束时间不能早于开始时间
+ {loading ? (
+ <div style={{ textAlign: "center", color: "#666", margin: 40 }}>正在加载...</div>
+ ) : (
+ <table style={{ width: "100%", background: "#fff", borderRadius: 10, boxShadow: "0 2px 8px #e0e7ff" }}>
+ <thead>
+ <tr style={{ background: "#f5f5f5" }}>
+ <th>标题</th>
+ <th>标签</th>
+ <th>热度</th>
+ <th>促销开始时间</th>
+ <th>促销结束时间</th>
+ <th>促销倍率</th>
+ <th>操作</th>
+ </tr>
+ </thead>
+ <tbody>
+ {seeds.map((seed) => {
+ const { start_time, end_time, discount } = seed.promotion;
+ const endTimeInvalid = isEndTimeInvalid(start_time, end_time);
+ const canStartPromotion = start_time && end_time && !endTimeInvalid && discount >= 1;
+ return (
+ <tr key={seed.seed_id}>
+ <td>{seed.title}</td>
+ <td>{seed.tags}</td>
+ <td>{seed.popularity}</td>
+ <td>
+ <input
+ type="datetime-local"
+ value={start_time}
+ min={currentTime}
+ onChange={(e) =>
+ handlePromotionChange(seed.seed_id, "start_time", e.target.value)
+ }
+ />
+ </td>
+ <td>
+ <input
+ type="datetime-local"
+ value={end_time}
+ min={start_time || currentTime}
+ onChange={(e) =>
+ handlePromotionChange(seed.seed_id, "end_time", e.target.value)
+ }
+ style={endTimeInvalid ? { border: "1.5px solid #e53935" } : {}}
+ />
+ {endTimeInvalid && (
+ <div style={{ color: "#e53935", fontSize: 12 }}>
+ 结束时间不能早于开始时间
+ </div>
+ )}
+ </td>
+ <td>
+ <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
+ <button
+ style={{ width: 28, height: 28, fontSize: 18, borderRadius: 4, border: "1px solid #ccc", background: "#f5f5f5", cursor: discount > 1 ? "pointer" : "not-allowed" }}
+ onClick={() =>
+ discount > 1 &&
+ handlePromotionChange(seed.seed_id, "discount", discount - 1)
+ }
+ disabled={discount <= 1}
+ >-</button>
+ <span style={{ minWidth: 24, textAlign: "center" }}>{discount}</span>
+ <button
+ style={{ width: 28, height: 28, fontSize: 18, borderRadius: 4, border: "1px solid #ccc", background: "#f5f5f5", cursor: "pointer" }}
+ onClick={() =>
+ handlePromotionChange(seed.seed_id, "discount", discount + 1)
+ }
+ >+</button>
</div>
- )}
- </td>
- <td>
- <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
+ </td>
+ <td>
<button
- style={{ width: 28, height: 28, fontSize: 18, borderRadius: 4, border: "1px solid #ccc", background: "#f5f5f5", cursor: discount > 1 ? "pointer" : "not-allowed" }}
- onClick={() =>
- discount > 1 &&
- handlePromotionChange(seed.seed_id, "discount", discount - 1)
- }
- disabled={discount <= 1}
- >-</button>
- <span style={{ minWidth: 24, textAlign: "center" }}>{discount}</span>
- <button
- style={{ width: 28, height: 28, fontSize: 18, borderRadius: 4, border: "1px solid #ccc", background: "#f5f5f5", cursor: "pointer" }}
- onClick={() =>
- handlePromotionChange(seed.seed_id, "discount", discount + 1)
- }
- >+</button>
- </div>
- </td>
- <td>
- <button
- style={{
- background: canStartPromotion ? "#1976d2" : "#ccc",
- color: "#fff",
- border: "none",
- borderRadius: 6,
- padding: "4px 16px",
- cursor: canStartPromotion ? "pointer" : "not-allowed",
- fontWeight: 600,
- }}
- disabled={!canStartPromotion}
- onClick={() => {
- // 这里可调用后端API开启促销
- alert(`已为「${seed.title}」开启促销!`);
- }}
- >
- 开启促销
- </button>
- </td>
- </tr>
- );
- })}
- </tbody>
- </table>
+ style={{
+ background: canStartPromotion ? "#1976d2" : "#ccc",
+ color: "#fff",
+ border: "none",
+ borderRadius: 6,
+ padding: "4px 16px",
+ cursor: canStartPromotion ? "pointer" : "not-allowed",
+ fontWeight: 600,
+ }}
+ disabled={!canStartPromotion}
+ onClick={() => handleStartPromotion(seed)}
+ >
+ 开启促销
+ </button>
+ </td>
+ </tr>
+ );
+ })}
+ </tbody>
+ </table>
+ )}
</div>
);
}
\ No newline at end of file