用php写的简易论坛,可以注册,可以登录,可以发表帖子,可以签到,可以修改密码。
先创建数据库
CREATE DATABASE forum;
USE forum;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
points INT DEFAULT 0,
is_admin TINYINT DEFAULT 0
);
CREATE TABLE posts (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
title VARCHAR(255) NOT NULL,
content TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE TABLE signins (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
signin_date DATE NOT NULL,
UNIQUE KEY unique_signin (user_id, signin_date),
FOREIGN KEY (user_id) REFERENCES users(id)
);
INSERT INTO users (username, password, points, is_admin)
VALUES ('admin', '$2y$10$z4R7V7qL9b6Y5u3w8bU0E.Fzq7tTkQ1mR1XJdZ3vO9lN7sVjJW6yC', 1000, 1);
我用的XAMPP搭建的 在htdocs里面新建forum 把文件都放到这个里面。
config.php
[PHP] 纯文本查看 复制代码 <?php
session_start();
$host = 'localhost';
$db = 'forum';
$user = 'root';
$pass = '';
$charset = 'utf8mb4';
$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
$pdo = new PDO($dsn, $user, $pass, $options);
} catch (\PDOException $e) {
throw new \PDOException($e->getMessage(), (int)$e->getCode());
}
?>
index.php
[PHP] 纯文本查看 复制代码 <?php
include 'config.php';
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit();
}
// 处理签到
if (isset($_POST['signin'])) {
$today = date('Y-m-d');
$stmt = $pdo->prepare("SELECT * FROM signins WHERE user_id = ? AND signin_date = ?");
$stmt->execute([$_SESSION['user_id'], $today]);
if (!$stmt->fetch()) {
$pdo->beginTransaction();
try {
$pdo->exec("UPDATE users SET points = points + 1 WHERE id = {$_SESSION['user_id']}");
$stmt = $pdo->prepare("INSERT INTO signins (user_id, signin_date) VALUES (?, ?)");
$stmt->execute([$_SESSION['user_id'], $today]);
$pdo->commit();
$_SESSION['message'] = "签到成功!积分+1";
} catch (Exception $e) {
$pdo->rollBack();
$_SESSION['error'] = "签到失败";
}
} else {
$_SESSION['error'] = "今日已签到";
}
header("Location: index.php");
exit();
}
// 获取帖子
$stmt = $pdo->query("SELECT posts.*, users.username FROM posts JOIN users ON posts.user_id = users.id ORDER BY created_at DESC");
$posts = $stmt->fetchAll();
// 获取用户信息
$stmt = $pdo->prepare("SELECT points FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
$user = $stmt->fetch();
$level = min(floor($user['points'] / 100), 10);
$levels = ['青铜会员', '白银会员', '黄金会员', '铂金会员', '钻石会员', '星耀会员', '王者会员', '传奇会员', '不朽会员', '至尊会员', '至高管理员'];
?>
<!DOCTYPE html>
<html>
<head>
<title>提笔只两行论坛</title>
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<nav class="navbar">
<div class="container">
<a href="index.php" class="brand">提笔只两行论坛</a>
<div class="user-info">
<span class="level-badge"><?= $levels[$level] ?></span>
<span><?= htmlspecialchars($_SESSION['username']) ?></span>
<a href="logout.php" class="btn">退出</a>
</div>
</div>
</nav>
<div class="user-info">
<span class="level-badge"><?= $levels[$level] ?></span>
<span><?= htmlspecialchars($_SESSION['username']) ?></span>
<a href="change_password.php" class="btn">修改密码</a>
<a href="logout.php" class="btn">退出</a>
</div>
<div class="container">
<!-- 原有内容保持不变 -->
</div>
<h2>欢迎,<?= htmlspecialchars($_SESSION['username']) ?>!</h2>
<p>积分:<?= $user['points'] ?>(等级:<?= $levels[$level] ?>)</p>
<form method="post">
<button type="submit" name="signin">每日签到</button>
</form>
<?php if(isset($_SESSION['message'])): ?>
<div style="color:green"><?= $_SESSION['message']; unset($_SESSION['message']); ?></div>
<?php endif; ?>
<?php if(isset($_SESSION['error'])): ?>
<div style="color:red"><?= $_SESSION['error']; unset($_SESSION['error']); ?></div>
<?php endif; ?>
<h3>发表新帖</h3>
<form action="post_process.php" method="post">
标题:<input type="text" name="title" required><br>
内容:<textarea name="content" required></textarea><br>
<input type="submit" value="发表">
</form>
<h3>帖子列表</h3>
<?php foreach ($posts as $post): ?>
<div style="border:1px solid #ccc; margin:10px; padding:10px;">
<h4><?= htmlspecialchars($post['title']) ?></h4>
<p><?= nl2br(htmlspecialchars($post['content'])) ?></p>
<small>作者:<?= htmlspecialchars($post['username']) ?> 时间:<?= $post['created_at'] ?></small>
<?php if ($_SESSION['is_admin']): ?>
<form action="delete_post.php" method="post" style="display:inline;">
<input type="hidden" name="post_id" value="<?= $post['id'] ?>">
<button type="submit">删除</button>
</form>
<?php endif; ?>
</div>
<?php endforeach; ?>
<p><a href="logout.php">退出登录</a></p>
</body>
</html>
delete_post.php
[PHP] 纯文本查看 复制代码 <?php
include 'config.php';
if (!isset($_SESSION['user_id']) || !$_SESSION['is_admin']) {
header("Location: index.php");
exit();
}
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$post_id = $_POST['post_id'];
$stmt = $pdo->prepare("DELETE FROM posts WHERE id = ?");
$stmt->execute([$post_id]);
}
header("Location: index.php");
exit();
?>
login.php
[PHP] 纯文本查看 复制代码 <?php include 'config.php'; ?>
<!DOCTYPE html>
<html>
<head>
<title>提笔只两行论坛</title>
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<nav class="navbar">
<div class="container">
<a href="index.php" class="brand">提笔只两行论坛</a>
</div>
</nav>
<div class="container">
<div class="form-container">
<?php if (isset($_SESSION['error'])): ?>
<div class="alert alert-error"><?= $_SESSION['error']; unset($_SESSION['error']); ?></div>
<?php endif; ?>
<h2>用户登录</h2>
<form action="login_process.php" method="post">
<div class="form-group">
<label>用户名</label>
<input type="text" name="username" required>
</div>
<div class="form-group">
<label>密码</label>
<input type="password" name="password" required>
</div>
<button type="submit">立即登录</button>
</form>
<p style="margin-top:1rem">没有账号?<a href="register.php">立即注册</a></p>
</div>
</div>
</body>
</html>
login_process.php
[PHP] 纯文本查看 复制代码 <?php
include 'config.php';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$username = $_POST['username'];
$password = $_POST['password'];
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");
$stmt->execute([$username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
$_SESSION['is_admin'] = $user['is_admin'];
header("Location: index.php");
} else {
$_SESSION['error'] = "用户名或密码错误";
header("Location: login.php");
}
exit();
}
?>
logout.php
[PHP] 纯文本查看 复制代码 <?php
include 'config.php';
session_destroy();
header("Location: login.php");
exit();
?>
post_process.php
[PHP] 纯文本查看 复制代码 <?php
include 'config.php';
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit();
}
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$title = trim($_POST['title']);
$content = trim($_POST['content']);
$user_id = $_SESSION['user_id'];
$stmt = $pdo->prepare("INSERT INTO posts (user_id, title, content) VALUES (?, ?, ?)");
$stmt->execute([$user_id, $title, $content]);
header("Location: index.php");
exit();
}
?>
register.php
[PHP] 纯文本查看 复制代码 <?php include 'config.php'; ?>
<!DOCTYPE html>
<html>
<head>
<title>提笔只两行论坛</title>
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<h2>注册</h2>
<form action="register_process.php" method="post">
用户名:<input type="text" name="username" required><br>
密码:<input type="password" name="password" required><br>
<input type="submit" value="注册">
</form>
<a href="login.php">已有账号?立即登录</a>
</body>
</html>
register_process.php
[PHP] 纯文本查看 复制代码 <?php
include 'config.php';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$username = trim($_POST['username']);
$password = password_hash($_POST['password'], PASSWORD_DEFAULT);
try {
$stmt = $pdo->prepare("INSERT INTO users (username, password) VALUES (?, ?)");
$stmt->execute([$username, $password]);
$_SESSION['message'] = "注册成功!请登录";
header("Location: login.php");
} catch (PDOException $e) {
$_SESSION['error'] = "用户名已存在";
header("Location: register.php");
}
exit();
}
?>
style.css
[CSS] 纯文本查看 复制代码 /* 基础样式 */
:root {
--primary-color: #2c3e50;
--secondary-color: #3498db;
--success-color: #27ae60;
--danger-color: #e74c3c;
--light-bg: #f8f9fa;
--text-color: #2c3e50;
}
body {
font-family: 'Segoe UI', system-ui, sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
background-color: var(--light-bg);
color: var(--text-color);
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
/* 导航栏 */
.navbar {
background-color: white;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
padding: 1rem 0;
margin-bottom: 2rem;
}
.navbar .container {
display: flex;
justify-content: space-between;
align-items: center;
}
.brand {
font-size: 1.5rem;
font-weight: bold;
color: var(--primary-color);
text-decoration: none;
}
.user-info {
display: flex;
align-items: center;
gap: 1rem;
}
/* 表单样式 */
.form-container {
max-width: 500px;
margin: 2rem auto;
padding: 2rem;
background: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.form-group {
margin-bottom: 1.5rem;
}
input[type="text"],
input[type="password"],
textarea {
width: 100%;
padding: 0.8rem;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
button, .btn {
background-color: var(--secondary-color);
color: white;
padding: 0.6rem 1.2rem;
border: none;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
}
button:hover, .btn:hover {
opacity: 0.9;
transform: translateY(-1px);
}
/* 帖子列表 */
.post-list {
display: grid;
gap: 1.5rem;
}
.post-card {
background: white;
padding: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
position: relative;
}
.post-card h4 {
margin-top: 0;
color: var(--primary-color);
}
.post-meta {
font-size: 0.9rem;
color: #7f8c8d;
margin-top: 1rem;
}
/* 消息提示 */
.alert {
padding: 1rem;
border-radius: 4px;
margin-bottom: 1rem;
}
.alert-success {
background-color: #d4edda;
color: #155724;
}
.alert-error {
background-color: #f8d7da;
color: #721c24;
}
/* 等级徽章 */
.level-badge {
background: var(--secondary-color);
color: white;
padding: 0.3rem 0.8rem;
border-radius: 20px;
font-size: 0.9rem;
display: inline-block;
}
/* 响应式设计 */
@media (max-width: 768px) {
.navbar .container {
flex-direction: column;
gap: 1rem;
}
.user-info {
flex-direction: column;
}
}
成果展示
|