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...

相关推荐
ServBay1 天前
垃圾堆里编码?真的不要怪 PHP 不行
后端·php
用户962377954481 天前
CTF 伪协议
php
BingoGo3 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php
JaguarJack3 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php·服务端
BingoGo4 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php
JaguarJack4 天前
OpenSwoole 26.2.0 发布:支持 PHP 8.5、io_uring 后端及协程调试改进
后端·php·服务端
JaguarJack5 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
后端·php·服务端
BingoGo5 天前
推荐 PHP 属性(Attributes) 简洁读取 API 扩展包
php
JaguarJack6 天前
告别 Laravel 缓慢的 Blade!Livewire Blaze 来了,为你的 Laravel 性能提速
后端·php·laravel
郑州光合科技余经理7 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php