PHP+JS+CSS+JSON 单页新闻系统实现方案

PHP+JS+CSS+JSON 单页新闻系统实现方案

下面我将为您实现一个单页新闻系统,包含以下功能:

  • 使用JSON存储新闻数据
  • 支持翻页功能
  • 兼容IIS和宝塔伪静态
  • 每个新闻页面独立URL

目录结构

bash 复制代码
/news-system/
│── /data/
│   └── news.json          # 存储所有新闻数据
│── /news/
│   └── {id}.html          # 独立新闻页面
├── index.php              # 首页
├── api.php                # 新闻API接口
├── style.css              # 样式文件
└── script.js              # 前端交互

1. 新闻数据存储 (news.json)

json 复制代码
{
  "total": 2,
  "per_page": 5,
  "current_page": 1,
  "data": [
    {
      "id": 1,
      "title": "这是第一篇新闻标题",
      "content": "这是第一篇新闻的详细内容...",
      "created_at": "2023-01-01",
      "image": "news1.jpg"
    },
    {
      "id": 2,
      "title": "这是第二篇新闻标题",
      "content": "这是第二篇新闻的详细内容...",
      "created_at": "2023-01-02",
      "image": "news2.jpg"
    }
  ]
}

2. API接口 (api.php)

php 复制代码
<?php
header('Content-Type: application/json');

// 允许跨域请求
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET");

// 获取新闻数据
function getNewsData() {
    $json = file_get_contents('data/news.json');
    return json_decode($json, true);
}

// 保存新闻数据
function saveNewsData($data) {
    file_put_contents('data/news.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
}

// 获取分页新闻
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['action']) && $_GET['action'] === 'get_news') {
    $page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
    $perPage = 5; // 每页显示数量
    
    $newsData = getNewsData();
    $total = count($newsData['data']);
    $totalPages = ceil($total / $perPage);
    
    $offset = ($page - 1) * $perPage;
    $pagedData = array_slice($newsData['data'], $offset, $perPage);
    
    echo json_encode([
        'success' => true,
        'data' => $pagedData,
        'pagination' => [
            'total' => $total,
            'per_page' => $perPage,
            'current_page' => $page,
            'total_pages' => $totalPages
        ]
    ]);
    exit;
}

// 获取单条新闻
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['action']) && $_GET['action'] === 'get_single_news') {
    $id = isset($_GET['id']) ? intval($_GET['id']) : 0;
    
    if ($id <= 0) {
        echo json_encode(['success' => false, 'message' => 'Invalid news ID']);
        exit;
    }
    
    $newsData = getNewsData();
    $newsItem = null;
    
    foreach ($newsData['data'] as $item) {
        if ($item['id'] == $id) {
            $newsItem = $item;
            break;
        }
    }
    
    if ($newsItem) {
        echo json_encode(['success' => true, 'data' => $newsItem]);
    } else {
        echo json_encode(['success' => false, 'message' => 'News not found']);
    }
    exit;
}
?>

3. 首页 (index.php)

php 复制代码
<?php
// 读取新闻数据用于SEO优化
$newsData = json_decode(file_get_contents('data/news.json'), true);
$latestNews = array_slice($newsData['data'], 0, 5);
?>
<!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="style.css">
</head>
<body>
    <div class="container">
        <header>
            <h1>新闻中心</h1>
        </header>
        
        <div class="news-list" id="newsList">
            <!-- 新闻列表将通过JS动态加载 -->
        </div>
        
        <div class="pagination" id="pagination">
            <!-- 分页将通过JS动态加载 -->
        </div>
    </div>
    
    <!-- 为SEO提供基础内容,JS会覆盖这些内容 -->
    <noscript>
        <div class="news-list">
            <?php foreach ($latestNews as $item): ?>
            <div class="news-item">
                <h2><a href="news/<?php echo $item['id']; ?>.html"><?php echo htmlspecialchars($item['title']); ?></a></h2>
                <p class="date"><?php echo $item['created_at']; ?></p>
                <div class="excerpt"><?php echo mb_substr(strip_tags($item['content']), 0, 100); ?>...</div>
            </div>
            <?php endforeach; ?>
        </div>
    </noscript>
    
    <script src="script.js"></script>
</body>
</html>

4. 独立新闻页面 (/news/{id}.html)

php 复制代码
<?php
$id = basename($_SERVER['SCRIPT_NAME'], '.html');
if (!is_numeric($id)) {
    header("HTTP/1.0 404 Not Found");
    exit;
}

// 获取新闻数据
$newsData = json_decode(file_get_contents('../data/news.json'), true);
$newsItem = null;

foreach ($newsData['data'] as $item) {
    if ($item['id'] == $id) {
        $newsItem = $item;
        break;
    }
}

if (!$newsItem) {
    header("HTTP/1.0 404 Not Found");
    exit;
}
?>
<!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 htmlspecialchars($newsItem['title']); ?> - 新闻中心</title>
    <link rel="stylesheet" href="../style.css">
