feat(journalism): 全面升级新闻资讯列表为标题、简介、缩略图专业图文排版模式并适配多端

这个提交包含在:
wikiboss
2026-10-09 09:59:20 +08:00
父节点 c58a89ae31
当前提交 cbadce183b
共有 5 个文件被更改,包括 225 次插入 和 27 次删除

查看文件

@@ -55,7 +55,7 @@ class OfficialJournalism extends OfficialBaseController
->where('deletetime', null) ->where('deletetime', null)
->order('weigh', 'desc') ->order('weigh', 'desc')
->field(['id,title,image,subheading,createtime']) ->field(['id,title,image,subheading,createtime'])
->paginate(15, $simple = $this->isMobile(), $config = [ ->paginate(8, $simple = $this->isMobile(), $config = [
'query' => ['pid' => $p_id, 'sid' => $son_id] 'query' => ['pid' => $p_id, 'sid' => $son_id]
]); ]);
} }

查看文件

@@ -44,15 +44,39 @@
</div> </div>
</div> </div>
<!-- 文章列表:只显示标题即可 --> <!-- 文章资讯列表:标题、简介、缩略图专业图文排版 -->
{if count($data) > 0} {if count($data) > 0}
<div class="nhc-title-list"> <div class="nhc-news-list">
{foreach $data as $index => $item} {foreach $data as $index => $item}
<a href="{:addon_url('official/web.official_journalism/details',[':pid'=>(int)input('param.pid'),':sid'=>(int)input('param.sid'),':id'=>$item['id']])}" class="nhc-title-item" title="{$item['title']}"> <article class="nhc-news-row">
<div class="nhc-title-left"> <a href="{:addon_url('official/web.official_journalism/details',[':pid'=>(int)input('param.pid'),':sid'=>(int)input('param.sid'),':id'=>$item['id']])}" class="nhc-news-thumb" title="{$item['title']}">
<span class="nhc-title-text">{$item['title']}</span> {if !empty($item['image'])}
</div> <img src="{$item['image']}" alt="{$item['title']}" loading="lazy">
{else/}
<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; color: #94A3B8; font-size: 13px;">纳海川科技</div>
{/if}
</a> </a>
<div class="nhc-news-body">
<div>
<div class="nhc-news-meta">
<span class="nhc-news-badge">{if !empty($son_data)}{:ze($son_data,'name')}{else/}{:ze($res,'name')}{/if}</span>
<span class="nhc-news-date">{:date('Y-m-d', $item['createtime'])}</span>
</div>
<a href="{:addon_url('official/web.official_journalism/details',[':pid'=>(int)input('param.pid'),':sid'=>(int)input('param.sid'),':id'=>$item['id']])}" style="text-decoration: none;">
<h2 class="nhc-news-title">{$item['title']}</h2>
</a>
<p class="nhc-news-desc">
{$item['subheading']}
</p>
</div>
<div>
<a href="{:addon_url('official/web.official_journalism/details',[':pid'=>(int)input('param.pid'),':sid'=>(int)input('param.sid'),':id'=>$item['id']])}" class="nhc-news-more">
<span>阅读全文</span>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</a>
</div>
</div>
</article>
{/foreach} {/foreach}
</div> </div>

查看文件

