增加文章详情以及相关策略

This commit is contained in:
云泽网 2025-05-10 00:46:45 +08:00
parent d5da3a6ea8
commit 426e583fc9
13 changed files with 7846 additions and 4132 deletions

View File

@ -0,0 +1,232 @@
<?php
/**
* 文章控制器
*/
namespace app\index\controller;
use app\index\controller\Base;
use think\facade\Db;
use think\facade\View;
use think\facade\Request;
class Article extends Base
{
// 文章列表页
public function list()
{
// 获取分类ID
$cateId = Request::param('cate/d', 0);
// 构建查询条件
$where = [
['delete_time', '=', null],
['status', '=', 1]
];
if ($cateId > 0) {
$where[] = ['cate', '=', $cateId];
}
// 获取文章列表
$articles = Db::table('yz_article')
->where($where)
->order('id DESC')
->paginate([
'list_rows' => 10,
'query' => Request::instance()->param()
]);
// 获取分类信息
$category = null;
if ($cateId > 0) {
$category = Db::table('yz_article_category')
->where('id', $cateId)
->where('delete_time', null)
->where('status', 3)
->find();
}
// 获取所有分类
$categories = Db::table('yz_article_category')
->where('delete_time', null)
->where('status', 3)
->select()
->toArray();
// 将变量传递给视图
View::assign([
'articles' => $articles,
'category' => $category,
'categories' => $categories
]);
return view('list');
}
// 文章详情页
public function detail()
{
$id = Request::param('id/d', 0);
$article = Db::table('yz_article')->where('id', $id)->find();
if (!$article) {
return json(['code' => 0, 'msg' => '文章不存在或已被删除']);
}
// 获取分类名称
$cateName = Db::table('yz_article_category')
->where('id', $article['cate'])
->value('name');
// 获取上一篇和下一篇文章
$prevArticle = Db::table('yz_article')
->where('id', '<', $id)
->where('delete_time', null)
->where('status', '<>', 3)
->order('id DESC')
->find();
$nextArticle = Db::table('yz_article')
->where('id', '>', $id)
->where('delete_time', null)
->where('status', '<>', 3)
->order('id ASC')
->find();
// 获取相关文章(同分类的其他文章)
$relatedArticles = Db::table('yz_article')
->where('cate', $article['cate'])
->where('id', '<>', $id)
->where('delete_time', null)
->where('status', '<>', 3)
->order('id DESC')
->limit(3)
->select()
->toArray();
// 获取评论列表
// $comments = Db::table('yz_article_comment')
// ->where('article_id', $id)
// ->where('delete_time', null)
// ->order('id DESC')
// ->select()
// ->toArray();
// 更新浏览量使用Cookie和IP双重验证
$ip = Request::ip();
$cookieKey = 'article_view_' . $id;
$viewCookie = Request::cookie($cookieKey);
View::assign([
'article' => $article,
'cateName' => $cateName,
'prevArticle' => $prevArticle,
'nextArticle' => $nextArticle,
'relatedArticles' => $relatedArticles,
// 'comments' => $comments
]);
return View::fetch();
}
// 文章点赞
public function like()
{
if (!Request::isAjax()) {
return json(['code' => 0, 'msg' => '非法请求']);
}
$id = Request::param('id/d', 0);
// 更新点赞数
$result = Db::table('yz_article')
->where('id', $id)
->where('delete_time', null)
->inc('likes', 1)
->update();
if ($result) {
return json(['code' => 1, 'msg' => '点赞成功']);
} else {
return json(['code' => 0, 'msg' => '点赞失败']);
}
}
// 提交评论
public function comment()
{
if (!Request::isAjax() || !Request::isPost()) {
return json(['code' => 0, 'msg' => '非法请求']);
}
$articleId = Request::param('article_id/d', 0);
$content = Request::param('content/s', '');
$parentId = Request::param('parent_id/d', 0);
if (empty($content)) {
return json(['code' => 0, 'msg' => '评论内容不能为空']);
}
// 检查文章是否存在
$article = Db::table('yz_article')
->where('id', $articleId)
->where('delete_time', null)
->where('status', 3)
->find();
if (!$article) {
return json(['code' => 0, 'msg' => '文章不存在或已被删除']);
}
// 添加评论
// $data = [
// 'article_id' => $articleId,
// 'content' => $content,
// 'parent_id' => $parentId,
// 'user_id' => $this->getUserId(),
// 'user_name' => $this->getUserName(),
// 'status' => 1,
// 'create_time' => time()
// ];
// $result = Db::table('yz_article_comment')->insert($data);
// if ($result) {
// return json(['code' => 1, 'msg' => '评论成功']);
// } else {
// return json(['code' => 0, 'msg' => '评论失败']);
// }
}
// 获取当前用户ID示例方法实际应根据您的用户系统实现
private function getUserId()
{
// 这里应该返回当前登录用户的ID
return 1; // 示例返回值
}
// 获取当前用户名(示例方法,实际应根据您的用户系统实现)
private function getUserName()
{
// 这里应该返回当前登录用户的用户名
return '游客'; // 示例返回值
}
// 获取访问统计
public function viewStats()
{
$id = Request::param('id/d', 0);
// 获取总访问量
$totalViews = Db::table('yz_article')
->where('id', $id)
->value('views');
return json([
'code' => 1,
'data' => [
'total' => $totalViews
]
]);
}
}