</head>
<body>
    <div class="container">
        <header>
            <h1><a href="../">新闻中心</a></h1>
        </header>
        
        <article class="news-detail">
            <h2><?php echo htmlspecialchars($newsItem['title']); ?></h2>
            <p class="date">发布日期: <?php echo $newsItem['created_at']; ?></p>
            
            <?php if (!empty($newsItem['image'])): ?>
            <div class="news-image">
                <img src="../images/<?php echo htmlspecialchars($newsItem['image']); ?>" alt="<?php echo htmlspecialchars($newsItem['title']); ?>">
            </div>
            <?php endif; ?>
            
            <div class="news-content">
                <?php echo nl2br(htmlspecialchars($newsItem['content'])); ?>
            </div>
        </article>
    </div>
</body>
</html>

5. 样式文件 (style.css)

css 复制代码
/* 基础样式 */
body {
    font-family: 'Microsoft YaHei', Arial, sans-serif;
    line-height: 1.6;
    margin: 0;
    padding: 0;
    color: #333;
    background-color: #f5f5f5;
}

.container {
    max-width: 1200px;
    margin: 0 auto;
    padding: 20px;
}

header {
    background-color: #1e88e5;
    color: white;
    padding: 20px;
    margin-bottom: 30px;
    border-radius: 5px;
}

header h1 {
    margin: 0;
}

header h1 a {
    color: white;
    text-decoration: none;
}

/* 新闻列表样式 */
.news-list {
    display: grid;
    grid-template-columns: 1fr;
    gap: 20px;
}

.news-item {
    background: white;
    padding: 20px;
    border-radius: 5px;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
    transition: transform 0.3s ease;
}

.news-item:hover {
    transform: translateY(-5px);
    box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}

.news-item h2 {
    margin-top: 0;
    margin-bottom: 10px;
}

.news-item h2 a {
    color: #1e88e5;
    text-decoration: none;
}

.news-item .date {
    color: #757575;
    font-size: 0.9em;
    margin-bottom: 10px;
}

.news-item .excerpt {
    color: #616161;
}

/* 新闻详情页样式 */
.news-detail {
    background: white;
    padding: 30px;
    border-radius: 5px;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}

.news-detail h2 {
    margin-top: 0;
    color: #1e88e5;
}

.news-detail .date {
    color: #757575;
    font-size: 0.9em;
    margin-bottom: 20px;
}

.news-image {
    margin-bottom: 20px;
}

.news-image img {
    max-width: 100%;
    height: auto;
    border-radius: 5px;
}

.news-content {
    line-height: 1.8;
}

/* 分页样式 */
.pagination {
    display: flex;
    justify-content: center;
    margin-top: 30px;
    padding: 20px 0;
}

.pagination a, .pagination span {
    display: inline-block;
    padding: 8px 16px;
    margin: 0 5px;
    border: 1px solid #ddd;
    text-decoration: none;
    color: #1e88e5;
    border-radius: 5px;
}

.pagination a:hover {
    background-color: #1e88e5;
    color: white;
    border-color: #1e88e5;
}

.pagination .current {
    background-color: #1e88e5;
    color: white;
    border-color: #1e88e5;
}

.pagination .disabled {
    color: #ddd;
    pointer-events: none;
}

/* 响应式设计 */
@media (max-width: 768px) {
    .container {
        padding: 10px;
    }
    
    header {
        padding: 15px;
    }
    
    .news-item, .news-detail {
        padding: 15px;
    }
}

6. 前端交互 (script.js)

javascript 复制代码
document.addEventListener('DOMContentLoaded', function() {
    // 加载新闻列表
    loadNews(1);
    
    // 如果是新闻详情页,加载新闻内容
    if (window.location.pathname.includes('/news/')) {
        const newsId = window.location.pathname.split('/').pop().replace('.html', '');
        loadSingleNews(newsId);
    }
});

// 加载新闻列表
function loadNews(page) {
    fetch(`api.php?action=get_news&page=${page}`)
        .then(response => response.json())
        .then(data => {
            if (data.success) {
                renderNewsList(data.data);
                renderPagination(data.pagination);
            } else {
                console.error('Failed to load news:', data.message);
            }
        })
        .catch(error => {
            console.error('Error loading news:', error);
        });
}

// 渲染新闻列表
function renderNewsList(newsItems) {
    const newsListContainer = document.getElementById('newsList');
    
    if (!newsListContainer) return;
    
    newsListContainer.innerHTML = '';
    
    newsItems.forEach(item => {
        const excerpt = item.content.replace(/<[^>]*>/g, '').substring(0, 100) + '...';
        
        const newsItem = document.createElement('div');
        newsItem.className = 'news-item';
        newsItem.innerHTML = `
            <h2><a href="news/${item.id}.html">${item.title}</a></h2>
            <p class="date">${item.created_at}</p>
            <div class="excerpt">${excerpt}</div>
        `;
        
        newsListContainer.appendChild(newsItem);
    });
}

