新增求种和个人中心接口
Change-Id: Ibf3eef5b91a45a0ccf7b99d08afa29960884a8cf
diff --git a/front/src/SeedPromotionPage.js b/front/src/SeedPromotionPage.js
new file mode 100644
index 0000000..249533c
--- /dev/null
+++ b/front/src/SeedPromotionPage.js
@@ -0,0 +1,164 @@
+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("");
+
+ 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);
+ }, []);
+
+ // 输入框变更处理
+ const handlePromotionChange = (seedId, field, value) => {
+ setSeeds((prev) =>
+ prev.map((s) =>
+ s.seed_id === seedId
+ ? {
+ ...s,
+ promotion: {
+ ...s.promotion,
+ [field]: value,
+ },
+ }
+ : s
+ )
+ );
+ };
+
+ // 结束时间校验
+ const isEndTimeInvalid = (start, end) => {
+ return start && end && end < start;
+ };
+
+ 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 }}>
+ 结束时间不能早于开始时间
+ </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>
+ <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>
+ </div>
+ );
+}
\ No newline at end of file