+
diff --git a/public/assets/addons/official/css/modern_nhc.css b/public/assets/addons/official/css/modern_nhc.css
index 5ddc945..f0fad71 100644
--- a/public/assets/addons/official/css/modern_nhc.css
+++ b/public/assets/addons/official/css/modern_nhc.css
@@ -4108,6 +4108,156 @@ img {
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) */
.nhc-title-list {
background: #FFFFFF;
diff --git a/tests/test_journalism_and_articles.py b/tests/test_journalism_and_articles.py
index 92a7492..f070791 100644
--- a/tests/test_journalism_and_articles.py
+++ b/tests/test_journalism_and_articles.py
@@ -23,19 +23,18 @@ class TestJournalismAndArticles(unittest.TestCase):
self.seed_sql_path = os.path.join(BASE_DIR, 'database', 'seeds', 'journalism_articles.sql')
def test_pc_list_template_spec(self):
- """验证 PC 端文章资讯列表页规范(只显示标题即可)"""
+ """验证 PC 端文章资讯列表页规范(标题、简介、缩略图专业排版)"""
self.assertTrue(os.path.exists(self.pc_list_path), "PC 文章列表模板 list.html 必须存在")
content = open(self.pc_list_path, 'r', encoding='utf-8').read()
self.assertIn("纳海川", content, "必须包含品牌名称【纳海川】")
self.assertIsNone(re.search(r'纳川(?!海)', content), "严禁将品牌错误简写为【纳川】")
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-title-list", content, "必须包含现代中式纯标题列表容器类 nhc-title-list")
- self.assertIn("nhc-title-item", content, "必须包含纯标题列表单项类 nhc-title-item")
- self.assertNotIn("nhc-article-card-thumb", content, "文章列表已切换为纯标题列表,严禁展示大封面图")
- self.assertNotIn("nhc-title-badge", content, "只显示标题:严禁在列表中显示分类徽章")
- self.assertNotIn("nhc-title-date", content, "只显示标题:严禁在列表中显示日期")
- self.assertNotIn("nhc-title-arrow", content, "只显示标题:严禁在列表中显示箭头")
+ self.assertIn("nhc-news-list", content, "必须包含新闻资讯列表容器 nhc-news-list")
+ self.assertIn("nhc-news-row", content, "必须包含图文行卡片 nhc-news-row")
+ self.assertIn("nhc-news-thumb", content, "必须包含缩略图容器 nhc-news-thumb")
+ self.assertIn("nhc-news-title", content, "必须包含文章标题 nhc-news-title")
+ self.assertIn("nhc-news-desc", content, "必须包含文章简介 nhc-news-desc")
self.assertIn("render", content, "必须包含分页渲染组件")
def test_pc_details_template_spec(self):
@@ -50,23 +49,26 @@ class TestJournalismAndArticles(unittest.TestCase):
self.assertIn("info_previous_next_wrap", content, "必须包含上一篇/下一篇自然导流结构")
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_details_path), "移动端详情模板 details_e.html 必须存在")
list_content = open(self.mobile_list_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.assertNotIn("nhc-title-badge", list_content, "移动端只显示标题:严禁显示分类徽章")
+ self.assertIn("nhc-news-thumb", 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.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")
def test_css_design_tokens_for_articles(self):
- """验证 CSS 中已包含文章与资讯专属样式定义"""
+ """验证 CSS 中已包含文章与资讯专属图文排版样式定义"""
css = open(self.css_path, 'r', encoding='utf-8').read()
- self.assertIn(".nhc-title-list", css, "CSS 必须定义 .nhc-title-list 样式")
- self.assertIn(".nhc-title-item", css, "CSS 必须定义 .nhc-title-item 样式")
+ self.assertIn(".nhc-news-row", css, "CSS 必须定义 .nhc-news-row 图文行卡片样式")
+ 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 样式")
def test_seed_sql_rich_articles(self):