Skip to content

实战 5:简单后台管理系统(CMS)

项目简介

本实战项目将开发一个简单的后台管理系统(CMS - Content Management System),用于管理网站内容。通过这个项目,你将学习如何构建一个完整的后台管理系统,包括用户认证、内容管理、权限控制等核心功能。

技术栈

  • PHP 7.4+
  • MySQL 5.7+
  • HTML5 + CSS3 + JavaScript
  • Bootstrap 5(前端框架)

项目结构

cms/
├── admin/
│   ├── dashboard.php        # 后台首页
│   ├── users.php            # 用户管理
│   ├── articles.php         # 文章管理
│   ├── categories.php       # 分类管理
│   ├── settings.php         # 系统设置
│   └── profile.php          # 个人资料
├── includes/
│   ├── config.php           # 配置文件
│   ├── db.php               # 数据库连接
│   ├── functions.php        # 函数库
│   └── header.php           # 头部模板
├── public/
│   ├── css/                 # 样式文件
│   ├── js/                  # JavaScript 文件
│   └── uploads/             # 上传文件目录
├── index.php                # 前台首页
├── login.php                # 登录页面
└── logout.php               # 退出页面

数据库设计

users 表

字段名数据类型描述
idINT(11)用户 ID
usernameVARCHAR(50)用户名
passwordVARCHAR(255)密码(哈希加密)
emailVARCHAR(100)邮箱
roleENUM('admin', 'editor')用户角色
created_atTIMESTAMP创建时间
updated_atTIMESTAMP更新时间

categories 表

字段名数据类型描述
idINT(11)分类 ID
nameVARCHAR(50)分类名称
slugVARCHAR(50)分类别名
created_atTIMESTAMP创建时间
updated_atTIMESTAMP更新时间

articles 表

字段名数据类型描述
idINT(11)文章 ID
titleVARCHAR(255)文章标题
slugVARCHAR(255)文章别名
contentTEXT文章内容
category_idINT(11)分类 ID
user_idINT(11)作者 ID
statusENUM('published', 'draft')文章状态
created_atTIMESTAMP创建时间
updated_atTIMESTAMP更新时间

核心功能实现

1. 用户登录与认证

php
// login.php
<?php
session_start();
include 'includes/config.php';
include 'includes/db.php';