View File

@ -0,0 +1,533 @@
{include file="component/head" /}
{include file="component/header-simple" /}
<div class="main">
<div class="location">
<div class="container">
<div class="location-item">
<a href="{:url('index/index/index')}">首页</a>
<span>></span>
<a href="{:url('index/article/index')}">技术文章</a>
</div>
</div>
</div>
<div class="article-detail-container">
<div class="article-header">
<h1 class="article-title">{$article.title}</h1>
<div class="article-meta">
<span class="article-author"><i class="fa fa-user"></i> {$article.author}</span>
<span class="article-date"><i class="fa fa-calendar"></i> {$article.create_time|date='Y-m-d H:i'}</span>
<span class="article-category"><i class="fa fa-folder"></i> {$cateName}</span>
<span class="article-views"><i class="fa fa-eye"></i> {$article.views} 阅读</span>
</div>
</div>
<div class="article-content">
{$article.content|raw}
</div>
<div class="article-tags">
<span class="tag-label">标签:</span>
{if !empty($article.tags)}
{foreach $article.tags as $tag}
<span class="tag-item">{$tag}</span>
{/foreach}
{else}
<span class="no-tags">暂无标签</span>
{/if}
</div>
<div class="article-actions">
<div class="action-item like-btn">
<i class="fa fa-thumbs-up"></i>
<span class="action-text">点赞</span>
<span class="action-count">{$article.likes|default=0}</span>
</div>
<div class="action-item share-btn">
<i class="fa fa-share-alt"></i>
<span class="action-text">分享</span>
</div>
</div>
<div class="article-navigation">
<div class="prev-article">
{if !empty($prevArticle)}
<a href="{:url('index/article/index', ['id' => $prevArticle.id])}">
<i class="fa fa-arrow-left"></i> 上一篇:{$prevArticle.title}
</a>
{else}
<span class="disabled"><i class="fa fa-arrow-left"></i> 没有上一篇了</span>
{/if}
</div>
<div class="next-article">
{if !empty($nextArticle)}
<a href="{:url('index/article/index', ['id' => $nextArticle.id])}">
下一篇:{$nextArticle.title} <i class="fa fa-arrow-right"></i>
</a>
{else}
<span class="disabled">没有下一篇了 <i class="fa fa-arrow-right"></i></span>
{/if}
</div>
</div>
<div class="related-articles">
<h3 class="related-title">相关推荐</h3>
<div class="related-list">
{if !empty($relatedArticles)}
{foreach $relatedArticles as $related}
<div class="related-item">
<a href="{:url('index/article/index', ['id' => $related.id])}">
<div class="related-image">
<img src="{$related.image}" alt="{$related.title}">
</div>
<div class="related-info">
<div class="related-item-title">{$related.title}</div>
<div class="related-item-desc">{$related.desc}</div>
</div>
</a>
</div>
{/foreach}
{else}
<div class="no-related">暂无相关文章</div>
{/if}
</div>
</div>
<!-- <div class="article-comments">
<h3 class="comments-title">评论区</h3>
<div class="comment-form">
<textarea placeholder="请输入您的评论..." class="comment-textarea"></textarea>
<button class="comment-submit">发表评论</button>
</div>
<div class="comment-list">
{if !empty($comments)}
{foreach $comments as $comment}
<div class="comment-item">
<div class="comment-avatar">
<img src="{$comment.avatar|default='/static/images/default-avatar.png'}" alt="用户头像">
</div>
<div class="comment-content">
<div class="comment-user">{$comment.username}</div>
<div class="comment-text">{$comment.content}</div>
<div class="comment-footer">
<span class="comment-time">{$comment.create_time|date='Y-m-d H:i'}</span>
<span class="comment-reply">回复</span>
</div>
</div>
</div>
{/foreach}
{else}
<div class="no-comments">暂无评论,快来抢沙发吧!</div>
{/if}
</div>
</div> -->
</div>
</div>
<!-- 返回顶部按钮 -->
<div class="go-to-top" id="goToTop">
<i class="layui-icon-up"></i>
</div>
{include file="component/footer" /}
<style>
.location {
max-width: 1000px;
margin: 30px auto;
}
.article-detail-container {
max-width: 1000px;
margin: 30px auto;
padding: 50px;
background: #fff;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
border-radius: 8px;
}
.article-header {
margin-bottom: 30px;
border-bottom: 1px solid #eee;
padding-bottom: 20px;
}
.article-title {
font-size: 28px;
font-weight: 700;
color: #333;
margin-bottom: 15px;
line-height: 1.4;
}
.article-meta {
display: flex;
flex-wrap: wrap;
gap: 20px;
color: #666;
font-size: 14px;
}
.article-meta span {
display: flex;
align-items: center;
}
.article-meta i {
margin-right: 5px;
}
.article-content {
line-height: 1.8;
color: #333;
font-size: 16px;
margin-bottom: 30px;
}
.article-content img {
max-width: 100%;
height: auto;
margin: 15px 0;
border-radius: 4px;
}
.article-tags {
margin: 20px 0;
display: flex;
align-items: center;
flex-wrap: wrap;
}
.tag-label {
font-weight: bold;
margin-right: 10px;
}
.tag-item {
background: #f2f2f2;
padding: 4px 10px;
border-radius: 15px;
font-size: 12px;
margin-right: 8px;
color: #666;
}
.article-actions {
display: flex;
justify-content: center;
gap: 40px;
margin: 30px 0;
padding: 20px 0;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
}
.action-item {
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
}
.action-item i {
font-size: 24px;
color: #666;
margin-bottom: 5px;
}
.action-text {
font-size: 14px;
color: #666;
}
.action-count {
font-size: 12px;
color: #999;
margin-top: 3px;
}
.article-navigation {
display: flex;
justify-content: space-between;
margin: 30px 0;
}
.prev-article,
.next-article {
max-width: 45%;
}
.prev-article a,
.next-article a {
color: #333;
text-decoration: none;
}
.prev-article a:hover,
.next-article a:hover {
color: #f57005;
}
.disabled {
color: #999;
}
.related-articles {
margin: 40px 0;
}
.related-title {
font-size: 20px;
font-weight: 600;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.related-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
.related-item {
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.3s;
}
.related-item:hover {
transform: translateY(-5px);
}
.related-item a {
text-decoration: none;
color: inherit;
}
.related-image img {
width: 100%;
height: 150px;
object-fit: cover;
}
.related-info {
padding: 10px;
}
.related-item-title {
font-size: 16px;
font-weight: 600;
margin-bottom: 5px;
color: #333;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.related-item-desc {
font-size: 12px;
color: #666;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.article-comments {
margin-top: 40px;
}
.comments-title {
font-size: 20px;
font-weight: 600;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.comment-form {
margin-bottom: 30px;
}
.comment-textarea {
width: 100%;
height: 100px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
resize: none;
margin-bottom: 10px;
}
.comment-submit {
background: #f57005;
color: white;
border: none;
padding: 8px 20px;
border-radius: 4px;
cursor: pointer;
float: right;
}
.comment-item {
display: flex;
margin-bottom: 20px;
padding-bottom: 20px;
border-bottom: 1px solid #eee;
}
.comment-avatar img {
width: 50px;
height: 50px;
border-radius: 50%;
margin-right: 15px;
}
.comment-content {
flex: 1;
}
.comment-user {
font-weight: 600;
margin-bottom: 5px;
}
.comment-text {
line-height: 1.6;
margin-bottom: 10px;
}
.comment-footer {
display: flex;
justify-content: space-between;
color: #999;
font-size: 12px;
}
.comment-reply {
cursor: pointer;
color: #f57005;
}
.no-comments,
.no-related,
.no-tags {
color: #999;
text-align: center;
padding: 20px;
}
@media (max-width: 768px) {
.article-title {
font-size: 24px;
}
.related-list {
grid-template-columns: repeat(1, 1fr);
}
.article-meta {
gap: 10px;
}
}
/* 返回顶部按钮样式 */
.go-to-top {
position: fixed;
right: 30px;
bottom: 30px;
width: 40px;
height: 40px;
background: #f57005;
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
z-index: 1000;
}
.go-to-top.show {
opacity: 1;
visibility: visible;
}
.go-to-top:hover {
background: #e66600;
transform: translateY(-3px);
}
.go-to-top i {
font-size: 18px;
}
@media (max-width: 768px) {
.go-to-top {
right: 20px;
bottom: 20px;
width: 36px;
height: 36px;
}
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function () {
// 点赞功能
const likeBtn = document.querySelector('.like-btn');
if (likeBtn) {
likeBtn.addEventListener('click', function () {
const articleId = '{$article.id}';
fetch('/index/article/like?id=' + articleId, {
method: 'POST'
})
.then(response => response.json())
.then(data => {
if (data.code === 1) {
const countElement = this.querySelector('.action-count');
let count = parseInt(countElement.textContent);
countElement.textContent = count + 1;
this.classList.add('liked');
this.style.color = '#f57005';
} else {
alert('点赞失败:' + data.msg);
}
})
.catch(error => {
console.error('点赞请求失败:', error);
});
});
}
// 返回顶部功能
const goToTop = document.getElementById('goToTop');
// 监听滚动事件
window.addEventListener('scroll', function() {
if (window.pageYOffset > 300) {
goToTop.classList.add('show');
} else {
goToTop.classList.remove('show');
}
});
// 点击返回顶部
goToTop.addEventListener('click', function() {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
});
</script>
{include file="component/foot" /}

View File

@ -0,0 +1,3 @@
</body>
</html>

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{$config['admin_name']}</title>
<link rel="stylesheet" href="__LAYUI__/css/layui.css">
<link rel="stylesheet" href="__CSS__/style.css">
<link rel="stylesheet" href="__CSS__/bootstrap.min.css">
<script src="__LAYUI__/layui.js" charset="utf-8"></script>
</head>
<body>

View File

@ -0,0 +1,114 @@
<div style="display: flex;flex-direction: column;">
<div class="topbar-one">
<div class="container">
<div style="width: 70%;">
<ul class="list-unstyled topbar-one__info">
<li class="topbar-one__info__item"><span class="topbar-one__info__icon fas fa-phone-alt"></span><a
href="tel:629-555-0129">(629) 555-0129</a></li>
<li class="topbar-one__info__item"><span class="topbar-one__info__icon fas fa-envelope"></span><a
href="mailto:info@example.com">info@example.com</a></li>
</ul>
</div>
<div class="topbar-one__social" style="width: 30%;">
<a href="https://facebook.com"><i class="fab fa-facebook-f"></i></a>
<a href="https://twitter.com"><i class="fab fa-twitter"></i></a>
<a href="https://instagram.com"><i class="fab fa-instagram"></i></a>
<a href="https://www.youtube.com/"><i class="fab fa-linkedin"></i></a>
</div>
</div>
</div>
<!-- 导航栏 -->
<div class="main-menu">
<div class="container">
<div class="main-menu__logo">
<a href="index.html"><img src="__IMAGES__/logo.png" width="186" alt="Logo"></a>
</div>
<div class="main-menu__nav">
<ul class="main-menu__list">
<li><a href="index.html">首页</a></li>
<li><a href="about.html">关于我们</a></li>
<li><a href="products.html">产品服务</a></li>
<li><a href="contact.html">联系我们</a></li>
</ul>
</div>
<div class="main-menu__right">
<a href="#" class="main-menu__search"><i class="layui-icon layui-icon-search"></i></a>
<a href="login.html" class="main-menu__login"><i class="layui-icon layui-icon-username"></i></a>
</div>
</div>
</div>
</div>
<!-- 固定导航 -->
<div class="sticky-nav" style="display: none;">
<div class="container">
<div class="sticky-nav__logo">
<a href="index.html"><img src="__IMAGES__/logo.png" width="150" alt="Logo"></a>
</div>
<div class="sticky-nav__menu">
<ul>
<li><a href="index.html">首页</a></li>
<li><a href="about.html">关于我们</a></li>
<li><a href="products.html">产品服务</a></li>
<li><a href="contact.html">联系我们</a></li>
</ul>
</div>
<div class="sticky-nav__right">
<a href="#" class="main-menu__search"><i class="layui-icon layui-icon-search"></i></a>
<a href="login.html" class="main-menu__login"><i class="layui-icon layui-icon-username"></i></a>
</div>
</div>
</div>
<script>
layui.use(['carousel', 'form'], function () {
var carousel = layui.carousel
, form = layui.form;
//图片轮播
carousel.render({
elem: '#test10'
, width: '100%'
, height: '86vh'
, interval: 4000
});
var $ = layui.$, active = {
set: function (othis) {
var THIS = 'layui-bg-normal'
, key = othis.data('key')
, options = {};
othis.css('background-color', '#5FB878').siblings().removeAttr('style');
options[key] = othis.data('value');
ins3.reload(options);
}
};
//监听开关
form.on('switch(autoplay)', function () {
ins3.reload({
autoplay: this.checked
});
});
$('.demoSet').on('keyup', function () {
var value = this.value
, options = {};
if (!/^\d+$/.test(value)) return;
options[this.name] = value;
ins3.reload(options);
});
// 监听滚动事件
$(window).scroll(function () {
var scrollTop = $(window).scrollTop();
if (scrollTop > 150) { // 当滚动超过150px时显示固定导航
$('.sticky-nav').fadeIn();
} else {
$('.sticky-nav').fadeOut();
}
});
});
</script>

View File

@ -91,7 +91,7 @@
<div class="tab-content active" data-tab="all">
{foreach $articleList as $cateName => $articles}
{foreach $articles as $article}
<div class="opencourse product-item">
<div class="opencourse product-item" onclick="window.open('{:url('article/detail')}?id={$article.id}', '_blank')">
<div class="video">
<img src="{$article.image}" alt="" class="cover">
</div>
@ -111,7 +111,7 @@
{foreach $articleList as $cateName => $articles}
<div class="tab-content" data-tab="{$cateName}">
{foreach $articles as $article}
<div class="opencourse product-item">
<div class="opencourse product-item" onclick="window.open('{:url('article/detail')}?id={$article.id}', '_blank')">
<div class="video">
<img src="{$article.image}" alt="" class="cover">
</div>

View File

@ -0,0 +1,17 @@
<?php
use think\migration\Migrator;
use think\migration\db\Column;
class CreateArticleViewTable extends Migrator
{
public function change()
{
$table = $this->table('yz_article_view', ['engine' => 'InnoDB', 'collation' => 'utf8mb4_unicode_ci']);
$table
->addColumn('article_id', 'integer', ['signed' => false, 'null' => false, 'comment' => '文章ID'])
->addColumn('ip', 'string', ['limit' => 50, 'null' => false, 'comment' => '访问IP'])
->addColumn('view_time', 'integer', ['signed' => false, 'null' => false, 'comment' => '访问时间'])
->addIndex(['article_id', 'ip', 'view_time'], ['name' => 'idx_article_ip_time'])
->create();
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
<?php /*a:4:{s:49:"E:\Demo\PHP\yunzer\app\index\view\index\index.php";i:1746796639;s:54:"E:\Demo\PHP\yunzer\app\index\view\component\header.php";i:1746796639;s:52:"E:\Demo\PHP\yunzer\app\index\view\component\main.php";i:1746805815;s:54:"E:\Demo\PHP\yunzer\app\index\view\component\footer.php";i:1746796639;}*/ ?>
<?php /*a:4:{s:49:"E:\Demo\PHP\yunzer\app\index\view\index\index.php";i:1746796639;s:54:"E:\Demo\PHP\yunzer\app\index\view\component\header.php";i:1746796639;s:52:"E:\Demo\PHP\yunzer\app\index\view\component\main.php";i:1746806638;s:54:"E:\Demo\PHP\yunzer\app\index\view\component\footer.php";i:1746796639;}*/ ?>
<!DOCTYPE html>
<html>
@ -264,7 +264,7 @@ layui.use(['carousel', 'form'], function(){
<!-- 全部文章 -->
<div class="tab-content active" data-tab="all">
<?php foreach($articleList as $cateName => $articles): foreach($articles as $article): ?>
<div class="opencourse product-item">
<div class="opencourse product-item" onclick="window.open('<?php echo url('article/detail'); ?>?id=<?php echo htmlentities((string) $article['id']); ?>', '_blank')">
<div class="video">
<img src="<?php echo htmlentities((string) $article['image']); ?>" alt="" class="cover">
</div>
@ -284,7 +284,7 @@ layui.use(['carousel', 'form'], function(){
<?php foreach($articleList as $cateName => $articles): ?>
<div class="tab-content" data-tab="<?php echo htmlentities((string) $cateName); ?>">
<?php foreach($articles as $article): ?>
<div class="opencourse product-item">
<div class="opencourse product-item" onclick="window.open('<?php echo url('article/detail'); ?>?id=<?php echo htmlentities((string) $article['id']); ?>', '_blank')">
<div class="video">
<img src="<?php echo htmlentities((string) $article['image']); ?>" alt="" class="cover">
</div>

View File

@ -0,0 +1,49 @@
<?php /*a:1:{s:46:"E:\Demo\PHP\yunzer\app/tpl/think_exception.tpl";i:1745855804;}*/ ?>
<html lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>跳转提示</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style type="text/css">
*{box-sizing:border-box;margin:0;padding:0;font-family:Lantinghei SC,Open Sans,Arial,Hiragino Sans GB,Microsoft YaHei,"微软雅黑",STHeiti,WenQuanYi Micro Hei,SimSun,sans-serif;-webkit-font-smoothing:antialiased}
body{padding:70px 0;background:#edf1f4;font-weight:400;font-size:1pc;-webkit-text-size-adjust:none;color:#333}
a{outline:0;color:#3498db;text-decoration:none;cursor:pointer}
.system-message{margin:20px 5%;padding:40px 20px;background:#fff;box-shadow:1px 1px 1px hsla(0,0%,39%,.1);text-align:center}
.system-message h1{margin:0;margin-bottom:9pt;color:#444;font-weight:400;font-size:40px}
.system-message .jump,.system-message .image{margin:20px 0;padding:0;padding:10px 0;font-weight:400}
.system-message .jump{font-size:14px}
.system-message .jump a{color:#333}
.system-message p{font-size:9pt;line-height:20px}
.system-message .btn{display:inline-block;margin-right:10px;width:138px;height:2pc;border:1px solid #44a0e8;border-radius:30px;color:#44a0e8;text-align:center;font-size:1pc;line-height:2pc;margin-bottom:5px;}
.success .btn{border-color:#69bf4e;color:#69bf4e}
.error .btn{border-color:#ff8992;color:#ff8992}
.info .btn{border-color:#3498db;color:#3498db}
.copyright p{width:100%;color:#919191;text-align:center;font-size:10px}
.system-message .btn-grey{border-color:#bbb;color:#bbb}
.clearfix:after{clear:both;display:block;visibility:hidden;height:0;content:"."}
@media (max-width:768px){body {padding:20px 0;}}
@media (max-width:480px){.system-message h1{font-size:30px;}}
</style>
</head>
<body>
<div class="system-message">
<?php
switch ($code){
case 1:
?>
<h2>
<?php
echo (strip_tags($msg));
break;
case 0:
?>
<h2>
<?php
echo (strip_tags($msg));
break;
}
?>
</h2>
</div>
</body>
</html>

View File

@ -0,0 +1,707 @@
<?php /*a:5:{s:52:"E:\Demo\PHP\yunzer\app\index\view\article\detail.php";i:1746808591;s:52:"E:\Demo\PHP\yunzer\app\index\view\component\head.php";i:1746808024;s:61:"E:\Demo\PHP\yunzer\app\index\view\component\header-simple.php";i:1746808182;s:54:"E:\Demo\PHP\yunzer\app\index\view\component\footer.php";i:1746796639;s:52:"E:\Demo\PHP\yunzer\app\index\view\component\foot.php";i:1746808046;}*/ ?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo htmlentities((string) $config['admin_name']); ?></title>
<link rel="stylesheet" href="/static/layui/css/layui.css">
<link rel="stylesheet" href="/static/css/style.css">
<link rel="stylesheet" href="/static/css/bootstrap.min.css">
<script src="/static/layui/layui.js" charset="utf-8"></script>
</head>
<body>
<div style="display: flex;flex-direction: column;">
<div class="topbar-one">
<div class="container">
<div style="width: 70%;">
<ul class="list-unstyled topbar-one__info">
<li class="topbar-one__info__item"><span class="topbar-one__info__icon fas fa-phone-alt"></span><a
href="tel:629-555-0129">(629) 555-0129</a></li>
<li class="topbar-one__info__item"><span class="topbar-one__info__icon fas fa-envelope"></span><a
href="mailto:info@example.com">info@example.com</a></li>
</ul>
</div>
<div class="topbar-one__social" style="width: 30%;">
<a href="https://facebook.com"><i class="fab fa-facebook-f"></i></a>
<a href="https://twitter.com"><i class="fab fa-twitter"></i></a>
<a href="https://instagram.com"><i class="fab fa-instagram"></i></a>
<a href="https://www.youtube.com/"><i class="fab fa-linkedin"></i></a>
</div>
</div>
</div>
<!-- 导航栏 -->
<div class="main-menu">
<div class="container">
<div class="main-menu__logo">
<a href="index.html"><img src="/static/images/logo.png" width="186" alt="Logo"></a>
</div>
<div class="main-menu__nav">
<ul class="main-menu__list">
<li><a href="index.html">首页</a></li>
<li><a href="about.html">关于我们</a></li>
<li><a href="products.html">产品服务</a></li>
<li><a href="contact.html">联系我们</a></li>
</ul>
</div>
<div class="main-menu__right">
<a href="#" class="main-menu__search"><i class="layui-icon layui-icon-search"></i></a>
<a href="login.html" class="main-menu__login"><i class="layui-icon layui-icon-username"></i></a>
</div>
</div>
</div>
</div>
<!-- 固定导航 -->
<div class="sticky-nav" style="display: none;">
<div class="container">
<div class="sticky-nav__logo">
<a href="index.html"><img src="/static/images/logo.png" width="150" alt="Logo"></a>
</div>
<div class="sticky-nav__menu">
<ul>
<li><a href="index.html">首页</a></li>
<li><a href="about.html">关于我们</a></li>
<li><a href="products.html">产品服务</a></li>
<li><a href="contact.html">联系我们</a></li>
</ul>
</div>
<div class="sticky-nav__right">
<a href="#" class="main-menu__search"><i class="layui-icon layui-icon-search"></i></a>
<a href="login.html" class="main-menu__login"><i class="layui-icon layui-icon-username"></i></a>
</div>
</div>
</div>
<script>
layui.use(['carousel', 'form'], function () {
var carousel = layui.carousel
, form = layui.form;
//图片轮播
carousel.render({
elem: '#test10'
, width: '100%'
, height: '86vh'
, interval: 4000
});
var $ = layui.$, active = {
set: function (othis) {
var THIS = 'layui-bg-normal'
, key = othis.data('key')
, options = {};
othis.css('background-color', '#5FB878').siblings().removeAttr('style');
options[key] = othis.data('value');
ins3.reload(options);
}
};
//监听开关
form.on('switch(autoplay)', function () {
ins3.reload({
autoplay: this.checked
});
});
$('.demoSet').on('keyup', function () {
var value = this.value
, options = {};
if (!/^\d+$/.test(value)) return;
options[this.name] = value;
ins3.reload(options);
});
// 监听滚动事件
$(window).scroll(function () {
var scrollTop = $(window).scrollTop();
if (scrollTop > 150) { // 当滚动超过150px时显示固定导航
$('.sticky-nav').fadeIn();
} else {
$('.sticky-nav').fadeOut();
}
});
});
</script>
<div class="main">
<div class="location">
<div class="container">
<div class="location-item">
<a href="<?php echo url('index/index/index'); ?>">首页</a>
<span>></span>
<a href="<?php echo url('index/article/index'); ?>">技术文章</a>
</div>
</div>
</div>
<div class="article-detail-container">
<div class="article-header">
<h1 class="article-title"><?php echo htmlentities((string) $article['title']); ?></h1>
<div class="article-meta">
<span class="article-author"><i class="fa fa-user"></i> <?php echo htmlentities((string) $article['author']); ?></span>
<span class="article-date"><i class="fa fa-calendar"></i> <?php echo htmlentities((string) date('Y-m-d H:i',!is_numeric($article['create_time'])? strtotime($article['create_time']) : $article['create_time'])); ?></span>
<span class="article-category"><i class="fa fa-folder"></i> <?php echo htmlentities((string) $cateName); ?></span>
<span class="article-views"><i class="fa fa-eye"></i> <?php echo htmlentities((string) $article['views']); ?> 阅读</span>
</div>
</div>
<div class="article-content">
<?php echo $article['content']; ?>
</div>
<div class="article-tags">
<span class="tag-label">标签:</span>
<?php if(!empty($article['tags'])): foreach($article['tags'] as $tag): ?>
<span class="tag-item"><?php echo htmlentities((string) $tag); ?></span>
<?php endforeach; else: ?>
<span class="no-tags">暂无标签</span>
<?php endif; ?>
</div>
<div class="article-actions">
<div class="action-item like-btn">
<i class="fa fa-thumbs-up"></i>
<span class="action-text">点赞</span>
<span class="action-count"><?php echo htmlentities((string) (isset($article['likes']) && ($article['likes'] !== '')?$article['likes']:0)); ?></span>
</div>
<div class="action-item share-btn">
<i class="fa fa-share-alt"></i>
<span class="action-text">分享</span>
</div>
</div>
<div class="article-navigation">
<div class="prev-article">
<?php if(!empty($prevArticle)): ?>
<a href="<?php echo url('index/article/index', ['id' => $prevArticle['id']]); ?>">
<i class="fa fa-arrow-left"></i> 上一篇:<?php echo htmlentities((string) $prevArticle['title']); ?>
</a>
<?php else: ?>
<span class="disabled"><i class="fa fa-arrow-left"></i> 没有上一篇了</span>
<?php endif; ?>
</div>
<div class="next-article">
<?php if(!empty($nextArticle)): ?>
<a href="<?php echo url('index/article/index', ['id' => $nextArticle['id']]); ?>">
下一篇:<?php echo htmlentities((string) $nextArticle['title']); ?> <i class="fa fa-arrow-right"></i>
</a>
<?php else: ?>
<span class="disabled">没有下一篇了 <i class="fa fa-arrow-right"></i></span>
<?php endif; ?>
</div>
</div>
<div class="related-articles">
<h3 class="related-title">相关推荐</h3>
<div class="related-list">
<?php if(!empty($relatedArticles)): foreach($relatedArticles as $related): ?>
<div class="related-item">
<a href="<?php echo url('index/article/index', ['id' => $related['id']]); ?>">
<div class="related-image">
<img src="<?php echo htmlentities((string) $related['image']); ?>" alt="<?php echo htmlentities((string) $related['title']); ?>">
</div>
<div class="related-info">
<div class="related-item-title"><?php echo htmlentities((string) $related['title']); ?></div>
<div class="related-item-desc"><?php echo htmlentities((string) $related['desc']); ?></div>
</div>
</a>
</div>
<?php endforeach; else: ?>
<div class="no-related">暂无相关文章</div>
<?php endif; ?>
</div>
</div>
<!-- <div class="article-comments">
<h3 class="comments-title">评论区</h3>
<div class="comment-form">
<textarea placeholder="请输入您的评论..." class="comment-textarea"></textarea>
<button class="comment-submit">发表评论</button>
</div>
<div class="comment-list">
<?php if(!empty($comments)): foreach($comments as $comment): ?>
<div class="comment-item">
<div class="comment-avatar">
<img src="<?php echo htmlentities((string) (isset($comment['avatar']) && ($comment['avatar'] !== '')?$comment['avatar']:'/static/images/default-avatar.png')); ?>" alt="用户头像">
</div>
<div class="comment-content">
<div class="comment-user"><?php echo htmlentities((string) $comment['username']); ?></div>
<div class="comment-text"><?php echo htmlentities((string) $comment['content']); ?></div>
<div class="comment-footer">
<span class="comment-time"><?php echo htmlentities((string) date('Y-m-d H:i',!is_numeric($comment['create_time'])? strtotime($comment['create_time']) : $comment['create_time'])); ?></span>
<span class="comment-reply">回复</span>
</div>
</div>
</div>
<?php endforeach; else: ?>
<div class="no-comments">暂无评论,快来抢沙发吧!</div>
<?php endif; ?>
</div>
</div> -->
</div>
</div>
<!-- 返回顶部按钮 -->
<div class="go-to-top" id="goToTop">
<i class="layui-icon-up"></i>
</div>
<footer class="footer" style="background-image: url(/static/images/footer-bg-1.png)">
<div class="container">
<div class="row" style="width: 100%;">
<div class="row-main">
<div class="mr-20">
<img src="/static/images/logo-l-w.png" alt="" height="70">
<p class="text-white-50 my-4 f18" style="width: 400px;">美天智能科技,这里是介绍!</p>
</div>
<div style="display: flex; justify-content: space-between;width: 100%;margin-right: 200px;">
<div>
<h4 class="text-white f-20 font-weight-normal mb-3">关于我们</h4>
<ul class="list-unstyled footer-sub-menu">
<li><a href="#" class="footer-link">概况</a></li>
<li><a href="#" class="footer-link">资讯</a></li>
<li><a href="#" class="footer-link">加入我们</a></li>
<li><a href="#" class="footer-link">联系我们</a></li>
</ul>
</div>
<div>
<h4 class="text-white f-20 font-weight-normal mb-3">商务合作</h4>
<ul class="list-unstyled footer-sub-menu">
<li><a href="#" class="footer-link">商务合作</a></li>
</ul>
</div>
<div>
<h4 class="text-white f-20 font-weight-normal mb-3">服务支持</h4>
<ul class="list-unstyled footer-sub-menu">
<li><a href="#" class="footer-link">常见问答</a></li>
<li><a href="#" class="footer-link">软件下载</a></li>
<li><a href="#" class="footer-link">服务政策</a></li>
<li><a href="#" class="footer-link">投诉建议</a></li>
</ul>
</div>
</div>
<div>
<div class="text-center">
<img src="/static/images/code.png" alt="微信二维码" class="img-fluid" style="max-width: 150px;">
<p class="text-white-50 mt-2">微信公众号</p>
</div>
</div>
</div>
</div>
</div>
</footer>
<section class="copyright text-center">
<div class="container wow fadeInUp animated" data-wow-delay="400ms"
style="visibility: visible; animation-delay: 400ms; animation-name: fadeInUp;">
<p class="copyright__text">Copyright <span class="dynamic-year">2025</span> | All Rights By <a
href="http://www.yunzer.cn">Yunzer</a></p>
</div>
</section>
<style>
.location {
max-width: 1000px;
margin: 30px auto;
}
.article-detail-container {
max-width: 1000px;
margin: 30px auto;
padding: 50px;
background: #fff;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
border-radius: 8px;
}
.article-header {
margin-bottom: 30px;
border-bottom: 1px solid #eee;
padding-bottom: 20px;
}
.article-title {
font-size: 28px;
font-weight: 700;
color: #333;
margin-bottom: 15px;
line-height: 1.4;
}
.article-meta {
display: flex;
flex-wrap: wrap;
gap: 20px;
color: #666;
font-size: 14px;
}
.article-meta span {
display: flex;
align-items: center;
}
.article-meta i {
margin-right: 5px;
}
.article-content {
line-height: 1.8;
color: #333;
font-size: 16px;
margin-bottom: 30px;
}
.article-content img {
max-width: 100%;
height: auto;
margin: 15px 0;
border-radius: 4px;
}
.article-tags {
margin: 20px 0;
display: flex;
align-items: center;
flex-wrap: wrap;
}
.tag-label {
font-weight: bold;
margin-right: 10px;
}
.tag-item {
background: #f2f2f2;
padding: 4px 10px;
border-radius: 15px;
font-size: 12px;
margin-right: 8px;
color: #666;
}
.article-actions {
display: flex;
justify-content: center;
gap: 40px;
margin: 30px 0;
padding: 20px 0;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
}
.action-item {
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
}
.action-item i {
font-size: 24px;
color: #666;
margin-bottom: 5px;
}
.action-text {
font-size: 14px;
color: #666;
}
.action-count {
font-size: 12px;
color: #999;
margin-top: 3px;
}
.article-navigation {
display: flex;
justify-content: space-between;
margin: 30px 0;
}
.prev-article,
.next-article {
max-width: 45%;
}
.prev-article a,
.next-article a {
color: #333;
text-decoration: none;
}
.prev-article a:hover,
.next-article a:hover {
color: #f57005;
}
.disabled {
color: #999;
}
.related-articles {
margin: 40px 0;
}
.related-title {
font-size: 20px;
font-weight: 600;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.related-list {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20px;
}
.related-item {
border-radius: 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.3s;
}
.related-item:hover {
transform: translateY(-5px);
}
.related-item a {
text-decoration: none;
color: inherit;
}
.related-image img {
width: 100%;
height: 150px;
object-fit: cover;
}
.related-info {
padding: 10px;
}
.related-item-title {
font-size: 16px;
font-weight: 600;
margin-bottom: 5px;
color: #333;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.related-item-desc {
font-size: 12px;
color: #666;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.article-comments {
margin-top: 40px;
}
.comments-title {
font-size: 20px;
font-weight: 600;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #eee;
}
.comment-form {
margin-bottom: 30px;
}
.comment-textarea {
width: 100%;
height: 100px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
resize: none;
margin-bottom: 10px;
}
.comment-submit {
background: #f57005;
color: white;
border: none;
padding: 8px 20px;
border-radius: 4px;
cursor: pointer;
float: right;
}
.comment-item {
display: flex;
margin-bottom: 20px;
padding-bottom: 20px;
border-bottom: 1px solid #eee;
}
.comment-avatar img {
width: 50px;
height: 50px;
border-radius: 50%;
margin-right: 15px;
}
.comment-content {
flex: 1;
}
.comment-user {
font-weight: 600;
margin-bottom: 5px;
}
.comment-text {
line-height: 1.6;
margin-bottom: 10px;
}
.comment-footer {
display: flex;
justify-content: space-between;
color: #999;
font-size: 12px;
}
.comment-reply {
cursor: pointer;
color: #f57005;
}
.no-comments,
.no-related,
.no-tags {
color: #999;
text-align: center;
padding: 20px;
}
@media (max-width: 768px) {
.article-title {
font-size: 24px;
}
.related-list {
grid-template-columns: repeat(1, 1fr);
}
.article-meta {
gap: 10px;
}
}
/* 返回顶部按钮样式 */
.go-to-top {
position: fixed;
right: 30px;
bottom: 30px;
width: 40px;
height: 40px;
background: #f57005;
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
z-index: 1000;
}
.go-to-top.show {
opacity: 1;
visibility: visible;
}
.go-to-top:hover {
background: #e66600;
transform: translateY(-3px);
}
.go-to-top i {
font-size: 18px;
}
@media (max-width: 768px) {
.go-to-top {
right: 20px;
bottom: 20px;
width: 36px;
height: 36px;
}
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function () {
// 点赞功能
const likeBtn = document.querySelector('.like-btn');
if (likeBtn) {
likeBtn.addEventListener('click', function () {
const articleId = '<?php echo htmlentities((string) $article['id']); ?>';
fetch('/index/article/like?id=' + articleId, {
method: 'POST'
})
.then(response => response.json())
.then(data => {
if (data.code === 1) {
const countElement = this.querySelector('.action-count');
let count = parseInt(countElement.textContent);
countElement.textContent = count + 1;
this.classList.add('liked');
this.style.color = '#f57005';
} else {
alert('点赞失败:' + data.msg);
}
})
.catch(error => {
console.error('点赞请求失败:', error);
});
});
}
// 返回顶部功能
const goToTop = document.getElementById('goToTop');
// 监听滚动事件
window.addEventListener('scroll', function() {
if (window.pageYOffset > 300) {
goToTop.classList.add('show');
} else {
goToTop.classList.remove('show');
}
});
// 点击返回顶部
goToTop.addEventListener('click', function() {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
});
</script>
</body>
</html>