博客论坛系统,包含用户注册、后台管理、图片上传、验证码验证等功能。
设计思路
- 前端使用HTML5+CSS3+JavaScript实现响应式界面
- 后端使用PHP处理业务逻辑
- MySQL数据库存储数据
- 实现伪静态URL
- 支持多板块发布和管理
完整代码实现
1. 数据库结构
-- 创建数据库
CREATE DATABASE blog_forum;
USE blog_forum;
-- 用户表
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
role ENUM('admin', 'user') DEFAULT 'user',
avatar VARCHAR(255) DEFAULT 'default.jpg',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 板块表
CREATE TABLE categories (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 帖子表
CREATE TABLE posts (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
user_id INT NOT NULL,
category_id INT NOT NULL,
image_path VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (category_id) REFERENCES categories(id)
);
-- 插入初始数据
INSERT INTO categories (name, description) VALUES
('技术交流', '分享编程技术和开发经验'),
('生活随笔', '记录生活中的点滴感悟'),
('学习资源', '分享优质学习资料和教程');
2. 配置文件
<?php
// config.php
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_NAME', 'blog_forum');
define('SITE_URL', 'http://localhost/blog_forum');
// 创建数据库连接
function getDBConnection() {
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
if ($conn->connect_error) {
die("连接失败: " . $conn->connect_error);
}
$conn->set_charset("utf8mb4");
return $conn;
}
// 启动会话
session_start();
?>
3. 前端页面结构
<!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 rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<header>
<nav class="navbar">
<div class="nav-brand">
<h1><a href="index.php">博客论坛</a></h1>
</div>
<div class="nav-menu">
<?php if(isset($_SESSION['user_id'])): ?>
<a href="dashboard.php">控制台</a>
<a href="new_post.php">发布帖子</a>
<?php if($_SESSION['role'] == 'admin'): ?>
<a href="admin.php">后台管理</a>
<?php endif; ?>
<a href="profile.php"><?php echo $_SESSION['username']; ?></a>
<a href="logout.php">退出</a>
<?php else: ?>
<a href="login.php">登录</a>
<a href="register.php">注册</a>
<?php endif; ?>
</div>
</nav>
</header>
<main>
<!-- 内容区域 -->
</main>
<footer>
<p>© 2023 博客论坛 版权所有</p>
</footer>
<script src="assets/js/main.js"></script>
</body>
</html>
4. 样式文件
/* assets/css/style.css */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
background-color: #f5f5f5;
}
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #2c3e50;
padding: 1rem 2rem;
color: white;
}
.nav-brand h1 a {
color: white;
text-decoration: none;
}
.nav-menu a {
color: white;
text-decoration: none;
margin-left: 1.5rem;
transition: color 0.3s;
}
.nav-menu a:hover {
color: #3498db;
}
.container {
max-width: 1200px;
margin: 2rem auto;
padding: 0 1rem;
}
.card {
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 1.5rem;
margin-bottom: 1.5rem;
}
.form-group {
margin-bottom: 1rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-weight: bold;
}
.form-control {
width: 100%;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
}
.btn {
display: inline-block;
padding: 0.75rem 1.5rem;
background-color: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
transition: background-color 0.3s;
}
.btn:hover {
background-color: #2980b9;
}
.btn-danger {
background-color: #e74c3c;
}
.btn-danger:hover {
background-color: #c0392b;
}
.post-list {
list-style: none;
}
.post-item {
border-bottom: 1px solid #eee;
padding: 1rem 0;
}
.post-item:last-child {
border-bottom: none;
}
.post-title {
font-size: 1.25rem;
margin-bottom: 0.5rem;
}
.post-meta {
color: #777;
font-size: 0.9rem;
}
.captcha-container {
display: flex;
align-items: center;
}
.captcha-image {
margin-left: 1rem;
border: 1px solid #ddd;
border-radius: 4px;
cursor: pointer;
}
/* 响应式设计 */
@media (max-width: 768px) {
.navbar {
flex-direction: column;
padding: 1rem;
}
.nav-menu {
margin-top: 1rem;
}
.nav-menu a {
margin: 0 0.5rem;
}
}
5. 用户注册
<?php
// register.php
require_once 'config.php';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$username = trim($_POST['username']);
$email = trim($_POST['email']);
$password = $_POST['password'];
$confirm_password = $_POST['confirm_password'];
$errors = [];
// 验证输入
if (empty($username) || strlen($username) < 3) {
$errors[] = "用户名至少需要3个字符";
}
if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = "请输入有效的邮箱地址";
}
if (empty($password) || strlen($password) < 6) {
$errors[] = "密码至少需要6个字符";
}
if ($password !== $confirm_password) {
$errors[] = "两次输入的密码不一致";
}
// 检查用户名和邮箱是否已存在
$conn = getDBConnection();
$stmt = $conn->prepare("SELECT id FROM users WHERE username = ? OR email = ?");
$stmt->bind_param("ss", $username, $email);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
$errors[] = "用户名或邮箱已被使用";
}
if (empty($errors)) {
// 创建用户
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
$stmt = $conn->prepare("INSERT INTO users (username, email, password) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $username, $email, $hashed_password);
if ($stmt->execute()) {
$_SESSION['success'] = "注册成功,请登录";
header("Location: login.php");
exit();
} else {
$errors[] = "注册失败,请稍后重试";
}
}
$conn->close();
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>用户注册 - 博客论坛</title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<?php include 'header.php'; ?>
<div class="container">
<div class="card">
<h2>用户注册</h2>
<?php if (!empty($errors)): ?>
<div class="alert alert-danger">
<?php foreach ($errors as $error): ?>
<p><?php echo $error; ?></p>
<?php endforeach; ?>
</div>
<?php endif; ?>
<form method="POST" action="">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" id="username" name="username" class="form-control"
value="<?php echo isset($_POST['username']) ? htmlspecialchars($_POST['username']) : ''; ?>" required>
</div>
<div class="form-group">
<label for="email">邮箱地址</label>
<input type="email" id="email" name="email" class="form-control"
value="<?php echo isset($_POST['email']) ? htmlspecialchars($_POST['email']) : ''; ?>" required>
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" id="password" name="password" class="form-control" required>
</div>
<div class="form-group">
<label for="confirm_password">确认密码</label>
<input type="password" id="confirm_password" name="confirm_password" class="form-control" required>
</div>
<button type="submit" class="btn">注册</button>
</form>
<p style="margin-top: 1rem;">已有账户?<a href="login.php">立即登录</a></p>
</div>
</div>
</body>
</html>
6. 验证码生成
<?php
// captcha.php
session_start();
// 创建验证码图片
$width = 120;
$height = 40;
$image = imagecreate($width, $height);
// 设置背景和文本颜色
$bg_color = imagecolorallocate($image, 255, 255, 255);
$text_color = imagecolorallocate($image, 0, 0, 0);
$line_color = imagecolorallocate($image, 200, 200, 200);
// 填充背景
imagefilledrectangle($image, 0, 0, $width, $height, $bg_color);
// 添加干扰线
for ($i = 0; $i < 5; $i++) {
imageline($image, 0, rand() % $height, $width, rand() % $height, $line_color);
}
// 生成随机验证码
$chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789';
$captcha = '';
for ($i = 0; $i < 4; $i++) {
$captcha .= $chars[rand(0, strlen($chars) - 1)];
}
// 保存验证码到会话
$_SESSION['captcha'] = $captcha;
// 添加文本到图片
imagestring($image, 5, 30, 12, $captcha, $text_color);
// 输出图片
header('Content-type: image/png');
imagepng($image);
imagedestroy($image);
?>
7. 发布帖子
<?php
// new_post.php
require_once 'config.php';
// 检查用户是否登录
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit();
}
// 获取所有板块
$conn = getDBConnection();
$categories_result = $conn->query("SELECT * FROM categories ORDER BY name");
$conn->close();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$title = trim($_POST['title']);
$content = trim($_POST['content']);
$category_id = intval($_POST['category_id']);
$captcha = trim($_POST['captcha']);
$errors = [];
// 验证输入
if (empty($title)) {
$errors[] = "请输入帖子标题";
}
if (empty($content)) {
$errors[] = "请输入帖子内容";
}
if ($category_id <= 0) {
$errors[] = "请选择帖子板块";
}
// 验证验证码
if (empty($captcha) || $captcha !== $_SESSION['captcha']) {
$errors[] = "验证码错误";
}
// 处理图片上传
$image_path = null;
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
$allowed_types = ['image/jpeg', 'image/png', 'image/gif'];
$file_type = $_FILES['image']['type'];
if (in_array($file_type, $allowed_types)) {
$upload_dir = 'uploads/';
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0755, true);
}
$file_extension = pathinfo($_FILES['image']['name'], PATHINFO_EXTENSION);
$file_name = uniqid() . '.' . $file_extension;
$file_path = $upload_dir . $file_name;
if (move_uploaded_file($_FILES['image']['tmp_name'], $file_path)) {
$image_path = $file_path;
} else {
$errors[] = "图片上传失败";
}
} else {
$errors[] = "只支持JPEG、PNG和GIF格式的图片";
}
}
if (empty($errors)) {
// 保存帖子到数据库
$conn = getDBConnection();
$user_id = $_SESSION['user_id'];
$stmt = $conn->prepare("INSERT INTO posts (title, content, user_id, category_id, image_path) VALUES (?, ?, ?, ?, ?)");
$stmt->bind_param("ssiis", $title, $content, $user_id, $category_id, $image_path);
if ($stmt->execute()) {
$_SESSION['success'] = "帖子发布成功";
header("Location: index.php");
exit();
} else {
$errors[] = "发布失败,请稍后重试";
}
$conn->close();
}
}
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>发布帖子 - 博客论坛</title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<?php include 'header.php'; ?>
<div class="container">
<div class="card">
<h2>发布新帖子</h2>
<?php if (!empty($errors)): ?>
<div class="alert alert-danger">
<?php foreach ($errors as $error): ?>
<p><?php echo $error; ?></p>
<?php endforeach; ?>
</div>
<?php endif; ?>
<form method="POST" action="" enctype="multipart/form-data">
<div class="form-group">
<label for="title">帖子标题</label>
<input type="text" id="title" name="title" class="form-control"
value="<?php echo isset($_POST['title']) ? htmlspecialchars($_POST['title']) : ''; ?>" required>
</div>
<div class="form-group">
<label for="category_id">选择板块</label>
<select id="category_id" name="category_id" class="form-control" required>
<option value="">请选择板块</option>
<?php while ($category = $categories_result->fetch_assoc()): ?>
<option value="<?php echo $category['id']; ?>"
<?php echo (isset($_POST['category_id']) && $_POST['category_id'] == $category['id']) ? 'selected' : ''; ?>>
<?php echo htmlspecialchars($category['name']); ?>
</option>
<?php endwhile; ?>
</select>
</div>
<div class="form-group">
<label for="content">帖子内容</label>
<textarea id="content" name="content" class="form-control" rows="10" required><?php
echo isset($_POST['content']) ? htmlspecialchars($_POST['content']) : '';
?></textarea>
</div>
<div class="form-group">
<label for="image">上传图片 (可选)</label>
<input type="file" id="image" name="image" class="form-control" accept="image/*">
</div>
<div class="form-group">
<label for="captcha">验证码</label>
<div class="captcha-container">
<input type="text" id="captcha" name="captcha" class="form-control" required>
<img src="captcha.php" alt="验证码" class="captcha-image" onclick="this.src='captcha.php?'+Math.random()">
</div>
</div>
<button type="submit" class="btn">发布帖子</button>
</form>
</div>
</div>
</body>
</html>
8. 后台管理
<?php
// admin.php
require_once 'config.php';
// 检查用户是否为管理员
if (!isset($_SESSION['user_id']) || $_SESSION['role'] !== 'admin') {
header("Location: index.php");
exit();
}
$conn = getDBConnection();
// 处理新增板块
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_POST['add_category'])) {
$name = trim($_POST['name']);
$description = trim($_POST['description']);
if (!empty($name)) {
$stmt = $conn->prepare("INSERT INTO categories (name, description) VALUES (?, ?)");
$stmt->bind_param("ss", $name, $description);
$stmt->execute();
}
}
// 处理删除板块
if (isset($_GET['delete_category'])) {
$category_id = intval($_GET['delete_category']);
$stmt = $conn->prepare("DELETE FROM categories WHERE id = ?");
$stmt->bind_param("i", $category_id);
$stmt->execute();
}
// 获取所有板块
$categories_result = $conn->query("SELECT * FROM categories ORDER BY id");
// 获取所有用户
$users_result = $conn->query("SELECT id, username, email, role, created_at FROM users ORDER BY created_at DESC");
// 获取所有帖子
$posts_result = $conn->query("
SELECT p.*, u.username, c.name as category_name
FROM posts p
JOIN users u ON p.user_id = u.id
JOIN categories c ON p.category_id = c.id
ORDER BY p.created_at DESC
");
$conn->close();
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>后台管理 - 博客论坛</title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<?php include 'header.php'; ?>
<div class="container">
<h1>后台管理</h1>
<div class="card">
<h2>板块管理</h2>
<form method="POST" action="" style="margin-bottom: 1.5rem;">
<div class="form-group">
<label for="name">板块名称</label>
<input type="text" id="name" name="name" class="form-control" required>
</div>
<div class="form-group">
<label for="description">板块描述</label>
<textarea id="description" name="description" class="form-control" rows="3"></textarea>
</div>
<button type="submit" name="add_category" class="btn">添加板块</button>
</form>
<table style="width: 100%; border-collapse: collapse;">
<thead>
<tr style="background-color: #f8f9fa;">
<th style="padding: 0.75rem; text-align: left; border-bottom: 1px solid #dee2e6;">ID</th>
<th style="padding: 0.75rem; text-align: left; border-bottom: 1px solid #dee2e6;">名称</th>
<th style="padding: 0.75rem; text-align: left; border-bottom: 1px solid #dee2e6;">描述</th>
<th style="padding: 0.75rem; text-align: left; border-bottom: 1px solid #dee2e6;">操作</th>
</tr>
</thead>
<tbody>
<?php while ($category = $categories_result->fetch_assoc()): ?>
<tr>
<td style="padding: 0.75rem; border-bottom: 1px solid #dee2e6;"><?php echo $category['id']; ?></td>
<td style="padding: 0.75rem; border-bottom: 1px solid #dee2e6;"><?php echo htmlspecialchars($category['name']); ?></td>
<td style="padding: 0.75rem; border-bottom: 1px solid #dee2e6;"><?php echo htmlspecialchars($category['description']); ?></td>
<td style="padding: 0.75rem; border-bottom: 1px solid #dee2e6;">
<a href="?delete_category=<?php echo $category['id']; ?>" class="btn btn-danger"
onclick="return confirm('确定要删除这个板块吗?')">删除</a>
</td>
</tr>
<?php endwhile; ?>
</tbody>
</table>
</div>
<div class="card">
<h2>用户管理</h2>
<table style="width: 100%; border-collapse: collapse;">
<thead>
<tr style="background-color: #f8f9fa;">
<th style="padding: 0.75rem; text-align: left; border-bottom: 1px solid #dee2e6;">ID</th>
<th style="padding: 0.75rem; text-align: left; border-bottom: 1px solid #dee2e6;">用户名</th>
<th style="padding: 0.75rem; text-align: left; border-bottom: 1px solid #dee2e6;">邮箱</th>
<th style="padding: 0.75rem; text-align: left; border-bottom: 1px solid #dee2e6;">角色</th>
<th style="padding: 0.75rem; text-align: left; border-bottom: 1px solid #dee2e6;">注册时间</th>
</tr>
</thead>
<tbody>
<?php while ($user = $users_result->fetch_assoc()): ?>
<tr>
<td style="padding: 0.75rem; border-bottom: 1px solid #dee2e6;"><?php echo $user['id']; ?></td>
<td style="padding: 0.75rem; border-bottom: 1px solid #dee2e6;"><?php echo htmlspecialchars($user['username']); ?></td>
<td style="padding: 0.75rem; border-bottom: 1px solid #dee2e6;"><?php echo htmlspecialchars($user['email']); ?></td>
<td style="padding: 0.75rem; border-bottom: 1px solid #dee2e6;"><?php echo $user['role']; ?></td>
<td style="padding: 0.75rem; border-bottom: 1px solid #dee2e6;"><?php echo $user['created_at']; ?></td>
</tr>
<?php endwhile; ?>
</tbody>
</table>
</div>
<div class="card">
<h2>帖子管理</h2>
<table style="width: 100%; border-collapse: collapse;">
<thead>
<tr style="background-color: #f8f9fa;">
<th style="padding: 0.75rem; text-align: left; border-bottom: 1px solid #dee2e6;">ID</th>
<th style="padding: 0.75rem; text-align: left; border-bottom: 1px solid #dee2e6;">标题</th>
<th style="padding: 0.75rem; text-align: left; border-bottom: 1px solid #dee2e6;">作者</th>
<th style="padding: 0.75rem; text-align: left; border-bottom: 1px solid #dee2e6;">板块</th>
<th style="padding: 0.75rem; text-align: left; border-bottom: 1px solid #dee2e6;">发布时间</th>
</tr>
</thead>
<tbody>
<?php while ($post = $posts_result->fetch_assoc()): ?>
<tr>
<td style="padding: 0.75rem; border-bottom: 1px solid #dee2e6;"><?php echo $post['id']; ?></td>
<td style="padding: 0.75rem; border-bottom: 1px solid #dee2e6;">
<a href="post.php?id=<?php echo $post['id']; ?>"><?php echo htmlspecialchars($post['title']); ?></a>
</td>
<td style="padding: 0.75rem; border-bottom: 1px solid #dee2e6;"><?php echo htmlspecialchars($post['username']); ?></td>
<td style="padding: 0.75rem; border-bottom: 1px solid #dee2e6;"><?php echo htmlspecialchars($post['category_name']); ?></td>
<td style="padding: 0.75rem; border-bottom: 1px solid #dee2e6;"><?php echo $post['created_at']; ?></td>
</tr>
<?php endwhile; ?>
</tbody>
</table>
</div>
</div>
</body>
</html>
9. 伪静态实现
创建.htaccess文件(Apache服务器):
# .htaccess
RewriteEngine On
# 帖子详情页
RewriteRule ^post/([0-9]+)/?$ post.php?id=$1 [L,QSA]
# 板块页面
RewriteRule ^category/([0-9]+)/?$ category.php?id=$1 [L,QSA]
# 用户个人页面
RewriteRule ^user/([a-zA-Z0-9_]+)/?$ profile.php?username=$1 [L,QSA]
10. JavaScript功能增强
// assets/js/main.js
// 表单验证
document.addEventListener('DOMContentLoaded', function() {
// 注册表单验证
const registerForm = document.getElementById('register-form');
if (registerForm) {
registerForm.addEventListener('submit', function(e) {
const password = document.getElementById('password').value;
const confirmPassword = document.getElementById('confirm_password').value;
if (password !== confirmPassword) {
e.preventDefault();
alert('两次输入的密码不一致');
}
});
}
// 刷新验证码
const captchaImages = document.querySelectorAll('.captcha-image');
captchaImages.forEach(img => {
img.addEventListener('click', function() {
this.src = 'captcha.php?' + Math.random();
});
});
// 帖子内容预览
const contentTextarea = document.getElementById('content');
const previewButton = document.getElementById('preview-btn');
const previewContainer = document.getElementById('preview-container');
if (contentTextarea && previewButton && previewContainer) {
previewButton.addEventListener('click', function() {
const content = contentTextarea.value;
previewContainer.innerHTML = content;
previewContainer.style.display = 'block';
});
}
});
部署说明
- 将代码上传到支持PHP和MySQL的Web服务器
- 创建数据库并导入SQL结构
- 修改
config.php中的数据库连接信息 - 确保
uploads目录有写权限 - 启用Apache的
mod_rewrite模块以支持伪静态
功能特点
- 用户注册和登录系统
- 多板块帖子发布
- 图片上传功能
- 验证码验证
- 后台管理面板
- 响应式设计
- 伪静态URL
- 管理员权限控制