first commit
This commit is contained in:
BIN
admin/arial.ttf
Normal file
BIN
admin/arial.ttf
Normal file
Binary file not shown.
60
admin/captcha.php
Normal file
60
admin/captcha.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
// 生成验证码
|
||||
$code = '';
|
||||
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
||||
for ($i = 0; $i < 4; $i++) {
|
||||
$code .= $chars[rand(0, strlen($chars) - 1)];
|
||||
}
|
||||
|
||||
$_SESSION['captcha'] = $code;
|
||||
|
||||
// 创建图片
|
||||
$width = 100;
|
||||
$height = 40;
|
||||
$image = imagecreate($width, $height);
|
||||
|
||||
// 颜色定义
|
||||
$bg_color = imagecolorallocate($image, 245, 245, 245);
|
||||
$text_color = imagecolorallocate($image, 50, 50, 50);
|
||||
$line_color = imagecolorallocate($image, 200, 200, 200);
|
||||
$noise_color = imagecolorallocate($image, 180, 180, 180);
|
||||
|
||||
// 填充背景
|
||||
imagefill($image, 0, 0, $bg_color);
|
||||
|
||||
// 添加干扰线
|
||||
for ($i = 0; $i < 5; $i++) {
|
||||
imageline($image, rand(0, $width), rand(0, $height), rand(0, $width), rand(0, $height), $line_color);
|
||||
}
|
||||
|
||||
// 添加噪点
|
||||
for ($i = 0; $i < 50; $i++) {
|
||||
imagesetpixel($image, rand(0, $width), rand(0, $height), $noise_color);
|
||||
}
|
||||
|
||||
// 添加验证码文字
|
||||
for ($i = 0; $i < 4; $i++) {
|
||||
$x = 15 + $i * 18;
|
||||
$y = rand(8, 15);
|
||||
$angle = rand(-15, 15);
|
||||
|
||||
if (function_exists('imagettftext')) {
|
||||
// 如果支持TTF字体
|
||||
imagettftext($image, 16, $angle, $x, 25, $text_color, __DIR__ . '/arial.ttf', $code[$i]);
|
||||
} else {
|
||||
// 使用内置字体
|
||||
imagestring($image, 5, $x, $y, $code[$i], $text_color);
|
||||
}
|
||||
}
|
||||
|
||||
// 输出图片
|
||||
header('Content-Type: image/png');
|
||||
header('Cache-Control: no-cache, no-store, must-revalidate');
|
||||
header('Pragma: no-cache');
|
||||
header('Expires: 0');
|
||||
|
||||
imagepng($image);
|
||||
imagedestroy($image);
|
||||
?>
|
||||
750
admin/index.php
Normal file
750
admin/index.php
Normal file
@@ -0,0 +1,750 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
// 检查登录状态
|
||||
if (!isset($_SESSION['admin_logged_in']) || !$_SESSION['admin_logged_in']) {
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once '../config/database.php';
|
||||
require_once '../includes/utils.php';
|
||||
|
||||
$database = new Database();
|
||||
$db = $database->getConnection();
|
||||
|
||||
$message = '';
|
||||
$message_type = '';
|
||||
|
||||
// 处理操作
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = $_POST['action'] ?? '';
|
||||
|
||||
if ($action === 'update_status') {
|
||||
$type = $_POST['type'] ?? '';
|
||||
$id = $_POST['id'] ?? '';
|
||||
$status = $_POST['status'] ?? '';
|
||||
$note = $_POST['note'] ?? '';
|
||||
|
||||
if ($type === 'website') {
|
||||
$stmt = $db->prepare("UPDATE website_submissions SET status = ?, admin_note = ? WHERE id = ?");
|
||||
} else {
|
||||
$stmt = $db->prepare("UPDATE app_submissions SET status = ?, admin_note = ? WHERE id = ?");
|
||||
}
|
||||
|
||||
if ($stmt->execute([$status, $note, $id])) {
|
||||
$message = '状态更新成功';
|
||||
$message_type = 'success';
|
||||
} else {
|
||||
$message = '状态更新失败';
|
||||
$message_type = 'error';
|
||||
}
|
||||
} elseif ($action === 'update_account') {
|
||||
$new_username = trim($_POST['new_username'] ?? '');
|
||||
$new_password = $_POST['new_password'] ?? '';
|
||||
$confirm_password = $_POST['confirm_password'] ?? '';
|
||||
|
||||
if (empty($new_username)) {
|
||||
$message = '用户名不能为空';
|
||||
$message_type = 'error';
|
||||
} elseif (!empty($new_password) && $new_password !== $confirm_password) {
|
||||
$message = '两次输入的密码不一致';
|
||||
$message_type = 'error';
|
||||
} else {
|
||||
$admin_id = $_SESSION['admin_id'];
|
||||
|
||||
if (!empty($new_password)) {
|
||||
$hashed_password = password_hash($new_password, PASSWORD_DEFAULT);
|
||||
$stmt = $db->prepare("UPDATE admins SET username = ?, password = ? WHERE id = ?");
|
||||
$stmt->execute([$new_username, $hashed_password, $admin_id]);
|
||||
} else {
|
||||
$stmt = $db->prepare("UPDATE admins SET username = ? WHERE id = ?");
|
||||
$stmt->execute([$new_username, $admin_id]);
|
||||
}
|
||||
|
||||
$_SESSION['admin_username'] = $new_username;
|
||||
$message = '账户信息更新成功';
|
||||
$message_type = 'success';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取统计数据
|
||||
$stats = [
|
||||
'website_pending' => 0,
|
||||
'website_approved' => 0,
|
||||
'website_rejected' => 0,
|
||||
'app_pending' => 0,
|
||||
'app_approved' => 0,
|
||||
'app_rejected' => 0
|
||||
];
|
||||
|
||||
$stmt = $db->prepare("SELECT status, COUNT(*) as count FROM website_submissions GROUP BY status");
|
||||
$stmt->execute();
|
||||
while ($row = $stmt->fetch()) {
|
||||
$stats['website_' . $row['status']] = $row['count'];
|
||||
}
|
||||
|
||||
$stmt = $db->prepare("SELECT status, COUNT(*) as count FROM app_submissions GROUP BY status");
|
||||
$stmt->execute();
|
||||
while ($row = $stmt->fetch()) {
|
||||
$stats['app_' . $row['status']] = $row['count'];
|
||||
}
|
||||
|
||||
// 获取列表数据
|
||||
$filter = $_GET['filter'] ?? 'pending';
|
||||
$type = $_GET['type'] ?? 'website';
|
||||
$page = max(1, intval($_GET['page'] ?? 1));
|
||||
$limit = 10;
|
||||
$offset = ($page - 1) * $limit;
|
||||
|
||||
// 强制转换为整数
|
||||
$limit = (int)$limit;
|
||||
$offset = (int)$offset;
|
||||
|
||||
if ($type === 'website') {
|
||||
$count_stmt = $db->prepare("SELECT COUNT(*) FROM website_submissions WHERE status = ?");
|
||||
$count_stmt->execute([$filter]);
|
||||
$total = $count_stmt->fetchColumn();
|
||||
|
||||
$stmt = $db->prepare("
|
||||
SELECT id, url, title, description, platforms, contact, status, admin_note, created_at
|
||||
FROM website_submissions
|
||||
WHERE status = ?
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ?, ?
|
||||
");
|
||||
$stmt->bindValue(1, $filter, PDO::PARAM_STR);
|
||||
$stmt->bindValue(2, $offset, PDO::PARAM_INT);
|
||||
$stmt->bindValue(3, $limit, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
} else {
|
||||
$count_stmt = $db->prepare("SELECT COUNT(*) FROM app_submissions WHERE status = ?");
|
||||
$count_stmt->execute([$filter]);
|
||||
$total = $count_stmt->fetchColumn();
|
||||
|
||||
$stmt = $db->prepare("
|
||||
SELECT id, name, platform, version, icon_url, download_url, website_url, description, platforms, contact, status, admin_note, created_at
|
||||
FROM app_submissions
|
||||
WHERE status = ?
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ?, ?
|
||||
");
|
||||
$stmt->bindValue(1, $filter, PDO::PARAM_STR);
|
||||
$stmt->bindValue(2, $offset, PDO::PARAM_INT);
|
||||
$stmt->bindValue(3, $limit, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
$submissions = $stmt->fetchAll();
|
||||
$total_pages = ceil($total / $limit);
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>管理后台 - 内容投稿系统</title>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
background: #f8fafc;
|
||||
color: #1a202c;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 20px 0;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.header-content {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
color: white;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: #ef4444;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
background: #dc2626;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 30px 20px;
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: white;
|
||||
padding: 25px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||
border-left: 4px solid #667eea;
|
||||
}
|
||||
|
||||
.stat-card h3 {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: #667eea;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat-card p {
|
||||
color: #64748b;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.controls {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.controls select {
|
||||
padding: 8px 12px;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.submissions-table {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table td {
|
||||
padding: 15px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.table th {
|
||||
background: #f8fafc;
|
||||
font-weight: 600;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.table tr:hover {
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-pending {
|
||||
background: #fef3c7;
|
||||
color: #92400e;
|
||||
}
|
||||
|
||||
.status-approved {
|
||||
background: #d1fae5;
|
||||
color: #065f46;
|
||||
}
|
||||
|
||||
.status-rejected {
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
padding: 4px 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background: #10b981;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
background: #f59e0b;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.pagination a {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
text-decoration: none;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.pagination a.active {
|
||||
background: #667eea;
|
||||
color: white;
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 12px;
|
||||
width: 90%;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.modal h3 {
|
||||
margin-bottom: 20px;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: 500;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.form-group select,
|
||||
.form-group textarea,
|
||||
.form-group input {
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 2px solid #e2e8f0;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-group textarea {
|
||||
resize: vertical;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.modal-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
justify-content: flex-end;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.message {
|
||||
padding: 15px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.message.success {
|
||||
background: #d1fae5;
|
||||
color: #065f46;
|
||||
border: 1px solid #10b981;
|
||||
}
|
||||
|
||||
.message.error {
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
border: 1px solid #ef4444;
|
||||
}
|
||||
|
||||
.account-settings {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.account-settings.collapsed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.header-content {
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.controls {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.table {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.table th,
|
||||
.table td {
|
||||
padding: 10px 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<div class="header-content">
|
||||
<h1><i class="fas fa-tachometer-alt"></i> 管理后台</h1>
|
||||
<div class="header-actions">
|
||||
<span>欢迎,<?php echo htmlspecialchars($_SESSION['admin_username']); ?></span>
|
||||
<button class="btn btn-primary" onclick="toggleAccountSettings()">
|
||||
<i class="fas fa-cog"></i> 账户设置
|
||||
</button>
|
||||
<a href="../index.php" class="btn btn-primary" target="_blank">
|
||||
<i class="fas fa-external-link-alt"></i> 前台
|
||||
</a>
|
||||
<a href="logout.php" class="btn btn-danger">
|
||||
<i class="fas fa-sign-out-alt"></i> 退出
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<?php if ($message): ?>
|
||||
<div class="message <?php echo $message_type; ?>">
|
||||
<?php echo htmlspecialchars($message); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- 账户设置 -->
|
||||
<div class="account-settings" id="accountSettings">
|
||||
<h3><i class="fas fa-user-cog"></i> 账户设置</h3>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="action" value="update_account">
|
||||
<div class="form-group">
|
||||
<label for="new_username">新用户名</label>
|
||||
<input type="text" id="new_username" name="new_username"
|
||||
value="<?php echo htmlspecialchars($_SESSION['admin_username']); ?>" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="new_password">新密码(留空则不修改)</label>
|
||||
<input type="password" id="new_password" name="new_password">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="confirm_password">确认新密码</label>
|
||||
<input type="password" id="confirm_password" name="confirm_password">
|
||||
</div>
|
||||
<div class="modal-actions">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="fas fa-save"></i> 保存
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning" onclick="toggleAccountSettings()">
|
||||
<i class="fas fa-times"></i> 取消
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- 统计数据 -->
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<h3><?php echo $stats['website_pending']; ?></h3>
|
||||
<p>网址待审核</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3><?php echo $stats['website_approved']; ?></h3>
|
||||
<p>网址已通过</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3><?php echo $stats['app_pending']; ?></h3>
|
||||
<p>应用待审核</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<h3><?php echo $stats['app_approved']; ?></h3>
|
||||
<p>应用已通过</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选控制 -->
|
||||
<div class="controls">
|
||||
<label>类型:</label>
|
||||
<select onchange="changeType(this.value)">
|
||||
<option value="website" <?php echo $type === 'website' ? 'selected' : ''; ?>>网址投稿</option>
|
||||
<option value="app" <?php echo $type === 'app' ? 'selected' : ''; ?>>APP投稿</option>
|
||||
</select>
|
||||
|
||||
<label>状态:</label>
|
||||
<select onchange="changeFilter(this.value)">
|
||||
<option value="pending" <?php echo $filter === 'pending' ? 'selected' : ''; ?>>待处理</option>
|
||||
<option value="approved" <?php echo $filter === 'approved' ? 'selected' : ''; ?>>已通过</option>
|
||||
<option value="rejected" <?php echo $filter === 'rejected' ? 'selected' : ''; ?>>已拒绝</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- 投稿列表 -->
|
||||
<div class="submissions-table">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<?php if ($type === 'website'): ?>
|
||||
<th>网址</th>
|
||||
<th>标题</th>
|
||||
<?php else: ?>
|
||||
<th>应用名称</th>
|
||||
<th>平台</th>
|
||||
<th>版本</th>
|
||||
<?php endif; ?>
|
||||
<th>收录平台</th>
|
||||
<th>联系方式</th>
|
||||
<th>状态</th>
|
||||
<th>提交时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($submissions as $submission): ?>
|
||||
<tr>
|
||||
<td><?php echo $submission['id']; ?></td>
|
||||
<?php if ($type === 'website'): ?>
|
||||
<td>
|
||||
<a href="<?php echo htmlspecialchars($submission['url']); ?>" target="_blank">
|
||||
<?php echo htmlspecialchars(substr($submission['url'], 0, 30)) . (strlen($submission['url']) > 30 ? '...' : ''); ?>
|
||||
</a>
|
||||
</td>
|
||||
<td><?php echo htmlspecialchars($submission['title'] ?: '未获取'); ?></td>
|
||||
<?php else: ?>
|
||||
<td><?php echo htmlspecialchars($submission['name']); ?></td>
|
||||
<td><?php echo htmlspecialchars($submission['platform']); ?></td>
|
||||
<td><?php echo htmlspecialchars($submission['version'] ?: '-'); ?></td>
|
||||
<?php endif; ?>
|
||||
<td><?php echo htmlspecialchars($submission['platforms'] ?: '-'); ?></td>
|
||||
<td><?php echo htmlspecialchars($submission['contact'] ?: '-'); ?></td>
|
||||
<td>
|
||||
<span class="status-badge status-<?php echo $submission['status']; ?>">
|
||||
<?php
|
||||
$status_text = [
|
||||
'pending' => '待处理',
|
||||
'approved' => '已通过',
|
||||
'rejected' => '已拒绝'
|
||||
];
|
||||
echo $status_text[$submission['status']];
|
||||
?>
|
||||
</span>
|
||||
</td>
|
||||
<td><?php echo date('Y-m-d H:i', strtotime($submission['created_at'])); ?></td>
|
||||
<td>
|
||||
<div class="action-buttons">
|
||||
<button class="btn btn-sm btn-primary"
|
||||
onclick="showStatusModal(<?php echo $submission['id']; ?>, '<?php echo $type; ?>', '<?php echo $submission['status']; ?>', '<?php echo htmlspecialchars($submission['admin_note'] ?? '', ENT_QUOTES); ?>')">
|
||||
<i class="fas fa-edit"></i>
|
||||
</button>
|
||||
<?php if ($submission['status'] === 'pending'): ?>
|
||||
<button class="btn btn-sm btn-success"
|
||||
onclick="quickUpdate(<?php echo $submission['id']; ?>, '<?php echo $type; ?>', 'approved')">
|
||||
<i class="fas fa-check"></i>
|
||||
</button>
|
||||
<button class="btn btn-sm btn-danger"
|
||||
onclick="quickUpdate(<?php echo $submission['id']; ?>, '<?php echo $type; ?>', 'rejected')">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 分页 -->
|
||||
<?php if ($total_pages > 1): ?>
|
||||
<div class="pagination">
|
||||
<?php for ($i = 1; $i <= $total_pages; $i++): ?>
|
||||
<a href="?type=<?php echo $type; ?>&filter=<?php echo $filter; ?>&page=<?php echo $i; ?>"
|
||||
class="<?php echo $i === $page ? 'active' : ''; ?>">
|
||||
<?php echo $i; ?>
|
||||
</a>
|
||||
<?php endfor; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- 状态更新模态框 -->
|
||||
<div class="modal" id="statusModal">
|
||||
<div class="modal-content">
|
||||
<h3>更新状态</h3>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="action" value="update_status">
|
||||
<input type="hidden" name="type" id="modal_type">
|
||||
<input type="hidden" name="id" id="modal_id">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="modal_status">状态</label>
|
||||
<select name="status" id="modal_status">
|
||||
<option value="pending">待处理</option>
|
||||
<option value="approved">通过</option>
|
||||
<option value="rejected">拒绝</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="modal_note">备注</label>
|
||||
<textarea name="note" id="modal_note" placeholder="审核备注(可选)"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="modal-actions">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="fas fa-save"></i> 保存
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning" onclick="closeModal()">
|
||||
<i class="fas fa-times"></i> 取消
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function changeType(type) {
|
||||
window.location.href = `?type=${type}&filter=<?php echo $filter; ?>`;
|
||||
}
|
||||
|
||||
function changeFilter(filter) {
|
||||
window.location.href = `?type=<?php echo $type; ?>&filter=${filter}`;
|
||||
}
|
||||
|
||||
function showStatusModal(id, type, status, note) {
|
||||
document.getElementById('modal_id').value = id;
|
||||
document.getElementById('modal_type').value = type;
|
||||
document.getElementById('modal_status').value = status;
|
||||
document.getElementById('modal_note').value = note;
|
||||
document.getElementById('statusModal').style.display = 'block';
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
document.getElementById('statusModal').style.display = 'none';
|
||||
}
|
||||
|
||||
function quickUpdate(id, type, status) {
|
||||
if (confirm(`确定要${status === 'approved' ? '通过' : '拒绝'}这个投稿吗?`)) {
|
||||
const form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.innerHTML = `
|
||||
<input type="hidden" name="action" value="update_status">
|
||||
<input type="hidden" name="type" value="${type}">
|
||||
<input type="hidden" name="id" value="${id}">
|
||||
<input type="hidden" name="status" value="${status}">
|
||||
<input type="hidden" name="note" value="">
|
||||
`;
|
||||
document.body.appendChild(form);
|
||||
form.submit();
|
||||
}
|
||||
}
|
||||
|
||||
function toggleAccountSettings() {
|
||||
const settings = document.getElementById('accountSettings');
|
||||
settings.classList.toggle('collapsed');
|
||||
}
|
||||
|
||||
// 点击模态框外部关闭
|
||||
window.onclick = function(event) {
|
||||
const modal = document.getElementById('statusModal');
|
||||
if (event.target === modal) {
|
||||
closeModal();
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化时折叠账户设置
|
||||
document.getElementById('accountSettings').classList.add('collapsed');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
280
admin/login.php
Normal file
280
admin/login.php
Normal file
@@ -0,0 +1,280 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
require_once '../config/database.php';
|
||||
require_once '../includes/utils.php';
|
||||
|
||||
// 如果已登录,跳转到管理页面
|
||||
if (isset($_SESSION['admin_logged_in']) && $_SESSION['admin_logged_in']) {
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
$error = '';
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$username = trim($_POST['username'] ?? '');
|
||||
$password = $_POST['password'] ?? '';
|
||||
$captcha = $_POST['captcha'] ?? '';
|
||||
|
||||
if (empty($username) || empty($password) || empty($captcha)) {
|
||||
$error = '请填写完整信息';
|
||||
} elseif (!Utils::verifyCaptcha($captcha)) {
|
||||
$error = '验证码错误';
|
||||
} else {
|
||||
$database = new Database();
|
||||
$db = $database->getConnection();
|
||||
|
||||
$stmt = $db->prepare("SELECT id, username, password FROM admins WHERE username = ?");
|
||||
$stmt->execute([$username]);
|
||||
$admin = $stmt->fetch();
|
||||
|
||||
if ($admin && password_verify($password, $admin['password'])) {
|
||||
$_SESSION['admin_logged_in'] = true;
|
||||
$_SESSION['admin_id'] = $admin['id'];
|
||||
$_SESSION['admin_username'] = $admin['username'];
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
} else {
|
||||
$error = '用户名或密码错误';
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>管理后台登录</title>
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.login-header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 40px 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-header h1 {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.login-header p {
|
||||
opacity: 0.9;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
padding: 40px 30px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 600;
|
||||
color: #374151;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.form-group input {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 10px;
|
||||
font-size: 16px;
|
||||
transition: all 0.3s ease;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.form-group input:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
.captcha-group {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.captcha-group input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.captcha-image {
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.captcha-image:hover {
|
||||
border-color: #667eea;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
border: 1px solid #fecaca;
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
width: 100%;
|
||||
padding: 14px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.login-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
.login-btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.default-account {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background: #f0f9ff;
|
||||
border: 1px solid #bae6fd;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
color: #0369a1;
|
||||
}
|
||||
|
||||
.default-account strong {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.login-container {
|
||||
margin: 10px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
|
||||
.login-header {
|
||||
padding: 30px 20px;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
padding: 30px 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-container">
|
||||
<div class="login-header">
|
||||
<h1><i class="fas fa-shield-alt"></i> 管理后台</h1>
|
||||
<p>内容投稿系统管理中心</p>
|
||||
</div>
|
||||
|
||||
<div class="login-form">
|
||||
<?php if ($error): ?>
|
||||
<div class="error-message">
|
||||
<i class="fas fa-exclamation-circle"></i> <?php echo htmlspecialchars($error); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="POST">
|
||||
<div class="form-group">
|
||||
<label for="username">用户名</label>
|
||||
<input type="text" id="username" name="username" required
|
||||
value="<?php echo htmlspecialchars($_POST['username'] ?? ''); ?>">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">密码</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="captcha">验证码</label>
|
||||
<div class="captcha-group">
|
||||
<input type="text" id="captcha" name="captcha" required
|
||||
placeholder="请输入验证码" maxlength="4">
|
||||
<img src="captcha.php" alt="验证码" class="captcha-image"
|
||||
onclick="this.src='captcha.php?'+Math.random()"
|
||||
title="点击刷新验证码">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="login-btn">
|
||||
<i class="fas fa-sign-in-alt"></i> 登录
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="default-account">
|
||||
<strong><i class="fas fa-info-circle"></i> 默认账户信息:</strong>
|
||||
用户名:admin<br>
|
||||
密码:admin
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 自动聚焦到用户名输入框
|
||||
document.getElementById('username').focus();
|
||||
|
||||
// 回车键提交表单
|
||||
document.addEventListener('keypress', function(e) {
|
||||
if (e.key === 'Enter') {
|
||||
document.querySelector('form').submit();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
22
admin/logout.php
Normal file
22
admin/logout.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
// 清除所有会话数据
|
||||
$_SESSION = array();
|
||||
|
||||
// 如果使用了cookie,也删除它
|
||||
if (ini_get("session.use_cookies")) {
|
||||
$params = session_get_cookie_params();
|
||||
setcookie(session_name(), '', time() - 42000,
|
||||
$params["path"], $params["domain"],
|
||||
$params["secure"], $params["httponly"]
|
||||
);
|
||||
}
|
||||
|
||||
// 销毁会话
|
||||
session_destroy();
|
||||
|
||||
// 重定向到登录页面
|
||||
header('Location: login.php');
|
||||
exit;
|
||||
?>
|
||||
Reference in New Issue
Block a user