单页论坛系统实现方案

45 阅读4分钟

单页论坛系统实现方案

下面我将为您提供一个基于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);
        }
    });
}

系统功能说明

  1. 单页论坛功能

    • 使用PHP生成HTML,JS处理交互
    • JSON存储帖子数据
    • 支持分页浏览
  2. 伪静态支持

    • 提供Apache、IIS和宝塔的伪静态规则
    • 美化URL,如 /page/2
  3. 自动采集功能

    • 可配置采集规则(使用正则表达式)
    • 支持标题、内容、作者、时间等字段的采集
    • 可设置定时采集
  4. 后台管理

    • 采集规则管理
    • 系统配置管理
    • 内容管理
  5. 响应式设计

    • 使用CSS3和HTML5实现响应式布局
    • 适配不同设备屏幕

部署说明

  1. 将代码上传到服务器
  2. 设置 data/ 目录可写权限
  3. 根据服务器类型配置伪静态规则
  4. 访问 /admin 进入后台管理界面
  5. 在后台添加采集规则并执行采集

这个系统提供了完整的单页论坛功能,支持伪静态URL,并具备强大的采集能力。您可以根据需要进一步扩展功能或调整界面样式。