init: 初始化仓库

This commit is contained in:
MKStoler 2025-01-29 22:30:31 +08:00
commit 432ffb8e5b
10 changed files with 614 additions and 0 deletions

3
.htaccess Normal file
View File

@ -0,0 +1,3 @@
<FilesMatch "\.enc$">
Deny from all
</FilesMatch>

16
admin/delete.php Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,4 @@
[{
"username": "admin",
"password": "21232f297a57a5a743894a0e4a801fc3"
}]

129
index.php Normal file
View 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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
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>