@@ -32,15 +32,37 @@
{/foreach} {/foreach}
</div> </div>
<!-- 移动端文章列表:只显示标题即可 --> <!-- 移动端文章资讯列表:标题、简介、缩略图专业图文排版 -->
{if count($data) > 0} {if count($data) > 0}
<div class="nhc-title-list"> <div style="display: flex; flex-direction: column; gap: 16px;">
{foreach $data as $index => $val} {foreach $data as $index => $val}
<a href="{:addon_url('official/web.official_journalism/details',[':pid'=>(int)input('param.pid'),':sid'=>(int)input('param.sid'),':id'=>$val['id']])}" class="nhc-title-item" style="padding: 14px 16px; border-bottom: 1px solid var(--border-color); display: block;"> <article class="nhc-news-row" style="padding: 16px; flex-direction: column; gap: 14px;">
<h2 style="font-family: var(--font-serif); font-size: 15px; font-weight: 600; color: var(--text-primary); line-height: 1.45; margin: 0;"> <a href="{:addon_url('official/web.official_journalism/details',[':pid'=>(int)input('param.pid'),':sid'=>(int)input('param.sid'),':id'=>$val['id']])}" class="nhc-news-thumb" style="width: 100%; height: 160px;">
{$val['title']} {if !empty($val['image'])}
</h2> <img src="{$val['image']}" alt="{$val['title']}" loading="lazy">
{else/}
<div style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; color: #94A3B8; font-size: 13px;">纳海川科技</div>
{/if}
</a> </a>
<div class="nhc-news-body" style="padding-left: 0;">
<div class="nhc-news-meta">
<span class="nhc-news-badge">{if !empty($son_data)}{:ze($son_data,'name')}{else/}{:ze($res,'name')}{/if}</span>
<span class="nhc-news-date">{:date('Y-m-d', $val['createtime'])}</span>
</div>
<a href="{:addon_url('official/web.official_journalism/details',[':pid'=>(int)input('param.pid'),':sid'=>(int)input('param.sid'),':id'=>$val['id']])}" style="text-decoration: none;">
<h2 class="nhc-news-title" style="font-size: 16px;">{$val['title']}</h2>
</a>
<p class="nhc-news-desc" style="font-size: 13px; margin-bottom: 10px;">
{$val['subheading']}
</p>
<div>
<a href="{:addon_url('official/web.official_journalism/details',[':pid'=>(int)input('param.pid'),':sid'=>(int)input('param.sid'),':id'=>$val['id']])}" class="nhc-news-more" style="font-size: 12px;">
<span>阅读全文</span>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
</a>
</div>
</div>
</article>
{/foreach} {/foreach}
</div> </div>

查看文件

