半梦留言板
- 轻量化部署
- 独立config.php
- 后台管理功能
- 回复功能
- 排序功能
- 无需注册
- 支持虚拟主机
源代码
index.php
<?php
// 引入数据库配置
require_once 'config.php';
// 处理排序参数
$sort = isset($_GET['sort']) ? $_GET['sort'] : 'newest'; // 默认最新在前
try {
// 连接数据库
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 检查是否需要创建表
$checkTable = $pdo->query("SHOW TABLES LIKE 'messages'");
if($checkTable->rowCount() == 0) {
// 创建留言表
$createTable = "
CREATE TABLE messages (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
message TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)";
$pdo->exec($createTable);
// 创建回复表
$createRepliesTable = "
CREATE TABLE replies (
id INT AUTO_INCREMENT PRIMARY KEY,
message_id INT NOT NULL,
name VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (message_id) REFERENCES messages(id) ON DELETE CASCADE
)";
$pdo->exec($createRepliesTable);
}
// 处理留言提交
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['submit_message'])) {
$name = trim($_POST['name']);
$email = trim($_POST['email']);
$message = trim($_POST['message']);
// 简单验证
if (!empty($name) && !empty($email) && !empty($message) && filter_var($email, FILTER_VALIDATE_EMAIL)) {
$stmt = $pdo->prepare("INSERT INTO messages (name, email, message) VALUES (:name, :email, :message)");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':email', $email);
$stmt->bindParam(':message', $message);
$stmt->execute();
// 刷新页面避免重复提交,保持当前排序方式
header("Location: " . $_SERVER['PHP_SELF'] . (isset($_GET['sort']) ? "?sort=" . $_GET['sort'] : ""));
exit();
} else {
$error = "请填写所有字段并确保邮箱格式正确";
}
}
// 处理回复提交
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['submit_reply'])) {
$messageId = isset($_POST['message_id']) ? (int)$_POST['message_id'] : 0;
$name = trim($_POST['reply_name']);
$email = trim($_POST['reply_email']);
$content = trim($_POST['reply_content']);
if ($messageId > 0 && !empty($name) && !empty($email) && !empty($content) && filter_var($email, FILTER_VALIDATE_EMAIL)) {
$stmt = $pdo->prepare("INSERT INTO replies (message_id, name, email, content) VALUES (:message_id, :name, :email, :content)");
$stmt->bindParam(':message_id', $messageId);
$stmt->bindParam(':name', $name);
$stmt->bindParam(':email', $email);
$stmt->bindParam(':content', $content);
$stmt->execute();
// 刷新页面,保持当前排序方式
header("Location: " . $_SERVER['PHP_SELF'] . (isset($_GET['sort']) ? "?sort=" . $_GET['sort'] : ""));
exit();
} else {
$replyError = "请填写所有字段并确保邮箱格式正确";
}
}
// 根据排序参数获取留言
$messages = [];
switch($sort) {
case 'oldest':
// 时间正序(最早的在前)
$stmt = $pdo->query("SELECT * FROM messages ORDER BY created_at ASC");
break;
case 'random':
// 随机显示
$stmt = $pdo->query("SELECT * FROM messages ORDER BY RAND()");
break;
case 'newest':
default:
// 时间倒序(最新的在前)
$stmt = $pdo->query("SELECT * FROM messages ORDER BY created_at DESC");
break;
}
$messageList = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($messageList as $msg) {
$stmt = $pdo->prepare("SELECT * FROM replies WHERE message_id = :message_id ORDER BY created_at ASC");
$stmt->bindParam(':message_id', $msg['id']);
$stmt->execute();
$msg['replies'] = $stmt->fetchAll(PDO::FETCH_ASSOC);
$messages[] = $msg;
}
} catch(PDOException $e) {
die("数据库连接失败: " . $e->getMessage());
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>半梦留言板</title>
<script src="https://cdn.tailwindcss.com"></script>
<!-- <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet"> -->
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#3B82F6',
secondary: '#10B981',
neutral: '#64748B',
user: '#8B5CF6',
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
}
}
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.content-auto {
content-visibility: auto;
}
.card-hover {
transition: all 0.3s ease;
}
.card-hover:hover {
transform: translateY(-3px);
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.reply-indent {
border-left: 3px solid #e5e7eb;
padding-left: 1rem;
margin-left: 0.5rem;
}
.sort-active {
@apply bg-primary text-white;
}
}
</style>
</head>
<body class="bg-gray-50 font-sans">
<div class="container mx-auto px-4 py-8 max-w-5xl">
<!-- 页面标题 -->
<header class="text-center mb-12">
<h1 class="text-[clamp(2rem,5vw,3rem)] font-bold text-gray-800 mb-2">
<i class="fa fa-comments text-primary mr-3"></i>半梦留言板
</h1>
<p class="text-neutral text-lg">分享你的想法,留下你的足迹</p>
</header>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
<!-- 留言表单 -->
<div class="lg:col-span-1">
<div class="bg-white rounded-xl shadow-md p-6 card-hover sticky top-4">
<h2 class="text-xl font-bold text-gray-800 mb-4 flex items-center">
<i class="fa fa-pencil-square-o text-primary mr-2"></i>发表留言
</h2>
<?php if (isset($error)): ?>
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
<i class="fa fa-exclamation-circle mr-2"></i><?php echo $error; ?>
</div>
<?php endif; ?>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]) . (isset($_GET['sort']) ? "?sort=" . $_GET['sort'] : ""); ?>">
<div class="mb-4">
<label for="name" class="block text-gray-700 mb-2 font-medium">
<i class="fa fa-user text-neutral mr-1"></i>昵称
</label>
<input type="text" id="name" name="name"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition duration-200"
placeholder="请输入你的姓名" required>
</div>
<div class="mb-4">
<label for="email" class="block text-gray-700 mb-2 font-medium">
<i class="fa fa-envelope text-neutral mr-1"></i>邮箱
</label>
<input type="email" id="email" name="email"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition duration-200"
placeholder="请输入你的邮箱" required>
</div>
<div class="mb-6">
<label for="message" class="block text-gray-700 mb-2 font-medium">
<i class="fa fa-comment text-neutral mr-1"></i>留言内容
</label>
<textarea id="message" name="message" rows="5"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition duration-200 resize-none"
placeholder="请输入你的留言内容..." required></textarea>
</div>
<button type="submit" name="submit_message"
class="w-full bg-primary hover:bg-primary/90 text-white font-medium py-2 px-4 rounded-lg transition duration-200 flex items-center justify-center">
<i class="fa fa-paper-plane mr-2"></i>提交留言
</button>
</form>
</div>
</div>
<!-- 留言列表 -->
<div class="lg:col-span-2">
<div class="mb-6 flex flex-wrap justify-between items-center gap-4">
<h2 class="text-xl font-bold text-gray-800 flex items-center">
<i class="fa fa-list-alt text-primary mr-2"></i>最新留言
<span class="ml-2 text-sm bg-gray-100 text-gray-600 px-2 py-1 rounded-full">
共 <?php echo count($messages); ?> 条
</span>
</h2>
<div class="flex items-center gap-2">
<!-- 排序菜单 -->
<div class="relative inline-block">
<button id="sortBtn" class="bg-white border border-gray-300 rounded-lg px-3 py-1.5 text-sm font-medium flex items-center hover:bg-gray-50 transition-colors">
<i class="fa fa-sort mr-2 text-neutral"></i>
<span>
<?php
switch($sort) {
case 'oldest': echo '时间正序'; break;
case 'random': echo '随机显示'; break;
default: echo '时间倒序';
}
?>
</span>
<i class="fa fa-chevron-down ml-2 text-xs text-neutral"></i>
</button>
<!-- 排序下拉菜单 -->
<div id="sortMenu" class="hidden absolute right-0 mt-2 w-40 bg-white rounded-lg shadow-lg border border-gray-100 z-10">
<ul class="py-1">
<li>
<a href="?sort=newest" class="block px-4 py-2 text-sm hover:bg-gray-100 <?php echo $sort == 'newest' ? 'sort-active' : 'text-gray-700'; ?>">
<i class="fa fa-sort-amount-desc mr-2"></i>时间倒序
</a>
</li>
<li>
<a href="?sort=oldest" class="block px-4 py-2 text-sm hover:bg-gray-100 <?php echo $sort == 'oldest' ? 'sort-active' : 'text-gray-700'; ?>">
<i class="fa fa-sort-amount-asc mr-2"></i>时间正序
</a>
</li>
<li>
<a href="?sort=random" class="block px-4 py-2 text-sm hover:bg-gray-100 <?php echo $sort == 'random' ? 'sort-active' : 'text-gray-700'; ?>">
<i class="fa fa-random mr-2"></i>随机显示
</a>
</li>
</ul>
</div>
</div>
<?php if (count($messages) > 0): ?>
<button id="refreshBtn" class="bg-white border border-gray-300 rounded-lg px-3 py-1.5 text-sm font-medium flex items-center hover:bg-gray-50 transition-colors">
<i class="fa fa-refresh mr-1 text-neutral"></i> 刷新
</button>
<?php endif; ?>
</div>
</div>
<?php if (count($messages) > 0): ?>
<div class="space-y-6">
<?php foreach ($messages as $msg): ?>
<div class="bg-white rounded-xl shadow-md overflow-hidden card-hover">
<div class="p-6 border-b border-gray-100">
<div class="flex justify-between items-start mb-3">
<div class="flex items-center">
<div class="w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center text-primary font-bold mr-3">
<?php echo strtoupper(substr($msg['name'], 0, 1)); ?>
</div>
<div>
<h3 class="font-semibold text-gray-800"><?php echo htmlspecialchars($msg['name']); ?></h3>
<p class="text-sm text-neutral">
<i class="fa fa-clock-o mr-1"></i>
<?php echo date('Y-m-d H:i', strtotime($msg['created_at'])); ?>
</p>
</div>
</div>
</div>
<div class="mb-4">
<p class="text-gray-700 leading-relaxed">
<?php echo nl2br(htmlspecialchars($msg['message'])); ?>
</p>
</div>
<!-- 显示回复 -->
<?php if (!empty($msg['replies'])): ?>
<div class="space-y-3 mt-4">
<p class="text-sm font-medium text-gray-600">
<i class="fa fa-reply text-user mr-1"></i>回复 (<?php echo count($msg['replies']); ?>)
</p>
<div class="max-h-64 overflow-y-auto pr-2 space-y-3">
<?php foreach ($msg['replies'] as $reply): ?>
<div class="reply-indent bg-gray-50 p-3 rounded-lg">
<p class="text-xs font-medium text-gray-800">
<?php echo htmlspecialchars($reply['name']); ?>
</p>
<p class="text-gray-700 text-sm mt-1">
<?php echo nl2br(htmlspecialchars($reply['content'])); ?>
</p>
<p class="text-xs text-gray-500 mt-1">
<i class="fa fa-clock-o mr-1"></i>
<?php echo date('Y-m-d H:i', strtotime($reply['created_at'])); ?>
</p>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>
<!-- 回复表单 -->
<div class="p-6 bg-gray-50">
<h4 class="text-sm font-medium text-gray-700 mb-3">
<i class="fa fa-reply-all text-user mr-1"></i>添加回复
</h4>
<?php if (isset($replyError) && isset($_POST['message_id']) && $_POST['message_id'] == $msg['id']): ?>
<div class="bg-red-100 border border-red-400 text-red-700 px-3 py-2 rounded mb-3 text-sm">
<i class="fa fa-exclamation-circle mr-1"></i><?php echo $replyError; ?>
</div>
<?php endif; ?>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]) . (isset($_GET['sort']) ? "?sort=" . $_GET['sort'] : ""); ?>">
<input type="hidden" name="message_id" value="<?php echo $msg['id']; ?>">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 mb-3">
<div>
<input type="text" name="reply_name"
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-user focus:border-user transition duration-200"
placeholder="昵称" required>
</div>
<div>
<input type="email" name="reply_email"
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-user focus:border-user transition duration-200"
placeholder="邮箱" required>
</div>
</div>
<div class="mb-3">
<textarea name="reply_content" rows="2"
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-user focus:border-user transition duration-200 resize-none"
placeholder="回复内容..." required></textarea>
</div>
<button type="submit" name="submit_reply"
class="w-full bg-user hover:bg-user/90 text-white text-sm font-medium py-2 px-3 rounded-lg transition duration-200 flex items-center justify-center">
<i class="fa fa-paper-plane mr-1"></i>提交回复
</button>
</form>
</div>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<div class="text-center py-16 bg-white rounded-xl shadow-md">
<i class="fa fa-comment-o text-5xl text-gray-300 mb-4"></i>
<p class="text-neutral text-lg">还没有留言,快来发表第一条留言吧!</p>
</div>
<?php endif; ?>
</div>
</div>
<!-- 页脚 -->
<footer class="mt-12 text-center text-neutral text-sm">
<p>© <?php echo date('Y'); ?> 半梦留言板</p>
</footer>
</div>
<script>
// 排序下拉菜单切换
document.addEventListener('DOMContentLoaded', function() {
const sortBtn = document.getElementById('sortBtn');
const sortMenu = document.getElementById('sortMenu');
if (sortBtn && sortMenu) {
sortBtn.addEventListener('click', function(e) {
e.stopPropagation();
sortMenu.classList.toggle('hidden');
});
// 点击其他地方关闭下拉菜单
document.addEventListener('click', function() {
sortMenu.classList.add('hidden');
});
// 防止下拉菜单内部点击关闭菜单
sortMenu.addEventListener('click', function(e) {
e.stopPropagation();
});
}
// 刷新按钮功能
document.getElementById('refreshBtn')?.addEventListener('click', function() {
// 添加加载动画
this.innerHTML = '<i class="fa fa-circle-o-notch fa-spin mr-1 text-neutral"></i> 加载中...';
this.disabled = true;
// 刷新页面,保持当前排序方式
setTimeout(() => {
window.location.reload();
}, 500);
});
// 表单提交前的简单验证
document.querySelectorAll('form').forEach(form => {
form.addEventListener('submit', function(e) {
const nameField = this.querySelector('input[name="name"], input[name="reply_name"]');
const emailField = this.querySelector('input[name="email"], input[name="reply_email"]');
const contentField = this.querySelector('textarea');
if (!nameField.value.trim() || !emailField.value.trim() || !contentField.value.trim()) {
alert('请填写所有必填字段');
e.preventDefault();
return false;
}
// 简单的邮箱格式验证
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(emailField.value.trim())) {
alert('请输入有效的邮箱地址');
e.preventDefault();
return false;
}
return true;
});
});
});
</script>
</body>
</html>
config.php
<?php
// 数据库连接配置
$host = '数据库ip地址';
$dbname = '数据库名';
$username = '数据库账户名称';
$password = '数据库账户密码';
// 数据库连接函数
function getDbConnection() {
global $host, $dbname, $username, $password;
try {
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
} catch(PDOException $e) {
die("数据库连接失败: " . $e->getMessage());
}
}
?>
admin.php
<?php
// 引入数据库配置
require_once 'config.php';
// 简单的访问控制
$adminPassword = '123456'; // 管理员密码,实际部署中应更改
$isLoggedIn = false;
// 处理登录请求
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['login'])) {
if ($_POST['password'] === $adminPassword) {
session_start();
$_SESSION['admin_logged_in'] = true;
header("Location: " . $_SERVER['PHP_SELF']);
exit();
} else {
$loginError = "密码错误,请重试";
}
}
// 处理登出请求
if (isset($_GET['logout'])) {
session_start();
unset($_SESSION['admin_logged_in']);
session_destroy();
header("Location: " . $_SERVER['PHP_SELF']);
exit();
}
// 检查登录状态
session_start();
if (isset($_SESSION['admin_logged_in']) && $_SESSION['admin_logged_in'] === true) {
$isLoggedIn = true;
try {
// 连接数据库
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 处理删除留言
if (isset($_GET['delete']) && is_numeric($_GET['delete'])) {
$id = $_GET['delete'];
$stmt = $pdo->prepare("DELETE FROM messages WHERE id = :id");
$stmt->bindParam(':id', $id);
$stmt->execute();
header("Location: " . $_SERVER['PHP_SELF']);
exit();
}
// 处理删除回复
if (isset($_GET['delete_reply']) && is_numeric($_GET['delete_reply'])) {
$replyId = $_GET['delete_reply'];
$messageId = isset($_GET['message_id']) ? (int)$_GET['message_id'] : 0;
$stmt = $pdo->prepare("DELETE FROM replies WHERE id = :id");
$stmt->bindParam(':id', $replyId);
$stmt->execute();
// 返回到相应页面
if ($messageId > 0) {
header("Location: ?edit=" . $messageId);
} else {
header("Location: " . $_SERVER['PHP_SELF']);
}
exit();
}
// 处理编辑留言
$editMessage = null;
$messageReplies = [];
if (isset($_GET['edit']) && is_numeric($_GET['edit'])) {
$id = $_GET['edit'];
$stmt = $pdo->prepare("SELECT * FROM messages WHERE id = :id");
$stmt->bindParam(':id', $id);
$stmt->execute();
$editMessage = $stmt->fetch(PDO::FETCH_ASSOC);
// 获取该留言的所有回复
if ($editMessage) {
$stmt = $pdo->prepare("SELECT * FROM replies WHERE message_id = :message_id");
$stmt->bindParam(':message_id', $id);
$stmt->execute();
$messageReplies = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
// 处理保存编辑
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['save_edit'])) {
if (isset($_POST['id']) && is_numeric($_POST['id'])) {
$id = $_POST['id'];
$name = trim($_POST['name']);
$email = trim($_POST['email']);
$message = trim($_POST['message']);
if (!empty($name) && !empty($email) && !empty($message) && filter_var($email, FILTER_VALIDATE_EMAIL)) {
$stmt = $pdo->prepare("UPDATE messages SET name = :name, email = :email, message = :message WHERE id = :id");
$stmt->bindParam(':name', $name);
$stmt->bindParam(':email', $email);
$stmt->bindParam(':message', $message);
$stmt->bindParam(':id', $id);
$stmt->execute();
}
header("Location: ?edit=" . $id);
exit();
}
}
// 处理批量删除
if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['bulk_delete'])) {
if (!empty($_POST['message_ids'])) {
$ids = implode(',', array_map('intval', $_POST['message_ids']));
$stmt = $pdo->prepare("DELETE FROM messages WHERE id IN ($ids)");
$stmt->execute();
header("Location: " . $_SERVER['PHP_SELF']);
exit();
}
}
// 获取所有留言,按时间倒序
$stmt = $pdo->query("SELECT * FROM messages");
$messages = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch(PDOException $e) {
die("数据库连接失败: " . $e->getMessage());
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>留言板管理系统</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#3B82F6',
danger: '#EF4444',
success: '#10B981',
neutral: '#64748B',
user: '#8B5CF6',
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
}
}
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.content-auto {
content-visibility: auto;
}
.table-hover-row {
transition: background-color 0.2s ease;
}
.table-hover-row:hover {
background-color: rgba(59, 130, 246, 0.05);
}
.reply-indent {
border-left: 3px solid #e5e7eb;
padding-left: 1rem;
margin-left: 0.5rem;
}
}
</style>
</head>
<body class="bg-gray-50 font-sans min-h-screen">
<div class="container mx-auto px-4 py-8 max-w-6xl">
<!-- 页面标题 -->
<header class="mb-8">
<div class="flex justify-between items-center">
<h1 class="text-2xl md:text-3xl font-bold text-gray-800 flex items-center">
<i class="fa fa-cog text-primary mr-3"></i>留言板管理系统
</h1>
<?php if ($isLoggedIn): ?>
<a href="?logout" class="text-neutral-500 hover:text-danger transition-colors flex items-center">
<i class="fa fa-sign-out mr-1"></i> 退出登录
</a>
<?php endif; ?>
</div>
<p class="text-neutral mt-1">管理所有留言和回复内容</p>
</header>
<?php if (!$isLoggedIn): ?>
<!-- 登录表单 -->
<div class="max-w-md-md mx-auto bg-white rounded-xl shadow-md p-8">
<h2 class="text-xl font-bold text-gray-800 mb-6 text-center">管理员登录</h2>
<?php if (isset($loginError)): ?>
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
<i class="fa fa-exclamation-circle mr-2"></i><?php echo $loginError; ?>
</div>
<?php endif; ?>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
<div class="mb-6">
<label for="password" class="block text-gray-700 mb-2 font-medium">
<i class="fa fa-lock text-neutral mr-1"></i>管理员密码
</label>
<input type="password" id="password" name="password"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition duration-200"
placeholder="请输入管理员密码" required>
</div>
<button type="submit" name="login"
class="w-full bg-primary hover:bg-primary/90 text-white font-medium py-2 px-4 rounded-lg transition duration-200 flex items-center justify-center">
<i class="fa fa-sign-in mr-2"></i>登录
</button>
</form>
<div class="mt-4 text-center text-sm text-neutral">
<p>回到 <a href="index.php" class="text-primary hover:underline">留言板首页</a></p>
</div>
</div>
<?php else: ?>
<!-- 编辑留言表单 -->
<?php if ($editMessage): ?>
<div class="bg-white rounded-xl shadow-md p-6 mb-8 border-l-4 border-primary">
<div class="flex justify-between items-center mb-6">
<h2 class="text-xl font-bold text-gray-800 flex items-center">
<i class="fa fa-pencil text-primary mr-2"></i>编辑留言 #<?php echo $editMessage['id']; ?>
</h2>
<a href="admin.php" class="text-neutral hover:text-gray-700 transition-colors">
<i class="fa fa-times"></i> 取消
</a>
</div>
<form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
<input type="hidden" name="id" value="<?php echo $editMessage['id']; ?>">
<div class="mb-4">
<label for="edit_name" class="block text-gray-700 mb-2 font-medium">
<i class="fa fa-user text-neutral mr-1"></i>姓名
</label>
<input type="text" id="edit_name" name="name"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition duration-200"
value="<?php echo htmlspecialchars($editMessage['name']); ?>" required>
</div>
<div class="mb-4">
<label for="edit_email" class="block text-gray-700 mb-2 font-medium">
<i class="fa fa-envelope text-neutral mr-1"></i>邮箱
</label>
<input type="email" id="edit_email" name="email"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition duration-200"
value="<?php echo htmlspecialchars($editMessage['email']); ?>" required>
</div>
<div class="mb-6">
<label for="edit_message" class="block text-gray-700 mb-2 font-medium">
<i class="fa fa-comment text-neutral mr-1"></i>留言内容
</label>
<textarea id="edit_message" name="message" rows="4"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary transition duration-200 resize-none"
required><?php echo htmlspecialchars($editMessage['message']); ?></textarea>
</div>
<div class="flex gap-4 mb-8">
<button type="submit" name="save_edit"
class="flex-1 bg-success hover:bg-success/90 text-white font-medium py-2 px-4 rounded-lg transition duration-200 flex items-center justify-center">
<i class="fa fa-save mr-2"></i>保存修改
</button>
<a href="?delete=<?php echo $editMessage['id']; ?>"
class="bg-danger hover:bg-danger/90 text-white font-medium py-2 px-4 rounded-lg transition duration-200 flex items-center justify-center"
onclick="return confirm('确定要删除这条留言吗?相关回复也会被删除。')">
<i class="fa fa-trash-o mr-2"></i>删除留言
</a>
</div>
</form>
<!-- 回复管理区域 -->
<div class="border-t border-gray-200 pt-6">
<h3 class="text-lg font-semibold text-gray-800 mb-4 flex items-center">
<i class="fa fa-reply text-user mr-2"></i>回复管理
<span class="ml-2 text-sm bg-gray-100 text-gray-600 px-2 py-1 rounded-full">
共 <?php echo count($messageReplies); ?> 条回复
</span>
</h3>
<?php if (!empty($messageReplies)): ?>
<div class="space-y-4">
<?php foreach ($messageReplies as $reply): ?>
<div class="reply-indent bg-gray-50 p-4 rounded-lg">
<div class="flex justify-between items-start">
<div>
<p class="text-sm font-medium text-gray-800">
<?php echo htmlspecialchars($reply['name']); ?>
<span class="text-xs text-gray-500 ml-2">(<?php echo htmlspecialchars($reply['email']); ?>)</span>
</p>
<p class="text-gray-700 mt-2">
<?php echo nl2br(htmlspecialchars($reply['content'])); ?>
</p>
</div>
<a href="?delete_reply=<?php echo $reply['id']; ?>&message_id=<?php echo $editMessage['id']; ?>"
class="text-danger hover:text-red-700 transition duration-200 text-sm ml-2"
onclick="return confirm('确定要删除这条回复吗?')">
<i class="fa fa-trash-o"></i>
</a>
</div>
<p class="text-xs text-gray-500 mt-2">
<i class="fa fa-clock-o mr-1"></i>
<?php echo date('Y-m-d H:i', strtotime($reply['created_at'])); ?>
</p>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<div class="text-neutral text-sm bg-gray-50 p-3 rounded-lg">
暂无回复
</div>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<!-- 管理内容区域 -->
<div class="bg-white rounded-xl shadow-md overflow-hidden">
<div class="bg-white p-6">
<div class="flex flex-wrap justify-between items-center mb-6">
<h2 class="text-xl font-bold text-gray-800 flex items-center">
<i class="fa fa-list-alt text-primary mr-2"></i>留言管理
<span class="ml-2 text-sm bg-gray-100 text-gray-600 px-2 py-1 rounded-full">
共 <?php echo count($messages); ?> 条留言
</span>
</h2>
<?php if (count($messages) > 0): ?>
<button type="button" id="deleteSelectedBtn"
class="bg-danger hover:bg-danger/90 text-white font-medium py-1 px-3 rounded-lg transition duration-200 flex items-center text-sm">
<i class="fa fa-trash mr-1"></i> 删除选中
</button>
<?php endif; ?>
</div>
<?php if (count($messages) > 0): ?>
<form id="messagesForm" method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>">
<input type="hidden" name="bulk_delete" value="1">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider w-12">
<input type="checkbox" id="selectAll" class="h-4 w-4 text-primary border-gray-300 rounded">
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">姓名</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">邮箱</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">留言内容</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">时间</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">回复数</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">操作</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<?php foreach ($messages as $msg): ?>
<?php
// 获取该留言的回复数量
$stmt = $pdo->prepare("SELECT COUNT(*) as reply_count FROM replies WHERE message_id = :message_id");
$stmt->bindParam(':message_id', $msg['id']);
$stmt->execute();
$replyCount = $stmt->fetchColumn();
?>
<tr class="table-hover-row">
<td class="px-6 py-4 whitespace-nowrap">
<input type="checkbox" name="message_ids[]" value="<?php echo $msg['id']; ?>"
class="message-checkbox h-4 w-4 text-primary border-gray-300 rounded">
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"><?php echo $msg['id']; ?></td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900"><?php echo htmlspecialchars($msg['name']); ?></td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"><?php echo htmlspecialchars($msg['email']); ?></td>
<td class="px-6 py-4 text-sm text-gray-500 max-w-xs truncate">
<?php echo nl2br(htmlspecialchars($msg['message'])); ?>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<?php echo date('Y-m-d H:i', strtotime($msg['created_at'])); ?>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
<span class="bg-user/10 text-user px-2 py-1 rounded-full text-xs">
<?php echo $replyCount; ?>
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
<a href="?edit=<?php echo $msg['id']; ?>"
class="text-primary hover:text-primary/80 transition duration-200 mr-3">
<i class="fa fa-pencil mr-1"></i>管理
</a>
<a href="?delete=<?php echo $msg['id']; ?>"
class="text-danger hover:text-red-700 transition duration-200"
onclick="return confirm('确定要删除这条留言吗?相关回复也会被删除。')">
<i class="fa fa-trash-o mr-1"></i>删除
</a>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</form>
<?php else: ?>
<div class="text-center py-12 bg-gray-50 rounded-lg">
<i class="fa fa-comment-o text-5xl text-gray-300 mb-4"></i>
<p class="text-neutral text-lg">暂无留言数据</p>
<p class="text-neutral mt-2">访客可以在前台提交留言</p>
</div>
<?php endif; ?>
</div>
</div>
<!-- 底部链接 -->
<div class="mt-6 text-center">
<a href="index.php" class="inline-flex items-center text-primary hover:text-primary/80 transition-colors">
<i class="fa fa-arrow-left mr-1"></i> 回到留言板首页
</a>
</div>
<?php endif; ?>
<!-- 页脚 -->
<footer class="mt-12 text-center text-neutral text-sm">
<p>© <?php echo date('Y'); ?> 留言板管理系统</p>
</footer>
</div>
<script>
// 全选/取消全选功能
document.addEventListener('DOMContentLoaded', function() {
const selectAllCheckbox = document.getElementById('selectAll');
const messageCheckboxes = document.querySelectorAll('.message-checkbox');
const deleteSelectedBtn = document.getElementById('deleteSelectedBtn');
const messagesForm = document.getElementById('messagesForm');
if (selectAllCheckbox) {
selectAllCheckbox.addEventListener('change', function() {
messageCheckboxes.forEach(checkbox => {
checkbox.checked = selectAllCheckbox.checked;
});
});
}
// 批量删除确认
if (deleteSelectedBtn && messagesForm) {
deleteSelectedBtn.addEventListener('click', function() {
const checkedCount = document.querySelectorAll('.message-checkbox:checked').length;
if (checkedCount === 0) {
alert('请先选择要删除的留言');
return;
}
if (confirm(`确定要删除选中的 ${checkedCount} 条留言吗?相关回复也会被删除。`)) {
messagesForm.submit();
}
});
}
// 如果是编辑页面,自动滚动到编辑表单
<?php if ($editMessage): ?>
window.scrollTo({top: 0, behavior: 'smooth'});
<?php endif; ?>
});
</script>
</body>
</html>