450 lines
11 KiB
JavaScript
450 lines
11 KiB
JavaScript
import {
|
||
Container,
|
||
Box,
|
||
Alert,
|
||
Snackbar,
|
||
Button,
|
||
Stack,
|
||
Dialog,
|
||
DialogTitle,
|
||
DialogContent,
|
||
DialogActions,
|
||
Typography,
|
||
TextField,
|
||
TableContainer,
|
||
Table,
|
||
TableHead,
|
||
TableRow,
|
||
TableCell,
|
||
TableBody,
|
||
} from "@mui/material";
|
||
import { useEffect, useState } from "react";
|
||
import { useRouter } from "next/router";
|
||
import config from "../next.config";
|
||
|
||
const prefix = config.basePath ? config.basePath : "";
|
||
|
||
export default function Time(props) {
|
||
const [ranges, setRanges] = useState([]);
|
||
const [range, setRange] = useState("");
|
||
const [newName, setNewName] = useState("");
|
||
const [snackbarError, setSnackbarError] = useState(false);
|
||
const [snackbarErrorMessage, setSnackbarErrorMessage] = useState("");
|
||
const [snackbarSuccess, setSnackbarSuccess] = useState(false);
|
||
const [limit, setLimit] = useState(1);
|
||
const [inputedLimit, setInputedLimit] = useState(1);
|
||
const [token, setToken] = useState("");
|
||
const [stats, setStats] = useState({});
|
||
|
||
const [modifyTime, setModifyTime] = useState({});
|
||
|
||
const router = useRouter();
|
||
|
||
const modifyRange = () => {
|
||
fetch(`${prefix}/api/time/ranges/${modifyTime.id}`, {
|
||
method: "PUT",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({ token, ...modifyTime }),
|
||
})
|
||
.then((res) => res.json())
|
||
.then((res) => {
|
||
if (res.error) {
|
||
setSnackbarError(true);
|
||
setSnackbarErrorMessage(res.error);
|
||
} else {
|
||
setModifyTime({});
|
||
setSnackbarSuccess(true);
|
||
refreshRanges();
|
||
}
|
||
});
|
||
};
|
||
|
||
const getStats = () => {
|
||
fetch(`${prefix}/api/stats`, {
|
||
method: "GET",
|
||
})
|
||
.then((res) => res.json())
|
||
.then((res) => {
|
||
setStats(res);
|
||
});
|
||
};
|
||
|
||
const setStarted = (started) => {
|
||
fetch(`${prefix}/api/time/started`, {
|
||
method: "PUT",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({ token, started }),
|
||
})
|
||
.then((res) => res.json())
|
||
.then((res) => {
|
||
if (res.error) {
|
||
setSnackbarError(true);
|
||
setSnackbarErrorMessage(res.error);
|
||
} else {
|
||
setSnackbarSuccess(true);
|
||
}
|
||
});
|
||
};
|
||
|
||
const isAdmin = () => {
|
||
if (props.username === "admin") {
|
||
return true;
|
||
} else {
|
||
return false;
|
||
}
|
||
};
|
||
|
||
const getLimit = () => {
|
||
fetch(`${prefix}/api/time/limit`, {
|
||
method: "GET",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
})
|
||
.then((res) => res.json())
|
||
.then((res) => {
|
||
setLimit(res.limit);
|
||
});
|
||
};
|
||
|
||
const addRange = () => {
|
||
fetch(`${prefix}/api/time/ranges`, {
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({
|
||
name: newName,
|
||
range,
|
||
token,
|
||
}),
|
||
}).then((res) =>
|
||
res.json().then((res) => {
|
||
if (res.error) {
|
||
setSnackbarError(true);
|
||
setSnackbarErrorMessage(res.error);
|
||
} else {
|
||
setSnackbarSuccess(true);
|
||
refreshRanges();
|
||
}
|
||
})
|
||
);
|
||
};
|
||
|
||
const refreshRanges = () => {
|
||
getLimit();
|
||
getStats();
|
||
fetch(`${prefix}/api/time/ranges`)
|
||
.then((res) => res.json())
|
||
.then((res) => {
|
||
if (res.error) {
|
||
setSnackbarError(true);
|
||
setSnackbarErrorMessage(res.error);
|
||
} else {
|
||
setRanges(res);
|
||
}
|
||
});
|
||
};
|
||
|
||
const deleteRange = (id) => {
|
||
fetch(`${prefix}/api/time/ranges/${id}`, {
|
||
method: "DELETE",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({ token }),
|
||
})
|
||
.then((res) => res.json())
|
||
.then((res) => {
|
||
if (res.error) {
|
||
setSnackbarError(true);
|
||
setSnackbarErrorMessage(res.error);
|
||
} else {
|
||
setSnackbarSuccess(true);
|
||
refreshRanges();
|
||
}
|
||
});
|
||
};
|
||
|
||
const updateUsername = (id, username) => {
|
||
fetch(`${prefix}/api/time/ranges/${id}`, {
|
||
method: "PUT",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({ username }),
|
||
})
|
||
.then((res) => res.json())
|
||
.then((res) => {
|
||
if (res.error) {
|
||
setSnackbarError(true);
|
||
setSnackbarErrorMessage(res.error);
|
||
refreshRanges();
|
||
} else {
|
||
setSnackbarSuccess(true);
|
||
refreshRanges();
|
||
}
|
||
});
|
||
};
|
||
|
||
const updateLimit = (limit) => {
|
||
fetch(`${prefix}/api/time/limit`, {
|
||
method: "PUT",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({ limit, token }),
|
||
})
|
||
.then((res) => res.json())
|
||
.then((res) => {
|
||
if (res.error) {
|
||
setSnackbarError(true);
|
||
setSnackbarErrorMessage(res.error);
|
||
} else {
|
||
setSnackbarSuccess(true);
|
||
refreshRanges();
|
||
}
|
||
});
|
||
};
|
||
|
||
useEffect(() => {
|
||
if (!props.username) {
|
||
router.push("/");
|
||
}
|
||
});
|
||
|
||
useEffect(() => {
|
||
refreshRanges();
|
||
const interval = setInterval(() => {
|
||
refreshRanges();
|
||
}, 1000);
|
||
return () => clearInterval(interval);
|
||
}, []);
|
||
|
||
/*
|
||
useEffect(() => {
|
||
refreshRanges();
|
||
}, []);
|
||
*/
|
||
|
||
return (
|
||
<Container>
|
||
{isAdmin() && (
|
||
<Box>
|
||
<Box
|
||
sx={{
|
||
my: 2,
|
||
}}
|
||
>
|
||
<TextField
|
||
label="Token"
|
||
value={token}
|
||
onChange={(e) => setToken(e.target.value)}
|
||
/>
|
||
<Box
|
||
sx={{
|
||
my: 2,
|
||
}}
|
||
>
|
||
<TextField
|
||
label="名称"
|
||
value={newName}
|
||
onChange={(e) => setNewName(e.target.value)}
|
||
/>
|
||
<TextField
|
||
label="时间段"
|
||
value={range}
|
||
onChange={(e) => setRange(e.target.value)}
|
||
placeholder="2022-01-01 00:00:00"
|
||
/>
|
||
</Box>
|
||
<Button
|
||
variant="contained"
|
||
color="primary"
|
||
onClick={() => addRange()}
|
||
>
|
||
添加
|
||
</Button>
|
||
<Box
|
||
sx={{
|
||
my: 2,
|
||
}}
|
||
>
|
||
<TextField
|
||
label="每人数量上限"
|
||
value={inputedLimit}
|
||
onChange={(e) => setInputedLimit(e.target.value)}
|
||
/>
|
||
<Button
|
||
variant="contained"
|
||
color="primary"
|
||
onClick={() => {
|
||
updateLimit(inputedLimit);
|
||
}}
|
||
>
|
||
修改上限
|
||
</Button>
|
||
<Button
|
||
variant="contained"
|
||
color="primary"
|
||
onClick={() => setStarted(true)}
|
||
>
|
||
开始
|
||
</Button>
|
||
<Button
|
||
variant="contained"
|
||
color="primary"
|
||
onClick={() => setStarted(false)}
|
||
>
|
||
停止
|
||
</Button>
|
||
</Box>
|
||
</Box>
|
||
</Box>
|
||
)}
|
||
<Box
|
||
sx={{
|
||
display: "flex",
|
||
flexDirection: "row",
|
||
alignItems: "center",
|
||
justifyContent: "space-between",
|
||
}}
|
||
>
|
||
<Button
|
||
sx={{
|
||
userSelect: "none",
|
||
}}
|
||
variant="contained"
|
||
color="primary"
|
||
onClick={() => refreshRanges()}
|
||
>
|
||
Refresh
|
||
</Button>
|
||
<Typography>
|
||
当前每人数量上限: {limit}
|
||
<br />
|
||
服务器负载 (QPS): {stats.apiqps}
|
||
</Typography>
|
||
</Box>
|
||
<TableContainer>
|
||
<Table>
|
||
<TableHead>
|
||
<TableRow>
|
||
<TableCell>名称</TableCell>
|
||
<TableCell>时间段</TableCell>
|
||
<TableCell>操作</TableCell>
|
||
</TableRow>
|
||
</TableHead>
|
||
<TableBody>
|
||
{ranges.map((range) => (
|
||
<TableRow key={range.id}>
|
||
<TableCell>{range.name}</TableCell>
|
||
<TableCell>{range.range}</TableCell>
|
||
<TableCell>
|
||
<Button
|
||
sx={{
|
||
userSelect: "none",
|
||
}}
|
||
disabled={range.username !== ""}
|
||
variant="contained"
|
||
color="primary"
|
||
onClick={() => updateUsername(range.id, props.username)}
|
||
>
|
||
{range.username ? range.username : "抢!"}
|
||
</Button>
|
||
{isAdmin() && (
|
||
<>
|
||
<Button
|
||
variant="contained"
|
||
color="secondary"
|
||
onClick={() => setModifyTime(range)}
|
||
>
|
||
修改
|
||
</Button>
|
||
<Button
|
||
variant="contained"
|
||
color="secondary"
|
||
onClick={() => deleteRange(range.id)}
|
||
>
|
||
删除
|
||
</Button>
|
||
</>
|
||
)}
|
||
</TableCell>
|
||
</TableRow>
|
||
))}
|
||
</TableBody>
|
||
</Table>
|
||
</TableContainer>
|
||
<Snackbar
|
||
open={snackbarError}
|
||
autoHideDuration={1000}
|
||
onClose={() => setSnackbarError(false)}
|
||
>
|
||
<Alert
|
||
variant="filled"
|
||
onClose={() => setSnackbarError(false)}
|
||
severity="error"
|
||
>
|
||
{snackbarErrorMessage}
|
||
</Alert>
|
||
</Snackbar>
|
||
<Snackbar
|
||
open={snackbarSuccess}
|
||
autoHideDuration={1000}
|
||
onClose={() => setSnackbarSuccess(false)}
|
||
>
|
||
<Alert
|
||
variant="filled"
|
||
onClose={() => setSnackbarSuccess(false)}
|
||
severity="success"
|
||
>
|
||
操作成功!
|
||
</Alert>
|
||
</Snackbar>
|
||
<Dialog open={modifyTime.id} onClose={() => setModifyTime({})}>
|
||
<DialogTitle>修改时间段 {modifyTime.id}</DialogTitle>
|
||
<DialogContent>
|
||
<Stack
|
||
sx={{
|
||
mt: 1,
|
||
}}
|
||
spacing={2}
|
||
>
|
||
<TextField
|
||
label="名称"
|
||
value={modifyTime.name}
|
||
onChange={(e) =>
|
||
setModifyTime({ ...modifyTime, name: e.target.value })
|
||
}
|
||
/>
|
||
<TextField
|
||
label="时间段"
|
||
value={modifyTime.range}
|
||
onChange={(e) =>
|
||
setModifyTime({ ...modifyTime, range: e.target.value })
|
||
}
|
||
placeholder="2022-01-01 00:00:00"
|
||
/>
|
||
<TextField
|
||
label="姓名"
|
||
value={modifyTime.username}
|
||
onChange={(e) =>
|
||
setModifyTime({ ...modifyTime, username: e.target.value })
|
||
}
|
||
/>
|
||
</Stack>
|
||
</DialogContent>
|
||
<DialogActions>
|
||
<Button onClick={() => setModifyTime({})}>取消</Button>
|
||
<Button onClick={modifyRange}>确定</Button>
|
||
</DialogActions>
|
||
</Dialog>
|
||
</Container>
|
||
);
|
||
}
|