@@ -4108,6 +4108,156 @@ img {
overflow: hidden; overflow: hidden;
} }
/* 7.5 现代中式专业图文行卡片规范(标题、简介、缩略图专业排版) */
.nhc-news-list {
display: flex;
flex-direction: column;
gap: 20px;
}
.nhc-news-row {
display: flex;
align-items: stretch;
background: #FFFFFF;
border: 1px solid var(--border-color);
border-radius: var(--radius-sm);
padding: 24px;
box-shadow: var(--shadow-subtle);
transition: all var(--transition-fast);
text-decoration: none;
overflow: hidden;
}
.nhc-news-row:hover {
border-color: #BFDBFE;
box-shadow: 0 10px 25px -5px rgba(37, 99, 235, 0.08);
transform: translateY(-2px);
text-decoration: none;
}
.nhc-news-thumb {
flex-shrink: 0;
width: 280px;
height: 180px;
border-radius: var(--radius-sm);
overflow: hidden;
background: #F8FAFC;
border: 1px solid var(--border-color);
display: flex;
align-items: center;
justify-content: center;
}
.nhc-news-thumb img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.4s ease;
}
.nhc-news-row:hover .nhc-news-thumb img {
transform: scale(1.04);
}
.nhc-news-body {
flex: 1;
min-width: 0;
padding-left: 28px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.nhc-news-meta {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 8px;
}
.nhc-news-badge {
display: inline-flex;
align-items: center;
font-size: 12px;
font-weight: 500;
padding: 2px 10px;
border-radius: 3px;
background: #EFF6FF;
color: var(--primary);
border: 1px solid #BFDBFE;
white-space: nowrap;
}
.nhc-news-date {
font-size: 13px;
color: var(--text-muted);
font-variant-numeric: tabular-nums;
}
.nhc-news-title {
font-family: var(--font-serif);
font-size: 19px;
font-weight: 700;
color: var(--text-primary);
line-height: 1.45;
margin: 0 0 10px 0;
transition: color var(--transition-fast);
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.nhc-news-row:hover .nhc-news-title {
color: var(--primary);
}
.nhc-news-desc {
font-size: 14px;
color: var(--text-secondary);
line-height: 1.65;
margin: 0 0 14px 0;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.nhc-news-more {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 13px;
font-weight: 600;
color: var(--primary);
}
.nhc-news-row:hover .nhc-news-more svg {
transform: translateX(3px);
}
.nhc-news-more svg {
transition: transform var(--transition-fast);
}
@media (max-width: 768px) {
.nhc-news-row {
flex-direction: column;
padding: 16px;
gap: 14px;
}
.nhc-news-thumb {
width: 100%;
height: 180px;
}
.nhc-news-body {
padding-left: 0;
}
.nhc-news-title {
font-size: 16px;
}
}
/* 8. 现代中式紧凑纯标题列表规范 (nhc-title-list) */ /* 8. 现代中式紧凑纯标题列表规范 (nhc-title-list) */
.nhc-title-list { .nhc-title-list {
background: #FFFFFF; background: #FFFFFF;

查看文件

@@ -23,19 +23,18 @@ class TestJournalismAndArticles(unittest.TestCase):
self.seed_sql_path = os.path.join(BASE_DIR, 'database', 'seeds', 'journalism_articles.sql') self.seed_sql_path = os.path.join(BASE_DIR, 'database', 'seeds', 'journalism_articles.sql')
def test_pc_list_template_spec(self): def test_pc_list_template_spec(self):
"""验证 PC 端文章资讯列表页规范(只显示标题即可)""" """验证 PC 端文章资讯列表页规范(标题、简介、缩略图专业排版)"""
self.assertTrue(os.path.exists(self.pc_list_path), "PC 文章列表模板 list.html 必须存在") self.assertTrue(os.path.exists(self.pc_list_path), "PC 文章列表模板 list.html 必须存在")
content = open(self.pc_list_path, 'r', encoding='utf-8').read() content = open(self.pc_list_path, 'r', encoding='utf-8').read()
self.assertIn("纳海川", content, "必须包含品牌名称【纳海川】") self.assertIn("纳海川", content, "必须包含品牌名称【纳海川】")
self.assertIsNone(re.search(r'纳川(?!海)', content), "严禁将品牌错误简写为【纳川】") self.assertIsNone(re.search(r'纳川(?!海)', content), "严禁将品牌错误简写为【纳川】")
self.assertFalse(re.search(r'[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\u2600-\u26FF\u2700-\u27BF]', content), "严禁包含任何 emoji") self.assertFalse(re.search(r'[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\u2600-\u26FF\u2700-\u27BF]', content), "严禁包含任何 emoji")
self.assertIn("nhc-article-hero", content, "必须包含现代中式 Hero Banner 容器类") self.assertIn("nhc-article-hero", content, "必须包含现代中式 Hero Banner 容器类")
self.assertIn("nhc-title-list", content, "必须包含现代中式纯标题列表容器类 nhc-title-list") self.assertIn("nhc-news-list", content, "必须包含新闻资讯列表容器 nhc-news-list")
self.assertIn("nhc-title-item", content, "必须包含纯标题列表单项类 nhc-title-item") self.assertIn("nhc-news-row", content, "必须包含图文行卡片 nhc-news-row")
self.assertNotIn("nhc-article-card-thumb", content, "文章列表已切换为纯标题列表,严禁展示大封面图") self.assertIn("nhc-news-thumb", content, "必须包含缩略图容器 nhc-news-thumb")
self.assertNotIn("nhc-title-badge", content, "只显示标题:严禁在列表中显示分类徽章") self.assertIn("nhc-news-title", content, "必须包含文章标题 nhc-news-title")
self.assertNotIn("nhc-title-date", content, "只显示标题:严禁在列表中显示日期") self.assertIn("nhc-news-desc", content, "必须包含文章简介 nhc-news-desc")
self.assertNotIn("nhc-title-arrow", content, "只显示标题:严禁在列表中显示箭头")
self.assertIn("render", content, "必须包含分页渲染组件") self.assertIn("render", content, "必须包含分页渲染组件")
def test_pc_details_template_spec(self): def test_pc_details_template_spec(self):
@@ -50,23 +49,26 @@ class TestJournalismAndArticles(unittest.TestCase):
self.assertIn("info_previous_next_wrap", content, "必须包含上一篇/下一篇自然导流结构") self.assertIn("info_previous_next_wrap", content, "必须包含上一篇/下一篇自然导流结构")
def test_mobile_templates_spec_and_bugfix(self): def test_mobile_templates_spec_and_bugfix(self):
"""验证移动端模板规范并彻底修复循环变量与遗留链接 Bug""" """验证移动端模板规范(标题、简介、缩略图专业排版)"""
self.assertTrue(os.path.exists(self.mobile_list_path), "移动端列表模板 list_e.html 必须存在") self.assertTrue(os.path.exists(self.mobile_list_path), "移动端列表模板 list_e.html 必须存在")
self.assertTrue(os.path.exists(self.mobile_details_path), "移动端详情模板 details_e.html 必须存在") self.assertTrue(os.path.exists(self.mobile_details_path), "移动端详情模板 details_e.html 必须存在")
list_content = open(self.mobile_list_path, 'r', encoding='utf-8').read() list_content = open(self.mobile_list_path, 'r', encoding='utf-8').read()
details_content = open(self.mobile_details_path, 'r', encoding='utf-8').read() details_content = open(self.mobile_details_path, 'r', encoding='utf-8').read()
self.assertIn("nhc-title-list", list_content, "移动端列表必须包含纯标题列表结构") self.assertIn("nhc-news-thumb", list_content, "移动端列表必须包含缩略图结构")
self.assertNotIn("nhc-title-badge", list_content, "移动端只显示标题:严禁显示分类徽章") self.assertIn("nhc-news-title", list_content, "移动端列表必须包含标题结构")
self.assertIn("nhc-news-desc", list_content, "移动端列表必须包含简介结构")
self.assertNotIn("official_video/list", details_content, "详情页严禁遗留旧 official_video/list 错误路径") self.assertNotIn("official_video/list", details_content, "详情页严禁遗留旧 official_video/list 错误路径")
self.assertFalse(re.search(r'[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\u2600-\u26FF\u2700-\u27BF]', list_content), "移动端列表严禁 emoji") self.assertFalse(re.search(r'[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\u2600-\u26FF\u2700-\u27BF]', list_content), "移动端列表严禁 emoji")
self.assertFalse(re.search(r'[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\u2600-\u26FF\u2700-\u27BF]', details_content), "移动端详情严禁 emoji") self.assertFalse(re.search(r'[\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\u2600-\u26FF\u2700-\u27BF]', details_content), "移动端详情严禁 emoji")
def test_css_design_tokens_for_articles(self): def test_css_design_tokens_for_articles(self):
"""验证 CSS 中已包含文章与资讯专属样式定义""" """验证 CSS 中已包含文章与资讯专属图文排版样式定义"""
css = open(self.css_path, 'r', encoding='utf-8').read() css = open(self.css_path, 'r', encoding='utf-8').read()
self.assertIn(".nhc-title-list", css, "CSS 必须定义 .nhc-title-list 样式") self.assertIn(".nhc-news-row", css, "CSS 必须定义 .nhc-news-row 图文行卡片样式")
self.assertIn(".nhc-title-item", css, "CSS 必须定义 .nhc-title-item 样式") self.assertIn(".nhc-news-thumb", css, "CSS 必须定义 .nhc-news-thumb 缩略图样式")
self.assertIn(".nhc-news-title", css, "CSS 必须定义 .nhc-news-title 标题样式")
self.assertIn(".nhc-news-desc", css, "CSS 必须定义 .nhc-news-desc 简介样式")
self.assertIn(".nhc-article-body", css, "CSS 必须定义 .nhc-article-body 样式") self.assertIn(".nhc-article-body", css, "CSS 必须定义 .nhc-article-body 样式")
def test_seed_sql_rich_articles(self): def test_seed_sql_rich_articles(self):