diff --git a/addons/official/controller/web/OfficialJournalism.php b/addons/official/controller/web/OfficialJournalism.php index 4aca894..b102e99 100644 --- a/addons/official/controller/web/OfficialJournalism.php +++ b/addons/official/controller/web/OfficialJournalism.php @@ -55,7 +55,7 @@ class OfficialJournalism extends OfficialBaseController ->where('deletetime', null) ->order('weigh', 'desc') ->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] ]); } diff --git a/addons/official/view/web/official_journalism/list.html b/addons/official/view/web/official_journalism/list.html index 4438109..df46aaa 100644 --- a/addons/official/view/web/official_journalism/list.html +++ b/addons/official/view/web/official_journalism/list.html @@ -44,15 +44,39 @@ - + {if count($data) > 0} -
+
{foreach $data as $index => $item} - -
- {$item['title']} + {/foreach}
diff --git a/addons/official/view/web/official_journalism/list_e.html b/addons/official/view/web/official_journalism/list_e.html index 36896cd..7d43ad8 100644 --- a/addons/official/view/web/official_journalism/list_e.html +++ b/addons/official/view/web/official_journalism/list_e.html @@ -32,15 +32,37 @@ {/foreach}
- + {if count($data) > 0} -
+
{foreach $data as $index => $val} - -

- {$val['title']} -

-
+ {/foreach}
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):