个人博客系统,包含用户注册登录、文章发布、图片上传和头像设置等功能。
设计思路
- 使用PHP处理后端逻辑和数据库操作
- 使用MySQL存储用户信息和博客内容
- 使用HTML5/CSS3构建响应式前端界面
- 使用JavaScript处理前端交互和表单验证
- 使用AJAX实现无刷新操作
完整代码实现
1. 数据库设计 (blog.sql)
CREATE DATABASE blog;
USE blog;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
avatar VARCHAR(255) DEFAULT 'default.png',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE posts (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
image 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) ON DELETE CASCADE
);
CREATE TABLE sessions (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
session_id VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
2. 配置文件 (config.php)
<?php
// 数据库配置
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_NAME', 'blog');
// 文件上传配置
define('UPLOAD_PATH', 'uploads/');
define('MAX_FILE_SIZE', 5 * 1024 * 1024); // 5MB
define('ALLOWED_IMAGE_TYPES', ['jpg', 'jpeg', 'png', 'gif']);
// 会话配置
session_start();
// 创建数据库连接
function getDBConnection() {
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
if ($conn->connect_error) {
die("数据库连接失败: " . $conn->connect_error);
}
return $conn;
}
?>
3. 用户注册 (register.php)
<?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 (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = "请输入有效的邮箱地址";
}
if (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()) {
echo json_encode(['success' => true, 'message' => '注册成功,请登录']);
} else {
echo json_encode(['success' => false, 'message' => '注册失败,请稍后重试']);
}
} else {
echo json_encode(['success' => false, 'message' => implode('<br>', $errors)]);
}
$conn->close();
}
?>
4. 用户登录 (login.php)
<?php
require_once 'config.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = trim($_POST['username']);
$password = $_POST['password'];
$conn = getDBConnection();
$stmt = $conn->prepare("SELECT id, username, password FROM users WHERE username = ? OR email = ?");
$stmt->bind_param("ss", $username, $username);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows === 1) {
$user = $result->fetch_assoc();
if (password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
echo json_encode(['success' => true, 'message' => '登录成功']);
} else {
echo json_encode(['success' => false, 'message' => '密码错误']);
}
} else {
echo json_encode(['success' => false, 'message' => '用户不存在']);
}
$conn->close();
}
?>
5. 头像上传 (upload_avatar.php)
<?php
require_once 'config.php';
if (!isset($_SESSION['user_id'])) {
echo json_encode(['success' => false, 'message' => '请先登录']);
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['avatar'])) {
$user_id = $_SESSION['user_id'];
$file = $_FILES['avatar'];
// 检查文件错误
if ($file['error'] !== UPLOAD_ERR_OK) {
echo json_encode(['success' => false, 'message' => '文件上传错误']);
exit;
}
// 检查文件大小
if ($file['size'] > MAX_FILE_SIZE) {
echo json_encode(['success' => false, 'message' => '文件大小不能超过5MB']);
exit;
}
// 检查文件类型
$file_extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($file_extension, ALLOWED_IMAGE_TYPES)) {
echo json_encode(['success' => false, 'message' => '只允许上传JPG、PNG和GIF格式的图片']);
exit;
}
// 生成唯一文件名
$filename = uniqid() . '_' . $user_id . '.' . $file_extension;
$upload_path = UPLOAD_PATH . 'avatars/';
// 确保上传目录存在
if (!is_dir($upload_path)) {
mkdir($upload_path, 0777, true);
}
// 移动文件
if (move_uploaded_file($file['tmp_name'], $upload_path . $filename)) {
// 更新数据库
$conn = getDBConnection();
$stmt = $conn->prepare("UPDATE users SET avatar = ? WHERE id = ?");
$stmt->bind_param("si", $filename, $user_id);
if ($stmt->execute()) {
echo json_encode(['success' => true, 'message' => '头像上传成功', 'filename' => $filename]);
} else {
echo json_encode(['success' => false, 'message' => '头像更新失败']);
}
$conn->close();
} else {
echo json_encode(['success' => false, 'message' => '文件保存失败']);
}
}
?>
6. 发布文章 (create_post.php)
<?php
require_once 'config.php';
if (!isset($_SESSION['user_id'])) {
echo json_encode(['success' => false, 'message' => '请先登录']);
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$user_id = $_SESSION['user_id'];
$title = trim($_POST['title']);
$content = trim($_POST['content']);
$image = null;
$errors = [];
if (empty($title)) {
$errors[] = "标题不能为空";
}
if (empty($content)) {
$errors[] = "内容不能为空";
}
// 处理图片上传
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
$file = $_FILES['image'];
// 检查文件大小
if ($file['size'] > MAX_FILE_SIZE) {
$errors[] = "图片大小不能超过5MB";
}
// 检查文件类型
$file_extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($file_extension, ALLOWED_IMAGE_TYPES)) {
$errors[] = "只允许上传JPG、PNG和GIF格式的图片";
}
if (empty($errors)) {
// 生成唯一文件名
$filename = uniqid() . '_' . $user_id . '.' . $file_extension;
$upload_path = UPLOAD_PATH . 'posts/';
// 确保上传目录存在
if (!is_dir($upload_path)) {
mkdir($upload_path, 0777, true);
}
// 移动文件
if (move_uploaded_file($file['tmp_name'], $upload_path . $filename)) {
$image = $filename;
} else {
$errors[] = "图片上传失败";
}
}
}
if (empty($errors)) {
$conn = getDBConnection();
$stmt = $conn->prepare("INSERT INTO posts (user_id, title, content, image) VALUES (?, ?, ?, ?)");
$stmt->bind_param("isss", $user_id, $title, $content, $image);
if ($stmt->execute()) {
echo json_encode(['success' => true, 'message' => '文章发布成功']);
} else {
echo json_encode(['success' => false, 'message' => '文章发布失败']);
}
$conn->close();
} else {
echo json_encode(['success' => false, 'message' => implode('<br>', $errors)]);
}
}
?>
7. 获取文章列表 (get_posts.php)
<?php
require_once 'config.php';
$conn = getDBConnection();
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
$limit = 10;
$offset = ($page - 1) * $limit;
// 获取文章总数
$total_result = $conn->query("SELECT COUNT(*) as total FROM posts");
$total_row = $total_result->fetch_assoc();
$total_posts = $total_row['total'];
$total_pages = ceil($total_posts / $limit);
// 获取文章列表
$stmt = $conn->prepare("
SELECT p.*, u.username, u.avatar
FROM posts p
JOIN users u ON p.user_id = u.id
ORDER BY p.created_at DESC
LIMIT ? OFFSET ?
");
$stmt->bind_param("ii", $limit, $offset);
$stmt->execute();
$result = $stmt->get_result();
$posts = [];
while ($row = $result->fetch_assoc()) {
$posts[] = $row;
}
echo json_encode([
'success' => true,
'posts' => $posts,
'total_pages' => $total_pages,
'current_page' => $page
]);
$conn->close();
?>
8. 前端界面 (index.html)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>个人博客</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: #f5f5f5;
color: #333;
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
header {
background-color: #2c3e50;
color: white;
padding: 1rem 0;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
font-size: 1.8rem;
font-weight: bold;
}
nav ul {
display: flex;
list-style: none;
}
nav ul li {
margin-left: 20px;
}
nav ul li a {
color: white;
text-decoration: none;
padding: 5px 10px;
border-radius: 3px;
transition: background-color 0.3s;
}
nav ul li a:hover {
background-color: rgba(255,255,255,0.1);
}
.auth-buttons button {
background-color: #3498db;
color: white;
border: none;
padding: 8px 15px;
border-radius: 3px;
cursor: pointer;
margin-left: 10px;
transition: background-color 0.3s;
}
.auth-buttons button:hover {
background-color: #2980b9;
}
.user-info {
display: flex;
align-items: center;
}
.user-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px;
object-fit: cover;
}
.main-content {
display: flex;
margin-top: 30px;
}
.posts-container {
flex: 3;
margin-right: 30px;
}
.sidebar {
flex: 1;
background-color: white;
padding: 20px;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.post {
background-color: white;
padding: 20px;
margin-bottom: 20px;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.post-header {
display: flex;
align-items: center;
margin-bottom: 15px;
}
.post-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px;
object-fit: cover;
}
.post-author {
font-weight: bold;
}
.post-date {
color: #777;
font-size: 0.9rem;
}
.post-title {
font-size: 1.5rem;
margin-bottom: 10px;
color: #2c3e50;
}
.post-content {
margin-bottom: 15px;
}
.post-image {
max-width: 100%;
border-radius: 5px;
margin-bottom: 15px;
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
z-index: 1000;
align-items: center;
justify-content: center;
}
.modal-content {
background-color: white;
padding: 30px;
border-radius: 5px;
width: 90%;
max-width: 500px;
box-shadow: 0 5px 15px rgba(0,0,0,0.3);
}
.modal h2 {
margin-bottom: 20px;
color: #2c3e50;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.form-group input, .form-group textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 3px;
font-size: 1rem;
}
.form-group textarea {
min-height: 150px;
resize: vertical;
}
.btn {
background-color: #3498db;
color: white;
border: none;
padding: 10px 15px;
border-radius: 3px;
cursor: pointer;
font-size: 1rem;
transition: background-color 0.3s;
}
.btn:hover {
background-color: #2980b9;
}
.btn-block {
display: block;
width: 100%;
}
.error {
color: #e74c3c;
margin-top: 5px;
font-size: 0.9rem;
}
.success {
color: #27ae60;
margin-top: 5px;
font-size: 0.9rem;
}
.pagination {
display: flex;
justify-content: center;
margin-top: 30px;
}
.pagination button {
background-color: white;
border: 1px solid #ddd;
padding: 8px 12px;
margin: 0 5px;
cursor: pointer;
border-radius: 3px;
}
.pagination button.active {
background-color: #3498db;
color: white;
border-color: #3498db;
}
.pagination button:hover:not(.active) {
background-color: #f5f5f5;
}
.avatar-preview {
width: 100px;
height: 100px;
border-radius: 50%;
object-fit: cover;
margin-bottom: 10px;
display: none;
}
.image-preview {
max-width: 100%;
max-height: 200px;
margin-bottom: 10px;
display: none;
border-radius: 5px;
}
@media (max-width: 768px) {
.main-content {
flex-direction: column;
}
.posts-container {
margin-right: 0;
margin-bottom: 30px;
}
.header-content {
flex-direction: column;
text-align: center;
}
nav ul {
margin-top: 15px;
justify-content: center;
}
.auth-buttons {
margin-top: 15px;
}
}
</style>
</head>
<body>
<header>
<div class="container">
<div class="header-content">
<div class="logo">我的博客</div>
<nav>
<ul>
<li><a href="#" id="home-link">首页</a></li>
<li><a href="#" id="create-post-link">写文章</a></li>
</ul>
</nav>
<div class="auth-buttons" id="auth-buttons">
<button id="login-btn">登录</button>
<button id="register-btn">注册</button>
</div>
<div class="user-info" id="user-info" style="display: none;">
<img src="" alt="头像" class="user-avatar" id="user-avatar">
<span id="username-display"></span>
<button id="logout-btn" style="margin-left: 15px;">退出</button>
</div>
</div>
</div>
</header>
<div class="container">
<div class="main-content">
<div class="posts-container" id="posts-container">
<!-- 文章列表将通过JavaScript动态加载 -->
</div>
<div class="sidebar">
<h3>关于</h3>
<p>欢迎来到我的个人博客!这里分享我的想法、经验和学习笔记。</p>
<div id="user-actions" style="display: none; margin-top: 20px;">
<button id="change-avatar-btn" class="btn">更改头像</button>
</div>
</div>
</div>
<div class="pagination" id="pagination">
<!-- 分页按钮将通过JavaScript动态生成 -->
</div>
</div>
<!-- 登录模态框 -->
<div class="modal" id="login-modal">
<div class="modal-content">
<h2>登录</h2>
<form id="login-form">
<div class="form-group">
<label for="login-username">用户名或邮箱</label>
<input type="text" id="login-username" name="username" required>
</div>
<div class="form-group">
<label for="login-password">密码</label>
<input type="password" id="login-password" name="password" required>
</div>
<button type="submit" class="btn btn-block">登录</button>
<div id="login-message" class="error"></div>
</form>
</div>
</div>
<!-- 注册模态框 -->
<div class="modal" id="register-modal">
<div class="modal-content">
<h2>注册</h2>
<form id="register-form">
<div class="form-group">
<label for="register-username">用户名</label>
<input type="text" id="register-username" name="username" required>
</div>
<div class="form-group">
<label for="register-email">邮箱</label>
<input type="email" id="register-email" name="email" required>
</div>
<div class="form-group">
<label for="register-password">密码</label>
<input type="password" id="register-password" name="password" required>
</div>
<div class="form-group">
<label for="register-confirm-password">确认密码</label>
<input type="password" id="register-confirm-password" name="confirm_password" required>
</div>
<button type="submit" class="btn btn-block">注册</button>
<div id="register-message" class="error"></div>
</form>
</div>
</div>
<!-- 创建文章模态框 -->
<div class="modal" id="create-post-modal">
<div class="modal-content">
<h2>发布新文章</h2>
<form id="create-post-form" enctype="multipart/form-data">
<div class="form-group">
<label for="post-title">标题</label>
<input type="text" id="post-title" name="title" required>
</div>
<div class="form-group">
<label for="post-content">内容</label>
<textarea id="post-content" name="content" required></textarea>
</div>
<div class="form-group">
<label for="post-image">上传图片 (可选)</label>
<input type="file" id="post-image" name="image" accept="image/*">
<img id="post-image-preview" class="image-preview">
</div>
<button type="submit" class="btn btn-block">发布</button>
<div id="create-post-message" class="error"></div>
</form>
</div>
</div>
<!-- 更改头像模态框 -->
<div class="modal" id="change-avatar-modal">
<div class="modal-content">
<h2>更改头像</h2>
<form id="change-avatar-form" enctype="multipart/form-data">
<div class="form-group">
<label for="avatar">选择头像</label>
<input type="file" id="avatar" name="avatar" accept="image/*" required>
<img id="avatar-preview" class="avatar-preview">
</div>
<button type="submit" class="btn btn-block">上传</button>
<div id="change-avatar-message" class="error"></div>
</form>
</div>
</div>
<script>
// 全局变量
let currentPage = 1;
let totalPages = 1;
let isLoggedIn = false;
// DOM元素
const authButtons = document.getElementById('auth-buttons');
const userInfo = document.getElementById('user-info');
const usernameDisplay = document.getElementById('username-display');
const userAvatar = document.getElementById('user-avatar');
const postsContainer = document.getElementById('posts-container');
const pagination = document.getElementById('pagination');
const userActions = document.getElementById('user-actions');
// 模态框
const loginModal = document.getElementById('login-modal');
const registerModal = document.getElementById('register-modal');
const createPostModal = document.getElementById('create-post-modal');
const changeAvatarModal = document.getElementById('change-avatar-modal');
// 表单
const loginForm = document.getElementById('login-form');
const registerForm = document.getElementById('register-form');
const createPostForm = document.getElementById('create-post-form');
const changeAvatarForm = document.getElementById('change-avatar-form');
// 按钮
const loginBtn = document.getElementById('login-btn');
const registerBtn = document.getElementById('register-btn');
const logoutBtn = document.getElementById('logout-btn');
const createPostLink = document.getElementById('create-post-link');
const homeLink = document.getElementById('home-link');
const changeAvatarBtn = document.getElementById('change-avatar-btn');
// 图片预览
const avatarInput = document.getElementById('avatar');
const avatarPreview = document.getElementById('avatar-preview');
const postImageInput = document.getElementById('post-image');
const postImagePreview = document.getElementById('post-image-preview');
// 初始化
document.addEventListener('DOMContentLoaded', function() {
checkLoginStatus();
loadPosts(1);
// 事件监听
loginBtn.addEventListener('click', () => showModal(loginModal));
registerBtn.addEventListener('click', () => showModal(registerModal));
logoutBtn.addEventListener('click', logout);
createPostLink.addEventListener('click', () => {
if (isLoggedIn) {
showModal(createPostModal);
} else {
showModal(loginModal);
}
});
homeLink.addEventListener('click', () => loadPosts(1));
changeAvatarBtn.addEventListener('click', () => showModal(changeAvatarModal));
// 表单提交
loginForm.addEventListener('submit', handleLogin);
registerForm.addEventListener('submit', handleRegister);
createPostForm.addEventListener('submit', handleCreatePost);
changeAvatarForm.addEventListener('submit', handleChangeAvatar);
// 图片预览
avatarInput.addEventListener('change', function() {
previewImage(this, avatarPreview);
});
postImageInput.addEventListener('change', function() {
previewImage(this, postImagePreview);
});
// 点击模态框外部关闭
window.addEventListener('click', function(event) {
if (event.target.classList.contains('modal')) {
hideAllModals();
}
});
});
// 检查登录状态
function checkLoginStatus() {
// 在实际应用中,这里应该检查session或token
// 这里简化处理,假设用户已登录
// 在实际应用中,你需要通过AJAX请求检查登录状态
// 这里我们假设用户未登录
updateUIForLoginStatus(false);
}
// 更新UI根据登录状态
function updateUIForLoginStatus(loggedIn) {
isLoggedIn = loggedIn;
if (loggedIn) {
authButtons.style.display = 'none';
userInfo.style.display = 'flex';
userActions.style.display = 'block';
// 在实际应用中,这里应该从服务器获取用户信息
usernameDisplay.textContent = '用户';
} else {
authButtons.style.display = 'block';
userInfo.style.display = 'none';
userActions.style.display = 'none';
}
}
// 显示模态框
function showModal(modal) {
hideAllModals();
modal.style.display = 'flex';
}
// 隐藏所有模态框
function hideAllModals() {
loginModal.style.display = 'none';
registerModal.style.display = 'none';
createPostModal.style.display = 'none';
changeAvatarModal.style.display = 'none';
}
// 图片预览
function previewImage(input, previewElement) {
const file = input.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
previewElement.src = e.target.result;
previewElement.style.display = 'block';
}
reader.readAsDataURL(file);
}
}
// 加载文章
function loadPosts(page) {
currentPage = page;
fetch(`get_posts.php?page=${page}`)
.then(response => response.json())
.then(data => {
if (data.success) {
displayPosts(data.posts);
displayPagination(data.total_pages, data.current_page);
} else {
postsContainer.innerHTML = '<p>加载文章失败</p>';
}
})
.catch(error => {
console.error('Error:', error);
postsContainer.innerHTML = '<p>加载文章失败</p>';
});
}
// 显示文章
function displayPosts(posts) {
if (posts.length === 0) {
postsContainer.innerHTML = '<p>暂无文章</p>';
return;
}
let postsHTML = '';
posts.forEach(post => {
postsHTML += `
<div class="post">
<div class="post-header">
<img src="uploads/avatars/${post.avatar}" alt="${post.username}" class="post-avatar">
<div>
<div class="post-author">${post.username}</div>
<div class="post-date">${new Date(post.created_at).toLocaleString()}</div>
</div>
</div>
<h2 class="post-title">${post.title}</h2>
<div class="post-content">${post.content}</div>
${post.image ? `<img src="uploads/posts/${post.image}" alt="文章图片" class="post-image">` : ''}
</div>
`;
});
postsContainer.innerHTML = postsHTML;
}
// 显示分页
function displayPagination(totalPages, currentPage) {
if (totalPages <= 1) {
pagination.innerHTML = '';
return;
}
let paginationHTML = '';
// 上一页按钮
if (currentPage > 1) {
paginationHTML += `<button onclick="loadPosts(${currentPage - 1})">上一页</button>`;
}
// 页码按钮
for (let i = 1; i <= totalPages; i++) {
if (i === currentPage) {
paginationHTML += `<button class="active">${i}</button>`;
} else {
paginationHTML += `<button onclick="loadPosts(${i})">${i}</button>`;
}
}
// 下一页按钮
if (currentPage < totalPages) {
paginationHTML += `<button onclick="loadPosts(${currentPage + 1})">下一页</button>`;
}
pagination.innerHTML = paginationHTML;
}
// 处理登录
function handleLogin(e) {
e.preventDefault();
const formData = new FormData(loginForm);
const messageElement = document.getElementById('login-message');
fetch('login.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
messageElement.textContent = data.message;
messageElement.className = 'success';
updateUIForLoginStatus(true);
setTimeout(() => {
hideAllModals();
loadPosts(1);
}, 1000);
} else {
messageElement.textContent = data.message;
messageElement.className = 'error';
}
})
.catch(error => {
console.error('Error:', error);
messageElement.textContent = '登录失败,请稍后重试';
messageElement.className = 'error';
});
}
// 处理注册
function handleRegister(e) {
e.preventDefault();
const formData = new FormData(registerForm);
const messageElement = document.getElementById('register-message');
fetch('register.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
messageElement.textContent = data.message;
messageElement.className = 'success';
setTimeout(() => {
hideAllModals();
showModal(loginModal);
}, 1000);
} else {
messageElement.textContent = data.message;
messageElement.className = 'error';
}
})
.catch(error => {
console.error('Error:', error);
messageElement.textContent = '注册失败,请稍后重试';
messageElement.className = 'error';
});
}
// 处理创建文章
function handleCreatePost(e) {
e.preventDefault();
const formData = new FormData(createPostForm);
const messageElement = document.getElementById('create-post-message');
fetch('create_post.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
messageElement.textContent = data.message;
messageElement.className = 'success';
createPostForm.reset();
postImagePreview.style.display = 'none';
setTimeout(() => {
hideAllModals();
loadPosts(1);
}, 1000);
} else {
messageElement.textContent = data.message;
messageElement.className = 'error';
}
})
.catch(error => {
console.error('Error:', error);
messageElement.textContent = '发布失败,请稍后重试';
messageElement.className = 'error';
});
}
// 处理更改头像
function handleChangeAvatar(e) {
e.preventDefault();
const formData = new FormData(changeAvatarForm);
const messageElement = document.getElementById('change-avatar-message');
fetch('upload_avatar.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
messageElement.textContent = data.message;
messageElement.className = 'success';
userAvatar.src = 'uploads/avatars/' + data.filename;
setTimeout(() => {
hideAllModals();
}, 1000);
} else {
messageElement.textContent = data.message;
messageElement.className = 'error';
}
})
.catch(error => {
console.error('Error:', error);
messageElement.textContent = '上传失败,请稍后重试';
messageElement.className = 'error';
});
}
// 退出登录
function logout() {
// 在实际应用中,这里应该发送请求到服务器清除session
updateUIForLoginStatus(false);
loadPosts(1);
}
</script>
</body>
</html>
使用说明
- 首先创建数据库并导入SQL文件
- 修改config.php中的数据库配置
- 确保uploads目录及其子目录(avatars, posts)有写权限
- 访问index.html开始使用博客系统
功能特点
- 用户注册和登录
- 发布博客文章
- 上传文章图片
- 设置和更改用户头像
- 响应式设计,适配各种设备
- 分页显示文章列表
这个博客系统包含了所有要求的功能,并且代码结构清晰,易于理解和扩展。