blob: 6e09f6248f66392519c47fd1b978989448596a3e [file] [log] [blame]
wht10563a82025-06-08 15:52:18 +08001import React, { useEffect, useState } from "react";
2import { API_BASE_URL } from "./config";
3
wht10563a82025-06-08 15:52:18 +08004export default function SeedPromotionPage() {
5 const [seeds, setSeeds] = useState([]);
6 const [currentTime, setCurrentTime] = useState("");
wht338fc032025-06-09 17:16:22 +08007 const [loading, setLoading] = useState(true);
wht10563a82025-06-08 15:52:18 +08008
wht338fc032025-06-09 17:16:22 +08009 // 时间戳转datetime-local字符串
10 const tsToDatetimeLocal = (ts) => {
11 if (!ts) return "";
12 const d = new Date(ts);
13 const pad = (n) => n.toString().padStart(2, "0");
14 return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
15 };
16
17 // 加载所有种子和促销信息
wht10563a82025-06-08 15:52:18 +080018 useEffect(() => {
wht10563a82025-06-08 15:52:18 +080019 const now = new Date();
20 const pad = (n) => n.toString().padStart(2, "0");
21 const localISOTime = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}T${pad(now.getHours())}:${pad(now.getMinutes())}`;
22 setCurrentTime(localISOTime);
23
wht338fc032025-06-09 17:16:22 +080024 async function fetchData() {
25 setLoading(true);
26 try {
27 // 获取所有种子
28 const seedsRes = await fetch(`${API_BASE_URL}/api/all-seeds`);
29 if (!seedsRes.ok) throw new Error("获取种子列表失败");
30 const seedsData = await seedsRes.json();
31
32 // 获取所有促销信息
33 const promoRes = await fetch(`${API_BASE_URL}/api/all-seed-promotions`);
34 if (!promoRes.ok) throw new Error("获取促销信息失败");
35 const promoData = await promoRes.json();
36
37 // 构建促销信息映射
38 const promoMap = {};
39 promoData.forEach(p => {
40 const seedid = p.seed?.seedid;
41 promoMap[seedid] = {
42 start_time: p.startTime ? tsToDatetimeLocal(p.startTime) : "",
43 end_time: p.endTime ? tsToDatetimeLocal(p.endTime) : "",
44 discount: typeof p.discount === "number" ? p.discount : 1,
45 };
46 });
47
48 // 合并数据
49 const mergedSeeds = seedsData.map(seed => ({
50 seed_id: seed.seedid,
51 title: seed.title,
52 tags: seed.seedtag,
53 popularity: seed.downloadtimes,
54 promotion: promoMap[seed.seedid] || {
55 start_time: "",
56 end_time: "",
57 discount: 1,
58 },
59 }));
60
61 setSeeds(mergedSeeds);
62 } catch (err) {
63 setSeeds([]);
64 alert(err.message || "加载失败");
65 } finally {
66 setLoading(false);
67 }
68 }
69
70 fetchData();
wht10563a82025-06-08 15:52:18 +080071 }, []);
72
73 // 输入框变更处理
74 const handlePromotionChange = (seedId, field, value) => {
75 setSeeds((prev) =>
76 prev.map((s) =>
77 s.seed_id === seedId
78 ? {
79 ...s,
80 promotion: {
81 ...s.promotion,
82 [field]: value,
83 },
84 }
85 : s
86 )
87 );
88 };
89
90 // 结束时间校验
91 const isEndTimeInvalid = (start, end) => {
92 return start && end && end < start;
93 };
94
wht338fc032025-06-09 17:16:22 +080095 // 提交促销设置
96 const handleStartPromotion = async (seed) => {
97 const { start_time, end_time, discount } = seed.promotion;
98 try {
99 const res = await fetch(`${API_BASE_URL}/api/set-seed-promotion`, {
100 method: "POST",
101 headers: { "Content-Type": "application/json" },
102 body: JSON.stringify({
103 seed_id: seed.seed_id,
104 start_time,
105 end_time,
106 discount,
107 }),
108 });
109 if (!res.ok) {
110 const errData = await res.json();
111 throw new Error(errData.message || "促销设置失败");
112 }
113 alert(`已为「${seed.title}」开启促销!`);
114 } catch (err) {
115 alert(err.message || "促销设置失败");
116 }
117 };
118
wht10563a82025-06-08 15:52:18 +0800119 return (
120 <div style={{ padding: 40, maxWidth: 900, margin: "0 auto" }}>
121 <h1 style={{ textAlign: "center", marginBottom: 32 }}>种子促销管理</h1>
wht338fc032025-06-09 17:16:22 +0800122 {loading ? (
123 <div style={{ textAlign: "center", color: "#666", margin: 40 }}>正在加载...</div>
124 ) : (
125 <table style={{ width: "100%", background: "#fff", borderRadius: 10, boxShadow: "0 2px 8px #e0e7ff" }}>
126 <thead>
127 <tr style={{ background: "#f5f5f5" }}>
128 <th>标题</th>
129 <th>标签</th>
130 <th>热度</th>
131 <th>促销开始时间</th>
132 <th>促销结束时间</th>
133 <th>促销倍率</th>
134 <th>操作</th>
135 </tr>
136 </thead>
137 <tbody>
138 {seeds.map((seed) => {
139 const { start_time, end_time, discount } = seed.promotion;
140 const endTimeInvalid = isEndTimeInvalid(start_time, end_time);
141 const canStartPromotion = start_time && end_time && !endTimeInvalid && discount >= 1;
142 return (
143 <tr key={seed.seed_id}>
144 <td>{seed.title}</td>
145 <td>{seed.tags}</td>
146 <td>{seed.popularity}</td>
147 <td>
148 <input
149 type="datetime-local"
150 value={start_time}
151 min={currentTime}
152 onChange={(e) =>
153 handlePromotionChange(seed.seed_id, "start_time", e.target.value)
154 }
155 />
156 </td>
157 <td>
158 <input
159 type="datetime-local"
160 value={end_time}
161 min={start_time || currentTime}
162 onChange={(e) =>
163 handlePromotionChange(seed.seed_id, "end_time", e.target.value)
164 }
165 style={endTimeInvalid ? { border: "1.5px solid #e53935" } : {}}
166 />
167 {endTimeInvalid && (
168 <div style={{ color: "#e53935", fontSize: 12 }}>
169 结束时间不能早于开始时间
170 </div>
171 )}
172 </td>
173 <td>
174 <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
175 <button
176 style={{ width: 28, height: 28, fontSize: 18, borderRadius: 4, border: "1px solid #ccc", background: "#f5f5f5", cursor: discount > 1 ? "pointer" : "not-allowed" }}
177 onClick={() =>
178 discount > 1 &&
179 handlePromotionChange(seed.seed_id, "discount", discount - 1)
180 }
181 disabled={discount <= 1}
182 >-</button>
183 <span style={{ minWidth: 24, textAlign: "center" }}>{discount}</span>
184 <button
185 style={{ width: 28, height: 28, fontSize: 18, borderRadius: 4, border: "1px solid #ccc", background: "#f5f5f5", cursor: "pointer" }}
186 onClick={() =>
187 handlePromotionChange(seed.seed_id, "discount", discount + 1)
188 }
189 >+</button>
wht10563a82025-06-08 15:52:18 +0800190 </div>
wht338fc032025-06-09 17:16:22 +0800191 </td>
192 <td>
wht10563a82025-06-08 15:52:18 +0800193 <button
wht338fc032025-06-09 17:16:22 +0800194 style={{
195 background: canStartPromotion ? "#1976d2" : "#ccc",
196 color: "#fff",
197 border: "none",
198 borderRadius: 6,
199 padding: "4px 16px",
200 cursor: canStartPromotion ? "pointer" : "not-allowed",
201 fontWeight: 600,
202 }}
203 disabled={!canStartPromotion}
204 onClick={() => handleStartPromotion(seed)}
205 >
206 开启促销
207 </button>
208 </td>
209 </tr>
210 );
211 })}
212 </tbody>
213 </table>
214 )}
wht10563a82025-06-08 15:52:18 +0800215 </div>
216 );
217}