blob: dfbe307d758a5d382151e9d23b2b3e197cecbd89 [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
223011339e292152025-06-08 00:34:37 +08004// State for appeals fetched from backend
whtb1e79592025-06-07 16:03:09 +08005export default function AppealPage() {
223011339e292152025-06-08 00:34:37 +08006 const [appeals, setAppeals] = useState([]);
7 const [selectedId, setSelectedId] = useState(null);
8 const [loading, setLoading] = useState(true);
9 const [error, setError] = useState(null);
whtb1e79592025-06-07 16:03:09 +080010
223011339e292152025-06-08 00:34:37 +080011 // Helper to load appeals
12 const fetchAppeals = async () => {
13 setLoading(true);
14 try {
15 const res = await fetch(`${API_BASE_URL}/api/appeals`);
16 if (!res.ok) throw new Error(`请求失败,状态码 ${res.status}`);
17 const data = await res.json();
18 // console.log("Fetched appeals:", data);
19 setAppeals(data);
20 if (data.length > 0) setSelectedId(data[0].appealid);
21 setError(null);
22 } catch (err) {
23 setError(err.message);
24 } finally {
25 setLoading(false);
26 }
whtb1e79592025-06-07 16:03:09 +080027 };
223011339e292152025-06-08 00:34:37 +080028
29 useEffect(() => {
30 fetchAppeals();
31 }, []);
32
33 if (loading) return <div>加载中...</div>;
34 if (error) return <div>加载失败:{error}</div>;
35 const selectedAppeal = appeals.find(a => a.appealid === selectedId) || {};
36
37 // Approve selected appeal and refresh
38 const handleApprove = async () => {
39 try {
40 const res = await fetch(`${API_BASE_URL}/api/appeals-approve`, {
41 method: "POST",
42 headers: { "Content-Type": "application/json" },
43 body: JSON.stringify({ appealid: selectedAppeal.appealid })
44 });
45 if (!res.ok) throw new Error(`请求失败,状态码 ${res.status}`);
46 alert("已通过申诉");
47 await fetchAppeals();
48 } catch (err) {
49 alert(`操作失败:${err.message}`);
50 }
51 };
52 // Reject selected appeal and refresh
53 const handleReject = async () => {
54 try {
55 const res = await fetch(`${API_BASE_URL}/api/appeals-reject`, {
56 method: "POST",
57 headers: { "Content-Type": "application/json" },
58 body: JSON.stringify({ appealid: selectedAppeal.appealid })
59 });
60 if (!res.ok) throw new Error(`请求失败,状态码 ${res.status}`);
61 alert("已拒绝申诉");
62 await fetchAppeals();
63 } catch (err) {
64 alert(`操作失败:${err.message}`);
65 }
whtb1e79592025-06-07 16:03:09 +080066 };
67
68 return (
69 <div style={{ display: "flex", minHeight: "100vh", background: "#f7faff" }}>
70 {/* 侧栏 */}
71 <div style={{ width: 180, background: "#fff", borderRight: "1px solid #e0e7ff", padding: 0 }}>
72 <h3 style={{ textAlign: "center", padding: "18px 0 0 0", color: "#1976d2" }}>申诉列表</h3>
73 <div style={{ display: "flex", flexDirection: "column", gap: 12, marginTop: 18 }}>
74 {appeals.map(a => (
75 <div
223011339e292152025-06-08 00:34:37 +080076 key={a.appealid}
77 onClick={() => setSelectedId(a.appealid)}
whtb1e79592025-06-07 16:03:09 +080078 style={{
79 margin: "0 12px",
80 padding: "16px 10px",
81 borderRadius: 8,
223011339e292152025-06-08 00:34:37 +080082 background: selectedId === a.appealid ? "#e3f2fd" : "#fff",
83 border: `2px solid ${a.status === 1 || a.status === 2 ? "#43a047" : "#e53935"}`,
84 color: a.status === 1 || a.status === 2 ? "#43a047" : "#e53935",
whtb1e79592025-06-07 16:03:09 +080085 fontWeight: 600,
86 cursor: "pointer",
223011339e292152025-06-08 00:34:37 +080087 boxShadow: selectedId === a.appealid ? "0 2px 8px #b2d8ea" : "none",
whtb1e79592025-06-07 16:03:09 +080088 transition: "all 0.2s"
89 }}
90 >
223011339e292152025-06-08 00:34:37 +080091 {a.user.username}
whtb1e79592025-06-07 16:03:09 +080092 <span style={{
93 float: "right",
94 fontSize: 12,
223011339e292152025-06-08 00:34:37 +080095 color: a.status === 1 || a.status === 2 ? "#43a047" : "#e53935"
whtb1e79592025-06-07 16:03:09 +080096 }}>
223011339e292152025-06-08 00:34:37 +080097 {a.status === 1 || a.status === 2 ? "已审核" : "未审核"}
whtb1e79592025-06-07 16:03:09 +080098 </span>
99 </div>
100 ))}
101 </div>
102 </div>
103 {/* 申诉详情 */}
104 <div style={{ flex: 1, padding: "40px 48px" }}>
105 <h2 style={{ marginBottom: 24, color: "#1976d2" }}>申诉详情</h2>
106 <div style={{ background: "#fff", borderRadius: 12, padding: 32, boxShadow: "0 2px 8px #e0e7ff", marginBottom: 32 }}>
107 <div style={{ marginBottom: 18 }}>
223011339e292152025-06-08 00:34:37 +0800108 <b>申诉ID:</b>{selectedAppeal.appealid}
whtb1e79592025-06-07 16:03:09 +0800109 </div>
110 <div style={{ marginBottom: 18 }}>
223011339e292152025-06-08 00:34:37 +0800111 <b>用户ID:</b>{selectedAppeal.user.userid}
whtb1e79592025-06-07 16:03:09 +0800112 </div>
113 <div style={{ marginBottom: 18 }}>
114 <b>申诉内容:</b>{selectedAppeal.content}
115 </div>
116 <div style={{ marginBottom: 18 }}>
117 <b>申诉文件:</b>
223011339e292152025-06-08 00:34:37 +0800118 <FileViewer url={selectedAppeal.fileURL} />
whtb1e79592025-06-07 16:03:09 +0800119 </div>
120 </div>
121 {/* 审核按钮 */}
122 <div style={{ display: "flex", gap: 32, justifyContent: "center" }}>
123 <button
124 style={{
125 background: selectedAppeal.status === 1 ? "#bdbdbd" : "#43a047",
126 color: "#fff",
127 border: "none",
128 borderRadius: 8,
129 padding: "10px 38px",
130 fontWeight: 600,
131 fontSize: 18,
132 cursor: selectedAppeal.status === 1 ? "not-allowed" : "pointer"
133 }}
134 disabled={selectedAppeal.status === 1}
135 onClick={handleApprove}
136 >
137 通过
138 </button>
139 <button
140 style={{
141 background: selectedAppeal.status === 1 ? "#bdbdbd" : "#e53935",
142 color: "#fff",
143 border: "none",
144 borderRadius: 8,
145 padding: "10px 38px",
146 fontWeight: 600,
147 fontSize: 18,
148 cursor: selectedAppeal.status === 1 ? "not-allowed" : "pointer"
149 }}
150 disabled={selectedAppeal.status === 1}
151 onClick={handleReject}
152 >
153 不通过
154 </button>
155 </div>
156 </div>
157 </div>
158 );
223011339e292152025-06-08 00:34:37 +0800159}
160
161// 简单PDF预览组件
162function FileViewer({ url }) {
163 if (!url) return <div>无附件</div>;
164 if (url.endsWith(".pdf")) {
165 return (
166 <iframe
167 src={url}
168 title="PDF预览"
169 width="100%"
170 height="400px"
171 style={{ border: "1px solid #ccc", borderRadius: 8 }}
172 />
173 );
174 }
175 // 这里只做PDF示例,实际可扩展为DOC等
176 return <a href={url} target="_blank" rel="noopener noreferrer">下载附件</a>;
whtb1e79592025-06-07 16:03:09 +0800177}