feat(theme): 新增三套高校官网风皮肤 poly-blue/agri-forest/jade-normal
build / build-push (push) Successful in 51s
build / build-push (push) Successful in 51s
以 default 为基座(文件最全:含事项库 service/guide 两模板),设计语言与配色取自 真实高校官网 CSS(浙理工 #0558A5+橙、信阳农林 #1C6E19+麦穗金、信阳师大 #008D3F+暖橙)。 三包均自带九段 home-blocks、:root 契约变量别名、中文防塌列三件套、真实实拍截图 (1920 无头截图缩样)。 - UniversitySkinContractTest(7 用例):九段 fragment/homeStyle 分发/防塌列/变量别名/ 层序/自有截图/主色色域(蓝或绿通道主导,粉紫禁入) - ThemeRegistryTest +哨兵:三包注册成功且主色与 default/scholar-blue 及彼此互异 - jade-normal 修复:news_grid 全宽封面变体把正文列压成 0 宽致 390px 溢出 47px, 增 .jn-feat-a--stack 纵向变体,复测 overflowX=0 - 门禁:ThemeContractIT 14 主题×7 路由全绿;全量 mvn test 393/393 绿
This commit is contained in:
@@ -0,0 +1,257 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<!--
|
||||
agri-forest(农林绿)首页区块 fragment:站点配了 home_layout 时,区块正文必须由本文件渲染,
|
||||
不能落到 themes/common 的通用骨架——否则换肤只换了页头页脚,首页正文仍是骨架内置蓝灰卡(线上事故根因)。
|
||||
九段形态:news_grid(三图焦点+要闻列表)/ media_focus(来源徽标密行)/ compact(46px 日期方块行)/
|
||||
image_wall(四列图片墙)/ card(默认卡:头条+行式)/ ticker(速递横条)/ gallery(校园映像图廊)/
|
||||
links(专题网站缩略卡)/ empty(空态)。
|
||||
契约:block 为渲染端装配的同构 Map(type/title/category.name/homeStyle/items/moreUrl/subtitle/
|
||||
background/source/linkGroups),与回落模式 categoryBlocks 兼容;区块标题一律取 category.name。
|
||||
空态守卫必须包在 th:block 外层:th:replace(优先级 100)先于 th:if(300)求值,写在同一标签上等于没写。
|
||||
-->
|
||||
<!-- 形态一:三图焦点 + 要闻列表(homeStyle=news_grid)——横排 3 张封面卡 + 双列行式列表 -->
|
||||
<section class="af-section af-section--news" th:fragment="news_grid(block)" aria-label="栏目区块"
|
||||
th:with="newsBg=${block['background'] != null ? (block['background'] ? site.config['news_bg_image'] : null) : site.config['news_bg_image']}">
|
||||
<div class="af-sec-bg" th:if="${!#strings.isEmpty(newsBg)}"
|
||||
th:style="|background-image:url(${url.upload(newsBg)})|"></div>
|
||||
<div class="af-block af-container">
|
||||
<div class="af-block-head">
|
||||
<div class="af-block-title">
|
||||
<span class="af-block-icon" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false"><path d="M4 5h11a1 1 0 0 1 1 1v13H6a2 2 0 0 1-2-2V5z"/><path d="M16 9h3a1 1 0 0 1 1 1v7a2 2 0 0 1-2 2"/><path d="M7 9h6M7 13h6M7 17h4"/></svg>
|
||||
</span>
|
||||
<h2 th:text="${block.category.name}">学校要闻</h2>
|
||||
</div>
|
||||
<a class="af-more" th:href="${block.moreUrl}">更多 ></a>
|
||||
</div>
|
||||
<div class="af-news-body" th:if="${!#lists.isEmpty(block.items)}"
|
||||
th:with="rest=${#lists.size(block.items) > 3 ? block.items.subList(3, block.items.size()) : null}">
|
||||
<ul class="af-trio">
|
||||
<li class="af-trio-item" th:each="item, stat : ${block.items}" th:if="${stat.index < 3}">
|
||||
<a th:href="${url.article(item.id)}" th:title="${item.title}">
|
||||
<span class="af-trio-cover" th:if="${item.coverImage != null}">
|
||||
<img th:src="${url.upload(item.coverImage)}" th:alt="${item.title}" loading="lazy">
|
||||
</span>
|
||||
<span class="af-trio-cover af-trio-ph" th:if="${item.coverImage == null}" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24" width="30" height="30" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" focusable="false"><path d="M5 19C5 9 12 4 20 4c0 9-5 15-12 15-1.5 0-3-.4-3-1z"/><path d="M5 19c3-5 6-8 10-10"/></svg>
|
||||
</span>
|
||||
<span class="af-trio-cap">
|
||||
<span class="af-trio-title" th:text="${item.title}">焦点标题</span>
|
||||
<span class="af-trio-meta">
|
||||
<span class="af-top-badge" th:if="${item.topmost == 1}">置顶</span>
|
||||
<time class="af-item-date" th:if="${item.publishTime != null}"
|
||||
th:text="${#temporals.format(item.publishTime, 'yyyy-MM-dd')}">2026-01-01</time>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="af-block-list af-block-list--two" th:if="${rest != null}">
|
||||
<li class="af-line" th:each="item : ${rest}">
|
||||
<a class="af-block-item-title" th:href="${url.article(item.id)}" th:text="${item.title}">文章标题</a>
|
||||
<span class="af-top-badge" th:if="${item.topmost == 1}">置顶</span>
|
||||
<time class="af-item-date" th:if="${item.publishTime != null}"
|
||||
th:text="${#temporals.format(item.publishTime, 'MM-dd')}">01-01</time>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<th:block th:if="${#lists.isEmpty(block.items)}">
|
||||
<div th:replace="~{'themes/agri-forest/fragments/home-blocks' :: empty(${block})}"></div>
|
||||
</th:block>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 形态二:来源徽标密行(homeStyle=media_focus)——学术动态,双列紧凑行 + 来源徽标 -->
|
||||
<section class="af-section af-container" th:fragment="media_focus(block)" aria-label="栏目区块">
|
||||
<div class="af-block">
|
||||
<div class="af-block-head">
|
||||
<div class="af-block-title">
|
||||
<span class="af-block-icon" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false"><path d="M9 3h6M10 3v6L4.6 18A2 2 0 0 0 6.3 21h11.4a2 2 0 0 0 1.7-3L14 9V3"/><path d="M7.5 15h9"/></svg>
|
||||
</span>
|
||||
<h2 th:text="${block.category.name}">学术动态</h2>
|
||||
</div>
|
||||
<a class="af-more" th:href="${block.moreUrl}">更多 ></a>
|
||||
</div>
|
||||
<ul class="af-block-list af-block-list--two" th:if="${!#lists.isEmpty(block.items)}">
|
||||
<li class="af-line af-line--media" th:each="item : ${block.items}">
|
||||
<span class="af-src" th:if="${!#strings.isEmpty(item.source)}" th:text="${item.source}">来源</span>
|
||||
<a class="af-block-item-title" th:href="${url.article(item.id)}" th:text="${item.title}">文章标题</a>
|
||||
<span class="af-top-badge" th:if="${item.topmost == 1}">置顶</span>
|
||||
<time class="af-item-date" th:if="${item.publishTime != null}"
|
||||
th:text="${#temporals.format(item.publishTime, 'MM-dd')}">01-01</time>
|
||||
</li>
|
||||
</ul>
|
||||
<th:block th:if="${#lists.isEmpty(block.items)}">
|
||||
<div th:replace="~{'themes/agri-forest/fragments/home-blocks' :: empty(${block})}"></div>
|
||||
</th:block>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 形态三:46px 日期方块列表(homeStyle=compact)——通知公告:日大号 + 年月小号,墨绿底白字,hover 金线 -->
|
||||
<section class="af-section af-container" th:fragment="compact(block)" aria-label="栏目区块">
|
||||
<div class="af-block">
|
||||
<div class="af-block-head">
|
||||
<div class="af-block-title">
|
||||
<span class="af-block-icon" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false"><path d="M3 10v4l6 1 9 5V4L9 9l-6 1z"/><path d="M7 15l1.5 5h3L10 16"/></svg>
|
||||
</span>
|
||||
<h2 th:text="${block.category.name}">通知公告</h2>
|
||||
</div>
|
||||
<a class="af-more" th:href="${block.moreUrl}">更多 ></a>
|
||||
</div>
|
||||
<ul class="af-block-list af-notice-list" th:if="${!#lists.isEmpty(block.items)}">
|
||||
<li class="af-line af-line--date" th:each="item : ${block.items}">
|
||||
<span class="af-date-chip" th:if="${item.publishTime != null}" th:with="d=${item.publishTime}">
|
||||
<b th:text="${#temporals.format(d, 'dd')}">01</b>
|
||||
<i th:text="${#temporals.format(d, 'yyyy-MM')}">2026-01</i>
|
||||
</span>
|
||||
<a class="af-block-item-title" th:href="${url.article(item.id)}" th:text="${item.title}">文章标题</a>
|
||||
<span class="af-top-badge" th:if="${item.topmost == 1}">置顶</span>
|
||||
</li>
|
||||
</ul>
|
||||
<th:block th:if="${#lists.isEmpty(block.items)}">
|
||||
<div th:replace="~{'themes/agri-forest/fragments/home-blocks' :: empty(${block})}"></div>
|
||||
</th:block>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 形态四:四列图片墙(homeStyle=image_wall)——仅取有封面的条目,悬浮标题 -->
|
||||
<section class="af-section af-container" th:fragment="image_wall(block)" aria-label="栏目区块">
|
||||
<div class="af-block-head">
|
||||
<div class="af-block-title">
|
||||
<span class="af-block-icon" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false"><rect x="3" y="5" width="18" height="14" rx="1"/><circle cx="8.5" cy="10" r="1.5"/><path d="M21 15.5l-4.5-4.5-9 8"/></svg>
|
||||
</span>
|
||||
<h2 th:text="${block.category.name}">图片新闻</h2>
|
||||
</div>
|
||||
<a class="af-more" th:href="${block.moreUrl}">更多 ></a>
|
||||
</div>
|
||||
<ul class="af-wall" th:if="${!#lists.isEmpty(block.items)}">
|
||||
<li class="af-wall-item" th:each="item : ${block.items}" th:if="${item.coverImage != null}">
|
||||
<a th:href="${url.article(item.id)}" th:title="${item.title}">
|
||||
<img th:src="${url.upload(item.coverImage)}" th:alt="${item.title}" loading="lazy">
|
||||
<span class="af-wall-cap" th:text="${item.title}">图片标题</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<th:block th:if="${#lists.isEmpty(block.items)}">
|
||||
<div th:replace="~{'themes/agri-forest/fragments/home-blocks' :: empty(${block})}"></div>
|
||||
</th:block>
|
||||
</section>
|
||||
|
||||
<!-- 形态五:默认卡(homeStyle 未设/未知)——整宽头条(有封面则图文)+ 行式列表 -->
|
||||
<section class="af-section af-container" th:fragment="card(block)" aria-label="栏目区块">
|
||||
<div class="af-block">
|
||||
<div class="af-block-head">
|
||||
<div class="af-block-title">
|
||||
<span class="af-block-icon" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false"><path d="M6 3h8l4 4v14H6V3z"/><path d="M14 3v4h4M9 12h6M9 16h6"/></svg>
|
||||
</span>
|
||||
<h2 th:text="${block.category.name}">栏目名</h2>
|
||||
</div>
|
||||
<a class="af-more" th:href="${block.moreUrl}">更多 ></a>
|
||||
</div>
|
||||
<div class="af-feat" th:if="${!#lists.isEmpty(block.items)}" th:with="feat=${block.items[0]}">
|
||||
<a th:href="${url.article(feat.id)}">
|
||||
<span class="af-feat-cover" th:if="${feat.coverImage != null}">
|
||||
<img th:src="${url.upload(feat.coverImage)}" th:alt="${feat.title}" loading="lazy">
|
||||
</span>
|
||||
<span class="af-feat-body">
|
||||
<span class="af-feat-title" th:text="${feat.title}">头条标题</span>
|
||||
<span class="af-feat-summary" th:if="${!#strings.isEmpty(feat.summary)}"
|
||||
th:text="${feat.summary}">摘要</span>
|
||||
<span class="af-feat-meta">
|
||||
<span class="af-top-badge" th:if="${feat.topmost == 1}">置顶</span>
|
||||
<time class="af-item-date" th:if="${feat.publishTime != null}"
|
||||
th:text="${#temporals.format(feat.publishTime, 'yyyy-MM-dd')}">2026-01-01</time>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<ul class="af-block-list" th:if="${#lists.size(block.items) > 1}">
|
||||
<li class="af-line" th:each="item, stat : ${block.items}" th:if="${stat.index > 0}">
|
||||
<a class="af-block-item-title" th:href="${url.article(item.id)}" th:text="${item.title}">文章标题</a>
|
||||
<span class="af-top-badge" th:if="${item.topmost == 1}">置顶</span>
|
||||
<time class="af-item-date" th:if="${item.publishTime != null}"
|
||||
th:text="${#temporals.format(item.publishTime, 'MM-dd')}">01-01</time>
|
||||
</li>
|
||||
</ul>
|
||||
<th:block th:if="${#lists.isEmpty(block.items)}">
|
||||
<div th:replace="~{'themes/agri-forest/fragments/home-blocks' :: empty(${block})}"></div>
|
||||
</th:block>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 布局模式:要闻速递横条(block.items 由渲染端按置顶+最新装配;单行不换行,中文不塌列) -->
|
||||
<section class="af-ticker" th:fragment="ticker(block)" aria-label="要闻速递"
|
||||
th:if="${!#lists.isEmpty(block.items)}">
|
||||
<div class="af-container af-ticker-inner">
|
||||
<span class="af-ticker-label">
|
||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M3 10v4l6 1 9 5V4L9 9l-6 1z"/><path d="M7 15l1.5 5h3L10 16"/></svg>
|
||||
<span th:text="${block.title}">要闻速递</span>
|
||||
</span>
|
||||
<ul class="af-ticker-list">
|
||||
<li th:each="item : ${block.items}">
|
||||
<a class="af-ticker-item" th:href="${url.article(item.id)}" th:text="${item.title}">要闻标题</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 布局模式:校园映像图廊(block.items = 轮播位 gallery,四列图片墙 + 悬浮标题) -->
|
||||
<section class="af-section af-container af-gallery" th:fragment="gallery(block)" aria-label="校园映像"
|
||||
th:if="${!#lists.isEmpty(block.items)}">
|
||||
<div class="af-gallery-head">
|
||||
<div class="af-block-title af-gallery-title">
|
||||
<span class="af-block-icon" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false"><rect x="3" y="5" width="18" height="14" rx="1"/><circle cx="8.5" cy="10" r="1.5"/><path d="M21 15.5l-4.5-4.5-9 8"/></svg>
|
||||
</span>
|
||||
<h2 th:text="${block.title}">校园映像</h2>
|
||||
</div>
|
||||
<p class="af-gallery-sub" th:if="${!#strings.isEmpty(block.subtitle)}" th:text="${block.subtitle}">
|
||||
Campus Gallery
|
||||
</p>
|
||||
</div>
|
||||
<ul class="af-wall af-wall--4">
|
||||
<li class="af-wall-item" th:each="g : ${block.items}">
|
||||
<a th:href="${g.linkUrl}" th:title="${g.title}">
|
||||
<img th:src="${g.imageUrl}" th:alt="${g.title}" loading="lazy">
|
||||
<span class="af-wall-cap" th:if="${!#strings.isEmpty(g.title)}" th:text="${g.title}">作品标题</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<!-- 布局模式:专题网站缩略卡(block.linkGroups 由渲染端按 group 过滤;4 列卡片含标题条) -->
|
||||
<section class="af-section af-container" th:fragment="links(block)" aria-label="专题网站">
|
||||
<div class="af-block-head">
|
||||
<div class="af-block-title">
|
||||
<span class="af-block-icon" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" focusable="false"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>
|
||||
</span>
|
||||
<h2 th:text="${block.title}">专题网站</h2>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="af-topic-grid">
|
||||
<th:block th:each="grp : ${block.linkGroups}">
|
||||
<li class="af-topic" th:each="l : ${grp.links}">
|
||||
<a th:href="${l.linkUrl}" target="_blank" rel="noopener" th:title="${l.name}">
|
||||
<span class="af-topic-bar" aria-hidden="true"></span>
|
||||
<span class="af-topic-glyph" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" focusable="false"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c3 3.5 3 14 0 18M12 3c-3 3.5-3 14 0 18"/></svg>
|
||||
</span>
|
||||
<span class="af-topic-name" th:text="${l.name}">专题名称</span>
|
||||
</a>
|
||||
</li>
|
||||
</th:block>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<!-- 空态(各形态引用时守卫必须包在 th:block 外层:th:replace 优先级 100 先于 th:if 300 求值) -->
|
||||
<div class="af-empty" th:fragment="empty(block)">
|
||||
<p>栏目暂无内容</p>
|
||||
<p class="af-empty-hint">内容发布后将在此展示</p>
|
||||
</div>
|
||||
</html>
|
||||
Reference in New Issue
Block a user