增加企业产品分类

This commit is contained in:
2026-03-21 14:12:21 +08:00
parent 9281c1dc35
commit 2a6b14f698
13 changed files with 1139 additions and 277 deletions
+249 -43
View File
@@ -12,13 +12,16 @@
--------------------------------------------------------------*/
/* Fonts */
:root {
--default-font: "Roboto", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--heading-font: "Montserrat", sans-serif;
--nav-font: "Raleway", sans-serif;
--default-font:
"Roboto", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji",
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--heading-font: "Montserrat", sans-serif;
--nav-font: "Raleway", sans-serif;
}
/* Global Colors - The following color variables are used throughout the website. Updating them here will change the color scheme of the entire website */
:root {
:root {
--background-color: #ffffff; /* Background color for the entire website, including individual sections */
--default-color: #2b180d; /* Default color used for the majority of the text content across the entire website */
--heading-color: #1b2f45; /* Color for headings, subheadings and title throughout the website */
@@ -29,7 +32,12 @@
/* Nav Menu Colors - The following color variables are used specifically for the navigation menu. They are separate from the global colors to allow for more customization options */
:root {
--nav-color: rgba(255, 255, 255, 0.6); /* The default color of the main navmenu links */
--nav-color: rgba(
255,
255,
255,
0.6
); /* The default color of the main navmenu links */
--nav-hover-color: #ffffff; /* Applied to main navmenu links when they are hovered over or active */
--nav-mobile-background-color: #ffffff; /* Used as the background color for mobile navigation menu */
--nav-dropdown-background-color: #ffffff; /* Used as the background color for dropdown items that appear when hovering over primary navigation items */
@@ -85,8 +93,12 @@ h5,
h6 {
color: var(--heading-color);
font-family: var(--heading-font);
line-height: 40px;
}
.h520 {
height: 520px;
}
/* PHP Email Form Messages
------------------------------*/
.php-email-form .error-message {
@@ -225,7 +237,7 @@ h6 {
padding-right: 0;
}
.navmenu li:hover>a,
.navmenu li:hover > a,
.navmenu .active,
.navmenu .active:focus {
color: var(--nav-hover-color);
@@ -264,11 +276,11 @@ h6 {
.navmenu .dropdown ul a:hover,
.navmenu .dropdown ul .active:hover,
.navmenu .dropdown ul li:hover>a {
.navmenu .dropdown ul li:hover > a {
color: var(--nav-dropdown-hover-color);
}
.navmenu .dropdown:hover>ul {
.navmenu .dropdown:hover > ul {
opacity: 1;
top: 100%;
visibility: visible;
@@ -280,7 +292,7 @@ h6 {
visibility: hidden;
}
.navmenu .dropdown .dropdown:hover>ul {
.navmenu .dropdown .dropdown:hover > ul {
opacity: 1;
top: 0;
left: -100%;
@@ -383,7 +395,7 @@ h6 {
background-color: rgba(33, 37, 41, 0.1);
}
.navmenu .dropdown>.dropdown-active {
.navmenu .dropdown > .dropdown-active {
display: block;
background-color: rgba(33, 37, 41, 0.03);
}
@@ -410,7 +422,7 @@ h6 {
transition: 0.3s;
}
.mobile-nav-active .navmenu>ul {
.mobile-nav-active .navmenu > ul {
display: block;
}
}
@@ -627,7 +639,11 @@ h6 {
.page-title:before {
content: "";
background-color: color-mix(in srgb, var(--background-color), transparent 40%);
background-color: color-mix(
in srgb,
var(--background-color),
transparent 40%
);
position: absolute;
inset: 0;
}
@@ -653,11 +669,11 @@ h6 {
font-weight: 400;
}
.page-title .breadcrumbs ol li+li {
.page-title .breadcrumbs ol li + li {
padding-left: 10px;
}
.page-title .breadcrumbs ol li+li::before {
.page-title .breadcrumbs ol li + li::before {
content: "/";
display: inline-block;
padding-right: 10px;
@@ -677,7 +693,6 @@ section,
}
@media (max-width: 1199px) {
section,
.section {
scroll-margin-top: 66px;
@@ -735,7 +750,13 @@ section,
position: absolute;
content: "";
width: 44%;
background-image: linear-gradient(180deg, color-mix(in srgb, var(--background-color), transparent 15%), color-mix(in srgb, var(--background-color), transparent 15%) 100%), linear-gradient(180deg, rgb(0, 0, 0), rgb(0, 0, 0) 100%);
background-image:
linear-gradient(
180deg,
color-mix(in srgb, var(--background-color), transparent 15%),
color-mix(in srgb, var(--background-color), transparent 15%) 100%
),
linear-gradient(180deg, rgb(0, 0, 0), rgb(0, 0, 0) 100%);
top: 0;
bottom: 0;
z-index: 2;
@@ -1441,14 +1462,15 @@ section,
box-shadow: 0px 2px 20px rgba(0, 0, 0, 0.1);
}
.service-details .service-box+.service-box {
.service-details .service-box + .service-box {
margin-top: 30px;
}
.service-details .service-box h4 {
font-size: 20px;
font-weight: 700;
border-bottom: 2px solid color-mix(in srgb, var(--default-color), transparent 92%);
border-bottom: 2px solid
color-mix(in srgb, var(--default-color), transparent 92%);
padding-bottom: 15px;
margin-bottom: 15px;
}
@@ -1497,7 +1519,8 @@ section,
align-items: center;
padding: 10px 0;
transition: 0.3s;
border-top: 1px solid color-mix(in srgb, var(--default-color), transparent 90%);
border-top: 1px solid
color-mix(in srgb, var(--default-color), transparent 90%);
}
.service-details .download-catalog a:first-child {
@@ -1676,14 +1699,20 @@ section,
position: relative;
}
.portfolio-details .portfolio-details-slider .swiper-pagination .swiper-pagination-bullet {
.portfolio-details
.portfolio-details-slider
.swiper-pagination
.swiper-pagination-bullet {
width: 12px;
height: 12px;
background-color: color-mix(in srgb, var(--default-color), transparent 85%);
opacity: 1;
}
.portfolio-details .portfolio-details-slider .swiper-pagination .swiper-pagination-bullet-active {
.portfolio-details
.portfolio-details-slider
.swiper-pagination
.swiper-pagination-bullet-active {
background-color: var(--accent-color);
}
@@ -1698,7 +1727,8 @@ section,
font-weight: 700;
margin-bottom: 20px;
padding-bottom: 20px;
border-bottom: 1px solid color-mix(in srgb, var(--default-color), transparent 85%);
border-bottom: 1px solid
color-mix(in srgb, var(--default-color), transparent 85%);
}
.portfolio-details .portfolio-info ul {
@@ -1707,7 +1737,7 @@ section,
font-size: 15px;
}
.portfolio-details .portfolio-info ul li+li {
.portfolio-details .portfolio-info ul li + li {
margin-top: 10px;
}
@@ -1824,7 +1854,7 @@ section,
right: 0;
}
.contact .info-item+.info-item {
.contact .info-item + .info-item {
margin-top: 40px;
}
@@ -1859,31 +1889,35 @@ section,
height: 100%;
}
.contact .php-email-form input[type=text],
.contact .php-email-form input[type=email],
.contact .php-email-form input[type="text"],
.contact .php-email-form input[type="email"],
.contact .php-email-form textarea {
font-size: 14px;
padding: 10px 15px;
box-shadow: none;
border-radius: 0;
color: var(--default-color);
background-color: color-mix(in srgb, var(--background-color), transparent 50%);
background-color: color-mix(
in srgb,
var(--background-color),
transparent 50%
);
border-color: color-mix(in srgb, var(--default-color), transparent 80%);
}
.contact .php-email-form input[type=text]:focus,
.contact .php-email-form input[type=email]:focus,
.contact .php-email-form input[type="text"]:focus,
.contact .php-email-form input[type="email"]:focus,
.contact .php-email-form textarea:focus {
border-color: var(--accent-color);
}
.contact .php-email-form input[type=text]::placeholder,
.contact .php-email-form input[type=email]::placeholder,
.contact .php-email-form input[type="text"]::placeholder,
.contact .php-email-form input[type="email"]::placeholder,
.contact .php-email-form textarea::placeholder {
color: color-mix(in srgb, var(--default-color), transparent 70%);
}
.contact .php-email-form button[type=submit] {
.contact .php-email-form button[type="submit"] {
color: var(--contrast-color);
background: var(--accent-color);
border: 0;
@@ -1892,17 +1926,97 @@ section,
border-radius: 50px;
}
.contact .php-email-form button[type=submit]:hover {
.contact .php-email-form button[type="submit"]:hover {
background: color-mix(in srgb, var(--accent-color), transparent 20%);
}
/*--------------------------------------------------------------
# Blog Layout — 左侧分类导航
--------------------------------------------------------------*/
.blog-layout-sidebar {
position: sticky;
top: 100px;
margin-top: 60px;
}
.blog-category-nav {
background-color: var(--surface-color);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
border-radius: 10px;
padding: 0 0 8px;
margin-bottom: 24px;
}
.blog-category-nav .nav-title {
font-size: 18px;
font-weight: 700;
padding: 16px 20px 12px;
margin: 0;
color: var(--heading-color);
border-bottom: 1px solid
color-mix(in srgb, var(--default-color), transparent 90%);
}
.blog-category-nav ul {
list-style: none;
padding: 0;
margin: 0;
}
.blog-category-nav li a {
display: block;
padding: 10px 20px;
color: var(--default-color);
text-decoration: none;
transition: 0.25s;
border-left: 3px solid transparent;
}
.blog-category-nav li a:hover {
color: var(--accent-color);
background: color-mix(in srgb, var(--accent-color), transparent 94%);
}
.blog-category-nav li a.active {
color: var(--accent-color);
font-weight: 600;
border-left-color: var(--accent-color);
background: color-mix(in srgb, var(--accent-color), transparent 92%);
}
.blog-category-nav li ul.children {
padding-left: 16px;
}
.blog-category-nav li ul.children li a {
font-size: 14px;
padding: 8px 20px;
color: color-mix(in srgb, var(--default-color), transparent 30%);
}
.blog-category-nav li ul.children li a:hover {
color: var(--accent-color);
}
.blog-category-nav li ul.children li a.active {
color: var(--accent-color);
font-weight: 600;
}
@media (max-width: 991px) {
.blog-layout-sidebar {
position: static;
margin-bottom: 1.5rem;
}
}
/*--------------------------------------------------------------
# Blog Posts Section
--------------------------------------------------------------*/
.blog-posts article {
background-color: var(--surface-color);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
padding: 30px;
padding: 15px;
height: 100%;
border-radius: 10px;
overflow: hidden;
@@ -1927,7 +2041,7 @@ section,
/* 新结构:blog.php 中的预览图容器 */
.blog-posts .post-media {
height: 240px;
height: 125px;
margin: -30px -30px 15px -30px;
overflow: hidden;
position: relative;
@@ -1971,7 +2085,7 @@ section,
}
.blog-posts .post-author-img {
width: 50px;
width: 35px;
border-radius: 50%;
margin-right: 15px;
}
@@ -1979,10 +2093,11 @@ section,
.blog-posts .post-author {
font-weight: 600;
margin-bottom: 5px;
font-size: 12px;
}
.blog-posts .post-date {
font-size: 14px;
font-size: 12px;
color: color-mix(in srgb, var(--default-color), transparent 40%);
margin-bottom: 0;
}
@@ -2092,9 +2207,99 @@ section,
margin-bottom: 20px;
}
.blog-details .content span {
border-radius: 4px;
padding: 0.15em 0.45em;
margin: 5px;
}
.blog-details .content code {
font-family:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
"Courier New", monospace;
font-size: 0.92em;
color: var(--heading-color);
background-color: color-mix(in srgb, var(--default-color), transparent 92%);
padding: 0.15em 0.45em;
border-radius: 4px;
word-break: break-word;
}
.blog-details .content pre {
font-family:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
"Courier New", monospace;
font-size: 14px;
line-height: 1.55;
color: var(--default-color);
background-color: color-mix(in srgb, var(--default-color), transparent 94%);
border: 1px solid color-mix(in srgb, var(--default-color), transparent 88%);
border-radius: 6px;
padding: 18px 20px;
margin: 1.25em 0;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.blog-details .content pre code {
font-size: inherit;
color: inherit;
background: none;
padding: 0;
border-radius: 0;
word-break: normal;
white-space: pre;
}
.blog-details .content .article-code-block {
position: relative;
margin: 1.25em 0;
}
.blog-details .content .article-code-block pre {
margin: 0;
padding-top: 42px;
}
.blog-details .content .article-code-copy {
position: absolute;
top: 10px;
right: 10px;
z-index: 2;
font-family: var(--default-font, system-ui, sans-serif);
font-size: 12px;
font-weight: 600;
line-height: 1;
padding: 6px 12px;
color: var(--contrast-color);
background-color: var(--accent-color);
border: none;
border-radius: 4px;
cursor: pointer;
transition:
opacity 0.2s ease,
transform 0.15s ease;
}
.blog-details .content .article-code-copy:hover {
opacity: 0.92;
}
.blog-details .content .article-code-copy:focus-visible {
outline: 2px solid var(--accent-color);
outline-offset: 2px;
}
.blog-details .content .article-code-copy.is-done {
background-color: color-mix(in srgb, var(--accent-color), #000 18%);
}
.blog-details .meta-top {
margin-top: 20px;
color: color-mix(in srgb, var(--default-color), transparent 40%);
padding-bottom: 25px;
border-bottom: 1px solid
color-mix(in srgb, var(--default-color), transparent 90%);
}
.blog-details .meta-top ul {
@@ -2106,7 +2311,7 @@ section,
margin: 0;
}
.blog-details .meta-top ul li+li {
.blog-details .meta-top ul li + li {
padding-left: 20px;
}
@@ -2126,7 +2331,8 @@ section,
.blog-details .meta-bottom {
padding-top: 10px;
border-top: 1px solid color-mix(in srgb, var(--default-color), transparent 90%);
border-top: 1px solid
color-mix(in srgb, var(--default-color), transparent 90%);
}
.blog-details .meta-bottom i {
@@ -2165,7 +2371,7 @@ section,
display: inline-block;
}
.blog-details .meta-bottom .tags li+li::before {
.blog-details .meta-bottom .tags li + li::before {
padding-right: 6px;
color: var(--default-color);
content: ",";
@@ -2414,7 +2620,7 @@ section,
transition: 0.3s;
}
.search-widget form input[type=text] {
.search-widget form input[type="text"] {
border: 0;
padding: 4px;
border-radius: 4px;
@@ -2423,7 +2629,7 @@ section,
color: var(--default-color);
}
.search-widget form input[type=text]:focus {
.search-widget form input[type="text"]:focus {
outline: none;
}
@@ -2554,4 +2760,4 @@ section,
padding-left: 5px;
color: color-mix(in srgb, var(--default-color), transparent 60%);
font-size: 14px;
}
}
+137 -78
View File
@@ -1,107 +1,166 @@
<?php
$pageTitle = '新闻中心详情 - Nova';
$pageDescription = 'Nova Bootstrap Template 的新闻中心详情页面';
$pageKeywords = 'blog, details, nova';
require_once __DIR__ . '/header.php';
?>
<!-- Page Title -->
<div class="page-title dark-background" data-aos="fade" style="background-image: url(<?php echo $baseUrl; ?>/themes/template3/assets/img/blog-page-title-bg.jpg);">
<div class="container">
<h1>博客详情</h1>
<nav class="breadcrumbs">
<ol>
<li><a href="<?php echo $baseUrl; ?>/">主页</a></li>
<li class="current">博客详情</li>
</ol>
</nav>
</div>
</div><!-- End Page Title -->
<?php
// 支持 Index@articleDetail 注入 $pageTitle 等;直接访问 /blog-details 时用默认文案
$pageTitle = (isset($pageTitle) && $pageTitle !== '') ? $pageTitle : '新闻中心详情 - Nova';
$pageDescription = (isset($pageDescription) && $pageDescription !== '') ? $pageDescription : 'Nova Bootstrap Template 的新闻中心详情页面';
$pageKeywords = (isset($pageKeywords) && $pageKeywords !== '') ? $pageKeywords : 'blog, details, nova';
$article = (isset($article) && is_array($article)) ? $article : [];
$hasArticle = !empty($article['id']);
// 面包屑第三段:文章分类名(与控制器注入的 $articleCategoryName 一致)
$cateName = (isset($articleCategoryName) && (string) $articleCategoryName !== '') ? (string) $articleCategoryName : '未分类';
require_once __DIR__ . '/header.php';
?>
<!-- Page Title -->
<div class="page-title dark-background" data-aos="fade" style="background-image: url(<?php echo $baseUrl; ?>/themes/template3/assets/img/blog-page-title-bg.jpg);">
<div class="container">
<div class="row">
<h1><?php echo $hasArticle ? htmlspecialchars($article['title'] ?? '博客详情') : '博客详情'; ?></h1>
<nav class="breadcrumbs">
<ol>
<li><a href="<?php echo htmlspecialchars($baseUrl); ?>/">主页</a></li>
<li><a href="<?php echo htmlspecialchars($baseUrl . '/blog'); ?>">新闻中心</a></li>
<li class="current"><?php echo htmlspecialchars($cateName); ?></li>
</ol>
</nav>
</div>
</div><!-- End Page Title -->
<div class="col-lg-12">
<div class="container">
<div class="row">
<!-- Blog Details Section -->
<section id="blog-details" class="blog-details section">
<div class="container">
<div class="col-lg-12">
<article class="article">
<!-- Blog Details Section -->
<section id="blog-details" class="blog-details section">
<div class="container">
<div class="post-img">
<img src="<?php echo $baseUrl; ?>/themes/template3/assets/img/blog/blog-1.jpg" alt="" class="img-fluid">
</div>
<article class="article">
<?php if ($hasArticle): ?>
<?php
$detailTitle = $article['title'] ?? '无标题';
$detailAuthor = $article['author'] ?? '';
$publishRaw = $article['publish_date'] ?? ($article['create_time'] ?? '');
$publishTs = ($publishRaw !== '') ? strtotime((string) $publishRaw) : false;
$publishDateAttr = ($publishTs !== false) ? date('c', $publishTs) : '';
$publishDateText = ($publishTs !== false) ? date('Y年m月d日', $publishTs) : '';
$views = (int) ($article['views'] ?? 0);
$coverSrc = !empty($articleCoverUrl)
? $articleCoverUrl
: ($baseUrl . '/themes/template3/assets/img/blog/blog-1.jpg');
?>
<h2 class="title">Dolorum optio tempore voluptas dignissimos cumque fuga qui quibusdam quia</h2>
<h2 class="title"><?php echo htmlspecialchars($detailTitle); ?></h2>
<div class="meta-top">
<ul>
<li class="d-flex align-items-center"><i class="bi bi-person"></i> <a href="<?php echo $baseUrl; ?>/blog-details">John Doe</a></li>
<li class="d-flex align-items-center"><i class="bi bi-clock"></i> <a href="<?php echo $baseUrl; ?>/blog-details"><time datetime="2020-01-01">Jan 1, 2022</time></a></li>
<li class="d-flex align-items-center"><i class="bi bi-chat-dots"></i> <a href="<?php echo $baseUrl; ?>/blog-details">12 Comments</a></li>
<li class="d-flex align-items-center"><i class="bi bi-person"></i> <span><?php echo htmlspecialchars($detailAuthor !== '' ? $detailAuthor : '—'); ?></span></li>
<li class="d-flex align-items-center"><i class="bi bi-clock"></i> <?php if ($publishDateText !== ''): ?><time datetime="<?php echo htmlspecialchars($publishDateAttr); ?>"><?php echo htmlspecialchars($publishDateText); ?></time><?php else: ?><span>—</span><?php endif; ?></li>
<li class="d-flex align-items-center"><i class="bi bi-eye"></i> <span id="article-views-count">阅读 <?php echo $views; ?></span></li>
</ul>
</div><!-- End meta top -->
<div class="content">
<p>
Similique neque nam consequuntur ad non maxime aliquam quas. Quibusdam animi praesentium. Aliquam et laboriosam eius aut nostrum quidem aliquid dicta.
Et eveniet enim. Qui velit est ea dolorem doloremque deleniti aperiam unde soluta. Est cum et quod quos aut ut et sit sunt. Voluptate porro consequatur assumenda perferendis dolore.
</p>
<p>
Sit repellat hic cupiditate hic ut nemo. Quis nihil sunt non reiciendis. Sequi in accusamus harum vel aspernatur. Excepturi numquam nihil cumque odio. Et voluptate cupiditate.
</p>
<blockquote>
<p>
Et vero doloremque tempore voluptatem ratione vel aut. Deleniti sunt animi aut. Aut eos aliquam doloribus minus autem quos.
</p>
</blockquote>
<p>
Sed quo laboriosam qui architecto. Occaecati repellendus omnis dicta inventore tempore provident voluptas mollitia aliquid. Id repellendus quia. Asperiores nihil magni dicta est suscipit perspiciatis. Voluptate ex rerum assumenda dolores nihil quaerat.
Dolor porro tempora et quibusdam voluptas. Beatae aut at ad qui tempore corrupti velit quisquam rerum. Omnis dolorum exercitationem harum qui qui blanditiis neque.
Iusto autem itaque. Repudiandae hic quae aspernatur ea neque qui. Architecto voluptatem magni. Vel magnam quod et tempora deleniti error rerum nihil tempora.
</p>
<h3>Et quae iure vel ut odit alias.</h3>
<p>
Officiis animi maxime nulla quo et harum eum quis a. Sit hic in qui quos fugit ut rerum atque. Optio provident dolores atque voluptatem rem excepturi molestiae qui. Voluptatem laborum omnis ullam quibusdam perspiciatis nulla nostrum. Voluptatum est libero eum nesciunt aliquid qui.
Quia et suscipit non sequi. Maxime sed odit. Beatae nesciunt nesciunt accusamus quia aut ratione aspernatur dolor. Sint harum eveniet dicta exercitationem minima. Exercitationem omnis asperiores natus aperiam dolor consequatur id ex sed. Quibusdam rerum dolores sint consequatur quidem ea.
Beatae minima sunt libero soluta sapiente in rem assumenda. Et qui odit voluptatem. Cum quibusdam voluptatem voluptatem accusamus mollitia aut atque aut.
</p>
<img src="<?php echo $baseUrl; ?>/themes/template3/assets/img/blog/blog-inside-post.jpg" class="img-fluid" alt="">
<h3>Ut repellat blanditiis est dolore sunt dolorum quae.</h3>
<p>
Rerum ea est assumenda pariatur quasi et quam. Facilis nam porro amet nostrum. In assumenda quia quae a id praesentium. Quos deleniti libero sed occaecati aut porro autem. Consectetur sed excepturi sint non placeat quia repellat incidunt labore. Autem facilis hic dolorum dolores vel.
Consectetur quasi id et optio praesentium aut asperiores eaque aut. Explicabo omnis quibusdam esse. Ex libero illum iusto totam et ut aut blanditiis. Veritatis numquam ut illum ut a quam vitae.
</p>
<p>
Alias quia non aliquid. Eos et ea velit. Voluptatem maxime enim omnis ipsa voluptas incidunt. Nulla sit eaque mollitia nisi asperiores est veniam.
</p>
<div class="content article-body">
<?php echo $article['content'] ?? ''; ?>
</div><!-- End post content -->
<div class="meta-bottom">
<i class="bi bi-folder"></i>
<ul class="cats">
<li><a href="#">Business</a></li>
<li><span><?php echo htmlspecialchars($cateName); ?></span></li>
</ul>
</div><!-- End meta bottom -->
<?php else: ?>
<p class="text-center py-5">暂无文章数据,请从<a href="<?php echo htmlspecialchars($baseUrl . '/blog'); ?>">新闻列表</a>进入详情。</p>
<?php endif; ?>
</article>
</article>
</div>
</section>
</div>
</section><!-- /Blog Details Section -->
</div>
</div>
</div>
</div>
</main>
<?php require_once __DIR__ . '/footer.php'; ?>
<?php if ($hasArticle): ?>
<script>
(function () {
var id = <?php echo (int) ($article['id'] ?? 0); ?>;
if (!id) return;
var url = <?php echo json_encode(rtrim($baseUrl, '/') . '/articleViews/', JSON_UNESCAPED_SLASHES); ?> + id;
var el = document.getElementById('article-views-count');
fetch(url, { method: 'POST', credentials: 'same-origin', headers: { 'Accept': 'application/json' } })
.then(function (r) { return r.json(); })
.then(function (res) {
if (res && res.code === 200 && res.data && typeof res.data.views !== 'undefined' && el) {
el.textContent = '阅读 ' + res.data.views;
}
})
.catch(function () { /* 静默失败,保留服务端渲染的阅读量 */ });
})();
</script>
<script>
(function () {
var root = document.querySelector('.blog-details .content.article-body');
if (!root) return;
root.querySelectorAll('pre').forEach(function (pre) {
if (pre.closest('.article-code-block')) return;
var wrap = document.createElement('div');
wrap.className = 'article-code-block';
pre.parentNode.insertBefore(wrap, pre);
wrap.appendChild(pre);
var btn = document.createElement('button');
btn.type = 'button';
btn.className = 'article-code-copy';
btn.setAttribute('aria-label', '复制代码');
btn.textContent = '复制';
wrap.insertBefore(btn, pre);
btn.addEventListener('click', function () {
var text = (pre.innerText !== undefined ? pre.innerText : pre.textContent) || '';
function showDone() {
btn.textContent = '已复制';
btn.classList.add('is-done');
clearTimeout(btn._copyReset);
btn._copyReset = setTimeout(function () {
btn.textContent = '复制';
btn.classList.remove('is-done');
}, 2000);
}
function showFail() {
btn.textContent = '复制失败';
clearTimeout(btn._copyReset);
btn._copyReset = setTimeout(function () { btn.textContent = '复制'; }, 2000);
}
if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
navigator.clipboard.writeText(text).then(showDone).catch(function () {
fallbackCopy();
});
} else {
fallbackCopy();
}
function fallbackCopy() {
var ta = document.createElement('textarea');
ta.value = text;
ta.setAttribute('readonly', '');
ta.style.position = 'fixed';
ta.style.left = '-9999px';
document.body.appendChild(ta);
ta.select();
try {
if (document.execCommand('copy')) showDone();
else showFail();
} catch (e) {
showFail();
}
document.body.removeChild(ta);
}
});
});
})();
</script>
<?php endif; ?>
<?php require_once __DIR__ . '/footer.php'; ?>
+243 -134
View File
@@ -1,162 +1,271 @@
<?php
$pageTitle = '新闻中心 - Nova';
$pageDescription = 'Nova Bootstrap Template 的新闻中心页面';
$pageKeywords = 'blog, news, nova';
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
$pageTitle = '新闻中心 - 云泽';
$pageDescription = '云泽的新闻中心页面';
$pageKeywords = 'blog, news, 云泽';
require_once __DIR__ . '/header.php';
?>
<?php
// 本页使用本地分页:根据 query 参数 ?page=n 只展示 newsList 的切片
$allNewsList = is_array($newsList ?? null) ? $newsList : [];
$currentPage = (int)($_GET['page'] ?? 1);
if ($currentPage < 1) {
$currentPage = 1;
use think\facade\Request;
// 确保 baseUrl 不带查询参数,避免分页链接出现双 ? 的问题
$scheme = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'] ?? '';
$baseUrlBlog = $scheme . '://' . $host;
// 新闻列表:服务端分页
// Nginx 未传递 QUERY_STRING,从 REQUEST_URI 解析
$requestUri = $_SERVER['REQUEST_URI'] ?? '';
$queryString = '';
if (strpos($requestUri, '?') !== false) {
$queryString = substr($requestUri, strpos($requestUri, '?') + 1);
}
parse_str($queryString, $queryArr);
$selectedCateId = isset($queryArr['cate']) ? (int) $queryArr['cate'] : 0;
if ($selectedCateId < 0) {
$selectedCateId = 0;
}
$pageSize = 8; // 每页展示 8 条(grid 中每条占 col-lg-6,所以刚好四行)
$totalCount = count($allNewsList);
$totalPages = (int)ceil($totalCount / $pageSize);
if ($totalPages < 1) {
$totalPages = 1;
$listPage = isset($queryArr['page']) ? max(1, (int) $queryArr['page']) : 1;
$pageSize = 8;
$host = $_SERVER['HTTP_HOST'] ?? '';
$blogNewsQuery = [
'baseUrl' => $host,
'page' => $listPage,
'page_size' => $pageSize,
];
if ($selectedCateId > 0) {
$blogNewsQuery['cate'] = $selectedCateId;
}
$blogNewsUrl = $apiUrl . '/getCenterNews?' . http_build_query($blogNewsQuery);
$blogNewsRes = fetchApiData($blogNewsUrl);
// count:当前「全部」或某分类下的条数(用于分页);total:当前租户下新闻总条数
$listTotal = array_key_exists('count', $blogNewsRes ?? [])
? (int) $blogNewsRes['count']
: (int) ($blogNewsRes['total'] ?? 0);
$allNewsList = is_array($blogNewsRes['list'] ?? null) ? $blogNewsRes['list'] : [];
$totalPages = max(1, (int) ceil($listTotal / $pageSize));
if ($listPage > $totalPages && $listTotal > 0) {
$listPage = $totalPages;
$blogNewsQuery['page'] = $listPage;
$blogNewsUrl = $apiUrl . '/getCenterNews?' . http_build_query($blogNewsQuery);
$blogNewsRes = fetchApiData($blogNewsUrl);
$listTotal = array_key_exists('count', $blogNewsRes ?? [])
? (int) $blogNewsRes['count']
: (int) ($blogNewsRes['total'] ?? 0);
$allNewsList = is_array($blogNewsRes['list'] ?? null) ? $blogNewsRes['list'] : [];
}
if ($currentPage > $totalPages) {
$currentPage = $totalPages;
// 分类排序:sort 升序,其次 id
$categories = is_array($articleCategoryList ?? null) ? $articleCategoryList : [];
usort($categories, static function ($a, $b) {
$sa = (int) ($a['sort'] ?? 0);
$sb = (int) ($b['sort'] ?? 0);
if ($sa !== $sb) {
return $sa <=> $sb;
}
return ((int) ($a['id'] ?? 0)) <=> ((int) ($b['id'] ?? 0));
});
// 构建分类父子结构
$rootCategories = [];
$childrenMap = [];
foreach ($categories as $cat) {
$catId = (int) ($cat['id'] ?? 0);
$catCid = (int) ($cat['cid'] ?? 0);
if ($catId <= 0) continue;
if ($catCid === 0) {
$rootCategories[] = $cat;
} else {
if (!isset($childrenMap[$catCid])) {
$childrenMap[$catCid] = [];
}
$childrenMap[$catCid][] = $cat;
}
}
$newsListForPage = array_slice($allNewsList, ($currentPage - 1) * $pageSize, $pageSize);
/** @return array<string, int|string> */
$blogPageQuery = static function (int $lp, int $cateId): array {
return ['cate' => $cateId, 'page' => $lp];
};
?>
<!-- Page Title -->
<div class="page-title dark-background" data-aos="fade" style="background-image: url(<?php echo $baseUrl; ?>/themes/template3/assets/img/blog-page-title-bg.jpg);">
<div class="container">
<h1>新闻中心</h1>
<nav class="breadcrumbs">
<ol>
<li><a href="<?php echo $baseUrl; ?>/">主页</a></li>
<li class="current">新闻中心</li>
</ol>
</nav>
</div>
</div><!-- End Page Title -->
<!-- Page Title -->
<div class="page-title dark-background" data-aos="fade" style="background-image: url(<?php echo $baseUrl; ?>/themes/template3/assets/img/blog-page-title-bg.jpg);">
<div class="container">
<h1>新闻中心</h1>
<nav class="breadcrumbs">
<ol>
<li><a href="<?php echo htmlspecialchars($baseUrl); ?>/">主页</a></li>
<li class="current">新闻中心</li>
</ol>
</nav>
</div>
</div><!-- End Page Title -->
<div class="container">
<div class="row">
<div class="container">
<div class="row">
<div class="col-lg-12">
<!-- Blog Posts Section -->
<section id="blog-posts" class="blog-posts section">
<div class="container">
<div class="row gy-4">
<?php if (!empty($newsList)): ?>
<?php foreach ($newsListForPage as $i => $item): ?>
<!-- 左侧:新闻分类 -->
<aside class="col-lg-3 col-md-4 blog-layout-sidebar">
<nav class="blog-category-nav" aria-label="新闻分类">
<h2 class="nav-title">新闻分类</h2>
<ul>
<li>
<a
href="<?php echo htmlspecialchars($baseUrlBlog); ?>/blog?<?php echo http_build_query(['cate' => 0, 'page' => 1]); ?>"
class="<?php echo $selectedCateId === 0 ? 'active' : ''; ?>">全部</a>
</li>
<?php foreach ($rootCategories as $rootCat): ?>
<?php
$rootId = (int) ($rootCat['id'] ?? 0);
$rootName = (string) ($rootCat['name'] ?? '未命名');
$rootLink = $baseUrlBlog . '/blog?' . http_build_query(['cate' => $rootId, 'page' => 1]);
$children = $childrenMap[$rootId] ?? [];
$hasChildren = !empty($children);
$isParentActive = $selectedCateId === $rootId;
?>
<li>
<a
href="<?php echo htmlspecialchars($rootLink); ?>"
class="<?php echo $isParentActive ? 'active' : ''; ?>"><?php echo htmlspecialchars($rootName); ?></a>
<?php if ($hasChildren): ?>
<ul class="children">
<?php foreach ($children as $childCat): ?>
<?php
$title = $item['title'] ?? '无标题';
$author = $item['author'] ?? '';
$category = $item['category'] ?? ($item['category_name'] ?? '');
$publishDateRaw = $item['publish_date'] ?? '';
$postDate = !empty($publishDateRaw) ? date('Y年m月d日', strtotime($publishDateRaw)) : '';
$thumbUrl = $item['thumb'] ?? ($item['image'] ?? '');
if (!empty($thumbUrl)) {
if (strpos($thumbUrl, 'http') === 0) {
$imgSrc = $thumbUrl;
} elseif (strpos($thumbUrl, '/') === 0) {
$imgSrc = $apiUrl . $thumbUrl;
} else {
$imgSrc = $apiUrl . '/' . $thumbUrl;
}
} else {
$imgIndex = ($i % 6) + 1; // blog-1.jpg ~ blog-6.jpg
$imgSrc = $baseUrl . "/themes/template3/assets/img/blog/blog-{$imgIndex}.jpg";
}
// 文章详情链接必须走前台域名,否则 api.yunzer.cn 上租户解析不生效
$articleUrl = $baseUrl . '/article_detail/' . ($item['id'] ?? 0);
$childId = (int) ($childCat['id'] ?? 0);
$childName = (string) ($childCat['name'] ?? '未命名');
$childLink = $baseUrlBlog . '/blog?' . http_build_query(['cate' => $childId, 'page' => 1]);
?>
<div class="col-lg-3 col-md-6">
<article>
<div class="post-media">
<img src="<?php echo htmlspecialchars($imgSrc); ?>" alt="" class="post-media-img" />
</div>
<p class="post-category"><?php echo htmlspecialchars($category); ?></p>
<h2 class="title">
<a href="<?php echo htmlspecialchars($articleUrl); ?>">
<?php echo htmlspecialchars($title); ?>
</a>
</h2>
<div class="d-flex align-items-center">
<img src="<?php echo $baseUrl; ?>/themes/template3/assets/img/blog/blog-author.jpg" alt="" class="img-fluid post-author-img flex-shrink-0">
<div class="post-meta">
<p class="post-author"><?php echo htmlspecialchars($author); ?></p>
<p class="post-date">
<time datetime="<?php echo htmlspecialchars($publishDateRaw); ?>">
<?php echo htmlspecialchars($postDate); ?>
</time>
</p>
</div>
</div>
</article>
</div><!-- End post list item -->
<?php endforeach; ?>
<?php else: ?>
<div class="col-12 text-center">
<p>暂无新闻数据</p>
</div>
<?php endif; ?>
</div>
</div>
</section><!-- /Blog Posts Section -->
<!-- Blog Pagination Section -->
<section id="blog-pagination" class="blog-pagination section">
<div class="container">
<div class="d-flex justify-content-center">
<ul>
<?php
$prevPage = max(1, $currentPage - 1);
$nextPage = min($totalPages, $currentPage + 1);
$showMax = 10;
$showTotal = min($totalPages, $showMax);
?>
<li><a href="<?php echo $baseUrl; ?>/blog?page=<?php echo $prevPage; ?>"><i class="bi bi-chevron-left"></i></a></li>
<?php for ($p = 1; $p <= $showTotal; $p++): ?>
<li>
<a
href="<?php echo $baseUrl; ?>/blog?page=<?php echo $p; ?>"
<?php echo ($p === $currentPage) ? 'class="active"' : ''; ?>
>
<?php echo $p; ?>
</a>
href="<?php echo htmlspecialchars($childLink); ?>"
class="<?php echo $selectedCateId === $childId ? 'active' : ''; ?>"><?php echo htmlspecialchars($childName); ?></a>
</li>
<?php endfor; ?>
<li><a href="<?php echo $baseUrl; ?>/blog?page=<?php echo $nextPage; ?>"><i class="bi bi-chevron-right"></i></a></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
</nav>
</aside>
<!-- 右侧:当前分类下的文章列表 -->
<div class="col-lg-9 col-md-8">
<section id="blog-posts" class="blog-posts section">
<div class="container h520 px-0">
<div class="row gy-4">
<?php if (!empty($allNewsList)): ?>
<?php foreach ($allNewsList as $i => $item): ?>
<?php
$title = $item['title'] ?? '无标题';
$author = $item['author'] ?? '';
$cateKey = (int) ($item['cate'] ?? 0);
$category = $item['category'] ?? ($item['category_name'] ?? '');
$publishDateRaw = $item['publish_date'] ?? '';
$postDate = !empty($publishDateRaw) ? date('Y年m月d日', strtotime((string) $publishDateRaw)) : '';
$thumbUrl = $item['thumb'] ?? ($item['image'] ?? '');
if (!empty($thumbUrl)) {
if (strpos($thumbUrl, 'http') === 0) {
$imgSrc = $thumbUrl;
} elseif (strpos($thumbUrl, '/') === 0) {
$imgSrc = $apiUrl . $thumbUrl;
} else {
$imgSrc = $apiUrl . '/' . $thumbUrl;
}
} else {
$imgIndex = ($i % 6) + 1;
$imgSrc = $baseUrl . "/themes/template3/assets/img/blog/blog-{$imgIndex}.jpg";
}
$articleUrl = $baseUrl . '/article_detail/' . ($item['id'] ?? 0);
?>
<div class="col-lg-3 col-md-6">
<article>
<div class="post-media">
<img src="<?php echo htmlspecialchars($imgSrc); ?>" alt="" class="post-media-img" />
</div>
<!-- <p class="post-category"><?php echo htmlspecialchars($category); ?></p> -->
<h2 class="title">
<a href="<?php echo htmlspecialchars($articleUrl); ?>">
<?php echo htmlspecialchars($title); ?>
</a>
</h2>
<div class="d-flex align-items-center">
<img src="<?php echo $baseUrl; ?>/themes/template3/assets/img/blog/blog-author.jpg" alt="" class="img-fluid post-author-img flex-shrink-0">
<div class="post-meta">
<p class="post-author"><?php echo htmlspecialchars($author); ?></p>
<p class="post-date">
<time datetime="<?php echo htmlspecialchars($publishDateRaw); ?>">
<?php echo htmlspecialchars($postDate); ?>
</time>
</p>
</div>
</div>
</article>
</div>
<?php endforeach; ?>
<?php else: ?>
<div class="col-12 text-center py-5">
<p class="mb-0"><?php echo $selectedCateId > 0 ? '该分类下暂无新闻' : '暂无新闻数据'; ?></p>
</div>
</div>
</section><!-- /Blog Pagination Section -->
<?php endif; ?>
</div>
</div>
</section><!-- /Blog Posts Section -->
<?php if (!empty($allNewsList) && $totalPages >= 1): ?>
<section id="blog-pagination" class="blog-pagination section">
<div class="container px-0">
<div class="d-flex justify-content-center">
<ul>
<?php
$prevPage = max(1, $listPage - 1);
$nextPage = min($totalPages, $listPage + 1);
$showMax = 10;
$showTotal = min($totalPages, $showMax);
$prevHref = $baseUrlBlog . '/blog?' . http_build_query($blogPageQuery($prevPage, $selectedCateId));
$nextHref = $baseUrlBlog . '/blog?' . http_build_query($blogPageQuery($nextPage, $selectedCateId));
?>
<li><a href="<?php echo htmlspecialchars($prevHref); ?>"><i class="bi bi-chevron-left"></i></a></li>
<?php for ($p = 1; $p <= $showTotal; $p++): ?>
<?php $pHref = $baseUrlBlog . '/blog?' . http_build_query($blogPageQuery($p, $selectedCateId)); ?>
<li>
<a
href="<?php echo htmlspecialchars($pHref); ?>"
<?php echo ($p === $listPage) ? 'class="active"' : ''; ?>>
<?php echo $p; ?>
</a>
</li>
<?php endfor; ?>
<li><a href="<?php echo htmlspecialchars($nextHref); ?>"><i class="bi bi-chevron-right"></i></a></li>
</ul>
</div>
</div>
</section><!-- /Blog Pagination Section -->
<?php endif; ?>
</div>
</div>
</main>
</div>
</div>
</main>
<?php require_once __DIR__ . '/footer.php'; ?>
-2
View File
@@ -55,8 +55,6 @@ $articleCategoryUrl = $apiUrl . '/getArticleCategories?baseUrl=' . urlencode($ho
$articleCategoryRes = fetchApiData($articleCategoryUrl);
$articleCategoryList = $articleCategoryRes['list'] ?? [];
print_r($articleCategoryList);
// 获取特色服务
$servicesList = $homeInfo['services'] ?? [];