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;
}
使用说明
- 将上述文件按照目录结构放置到您的网站目录中
- 根据您的服务器环境配置伪静态规则
- 修改
data/news.json
文件添加您的新闻内容 - 确保
data/
目录有写入权限(如果需要后台添加新闻功能)
扩展建议
- 后台管理:可以添加一个简单的后台管理系统来管理新闻
- 图片上传:为新闻添加图片上传功能
- 分类功能:扩展JSON结构支持新闻分类
- 缓存机制:添加缓存提高性能
这个实现方案既支持现代浏览器的AJAX加载,又为SEO提供了基本的静态内容,同时通过伪静态规则实现了美观的URL结构。更多数据详情:baijiahao.baidu.com/s?id=183050...