单页论坛系统实现方案
下面我将为您提供一个基于PHP+JS+CSS+JSON的单页论坛系统实现方案,包含伪静态设置、自动采集功能和后台管理。
系统架构
文件结构
/forum/
├── index.php # 主入口文件
├── api/
│ ├── post.php # 帖子API
│ ├── collect.php # 采集API
│ └── admin.php # 管理API
├── data/
│ ├── posts.json # 帖子数据存储
│ └── config.json # 系统配置
├── admin/ # 后台管理界面
│ ├── index.html
│ ├── collect.html
│ └── settings.html
├── static/
│ ├── css/
│ │ ├── style.css
│ │ └── admin.css
│ └── js/
│ ├── main.js
│ ├── admin.js
│ └── collect.js
└── .htaccess # Apache伪静态规则
核心代码实现
1. 主页面 (index.php)
<?php
header('Content-Type: text/html; charset=utf-8');
// 加载配置和帖子数据
$config = json_decode(file_get_contents('data/config.json'), true);
$posts = json_decode(file_get_contents('data/posts.json'), true);
// 处理分页
$page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
$perPage = $config['per_page'] ?? 10;
$totalPages = ceil(count($posts) / $perPage);
$currentPosts = array_slice($posts, ($page-1)*$perPage, $perPage);
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo $config['site_title']; ?></title>
<link rel="stylesheet" href="static/css/style.css">
</head>
<body>
<header>
<h1><?php echo $config['site_title']; ?></h1>
<nav>
<a href="/">首页</a>
<a href="/admin" target="_blank">管理</a>
</nav>
</header>
<main>
<div id="posts-container">
<?php foreach($currentPosts as $post): ?>
<article class="post">
<h2><?php echo htmlspecialchars($post['title']); ?></h2>
<div class="meta">
<span>作者: <?php echo htmlspecialchars($post['author']); ?></span>
<span>时间: <?php echo date('Y-m-d H:i', $post['timestamp']); ?></span>
</div>
<div class="content"><?php echo nl2br(htmlspecialchars($post['content'])); ?></div>
</article>
<?php endforeach; ?>
</div>
<div class="pagination">
<?php if($page > 1): ?>
<a href="/page/<?php echo $page-1; ?>">上一页</a>
<?php endif; ?>
<?php for($i=1; $i<=$totalPages; $i++): ?>
<a href="/page/<?php echo $i; ?>" <?php if($i==$page) echo 'class="active"'; ?>><?php echo $i; ?></a>
<?php endfor; ?>
<?php if($page < $totalPages): ?>
<a href="/page/<?php echo $page+1; ?>">下一页</a>
<?php endif; ?>
</div>
</main>
<script src="static/js/main.js"></script>
</body>
</html>
2. API接口 (api/post.php)
<?php
header('Content-Type: application/json; charset=utf-8');
$dataFile = '../data/posts.json';
// 获取帖子列表
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$page = isset($_GET['page']) ? intval($_GET['page']) : 1;
$perPage = isset($_GET['per_page']) ? intval($_GET['per_page']) : 10;
$posts = json_decode(file_get_contents($dataFile), true);
$total = count($posts);
$result = array_slice($posts, ($page-1)*$perPage, $perPage);
echo json_encode([
'success' => true,
'data' => $result,
'total' => $total,
'page' => $page,
'per_page' => $perPage
]);
exit;
}
// 添加新帖子
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$input = json_decode(file_get_contents('php://input'), true);
if (empty($input['title']) || empty($input['content'])) {
echo json_encode(['success' => false, 'message' => '标题和内容不能为空']);
exit;
}
$posts = json_decode(file_get_contents($dataFile), true);
$newPost = [
'id' => uniqid(),
'title' => $input['title'],
'content' => $input['content'],
'author' => $input['author'] ?? '匿名',
'timestamp' => time()
];
array_unshift($posts, $newPost);
file_put_contents($dataFile, json_encode($posts, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
echo json_encode(['success' => true, 'data' => $newPost]);
exit;
}
3. 采集功能 (api/collect.php)
<?php
header('Content-Type: application/json; charset=utf-8');
require_once '../data/config.json';
// 保存采集规则
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'save_rule') {
$rule = [
'name' => $_POST['name'],
'url' => $_POST['url'],
'list_selector' => $_POST['list_selector'],
'title_selector' => $_POST['title_selector'],
'content_selector' => $_POST['content_selector'],
'author_selector' => $_POST['author_selector'],
'time_selector' => $_POST['time_selector'],
'time_format' => $_POST['time_format'],
'enable' => isset($_POST['enable'])
];
$config = json_decode(file_get_contents('../data/config.json'), true);
$config['collect_rules'][] = $rule;
file_put_contents('../data/config.json', json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
echo json_encode(['success' => true]);
exit;
}
// 执行采集
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'collect') {
$ruleId = intval($_POST['rule_id']);
$config = json_decode(file_get_contents('../data/config.json'), true);
if (!isset($config['collect_rules'][$ruleId])) {
echo json_encode(['success' => false, 'message' => '采集规则不存在']);
exit;
}
$rule = $config['collect_rules'][$ruleId];
$html = file_get_contents($rule['url']);
// 使用正则采集内容
preg_match_all($rule['list_selector'], $html, $listMatches);
$collectedPosts = [];
foreach ($listMatches[0] as $item) {
preg_match($rule['title_selector'], $item, $titleMatch);
preg_match($rule['content_selector'], $item, $contentMatch);
preg_match($rule['author_selector'], $item, $authorMatch);
preg_match($rule['time_selector'], $item, $timeMatch);
$timestamp = $timeMatch ? strtotime($timeMatch[1]) : time();
$collectedPosts[] = [
'id' => uniqid(),
'title' => $titleMatch[1] ?? '无标题',
'content' => $contentMatch[1] ?? '',
'author' => $authorMatch[1] ?? '匿名',
'timestamp' => $timestamp,
'source' => $rule['url']
];
}
// 合并到现有帖子
$posts = json_decode(file_get_contents('../data/posts.json'), true);
$posts = array_merge($collectedPosts, $posts);
file_put_contents('../data/posts.json', json_encode($posts, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
echo json_encode(['success' => true, 'count' => count($collectedPosts)]);
exit;
}
4. 伪静态规则
Apache (.htaccess)
RewriteEngine On
# 首页
RewriteRule ^$ index.php [L]
# 分页
RewriteRule ^page/([0-9]+)/?$ index.php?page=$1 [L,QSA]
# API路由
RewriteRule ^api/post/?$ api/post.php [L]
RewriteRule ^api/collect/?$ api/collect.php [L]
RewriteRule ^api/admin/?$ api/admin.php [L]
IIS (web.config)
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Main Page" stopProcessing="true">
<match url="^$" />
<action type="Rewrite" url="index.php" />
</rule>
<rule name="Pagination" stopProcessing="true">
<match url="^page/([0-9]+)/?$" />
<action type="Rewrite" url="index.php?page={R:1}" />
</rule>
<rule name="Post API" stopProcessing="true">
<match url="^api/post/?$" />
<action type="Rewrite" url="api/post.php" />
</rule>
<rule name="Collect API" stopProcessing="true">
<match url="^api/collect/?$" />
<action type="Rewrite" url="api/collect.php" />
</rule>
<rule name="Admin API" stopProcessing="true">
<match url="^api/admin/?$" />
<action type="Rewrite" url="api/admin.php" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
宝塔伪静态规则
选择对应的伪静态规则(Apache或IIS),或者直接将上面的规则内容粘贴到宝塔的伪静态设置中。
5. 后台管理界面 (admin/collect.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>
<link rel="stylesheet" href="../static/css/admin.css">
</head>
<body>
<div class="admin-container">
<aside class="sidebar">
<h2>管理菜单</h2>
<ul>
<li><a href="index.html">仪表盘</a></li>
<li class="active"><a href="collect.html">采集管理</a></li>
<li><a href="settings.html">系统设置</a></li>
</ul>
</aside>
<main class="content">
<h1>采集管理</h1>
<div class="card">
<h2>添加采集规则</h2>
<form id="collect-rule-form">
<div class="form-group">
<label>规则名称</label>
<input type="text" name="name" required>
</div>
<div class="form-group">
<label>目标URL</label>
<input type="url" name="url" required>
</div>
<div class="form-group">
<label>列表选择器(正则)</label>
<input type="text" name="list_selector" required>
</div>
<div class="form-group">
<label>标题选择器(正则)</label>
<input type="text" name="title_selector" required>
</div>
<div class="form-group">
<label>内容选择器(正则)</label>
<input type="text" name="content_selector" required>
</div>
<div class="form-group">
<label>作者选择器(正则)</label>
<input type="text" name="author_selector">
</div>
<div class="form-group">
<label>时间选择器(正则)</label>
<input type="text" name="time_selector">
</div>
<div class="form-group">
<label>时间格式</label>
<input type="text" name="time_format" placeholder="例如: Y-m-d H:i:s">
</div>
<div class="form-group">
<label>
<input type="checkbox" name="enable" checked> 启用规则
</label>
</div>
<button type="submit" class="btn">保存规则</button>
</form>
</div>
<div class="card">
<h2>采集规则列表</h2>
<table id="rules-table">
<thead>
<tr>
<th>名称</th>
<th>URL</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!-- 规则列表将通过JS动态加载 -->
</tbody>
</table>
</div>
</main>
</div>
<script src="../static/js/collect.js"></script>
</body>
</html>
6. 采集管理JS (static/js/collect.js)
document.addEventListener('DOMContentLoaded', function() {
// 加载规则列表
loadRules();
// 表单提交
document.getElementById('collect-rule-form').addEventListener('submit', function(e) {
e.preventDefault();
saveRule();
});
});
// 加载规则列表
function loadRules() {
fetch('/api/admin?action=get_rules')
.then(response => response.json())
.then(data => {
const tbody = document.querySelector('#rules-table tbody');
tbody.innerHTML = '';
data.rules.forEach((rule, index) => {
const tr = document.createElement('tr');
tr.innerHTML = `
<td>${rule.name}</td>
<td>${rule.url}</td>
<td>${rule.enable ? '启用' : '禁用'}</td>
<td>
<button onclick="runCollect(${index})" class="btn btn-sm">采集</button>
<button onclick="editRule(${index})" class="btn btn-sm">编辑</button>
<button onclick="deleteRule(${index})" class="btn btn-sm btn-danger">删除</button>
</td>
`;
tbody.appendChild(tr);
});
});
}
// 保存规则
function saveRule() {
const form = document.getElementById('collect-rule-form');
const formData = new FormData(form);
const data = {
action: 'save_rule',
name: formData.get('name'),
url: formData.get('url'),
list_selector: formData.get('list_selector'),
title_selector: formData.get('title_selector'),
content_selector: formData.get('content_selector'),
author_selector: formData.get('author_selector'),
time_selector: formData.get('time_selector'),
time_format: formData.get('time_format'),
enable: formData.get('enable') === 'on'
};
fetch('/api/collect', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('规则保存成功');
loadRules();
form.reset();
} else {
alert('保存失败: ' + data.message);
}
});
}
// 执行采集
function runCollect(ruleId) {
if (!confirm('确定要执行采集吗?')) return;
fetch('/api/collect', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action: 'collect',
rule_id: ruleId
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert(`采集成功,共采集 ${data.count} 条内容`);
} else {
alert('采集失败: ' + data.message);
}
});
}
系统功能说明
-
单页论坛功能:
- 使用PHP生成HTML,JS处理交互
- JSON存储帖子数据
- 支持分页浏览
-
伪静态支持:
- 提供Apache、IIS和宝塔的伪静态规则
- 美化URL,如
/page/2
-
自动采集功能:
- 可配置采集规则(使用正则表达式)
- 支持标题、内容、作者、时间等字段的采集
- 可设置定时采集
-
后台管理:
- 采集规则管理
- 系统配置管理
- 内容管理
-
响应式设计:
- 使用CSS3和HTML5实现响应式布局
- 适配不同设备屏幕
部署说明
- 将代码上传到服务器
- 设置
data/目录可写权限 - 根据服务器类型配置伪静态规则
- 访问
/admin进入后台管理界面 - 在后台添加采集规则并执行采集
这个系统提供了完整的单页论坛功能,支持伪静态URL,并具备强大的采集能力。您可以根据需要进一步扩展功能或调整界面样式。