if (isset($_POST['login'])) {
    $username = $_POST['username'];
    $password = $_POST['password'];
    
    $sql = "SELECT * FROM users WHERE username = ?";
    $stmt = $conn->prepare($sql);
    $stmt->bind_param('s', $username);
    $stmt->execute();
    $result = $stmt->get_result();
    
    if ($result->num_rows > 0) {
        $user = $result->fetch_assoc();
        if (password_verify($password, $user['password'])) {
            $_SESSION['user_id'] = $user['id'];
            $_SESSION['username'] = $user['username'];
            $_SESSION['role'] = $user['role'];
            header('Location: admin/dashboard.php');
            exit;
        } else {
            $error = "密码错误";
        }
    } 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>登录 - CMS 管理系统</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <div class="container mt-5">
        <div class="row justify-content-center">
            <div class="col-md-6">
                <div class="card">
                    <div class="card-header">
                        <h3 class="text-center">CMS 管理系统登录</h3>
                    </div>
                    <div class="card-body">
                        <?php if (isset($error)): ?>
                            <div class="alert alert-danger" role="alert">
                                <?php echo $error; ?>
                            </div>
                        <?php endif; ?>
                        <form method="POST">
                            <div class="mb-3">
                                <label for="username" class="form-label">用户名</label>
                                <input type="text" class="form-control" id="username" name="username" required>
                            </div>
                            <div class="mb-3">
                                <label for="password" class="form-label">密码</label>
                                <input type="password" class="form-control" id="password" name="password" required>
                            </div>
                            <button type="submit" name="login" class="btn btn-primary w-100">登录</button>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

2. 后台首页(仪表盘)

php
// admin/dashboard.php
<?php
session_start();
include '../includes/config.php';
include '../includes/db.php';

// 检查登录状态
if (!isset($_SESSION['user_id'])) {
    header('Location: ../login.php');
    exit;
}

// 获取统计数据
$user_count = $conn->query("SELECT COUNT(*) as count FROM users")->fetch_assoc()['count'];
$article_count = $conn->query("SELECT COUNT(*) as count FROM articles")->fetch_assoc()['count'];
$category_count = $conn->query("SELECT COUNT(*) as count FROM categories")->fetch_assoc()['count'];
$published_count = $conn->query("SELECT COUNT(*) as count FROM articles WHERE status = 'published'")->fetch_assoc()['count'];
?>

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>仪表盘 - CMS 管理系统</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css">
</head>
<body>
    <?php include '../includes/header.php'; ?>
    
    <div class="container-fluid">
        <div class="row">
            <!-- 侧边栏 -->
            <nav class="col-md-3 col-lg-2 d-md-block bg-light sidebar">
                <div class="position-sticky pt-3">
                    <ul class="nav flex-column">
                        <li class="nav-item">
                            <a class="nav-link active" href="dashboard.php">
                                <i class="fa fa-dashboard"></i> 仪表盘
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="articles.php">
                                <i class="fa fa-file-text"></i> 文章管理
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="categories.php">
                                <i class="fa fa-list"></i> 分类管理
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="users.php">
                                <i class="fa fa-users"></i> 用户管理
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="settings.php">
                                <i class="fa fa-cog"></i> 系统设置
                            </a>
                        </li>
                    </ul>
                </div>
            </nav>
            
            <!-- 主内容 -->
            <main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
                <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
                    <h1 class="h2">仪表盘</h1>
                    <div class="btn-toolbar mb-2 mb-md-0">
                        <div class="btn-group me-2">
                            <a href="articles.php?action=add" class="btn btn-sm btn-outline-secondary">
                                <i class="fa fa-plus"></i> 新建文章
                            </a>
                        </div>
                    </div>
                </div>
                
                <!-- 统计卡片 -->
                <div class="row">
                    <div class="col-md-3">
                        <div class="card text-white bg-primary mb-3">
                            <div class="card-body">
                                <h5 class="card-title">用户总数</h5>
                                <p class="card-text display-4"><?php echo $user_count; ?></p>
                            </div>
                        </div>
                    </div>
                    <div class="col-md-3">
                        <div class="card text-white bg-success mb-3">
                            <div class="card-body">
                                <h5 class="card-title">文章总数</h5>
                                <p class="card-text display-4"><?php echo $article_count; ?></p>
                            </div>
                        </div>
                    </div>
                    <div class="col-md-3">
                        <div class="card text-white bg-warning mb-3">
                            <div class="card-body">
                                <h5 class="card-title">分类总数</h5>
                                <p class="card-text display-4"><?php echo $category_count; ?></p>
                            </div>
                        </div>
                    </div>
                    <div class="col-md-3">
                        <div class="card text-white bg-info mb-3">
                            <div class="card-body">
                                <h5 class="card-title">已发布文章</h5>
                                <p class="card-text display-4"><?php echo $published_count; ?></p>
                            </div>
                        </div>
                    </div>
                </div>
                
                <!-- 最近文章 -->
                <h2 class="h4 mt-4">最近文章</h2>
                <div class="table-responsive">
                    <table class="table table-striped table-sm">
                        <thead>
                            <tr>
                                <th>标题</th>
                                <th>分类</th>
                                <th>作者</th>
                                <th>状态</th>
                                <th>创建时间</th>
                                <th>操作</th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php
                            $sql = "SELECT a.id, a.title, c.name as category, u.username as author, a.status, a.created_at 
                                    FROM articles a 
                                    LEFT JOIN categories c ON a.category_id = c.id 
                                    LEFT JOIN users u ON a.user_id = u.id 
                                    ORDER BY a.created_at DESC 
                                    LIMIT 5";
                            $result = $conn->query($sql);
                            while ($row = $result->fetch_assoc()):
                            ?>
                            <tr>
                                <td><?php echo $row['title']; ?></td>
                                <td><?php echo $row['category']; ?></td>
                                <td><?php echo $row['author']; ?></td>
                                <td>
                                    <span class="badge <?php echo $row['status'] == 'published' ? 'bg-success' : 'bg-warning'; ?>">
                                        <?php echo $row['status'] == 'published' ? '已发布' : '草稿'; ?>
                                    </span>
                                </td>
                                <td><?php echo $row['created_at']; ?></td>
                                <td>
                                    <a href="articles.php?action=edit&id=<?php echo $row['id']; ?>" class="btn btn-sm btn-primary">编辑</a>
                                    <a href="articles.php?action=delete&id=<?php echo $row['id']; ?>" class="btn btn-sm btn-danger" onclick="return confirm('确定要删除吗?');">删除</a>
                                </td>
                            </tr>
                            <?php endwhile; ?>
                        </tbody>
                    </table>
                </div>
            </main>
        </div>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

3. 文章管理

php
// admin/articles.php
<?php
session_start();
include '../includes/config.php';
include '../includes/db.php';

// 检查登录状态
if (!isset($_SESSION['user_id'])) {
    header('Location: ../login.php');
    exit;
}

// 处理操作
if (isset($_GET['action'])) {
    $action = $_GET['action'];
    $id = isset($_GET['id']) ? $_GET['id'] : '';
    
    switch ($action) {
        case 'add':
            // 添加文章
            if (isset($_POST['submit'])) {
                $title = $_POST['title'];
                $slug = $_POST['slug'];
                $content = $_POST['content'];
                $category_id = $_POST['category_id'];
                $status = $_POST['status'];
                $user_id = $_SESSION['user_id'];
                
                $sql = "INSERT INTO articles (title, slug, content, category_id, user_id, status, created_at, updated_at) 
                        VALUES (?, ?, ?, ?, ?, ?, NOW(), NOW())";
                $stmt = $conn->prepare($sql);
                $stmt->bind_param('sssiii', $title, $slug, $content, $category_id, $user_id, $status);
                
                if ($stmt->execute()) {
                    $_SESSION['message'] = "文章添加成功";
                    header('Location: articles.php');
                    exit;
                } else {
                    $error = "文章添加失败";
                }
            }
            break;
            
        case 'edit':
            // 编辑文章
            if (isset($_POST['submit'])) {
                $title = $_POST['title'];
                $slug = $_POST['slug'];
                $content = $_POST['content'];
                $category_id = $_POST['category_id'];
                $status = $_POST['status'];
                
                $sql = "UPDATE articles SET title = ?, slug = ?, content = ?, category_id = ?, status = ?, updated_at = NOW() WHERE id = ?";
                $stmt = $conn->prepare($sql);
                $stmt->bind_param('sssiii', $title, $slug, $content, $category_id, $status, $id);
                
                if ($stmt->execute()) {
                    $_SESSION['message'] = "文章更新成功";
                    header('Location: articles.php');
                    exit;
                } else {
                    $error = "文章更新失败";
                }
            }
            
            // 获取文章信息
            $sql = "SELECT * FROM articles WHERE id = ?";
            $stmt = $conn->prepare($sql);
            $stmt->bind_param('i', $id);
            $stmt->execute();
            $article = $stmt->get_result()->fetch_assoc();
            break;
            
        case 'delete':
            // 删除文章
            $sql = "DELETE FROM articles WHERE id = ?";
            $stmt = $conn->prepare($sql);
            $stmt->bind_param('i', $id);
            
            if ($stmt->execute()) {
                $_SESSION['message'] = "文章删除成功";
            } else {
                $_SESSION['message'] = "文章删除失败";
            }
            header('Location: articles.php');
            exit;
            break;
    }
}

// 获取所有文章
$sql = "SELECT a.id, a.title, c.name as category, u.username as author, a.status, a.created_at 
        FROM articles a 
        LEFT JOIN categories c ON a.category_id = c.id 
        LEFT JOIN users u ON a.user_id = u.id 
        ORDER BY a.created_at DESC";
$result = $conn->query($sql);

// 获取所有分类
$categories = $conn->query("SELECT * FROM categories");
?>

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文章管理 - CMS 管理系统</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css">
</head>
<body>
    <?php include '../includes/header.php'; ?>
    
    <div class="container-fluid">
        <div class="row">
            <!-- 侧边栏 -->
            <nav class="col-md-3 col-lg-2 d-md-block bg-light sidebar">
                <div class="position-sticky pt-3">
                    <ul class="nav flex-column">
                        <li class="nav-item">
                            <a class="nav-link" href="dashboard.php">
                                <i class="fa fa-dashboard"></i> 仪表盘
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link active" href="articles.php">
                                <i class="fa fa-file-text"></i> 文章管理
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="categories.php">
                                <i class="fa fa-list"></i> 分类管理
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="users.php">
                                <i class="fa fa-users"></i> 用户管理
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="settings.php">
                                <i class="fa fa-cog"></i> 系统设置
                            </a>
                        </li>
                    </ul>
                </div>
            </nav>
            
            <!-- 主内容 -->
            <main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
                <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
                    <h1 class="h2">文章管理</h1>
                    <div class="btn-toolbar mb-2 mb-md-0">
                        <div class="btn-group me-2">
                            <a href="articles.php?action=add" class="btn btn-sm btn-outline-secondary">
                                <i class="fa fa-plus"></i> 新建文章
                            </a>
                        </div>
                    </div>
                </div>
                
                <!-- 消息提示 -->
                <?php if (isset($_SESSION['message'])): ?>
                    <div class="alert alert-success" role="alert">
                        <?php echo $_SESSION['message']; unset($_SESSION['message']); ?>
                    </div>
                <?php endif; ?>
                
                <!-- 错误提示 -->
                <?php if (isset($error)): ?>
                    <div class="alert alert-danger" role="alert">
                        <?php echo $error; ?>
                    </div>
                <?php endif; ?>
                
                <!-- 表单 -->
                <?php if (isset($action) && ($action == 'add' || $action == 'edit')): ?>
                    <div class="card mb-4">
                        <div class="card-header">
                            <h5 class="card-title"><?php echo $action == 'add' ? '新建文章' : '编辑文章'; ?></h5>
                        </div>
                        <div class="card-body">
                            <form method="POST">
                                <div class="mb-3">
                                    <label for="title" class="form-label">标题</label>
                                    <input type="text" class="form-control" id="title" name="title" value="<?php echo isset($article) ? $article['title'] : ''; ?>" required>
                                </div>
                                <div class="mb-3">
                                    <label for="slug" class="form-label">别名</label>
                                    <input type="text" class="form-control" id="slug" name="slug" value="<?php echo isset($article) ? $article['slug'] : ''; ?>" required>
                                </div>
                                <div class="mb-3">
                                    <label for="category_id" class="form-label">分类</label>
                                    <select class="form-select" id="category_id" name="category_id" required>
                                        <?php while ($category = $categories->fetch_assoc()): ?>
                                            <option value="<?php echo $category['id']; ?>" <?php echo isset($article) && $article['category_id'] == $category['id'] ? 'selected' : ''; ?>>
                                                <?php echo $category['name']; ?>
                                            </option>
                                        <?php endwhile; ?>
                                    </select>
                                </div>
                                <div class="mb-3">
                                    <label for="content" class="form-label">内容</label>
                                    <textarea class="form-control" id="content" name="content" rows="10" required><?php echo isset($article) ? $article['content'] : ''; ?></textarea>
                                </div>
                                <div class="mb-3">
                                    <label for="status" class="form-label">状态</label>
                                    <select class="form-select" id="status" name="status" required>
                                        <option value="published" <?php echo isset($article) && $article['status'] == 'published' ? 'selected' : ''; ?>>已发布</option>
                                        <option value="draft" <?php echo isset($article) && $article['status'] == 'draft' ? 'selected' : ''; ?>>草稿</option>
                                    </select>
                                </div>
                                <button type="submit" name="submit" class="btn btn-primary">保存</button>
                                <a href="articles.php" class="btn btn-secondary">取消</a>
                            </form>
                        </div>
                    </div>
                <?php endif; ?>
                
                <!-- 文章列表 -->
                <div class="table-responsive">
                    <table class="table table-striped table-sm">
                        <thead>
                            <tr>
                                <th>ID</th>
                                <th>标题</th>
                                <th>分类</th>
                                <th>作者</th>
                                <th>状态</th>
                                <th>创建时间</th>
                                <th>操作</th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php while ($row = $result->fetch_assoc()): ?>
                            <tr>
                                <td><?php echo $row['id']; ?></td>
                                <td><?php echo $row['title']; ?></td>
                                <td><?php echo $row['category']; ?></td>
                                <td><?php echo $row['author']; ?></td>
                                <td>
                                    <span class="badge <?php echo $row['status'] == 'published' ? 'bg-success' : 'bg-warning'; ?>">
                                        <?php echo $row['status'] == 'published' ? '已发布' : '草稿'; ?>
                                    </span>
                                </td>
                                <td><?php echo $row['created_at']; ?></td>
                                <td>
                                    <a href="articles.php?action=edit&id=<?php echo $row['id']; ?>" class="btn btn-sm btn-primary">编辑</a>
                                    <a href="articles.php?action=delete&id=<?php echo $row['id']; ?>" class="btn btn-sm btn-danger" onclick="return confirm('确定要删除吗?');">删除</a>
                                </td>
                            </tr>
                            <?php endwhile; ?>
                        </tbody>
                    </table>
                </div>
            </main>
        </div>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

4. 分类管理

php
// admin/categories.php
<?php
session_start();
include '../includes/config.php';
include '../includes/db.php';

// 检查登录状态
if (!isset($_SESSION['user_id'])) {
    header('Location: ../login.php');
    exit;
}

// 处理操作
if (isset($_GET['action'])) {
    $action = $_GET['action'];
    $id = isset($_GET['id']) ? $_GET['id'] : '';
    
    switch ($action) {
        case 'add':
            // 添加分类
            if (isset($_POST['submit'])) {
                $name = $_POST['name'];
                $slug = $_POST['slug'];
                
                $sql = "INSERT INTO categories (name, slug, created_at, updated_at) VALUES (?, ?, NOW(), NOW())";
                $stmt = $conn->prepare($sql);
                $stmt->bind_param('ss', $name, $slug);
                
                if ($stmt->execute()) {
                    $_SESSION['message'] = "分类添加成功";
                    header('Location: categories.php');
                    exit;
                } else {
                    $error = "分类添加失败";
                }
            }
            break;
            
        case 'edit':
            // 编辑分类
            if (isset($_POST['submit'])) {
                $name = $_POST['name'];
                $slug = $_POST['slug'];
                
                $sql = "UPDATE categories SET name = ?, slug = ?, updated_at = NOW() WHERE id = ?";
                $stmt = $conn->prepare($sql);
                $stmt->bind_param('ssi', $name, $slug, $id);
                
                if ($stmt->execute()) {
                    $_SESSION['message'] = "分类更新成功";
                    header('Location: categories.php');
                    exit;
                } else {
                    $error = "分类更新失败";
                }
            }
            
            // 获取分类信息
            $sql = "SELECT * FROM categories WHERE id = ?";
            $stmt = $conn->prepare($sql);
            $stmt->bind_param('i', $id);
            $stmt->execute();
            $category = $stmt->get_result()->fetch_assoc();
            break;
            
        case 'delete':
            // 删除分类
            $sql = "DELETE FROM categories WHERE id = ?";
            $stmt = $conn->prepare($sql);
            $stmt->bind_param('i', $id);
            
            if ($stmt->execute()) {
                $_SESSION['message'] = "分类删除成功";
            } else {
                $_SESSION['message'] = "分类删除失败";
            }
            header('Location: categories.php');
            exit;
            break;
    }
}

// 获取所有分类
$sql = "SELECT * FROM categories ORDER BY created_at DESC";
$result = $conn->query($sql);
?>

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>分类管理 - CMS 管理系统</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css">
</head>
<body>
    <?php include '../includes/header.php'; ?>
    
    <div class="container-fluid">
        <div class="row">
            <!-- 侧边栏 -->
            <nav class="col-md-3 col-lg-2 d-md-block bg-light sidebar">
                <div class="position-sticky pt-3">
                    <ul class="nav flex-column">
                        <li class="nav-item">
                            <a class="nav-link" href="dashboard.php">
                                <i class="fa fa-dashboard"></i> 仪表盘
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="articles.php">
                                <i class="fa fa-file-text"></i> 文章管理
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link active" href="categories.php">
                                <i class="fa fa-list"></i> 分类管理
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="users.php">
                                <i class="fa fa-users"></i> 用户管理
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="settings.php">
                                <i class="fa fa-cog"></i> 系统设置
                            </a>
                        </li>
                    </ul>
                </div>
            </nav>
            
            <!-- 主内容 -->
            <main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
                <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
                    <h1 class="h2">分类管理</h1>
                    <div class="btn-toolbar mb-2 mb-md-0">
                        <div class="btn-group me-2">
                            <a href="categories.php?action=add" class="btn btn-sm btn-outline-secondary">
                                <i class="fa fa-plus"></i> 新建分类
                            </a>
                        </div>
                    </div>
                </div>
                
                <!-- 消息提示 -->
                <?php if (isset($_SESSION['message'])): ?>
                    <div class="alert alert-success" role="alert">
                        <?php echo $_SESSION['message']; unset($_SESSION['message']); ?>
                    </div>
                <?php endif; ?>
                
                <!-- 错误提示 -->
                <?php if (isset($error)): ?>
                    <div class="alert alert-danger" role="alert">
                        <?php echo $error; ?>
                    </div>
                <?php endif; ?>
                
                <!-- 表单 -->
                <?php if (isset($action) && ($action == 'add' || $action == 'edit')): ?>
                    <div class="card mb-4">
                        <div class="card-header">
                            <h5 class="card-title"><?php echo $action == 'add' ? '新建分类' : '编辑分类'; ?></h5>
                        </div>
                        <div class="card-body">
                            <form method="POST">
                                <div class="mb-3">
                                    <label for="name" class="form-label">分类名称</label>
                                    <input type="text" class="form-control" id="name" name="name" value="<?php echo isset($category) ? $category['name'] : ''; ?>" required>
                                </div>
                                <div class="mb-3">
                                    <label for="slug" class="form-label">别名</label>
                                    <input type="text" class="form-control" id="slug" name="slug" value="<?php echo isset($category) ? $category['slug'] : ''; ?>" required>
                                </div>
                                <button type="submit" name="submit" class="btn btn-primary">保存</button>
                                <a href="categories.php" class="btn btn-secondary">取消</a>
                            </form>
                        </div>
                    </div>
                <?php endif; ?>
                
                <!-- 分类列表 -->
                <div class="table-responsive">
                    <table class="table table-striped table-sm">
                        <thead>
                            <tr>
                                <th>ID</th>
                                <th>分类名称</th>
                                <th>别名</th>
                                <th>创建时间</th>
                                <th>操作</th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php while ($row = $result->fetch_assoc()): ?>
                            <tr>
                                <td><?php echo $row['id']; ?></td>
                                <td><?php echo $row['name']; ?></td>
                                <td><?php echo $row['slug']; ?></td>
                                <td><?php echo $row['created_at']; ?></td>
                                <td>
                                    <a href="categories.php?action=edit&id=<?php echo $row['id']; ?>" class="btn btn-sm btn-primary">编辑</a>
                                    <a href="categories.php?action=delete&id=<?php echo $row['id']; ?>" class="btn btn-sm btn-danger" onclick="return confirm('确定要删除吗?');">删除</a>
                                </td>
                            </tr>
                            <?php endwhile; ?>
                        </tbody>
                    </table>
                </div>
            </main>
        </div>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

5. 用户管理

php
// admin/users.php
<?php
session_start();
include '../includes/config.php';
include '../includes/db.php';

// 检查登录状态
if (!isset($_SESSION['user_id'])) {
    header('Location: ../login.php');
    exit;
}

// 处理操作
if (isset($_GET['action'])) {
    $action = $_GET['action'];
    $id = isset($_GET['id']) ? $_GET['id'] : '';
    
    switch ($action) {
        case 'add':
            // 添加用户
            if (isset($_POST['submit'])) {
                $username = $_POST['username'];
                $email = $_POST['email'];
                $password = password_hash($_POST['password'], PASSWORD_DEFAULT);
                $role = $_POST['role'];
                
                $sql = "INSERT INTO users (username, email, password, role, created_at, updated_at) VALUES (?, ?, ?, ?, NOW(), NOW())";
                $stmt = $conn->prepare($sql);
                $stmt->bind_param('ssss', $username, $email, $password, $role);
                
                if ($stmt->execute()) {
                    $_SESSION['message'] = "用户添加成功";
                    header('Location: users.php');
                    exit;
                } else {
                    $error = "用户添加失败";
                }
            }
            break;
            
        case 'edit':
            // 编辑用户
            if (isset($_POST['submit'])) {
                $username = $_POST['username'];
                $email = $_POST['email'];
                $role = $_POST['role'];
                $password = $_POST['password'] ? password_hash($_POST['password'], PASSWORD_DEFAULT) : '';
                
                if ($password) {
                    $sql = "UPDATE users SET username = ?, email = ?, password = ?, role = ?, updated_at = NOW() WHERE id = ?";
                    $stmt = $conn->prepare($sql);
                    $stmt->bind_param('ssssi', $username, $email, $password, $role, $id);
                } else {
                    $sql = "UPDATE users SET username = ?, email = ?, role = ?, updated_at = NOW() WHERE id = ?";
                    $stmt = $conn->prepare($sql);
                    $stmt->bind_param('sssi', $username, $email, $role, $id);
                }
                
                if ($stmt->execute()) {
                    $_SESSION['message'] = "用户更新成功";
                    header('Location: users.php');
                    exit;
                } else {
                    $error = "用户更新失败";
                }
            }
            
            // 获取用户信息
            $sql = "SELECT * FROM users WHERE id = ?";
            $stmt = $conn->prepare($sql);
            $stmt->bind_param('i', $id);
            $stmt->execute();
            $user = $stmt->get_result()->fetch_assoc();
            break;
            
        case 'delete':
            // 删除用户
            $sql = "DELETE FROM users WHERE id = ?";
            $stmt = $conn->prepare($sql);
            $stmt->bind_param('i', $id);
            
            if ($stmt->execute()) {
                $_SESSION['message'] = "用户删除成功";
            } else {
                $_SESSION['message'] = "用户删除失败";
            }
            header('Location: users.php');
            exit;
            break;
    }
}

// 获取所有用户
$sql = "SELECT * FROM users ORDER BY created_at DESC";
$result = $conn->query($sql);
?>

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>用户管理 - CMS 管理系统</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css">
</head>
<body>
    <?php include '../includes/header.php'; ?>
    
    <div class="container-fluid">
        <div class="row">
            <!-- 侧边栏 -->
            <nav class="col-md-3 col-lg-2 d-md-block bg-light sidebar">
                <div class="position-sticky pt-3">
                    <ul class="nav flex-column">
                        <li class="nav-item">
                            <a class="nav-link" href="dashboard.php">
                                <i class="fa fa-dashboard"></i> 仪表盘
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="articles.php">
                                <i class="fa fa-file-text"></i> 文章管理
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="categories.php">
                                <i class="fa fa-list"></i> 分类管理
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link active" href="users.php">
                                <i class="fa fa-users"></i> 用户管理
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="settings.php">
                                <i class="fa fa-cog"></i> 系统设置
                            </a>
                        </li>
                    </ul>
                </div>
            </nav>
            
            <!-- 主内容 -->
            <main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
                <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
                    <h1 class="h2">用户管理</h1>
                    <div class="btn-toolbar mb-2 mb-md-0">
                        <div class="btn-group me-2">
                            <a href="users.php?action=add" class="btn btn-sm btn-outline-secondary">
                                <i class="fa fa-plus"></i> 新建用户
                            </a>
                        </div>
                    </div>
                </div>
                
                <!-- 消息提示 -->
                <?php if (isset($_SESSION['message'])): ?>
                    <div class="alert alert-success" role="alert">
                        <?php echo $_SESSION['message']; unset($_SESSION['message']); ?>
                    </div>
                <?php endif; ?>
                
                <!-- 错误提示 -->
                <?php if (isset($error)): ?>
                    <div class="alert alert-danger" role="alert">
                        <?php echo $error; ?>
                    </div>
                <?php endif; ?>
                
                <!-- 表单 -->
                <?php if (isset($action) && ($action == 'add' || $action == 'edit')): ?>
                    <div class="card mb-4">
                        <div class="card-header">
                            <h5 class="card-title"><?php echo $action == 'add' ? '新建用户' : '编辑用户'; ?></h5>
                        </div>
                        <div class="card-body">
                            <form method="POST">
                                <div class="mb-3">
                                    <label for="username" class="form-label">用户名</label>
                                    <input type="text" class="form-control" id="username" name="username" value="<?php echo isset($user) ? $user['username'] : ''; ?>" required>
                                </div>
                                <div class="mb-3">
                                    <label for="email" class="form-label">邮箱</label>
                                    <input type="email" class="form-control" id="email" name="email" value="<?php echo isset($user) ? $user['email'] : ''; ?>" required>
                                </div>
                                <div class="mb-3">
                                    <label for="password" class="form-label">密码 <?php echo $action == 'edit' ? '(留空表示不修改)' : ''; ?></label>
                                    <input type="password" class="form-control" id="password" name="password" <?php echo $action == 'add' ? 'required' : ''; ?>>
                                </div>
                                <div class="mb-3">
                                    <label for="role" class="form-label">角色</label>
                                    <select class="form-select" id="role" name="role" required>
                                        <option value="admin" <?php echo isset($user) && $user['role'] == 'admin' ? 'selected' : ''; ?>>管理员</option>
                                        <option value="editor" <?php echo isset($user) && $user['role'] == 'editor' ? 'selected' : ''; ?>>编辑</option>
                                    </select>
                                </div>
                                <button type="submit" name="submit" class="btn btn-primary">保存</button>
                                <a href="users.php" class="btn btn-secondary">取消</a>
                            </form>
                        </div>
                    </div>
                <?php endif; ?>
                
                <!-- 用户列表 -->
                <div class="table-responsive">
                    <table class="table table-striped table-sm">
                        <thead>
                            <tr>
                                <th>ID</th>
                                <th>用户名</th>
                                <th>邮箱</th>
                                <th>角色</th>
                                <th>创建时间</th>
                                <th>操作</th>
                            </tr>
                        </thead>
                        <tbody>
                            <?php while ($row = $result->fetch_assoc()): ?>
                            <tr>
                                <td><?php echo $row['id']; ?></td>
                                <td><?php echo $row['username']; ?></td>
                                <td><?php echo $row['email']; ?></td>
                                <td>
                                    <span class="badge <?php echo $row['role'] == 'admin' ? 'bg-danger' : 'bg-info'; ?>">
                                        <?php echo $row['role'] == 'admin' ? '管理员' : '编辑'; ?>
                                    </span>
                                </td>
                                <td><?php echo $row['created_at']; ?></td>
                                <td>
                                    <a href="users.php?action=edit&id=<?php echo $row['id']; ?>" class="btn btn-sm btn-primary">编辑</a>
                                    <a href="users.php?action=delete&id=<?php echo $row['id']; ?>" class="btn btn-sm btn-danger" onclick="return confirm('确定要删除吗?');">删除</a>
                                </td>
                            </tr>
                            <?php endwhile; ?>
                        </tbody>
                    </table>
                </div>
            </main>
        </div>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

6. 系统设置

php
// admin/settings.php
<?php
session_start();
include '../includes/config.php';
include '../includes/db.php';

// 检查登录状态
if (!isset($_SESSION['user_id'])) {
    header('Location: ../login.php');
    exit;
}

// 处理设置保存
if (isset($_POST['save'])) {
    $site_name = $_POST['site_name'];
    $site_description = $_POST['site_description'];
    $admin_email = $_POST['admin_email'];
    
    // 这里可以将设置保存到数据库或配置文件
    // 为了简化,我们直接显示保存成功的消息
    $_SESSION['message'] = "设置保存成功";
    header('Location: settings.php');
    exit;
}
?>

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>系统设置 - CMS 管理系统</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css">
</head>
<body>
    <?php include '../includes/header.php'; ?>
    
    <div class="container-fluid">
        <div class="row">
            <!-- 侧边栏 -->
            <nav class="col-md-3 col-lg-2 d-md-block bg-light sidebar">
                <div class="position-sticky pt-3">
                    <ul class="nav flex-column">
                        <li class="nav-item">
                            <a class="nav-link" href="dashboard.php">
                                <i class="fa fa-dashboard"></i> 仪表盘
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="articles.php">
                                <i class="fa fa-file-text"></i> 文章管理
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="categories.php">
                                <i class="fa fa-list"></i> 分类管理
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link" href="users.php">
                                <i class="fa fa-users"></i> 用户管理
                            </a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link active" href="settings.php">
                                <i class="fa fa-cog"></i> 系统设置
                            </a>
                        </li>
                    </ul>
                </div>
            </nav>
            
            <!-- 主内容 -->
            <main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
                <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
                    <h1 class="h2">系统设置</h1>
                </div>
                
                <!-- 消息提示 -->
                <?php if (isset($_SESSION['message'])): ?>
                    <div class="alert alert-success" role="alert">
                        <?php echo $_SESSION['message']; unset($_SESSION['message']); ?>
                    </div>
                <?php endif; ?>
                
                <!-- 设置表单 -->
                <div class="card mb-4">
                    <div class="card-header">
                        <h5 class="card-title">基本设置</h5>
                    </div>
                    <div class="card-body">
                        <form method="POST">
                            <div class="mb-3">
                                <label for="site_name" class="form-label">网站名称</label>
                                <input type="text" class="form-control" id="site_name" name="site_name" value="CMS 管理系统" required>
                            </div>
                            <div class="mb-3">
                                <label for="site_description" class="form-label">网站描述</label>
                                <textarea class="form-control" id="site_description" name="site_description" rows="3">一个简单的内容管理系统</textarea>
                            </div>
                            <div class="mb-3">
                                <label for="admin_email" class="form-label">管理员邮箱</label>
                                <input type="email" class="form-control" id="admin_email" name="admin_email" value="admin@example.com" required>
                            </div>
                            <button type="submit" name="save" class="btn btn-primary">保存设置</button>
                        </form>
                    </div>
                </div>
                
                <!-- 系统信息 -->
                <div class="card">
                    <div class="card-header">
                        <h5 class="card-title">系统信息</h5>
                    </div>
                    <div class="card-body">
                        <table class="table table-striped">
                            <tbody>
                                <tr>
                                    <td>PHP 版本</td>
                                    <td><?php echo phpversion(); ?></td>
                                </tr>
                                <tr>
                                    <td>MySQL 版本</td>
                                    <td><?php echo $conn->server_info; ?></td>
                                </tr>
                                <tr>
                                    <td>服务器操作系统</td>
                                    <td><?php echo PHP_OS; ?></td>
                                </tr>
                                <tr>
                                    <td>服务器软件</td>
                                    <td><?php echo $_SERVER['SERVER_SOFTWARE']; ?></td>
                                </tr>
                                <tr>
                                    <td>系统时间</td>
                                    <td><?php echo date('Y-m-d H:i:s'); ?></td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </div>
            </main>
        </div>
    </div>
    
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

7. 退出登录

php
// logout.php
<?php
session_start();
session_destroy();
header('Location: login.php');
exit;
?>

8. 头部模板

php
// includes/header.php
<nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4">
    <div class="container-fluid">
        <a class="navbar-brand" href="dashboard.php">CMS 管理系统</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarCollapse">
            <ul class="navbar-nav me-auto mb-2 mb-md-0">
                <li class="nav-item">
                    <a class="nav-link" href="../index.php" target="_blank">前台</a>
                </li>
            </ul>
            <div class="d-flex">
                <a class="nav-link text-white me-3" href="profile.php">
                    <?php echo $_SESSION['username']; ?>
                </a>
                <a class="btn btn-outline-light" href="../logout.php">退出</a>
            </div>
        </div>
    </div>
</nav>

项目部署

  1. 创建数据库:使用 phpMyAdmin 或 MySQL 命令行创建数据库,并导入以下 SQL 语句:
sql
-- 创建数据库
CREATE DATABASE IF NOT EXISTS cms;
USE cms;

-- 创建用户表
CREATE TABLE IF NOT EXISTS users (
    id INT(11) AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    email VARCHAR(100) NOT NULL UNIQUE,
    role ENUM('admin', 'editor') DEFAULT 'editor',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- 创建分类表
CREATE TABLE IF NOT EXISTS categories (
    id INT(11) AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL UNIQUE,
    slug VARCHAR(50) NOT NULL UNIQUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- 创建文章表
CREATE TABLE IF NOT EXISTS articles (
    id INT(11) AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    slug VARCHAR(255) NOT NULL UNIQUE,
    content TEXT NOT NULL,
    category_id INT(11) NOT NULL,
    user_id INT(11) NOT NULL,
    status ENUM('published', 'draft') DEFAULT 'draft',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE CASCADE,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

-- 插入默认管理员用户
INSERT INTO users (username, password, email, role) VALUES 
('admin', '$2y$10$e0pXbN1z9z9z9z9z9z9z9e9z9z9z9z9z9z9z9z9z9z9z9z9z9z', 'admin@example.com', 'admin');

-- 插入默认分类
INSERT INTO categories (name, slug) VALUES 
('技术', 'tech'),
('生活', 'life'),
('工作', 'work');
  1. 配置数据库连接:修改 includes/config.php 文件,设置数据库连接信息:
php
// includes/config.php
<?php
// 数据库配置
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_NAME', 'cms');

// 网站配置
define('SITE_URL', 'http://localhost/cms');
?>
  1. 配置数据库连接文件:创建 includes/db.php 文件:
php
// includes/db.php
<?php
include 'config.php';

// 连接数据库
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);

// 检查连接
if ($conn->connect_error) {
    die("连接失败: " . $conn->connect_error);
}

// 设置字符集
$conn->set_charset('utf8mb4');
?>
  1. 创建上传目录:在 public 目录下创建 uploads 目录,并设置可写权限。

  2. 访问系统:在浏览器中访问 http://localhost/cms/login.php,使用默认账号登录:

    • 用户名:admin
    • 密码:admin123

项目扩展

  1. 添加图片上传功能:在文章编辑页面添加图片上传功能,支持图片插入到文章内容中。

  2. 添加评论系统:为文章添加评论功能,支持用户发表评论。

  3. 添加标签功能:为文章添加标签,方便分类和搜索。

  4. 添加 SEO 优化:为文章添加 meta 标签、关键词等 SEO 相关功能。

  5. 添加主题切换:支持后台主题切换,提高用户体验。

  6. 添加权限管理:细化用户权限,实现更精细的权限控制。

总结

通过本实战项目,你已经学习了如何构建一个完整的后台管理系统(CMS),包括:

  • 用户认证与权限管理
  • 文章管理(增删改查)
  • 分类管理
  • 系统设置
  • 数据库设计与操作
  • 前端界面设计

这个项目涵盖了 CMS 系统的核心功能,是一个非常好的 PHP 实战练习。你可以根据自己的需求对项目进行扩展和优化,进一步提升你的 PHP 开发技能。

© 2026 编程马·菜鸟教程 版权所有