新增求种和个人中心接口

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