// 渲染分页
function renderPagination(pagination) {
    const paginationContainer = document.getElementById('pagination');
    
    if (!paginationContainer) return;
    
    paginationContainer.innerHTML = '';
    
    const { current_page, total_pages } = pagination;
    
    // 上一页按钮
    const prevLink = document.createElement('a');
    prevLink.href = '#';
    prevLink.textContent = '上一页';
    prevLink.addEventListener('click', (e) => {
        e.preventDefault();
        if (current_page > 1) {
            loadNews(current_page - 1);
            window.scrollTo(0, 0);
        }
    });
    
    if (current_page === 1) {
        prevLink.classList.add('disabled');
    }
    
    paginationContainer.appendChild(prevLink);
    
    // 页码按钮
    for (let i = 1; i <= total_pages; i++) {
        const pageLink = document.createElement(i === current_page ? 'span' : 'a');
        
        if (i !== current_page) {
            pageLink.href = '#';
            pageLink.addEventListener('click', (e) => {
                e.preventDefault();
                loadNews(i);
                window.scrollTo(0, 0);
            });
        } else {
            pageLink.className = 'current';
        }
        
        pageLink.textContent = i;
        paginationContainer.appendChild(pageLink);
    }
    
    // 下一页按钮
    const nextLink = document.createElement('a');
    nextLink.href = '#';
    nextLink.textContent = '下一页';
    nextLink.addEventListener('click', (e) => {
        e.preventDefault();
        if (current_page < total_pages) {
            loadNews(current_page + 1);
            window.scrollTo(0, 0);
        }
    });
    
    if (current_page === total_pages) {
        nextLink.classList.add('disabled');
    }
    
    paginationContainer.appendChild(nextLink);
}

// 加载单条新闻 (用于未来可能的AJAX加载)
function loadSingleNews(id) {
    fetch(`api.php?action=get_single_news&id=${id}`)
        .then(response => response.json())
        .then(data => {
            if (data.success) {
                // 可以在这里处理AJAX加载的新闻详情
                console.log('News loaded:', data.data);
            } else {
                console.error('Failed to load news:', data.message);
            }
        })
        .catch(error => {
            console.error('Error loading news:', error);
        });
}

7. 伪静态配置

IIS 伪静态规则 (web.config)

xml 复制代码
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <!-- 首页 -->
                <rule name="Main Page" stopProcessing="true">
                    <match url="^$" />
                    <action type="Rewrite" url="index.php" />
                </rule>
                
                <!-- 新闻详情页 -->
                <rule name="News Detail" stopProcessing="true">
                    <match url="^news/([0-9]+)\.html$" />
                    <action type="Rewrite" url="news/{R:1}.html" />
                </rule>
                
                <!-- API路由 -->
                <rule name="API Routes">
                    <match url="^api/(.*)$" />
                    <action type="Rewrite" url="api.php?action={R:1}" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>

宝塔伪静态规则 (Nginx)

bash 复制代码
location / {
    try_files $uri $uri/ /index.php?$query_string;
}

location /news/ {
    try_files $uri /news/$1.html;
}

location /api {
    rewrite ^/api/(.*)$ /api.php?action=$1 last;
}

使用说明

  1. 将上述文件按照目录结构放置到您的网站目录中
  2. 根据您的服务器环境配置伪静态规则
  3. 修改data/news.json文件添加您的新闻内容
  4. 确保data/目录有写入权限(如果需要后台添加新闻功能)

扩展建议

  1. 后台管理:可以添加一个简单的后台管理系统来管理新闻
  2. 图片上传:为新闻添加图片上传功能
  3. 分类功能:扩展JSON结构支持新闻分类
  4. 缓存机制:添加缓存提高性能

这个实现方案既支持现代浏览器的AJAX加载,又为SEO提供了基本的静态内容,同时通过伪静态规则实现了美观的URL结构。更多数据详情:baijiahao.baidu.com/s?id=183050...

相关推荐
两个人的幸福2 天前
Windows 桌面应用自研 PHP 队列(下):完整代码与六大工程化优化
php
BingoGo5 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
JaguarJack5 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
用户3074596982075 天前
PHP 扩展——从入门到理解
php
鹏仔先生6 天前
拷贝漫画APP下载页PHP程序,后台带免费AI写作
php
云水一下6 天前
从零开始学 PHP 系列(一):PHP 的前世今生与开发环境搭建
开发语言·php
xingpanvip6 天前
星盘接口开发文档:本命盘接口指南
android·开发语言·css·php·lua
酉鬼女又兒6 天前
零基础入门计算机网络运输层:端到端通信核心作用、端口号分类规则、复用分用工作机制及UDP与TCP协议全方位对比详解
网络·网络协议·tcp/ip·计算机网络·考研·udp·php
dog2506 天前
不要再继续优化 TCP
网络协议·tcp/ip·php
Channing Lewis6 天前
PHP 解析 Excel 的那些坑:一次“行号错位”引发的数据丢失
开发语言·php·excel