blob: f29a53419b36f9965199f6fd3dbc706ff5c90e05 [file] [log] [blame]
223011339e292152025-06-08 00:34:37 +08001import React, { useState, useEffect } from "react";
2import { API_BASE_URL } from "./config";
whtb1e79592025-06-07 16:03:09 +08003
4// 简单PDF预览组件
5function FileViewer({ url }) {
6 if (!url) return <div>无附件</div>;
7 if (url.endsWith(".pdf")) {
8 return (
9 <iframe
10 src={url}
11 title="PDF预览"
12 width="100%"
13 height="400px"
14 style={{ border: "1px solid #ccc", borderRadius: 8 }}
15 />
16 );
17 }
18 // 这里只做PDF示例,实际可扩展为DOC等
19 return <a href={url} target="_blank" rel="noopener noreferrer">下载附件</a>;
20}
21
22export default function MigrationPage() {
223011339e292152025-06-08 00:34:37 +080023 const [migrations, setMigrations] = useState([]);
24 const [selectedId, setSelectedId] = useState(null);
25 const [loading, setLoading] = useState(true);
26 const [error, setError] = useState(null);
whtb1e79592025-06-07 16:03:09 +080027
223011339e292152025-06-08 00:34:37 +080028 // Helper to load migrations list
29 const fetchMigrations = async () => {
30 setLoading(true);
31 try {
32 const res = await fetch(`${API_BASE_URL}/api/migrations`);
33 if (!res.ok) throw new Error(`请求失败,状态码 ${res.status}`);
34 const data = await res.json();
35 const formatted = data.map(item => ({
36 migration_id: item.profileurl,
37 user_id: item.user.userid,
38 application_url: item.applicationurl,
39 pending_magic: Number(item.magictogive),
40 granted_magic: Number(item.magicgived),
41 pending_uploaded: Number(item.uploadtogive),
42 granted_uploaded: Number(item.uploadgived),
43 approved: item.exampass ? 1 : 0
44 }));
45 setMigrations(formatted);
46 if (formatted.length > 0) setSelectedId(formatted[0].migration_id);
47 setError(null);
48 } catch (err) {
49 setError(err.message);
50 } finally {
51 setLoading(false);
52 }
whtb1e79592025-06-07 16:03:09 +080053 };
223011339e292152025-06-08 00:34:37 +080054
55 useEffect(() => {
56 fetchMigrations();
57 }, []);
58
59 if (loading) return <div>加载中...</div>;
60 if (error) return <div>加载失败:{error}</div>;
61
62 const selectedMigration = migrations.find(m => m.migration_id === selectedId) || {};
63
64 // Approve selected migration and refresh
65 const handleApprove = async () => {
66 try {
67 const res = await fetch(`${API_BASE_URL}/api/migrations-approve`, {
68 method: "POST",
69 headers: { "Content-Type": "application/json" },
70 body: JSON.stringify({ migration_id: selectedMigration.migration_id })
71 });
72 if (!res.ok) throw new Error(`请求失败,状态码 ${res.status}`);
73 alert("已通过迁移");
74 await fetchMigrations();
75 } catch (err) {
76 alert(`操作失败:${err.message}`);
77 }
78 };
79 // Reject selected migration and refresh
80 const handleReject = async () => {
81 try {
82 const res = await fetch(`${API_BASE_URL}/api/migrations-reject`, {
83 method: "POST",
84 headers: { "Content-Type": "application/json" },
85 body: JSON.stringify({ migration_id: selectedMigration.migration_id })
86 });
87 if (!res.ok) throw new Error(`请求失败,状态码 ${res.status}`);
88 alert("已拒绝迁移");
89 await fetchMigrations();
90 } catch (err) {
91 alert(`操作失败:${err.message}`);
92 }
whtb1e79592025-06-07 16:03:09 +080093 };
94
95 return (
96 <div style={{ display: "flex", minHeight: "100vh", background: "#f7faff" }}>
97 {/* 侧栏 */}
98 <div style={{ width: 180, background: "#fff", borderRight: "1px solid #e0e7ff", padding: 0 }}>
99 <h3 style={{ textAlign: "center", padding: "18px 0 0 0", color: "#1976d2" }}>迁移列表</h3>
100 <div style={{ display: "flex", flexDirection: "column", gap: 12, marginTop: 18 }}>
101 {migrations.map(m => (
102 <div
103 key={m.migration_id}
104 onClick={() => setSelectedId(m.migration_id)}
105 style={{
106 margin: "0 12px",
107 padding: "16px 10px",
108 borderRadius: 8,
109 background: selectedId === m.migration_id ? "#e3f2fd" : "#fff",
110 border: `2px solid ${m.approved === 1 ? "#43a047" : "#e53935"}`,
111 color: m.approved === 1 ? "#43a047" : "#e53935",
112 fontWeight: 600,
113 cursor: "pointer",
114 boxShadow: selectedId === m.migration_id ? "0 2px 8px #b2d8ea" : "none",
115 transition: "all 0.2s"
116 }}
117 >
118 {m.migration_id}
119 <span style={{
120 float: "right",
121 fontSize: 12,
122 color: m.approved === 1 ? "#43a047" : "#e53935"
123 }}>
124 {m.approved === 1 ? "已审核" : "未审核"}
125 </span>
126 </div>
127 ))}
128 </div>
129 </div>
130 {/* 迁移详情 */}
131 <div style={{ flex: 1, padding: "40px 48px" }}>
132 <h2 style={{ marginBottom: 24, color: "#1976d2" }}>迁移详情</h2>
133 <div style={{ background: "#fff", borderRadius: 12, padding: 32, boxShadow: "0 2px 8px #e0e7ff", marginBottom: 32 }}>
134 <div style={{ marginBottom: 18 }}>
135 <b>迁移ID:</b>{selectedMigration.migration_id}
136 </div>
137 <div style={{ marginBottom: 18 }}>
138 <b>用户ID:</b>{selectedMigration.user_id}
139 </div>
140 <div style={{ marginBottom: 18 }}>
141 <b>申请文件:</b>
142 <FileViewer url={selectedMigration.application_url} />
143 </div>
144 <div style={{ marginBottom: 18 }}>
145 <b>待迁移魔法值:</b>{selectedMigration.pending_magic},
146 <b>已迁移魔法值:</b>{selectedMigration.granted_magic}
147 </div>
148 <div style={{ marginBottom: 18 }}>
149 <b>待迁移上传量:</b>{selectedMigration.pending_uploaded},
150 <b>已迁移上传量:</b>{selectedMigration.granted_uploaded}
151 </div>
152 </div>
153 {/* 审核按钮 */}
154 <div style={{ display: "flex", gap: 32, justifyContent: "center" }}>
155 <button
156 style={{
157 background: selectedMigration.approved === 1 ? "#bdbdbd" : "#43a047",
158 color: "#fff",
159 border: "none",
160 borderRadius: 8,
161 padding: "10px 38px",
162 fontWeight: 600,
163 fontSize: 18,
164 cursor: selectedMigration.approved === 1 ? "not-allowed" : "pointer"
165 }}
166 disabled={selectedMigration.approved === 1}
167 onClick={handleApprove}
168 >
169 通过
170 </button>
171 <button
172 style={{
173 background: selectedMigration.approved === 1 ? "#bdbdbd" : "#e53935",
174 color: "#fff",
175 border: "none",
176 borderRadius: 8,
177 padding: "10px 38px",
178 fontWeight: 600,
179 fontSize: 18,
180 cursor: selectedMigration.approved === 1 ? "not-allowed" : "pointer"
181 }}
182 disabled={selectedMigration.approved === 1}
183 onClick={handleReject}
184 >
185 不通过
186 </button>
187 </div>
188 </div>
189 </div>
190 );
191}