mirror of
https://hub.gitmirror.com/https://github.com/ExamAware/ExamCloudSchedule
synced 2025-04-28 23:46:34 +00:00
init: 初始化仓库
This commit is contained in:
commit
432ffb8e5b
16
admin/delete.php
Normal file
16
admin/delete.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
require_once '../includes/auth.php';
|
||||
checkLogin();
|
||||
|
||||
$id = $_GET['id'] ?? '';
|
||||
|
||||
if (!empty($id)) {
|
||||
$filePath = "../configs/{$id}.json";
|
||||
if (file_exists($filePath)) {
|
||||
unlink($filePath);
|
||||
}
|
||||
}
|
||||
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
?>
|
166
admin/edit.php
Normal file
166
admin/edit.php
Normal file
@ -0,0 +1,166 @@
|
||||
<?php
|
||||
require_once '../includes/auth.php';
|
||||
checkLogin();
|
||||
|
||||
$id = $_GET['id'] ?? '';
|
||||
$config = ['examName' => '', 'message' => '', 'examInfos' => []];
|
||||
|
||||
// 保存逻辑
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$id = preg_replace('/[^a-zA-Z0-9]/', '', $_POST['id']);
|
||||
|
||||
$newConfig = [
|
||||
'examName' => $_POST['examName'],
|
||||
'message' => $_POST['message'],
|
||||
'room' => ' ',
|
||||
'examInfos' => []
|
||||
];
|
||||
|
||||
foreach ($_POST['subject'] as $index => $subject) {
|
||||
$newConfig['examInfos'][] = [
|
||||
'name' => $subject,
|
||||
'start' => $_POST['start'][$index],
|
||||
'end' => $_POST['end'][$index]
|
||||
];
|
||||
}
|
||||
|
||||
file_put_contents("../configs/{$id}.json", json_encode($newConfig, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// 加载现有配置
|
||||
if (!empty($id) && file_exists("../configs/{$id}.json")) {
|
||||
$config = json_decode(file_get_contents("../configs/{$id}.json"), true);
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>编辑配置</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f2f2f2;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
}
|
||||
.container {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
width: 500px;
|
||||
}
|
||||
.container h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
.container div {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.container label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.container input[type="text"],
|
||||
.container input[type="datetime-local"] {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.container button {
|
||||
padding: 10px 15px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.container button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
.subject {
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.subject button {
|
||||
background-color: #dc3545;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.subject button:hover {
|
||||
background-color: #c82333;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function addSubject() {
|
||||
const container = document.getElementById('subjects');
|
||||
const index = Date.now();
|
||||
const html = `
|
||||
<div class="subject">
|
||||
<div>
|
||||
<label>科目名称:</label>
|
||||
<input type="text" name="subject[]" required>
|
||||
</div>
|
||||
<div>
|
||||
<label>开始时间:</label>
|
||||
<input type="datetime-local" name="start[]" required>
|
||||
</div>
|
||||
<div>
|
||||
<label>结束时间:</label>
|
||||
<input type="datetime-local" name="end[]" required>
|
||||
</div>
|
||||
<button type="button" onclick="this.parentElement.remove()">删除</button>
|
||||
</div>`;
|
||||
container.insertAdjacentHTML('beforeend', html);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<form method="post">
|
||||
<div>
|
||||
<label>配置ID:</label>
|
||||
<input type="text" name="id" value="<?= htmlspecialchars($id) ?>" required>
|
||||
</div>
|
||||
<div>
|
||||
<label>考试名称:</label>
|
||||
<input type="text" name="examName" value="<?= htmlspecialchars($config['examName']) ?>" required>
|
||||
</div>
|
||||
<div>
|
||||
<label>考试提示语:</label>
|
||||
<input type="text" name="message" value="<?= htmlspecialchars($config['message']) ?>">
|
||||
</div>
|
||||
|
||||
<h3>考试科目安排</h3>
|
||||
<div id="subjects">
|
||||
<?php foreach ($config['examInfos'] as $subject): ?>
|
||||
<div class="subject">
|
||||
<div>
|
||||
<label>科目名称:</label>
|
||||
<input type="text" name="subject[]" value="<?= htmlspecialchars($subject['name']) ?>" required>
|
||||
</div>
|
||||
<div>
|
||||
<label>开始时间:</label>
|
||||
<input type="datetime-local" name="start[]" value="<?= str_replace(' ', 'T', $subject['start']) ?>" required>
|
||||
</div>
|
||||
<div>
|
||||
<label>结束时间:</label>
|
||||
<input type="datetime-local" name="end[]" value="<?= str_replace(' ', 'T', $subject['end']) ?>" required>
|
||||
</div>
|
||||
<button type="button" onclick="this.parentElement.remove()">删除</button>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<button type="button" onclick="addSubject()">添加科目</button>
|
||||
<hr>
|
||||
<button type="submit">保存配置</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
139
admin/index.php
Normal file
139
admin/index.php
Normal file
@ -0,0 +1,139 @@
|
||||
<?php
|
||||
require_once '../includes/auth.php';
|
||||
checkLogin();
|
||||
|
||||
// 获取所有配置文件列表
|
||||
$configs = [];
|
||||
foreach (scandir('../configs') as $file) {
|
||||
if ($file !== '.' && $file !== '..' && pathinfo($file, PATHINFO_EXTENSION) === 'json') {
|
||||
$id = pathinfo($file, PATHINFO_FILENAME);
|
||||
$configs[$id] = [
|
||||
'id' => $id,
|
||||
'mtime' => date("Y-m-d H:i:s", filemtime("../configs/$file")),
|
||||
'size' => filesize("../configs/$file")
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// 按创建时间排序
|
||||
uksort($configs, function($a, $b) {
|
||||
return filemtime("../configs/$b.json") - filemtime("../configs/$a.json");
|
||||
});
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>配置管理后台</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f2f2f2;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.header h1 {
|
||||
margin: 0;
|
||||
}
|
||||
.add-btn-container {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
th, td {
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
th {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.actions a {
|
||||
margin-right: 10px;
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
.actions a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.logout {
|
||||
color: #dc3545;
|
||||
}
|
||||
.add-btn {
|
||||
background: #28a745;
|
||||
color: white;
|
||||
padding: 10px 15px;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
.add-btn:hover {
|
||||
background: #218838;
|
||||
}
|
||||
.no-configs {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: #888;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>考试配置管理 <small>当前用户:<?= $_SESSION['username'] ?></small></h1>
|
||||
<a href="login.php?action=logout" class="logout">退出登录</a>
|
||||
</div>
|
||||
|
||||
<div class="add-btn-container">
|
||||
<a href="edit.php" class="add-btn">新建配置</a>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>配置ID</th>
|
||||
<th>最后修改时间</th>
|
||||
<th>文件大小</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if(empty($configs)): ?>
|
||||
<tr>
|
||||
<td colspan="4" class="no-configs">暂无配置文件</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach($configs as $config): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($config['id']) ?></td>
|
||||
<td><?= $config['mtime'] ?></td>
|
||||
<td><?= round($config['size']/1024, 2) ?> KB</td>
|
||||
<td class="actions">
|
||||
<a href="edit.php?id=<?= urlencode($config['id']) ?>">编辑</a>
|
||||
<a href="#" onclick="confirmDelete('<?= $config['id'] ?>')">删除</a>
|
||||
<a href="../get_config.php?id=<?= urlencode($config['id']) ?>" target="_blank">预览</a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
function confirmDelete(id) {
|
||||
if(confirm(`确定要删除 ${id} 的配置吗?`)) {
|
||||
window.location = `delete.php?id=${encodeURIComponent(id)}`;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
104
admin/login.php
Normal file
104
admin/login.php
Normal file
@ -0,0 +1,104 @@
|
||||
<?php
|
||||
require_once '../includes/auth.php';
|
||||
|
||||
if (isset($_GET['action']) && $_GET['action'] === 'logout') {
|
||||
session_destroy();
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if (isset($_SESSION['loggedin']) && $_SESSION['loggedin'] === true) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = $_POST['username'] ?? '';
|
||||
$password = $_POST['password'] ?? '';
|
||||
|
||||
if (verifyUser($username, $password)) {
|
||||
$_SESSION['loggedin'] = true;
|
||||
$_SESSION['username'] = $username;
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
} else {
|
||||
$error = '用户名或密码错误';
|
||||
}
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>登录</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f2f2f2;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
.login-container {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
width: 300px;
|
||||
}
|
||||
.login-container h2 {
|
||||
margin-top: 0;
|
||||
text-align: center;
|
||||
}
|
||||
.login-container div {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.login-container label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.login-container input {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.login-container button {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.login-container button:hover {
|
||||
background-color: #45a049;
|
||||
}
|
||||
.error {
|
||||
color: red;
|
||||
text-align: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-container">
|
||||
<h2>登录</h2>
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="error"><?= $error ?></div>
|
||||
<?php endif; ?>
|
||||
<form method="post">
|
||||
<div>
|
||||
<label>用户名:</label>
|
||||
<input type="text" name="username" required>
|
||||
</div>
|
||||
<div>
|
||||
<label>密码:</label>
|
||||
<input type="password" name="password" required>
|
||||
</div>
|
||||
<button type="submit">登录</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
12
configs/111111.json
Normal file
12
configs/111111.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"examName": "1111",
|
||||
"message": "1111",
|
||||
"room": " ",
|
||||
"examInfos": [
|
||||
{
|
||||
"name": "1111",
|
||||
"start": "2025-01-29T00:00",
|
||||
"end": "2025-01-29T22:15"
|
||||
}
|
||||
]
|
||||
}
|
18
get_config.php
Normal file
18
get_config.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if (!isset($_GET['id'])) {
|
||||
http_response_code(400);
|
||||
die(json_encode(['error' => '缺少ID参数']));
|
||||
}
|
||||
|
||||
$id = preg_replace('/[^a-zA-Z0-9]/', '', $_GET['id']);
|
||||
$file = "configs/{$id}.json";
|
||||
|
||||
if (!file_exists($file)) {
|
||||
http_response_code(404);
|
||||
die(json_encode(['error' => '配置不存在']));
|
||||
}
|
||||
|
||||
echo file_get_contents($file);
|
||||
?>
|
23
includes/auth.php
Normal file
23
includes/auth.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
$users_file = __DIR__.'/users.json';
|
||||
|
||||
function checkLogin() {
|
||||
if (!isset($_SESSION['loggedin']) || $_SESSION['loggedin'] !== true) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
function verifyUser($username, $password) {
|
||||
global $users_file;
|
||||
$users = json_decode(file_get_contents($users_file), true);
|
||||
foreach ($users as $user) {
|
||||
if ($user['username'] === $username && $user['password'] === md5($password)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
?>
|
4
includes/users.json
Normal file
4
includes/users.json
Normal file
@ -0,0 +1,4 @@
|
||||
[{
|
||||
"username": "admin",
|
||||
"password": "21232f297a57a5a743894a0e4a801fc3"
|
||||
}]
|
129
index.php
Normal file
129
index.php
Normal file
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
// exam_config/index.php
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>考试看板配置查询</title>
|
||||
<style>
|
||||
body {
|
||||
max-width: 800px;
|
||||
margin: 20px auto;
|
||||
padding: 20px;
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
.container {
|
||||
border: 1px solid #ddd;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
input[type="text"] {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
}
|
||||
button {
|
||||
background: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
}
|
||||
button:hover {
|
||||
background: #0056b3;
|
||||
}
|
||||
#result {
|
||||
margin-top: 20px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
pre {
|
||||
background: #f4f4f4;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
overflow: auto;
|
||||
}
|
||||
.string { color: green; }
|
||||
.number { color: darkorange; }
|
||||
.boolean { color: blue; }
|
||||
.null { color: magenta; }
|
||||
.key { color: red; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>考试看板配置查询</h1>
|
||||
<div class="form-group">
|
||||
<label for="configId">输入配置ID:</label>
|
||||
<input type="text" id="configId" placeholder="例如:room301">
|
||||
<button onclick="loadConfig()">获取配置</button>
|
||||
</div>
|
||||
|
||||
<div id="result"></div>
|
||||
|
||||
<hr>
|
||||
<p>管理员请前往 <a href="/admin/login.php">管理后台</a></p>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function loadConfig() {
|
||||
const configId = document.getElementById('configId').value.trim();
|
||||
const resultDiv = document.getElementById('result');
|
||||
resultDiv.innerHTML = '加载中...';
|
||||
|
||||
if(!configId) {
|
||||
resultDiv.innerHTML = '<span class="error">请输入配置ID</span>';
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`/get_config.php?id=${encodeURIComponent(configId)}`)
|
||||
.then(response => {
|
||||
if(!response.ok) {
|
||||
return response.json().then(err => { throw err; });
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
resultDiv.innerHTML = `<pre>${syntaxHighlight(JSON.stringify(data, null, 4))}</pre>`;
|
||||
})
|
||||
.catch(error => {
|
||||
resultDiv.innerHTML = `<span class="error">错误:${error.error || '获取配置失败'}</span>`;
|
||||
});
|
||||
}
|
||||
|
||||
// JSON高亮显示
|
||||
function syntaxHighlight(json) {
|
||||
json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,
|
||||
function (match) {
|
||||
let cls = 'number';
|
||||
if (/^"/.test(match)) {
|
||||
cls = /:$/.test(match) ? 'key' : 'string';
|
||||
} else if (/true|false/.test(match)) {
|
||||
cls = 'boolean';
|
||||
} else if (/null/.test(match)) {
|
||||
cls = 'null';
|
||||
}
|
||||
return '<span class="' + cls + '">' + match + '</span>';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user