本文目录导读:

我来为您详细介绍如何用PHP实现一个完整的FAQ系统。
系统设计
数据库设计
首先创建数据库表:
CREATE TABLE `categories` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100) NOT NULL, `sort_order` int(11) DEFAULT 0, `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `faqs` ( `id` int(11) NOT NULL AUTO_INCREMENT, `category_id` int(11) NOT NULL, `question` text NOT NULL, `answer` text NOT NULL, `status` tinyint(1) DEFAULT 1, `sort_order` int(11) DEFAULT 0, `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), FOREIGN KEY (`category_id`) REFERENCES `categories`(`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
项目结构
faq-system/
├── config/
│ └── database.php
├── includes/
│ ├── header.php
│ └── footer.php
├── admin/
│ ├── index.php
│ ├── categories.php
│ ├── faqs.php
│ └── login.php
├── api/
│ ├── search.php
│ └── categories.php
├── assets/
│ ├── css/
│ │ └── style.css
│ └── js/
│ └── main.js
├── index.php
└── search.php
核心代码实现
数据库连接配置 (config/database.php)
<?php
class Database {
private $host = "localhost";
private $db_name = "faq_system";
private $username = "root";
private $password = "";
public $conn;
public function getConnection() {
$this->conn = null;
try {
$this->conn = new PDO(
"mysql:host=" . $this->host . ";dbname=" . $this->db_name,
$this->username,
$this->password
);
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $exception) {
echo "Connection error: " . $exception->getMessage();
}
return $this->conn;
}
}
?>
FAQ主页面 (index.php)
<?php
require_once 'config/database.php';
$database = new Database();
$db = $database->getConnection();
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">FAQ 常见问题系统</title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<?php include 'includes/header.php'; ?>
<div class="container">
<!-- 搜索功能 -->
<div class="search-box">
<input type="text" id="searchInput" placeholder="搜索常见问题..." onkeyup="searchFAQ()">
</div>
<!-- FAQ列表 -->
<div class="faq-list">
<?php
// 获取所有分类
$query = "SELECT * FROM categories ORDER BY sort_order ASC";
$stmt = $db->prepare($query);
$stmt->execute();
$categories = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($categories as $category) {
echo "<div class='category'>";
echo "<h2>" . htmlspecialchars($category['name']) . "</h2>";
// 获取该分类下的FAQ
$faqQuery = "SELECT * FROM faqs WHERE category_id = :category_id AND status = 1 ORDER BY sort_order ASC";
$faqStmt = $db->prepare($faqQuery);
$faqStmt->bindParam(':category_id', $category['id']);
$faqStmt->execute();
$faqs = $faqStmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($faqs as $faq) {
echo "<div class='faq-item'>";
echo "<div class='faq-question' onclick='toggleAnswer(this)'>";
echo "<span>" . htmlspecialchars($faq['question']) . "</span>";
echo "<span class='arrow'>▶</span>";
echo "</div>";
echo "<div class='faq-answer' style='display:none'>";
echo nl2br(htmlspecialchars($faq['answer']));
echo "</div>";
echo "</div>";
}
echo "</div>";
}
?>
</div>
</div>
<?php include 'includes/footer.php'; ?>
<script src="assets/js/main.js"></script>
</body>
</html>
AJAX搜索功能 (api/search.php)
<?php
require_once '../config/database.php';
$database = new Database();
$db = $database->getConnection();
$searchTerm = isset($_GET['q']) ? $_GET['q'] : '';
if ($searchTerm) {
// 搜索相关问题
$query = "SELECT f.*, c.name as category_name
FROM faqs f
LEFT JOIN categories c ON f.category_id = c.id
WHERE (f.question LIKE :search1 OR f.answer LIKE :search2)
AND f.status = 1
ORDER BY f.sort_order ASC";
$stmt = $db->prepare($query);
$searchTerm = "%{$searchTerm}%";
$stmt->bindParam(':search1', $searchTerm);
$stmt->bindParam(':search2', $searchTerm);
$stmt->execute();
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
header('Content-Type: application/json');
echo json_encode($results);
}
?>
后台管理 (admin/faqs.php)
<?php
session_start();
require_once '../config/database.php';
// 简单登录检查
if (!isset($_SESSION['admin_logged_in'])) {
header('Location: login.php');
exit();
}
$database = new Database();
$db = $database->getConnection();
// 添加FAQ
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
if ($_POST['action'] === 'add') {
$query = "INSERT INTO faqs (category_id, question, answer, status) VALUES (:category_id, :question, :answer, :status)";
$stmt = $db->prepare($query);
$stmt->bindParam(':category_id', $_POST['category_id']);
$stmt->bindParam(':question', $_POST['question']);
$stmt->bindParam(':answer', $_POST['answer']);
$stmt->bindParam(':status', $_POST['status']);
$stmt->execute();
}
// 更新FAQ
if ($_POST['action'] === 'update') {
$query = "UPDATE faqs SET category_id = :category_id, question = :question, answer = :answer, status = :status WHERE id = :id";
$stmt = $db->prepare($query);
$stmt->bindParam(':id', $_POST['id']);
$stmt->bindParam(':category_id', $_POST['category_id']);
$stmt->bindParam(':question', $_POST['question']);
$stmt->bindParam(':answer', $_POST['answer']);
$stmt->bindParam(':status', $_POST['status']);
$stmt->execute();
}
// 删除FAQ
if ($_POST['action'] === 'delete') {
$query = "DELETE FROM faqs WHERE id = :id";
$stmt = $db->prepare($query);
$stmt->bindParam(':id', $_POST['id']);
$stmt->execute();
}
}
// 获取所有分类
$categoryQuery = "SELECT * FROM categories ORDER BY sort_order ASC";
$categoryStmt = $db->prepare($categoryQuery);
$categoryStmt->execute();
$categories = $categoryStmt->fetchAll(PDO::FETCH_ASSOC);
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">FAQ管理后台</title>
<link rel="stylesheet" href="../assets/css/style.css">
</head>
<body>
<div class="admin-container">
<h1>FAQ管理后台</h1>
<!-- 添加FAQ表单 -->
<div class="add-faq-form">
<h2>添加新FAQ</h2>
<form method="POST">
<input type="hidden" name="action" value="add">
<div class="form-group">
<label>分类:</label>
<select name="category_id" required>
<?php foreach ($categories as $category): ?>
<option value="<?php echo $category['id']; ?>">
<?php echo htmlspecialchars($category['name']); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label>问题:</label>
<input type="text" name="question" required>
</div>
<div class="form-group">
<label>答案:</label>
<textarea name="answer" rows="5" required></textarea>
</div>
<div class="form-group">
<label>状态:</label>
<select name="status">
<option value="1">启用</option>
<option value="0">禁用</option>
</select>
</div>
<button type="submit">添加FAQ</button>
</form>
</div>
<!-- FAQ列表 -->
<div class="faq-table">
<h2>FAQ列表</h2>
<table>
<thead>
<tr>
<th>ID</th>
<th>分类</th>
<th>问题</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php
$query = "SELECT f.*, c.name as category_name
FROM faqs f
LEFT JOIN categories c ON f.category_id = c.id
ORDER BY f.sort_order ASC";
$stmt = $db->prepare($query);
$stmt->execute();
$faqs = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($faqs as $faq): ?>
<tr>
<td><?php echo $faq['id']; ?></td>
<td><?php echo htmlspecialchars($faq['category_name']); ?></td>
<td><?php echo htmlspecialchars($faq['question']); ?></td>
<td><?php echo $faq['status'] ? '启用' : '禁用'; ?></td>
<td>
<button onclick="editFAQ(<?php echo $faq['id']; ?>)">编辑</button>
<form method="POST" style="display:inline">
<input type="hidden" name="action" value="delete">
<input type="hidden" name="id" value="<?php echo $faq['id']; ?>">
<button type="submit" onclick="return confirm('确定删除?')">删除</button>
</form>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</body>
</html>
JavaScript交互 (assets/js/main.js)
// 切换答案显示
function toggleAnswer(element) {
const answer = element.nextElementSibling;
const arrow = element.querySelector('.arrow');
if (answer.style.display === 'none') {
answer.style.display = 'block';
arrow.innerHTML = '▼';
} else {
answer.style.display = 'none';
arrow.innerHTML = '▶';
}
}
// 搜索功能
function searchFAQ() {
const searchTerm = document.getElementById('searchInput').value;
if (searchTerm.length < 2) {
// 如果搜索词太短,显示所有内容
document.querySelectorAll('.faq-item').forEach(item => {
item.style.display = 'block';
});
return;
}
fetch(`api/search.php?q=${encodeURIComponent(searchTerm)}`)
.then(response => response.json())
.then(data => {
// 隐藏所有FAQ项
document.querySelectorAll('.faq-item').forEach(item => {
item.style.display = 'none';
});
// 显示匹配的结果
data.forEach(faq => {
const items = document.querySelectorAll('.faq-item');
// 这里需要根据返回的数据显示匹配的FAQ
});
})
.catch(error => console.error('Error:', error));
}
// 编辑FAQ功能
function editFAQ(id) {
// 获取FAQ数据并显示编辑表单
fetch(`api/get_faq.php?id=${id}`)
.then(response => response.json())
.then(data => {
// 填充编辑表单
document.getElementById('editForm').style.display = 'block';
document.getElementById('edit_id').value = data.id;
document.getElementById('edit_question').value = data.question;
document.getElementById('edit_answer').value = data.answer;
});
}
CSS样式 (assets/css/style.css)
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
background: #f5f5f5;
color: #333;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.search-box {
margin: 20px 0;
text-align: center;
}
.search-box input {
width: 60%;
padding: 12px 20px;
border: 2px solid #ddd;
border-radius: 25px;
font-size: 16px;
transition: border-color 0.3s;
}
.search-box input:focus {
outline: none;
border-color: #007bff;
}
.category {
background: white;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.category h2 {
color: #007bff;
border-bottom: 2px solid #007bff;
padding-bottom: 10px;
margin-bottom: 15px;
}
.faq-item {
border: 1px solid #eee;
border-radius: 5px;
margin-bottom: 10px;
overflow: hidden;
}
.faq-question {
background: #f8f9fa;
padding: 15px;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
transition: background 0.3s;
}
.faq-question:hover {
background: #e9ecef;
}
.faq-question .arrow {
transition: transform 0.3s;
font-size: 12px;
}
.faq-answer {
padding: 15px;
line-height: 1.8;
background: white;
border-top: 1px solid #eee;
}
.admin-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.form-group input,
.form-group textarea,
.form-group select {
width: 100%;
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
background: #007bff;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #0056b3;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
th, td {
border: 1px solid #ddd;
padding: 12px;
text-align: left;
}
th {
background: #f8f9fa;
}
tr:hover {
background: #f5f5f5;
}
功能特点
- 分类管理:FAQ按分类组织,便于用户查找
- 搜索功能:实时搜索,用户快速找到答案
- 折叠展开:点击问题展开/关闭答案
- 后台管理:完整的CRUD操作
- 响应式设计:适配移动端和桌面端
- 状态管理:启用/禁用FAQ
安全建议
<?php
// SQL注入防护 - 使用预处理语句
$stmt = $db->prepare("SELECT * FROM faqs WHERE id = :id");
$stmt->bindParam(':id', $id, PDO::PARAM_INT);
$stmt->execute();
// XSS防护 - 输出过滤
echo htmlspecialchars($data, ENT_QUOTES, 'UTF-8');
// CSRF防护
session_start();
$token = bin2hex(random_bytes(32));
$_SESSION['csrf_token'] = $token;
?>
性能优化建议
- 缓存实现:使用memcached或Redis缓存热门FAQ
- 数据库索引:为搜索字段添加索引
- 懒加载:按需加载FAQ内容
- CDN加速:使用CDN托管静态资源
部署步骤
- 创建数据库并导入SQL文件
- 修改config/database.php中的数据库配置
- 设置目录权限(确保uploads目录可写)
- 配置Web服务器(Apache/Nginx)
- 访问后台地址/admin/login.php进行管理
这个FAQ系统提供了完整的解决方案,您可以根据实际需求进行扩展和修改。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播