feat(theme)!: 下线粉/紫两包,改以学院蓝为基座交付 academic-navy/campus-azure 两套高校官网风皮肤
build / build-push (push) Successful in 51s
build / build-push (push) Successful in 51s
线上 10027 首页配了 home_layout 后走 themes/common 通用骨架,派生皮肤只换了页头页脚, 正文仍是骨架内置蓝灰卡(外加 cherry-bloom 的粉紫配色),用户判定"完全不能用"。 - 新增 academic-navy(学府藏蓝,an-)/campus-azure(青蓝校园,cz-):以 scholar-blue 为基座, 各自自带 fragments/home-blocks.html 与 home.html 分发,news_grid/media_focus/compact/ image_wall/card/ticker/gallery/links 八种区块形态全部由皮肤渲染,未知类型才回落通用骨架 - 两套皮肤 :root 把私有变量别名到 --primary/--text/--card-bg/--border/--radius 契约变量; 中文行锁 nowrap+min-width:0+ellipsis,grid 轨道用 minmax(0,1fr) 防窄视口压塌 - 新闻背景带 .an-sec-bg/.cz-sec-bg 压 z-index:0、卡片抬到 1:绝对定位层默认画在普通流之上, 不抬起会整张卡片泛成底图色(无头截图实测) - common 骨架空态守卫改包 th:block(th:replace 优先级 100 先于 th:if 300) - 删除 cherry-bloom/violet-iris;sql/upgrade_theme_academic_skins.sql 幂等改指存量站点 (站点缓存 CacheNames.SITE TTL 30 分钟,改库后需重启或在管理端重存站点) - 测试:AcademicSkinContractTest(区块自带/分发/CJK 行/变量别名/主色必须学术蓝/背景带层级/ 预览图不得沿用基座)、HomeBlocksCssContractTest、PortalHomeLayoutIT 端到端断言零 plb- 输出; 全量 383 项通过
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" lang="zh-CN">
|
||||
<head th:replace="~{${themeBase} + '/fragments/head' :: head}"></head>
|
||||
<body>
|
||||
<header th:replace="~{${themeBase} + '/fragments/header' :: header}"></header>
|
||||
|
||||
<main class="an-home" id="main-content">
|
||||
<!-- 首页轮播位(cms_banner position=home;设计尺寸见 theme.json bannerSize) -->
|
||||
<section class="an-banner" th:if="${!#lists.isEmpty(banners)}" aria-label="焦点图片">
|
||||
<div class="an-banner-slides">
|
||||
<a class="an-banner-slide" th:each="b, stat : ${banners}"
|
||||
th:classappend="${stat.first} ? 'active'"
|
||||
th:href="${b.linkUrl}" th:title="${b.title}">
|
||||
<img th:src="${b.imageUrl}" th:alt="${b.title}">
|
||||
<span class="an-banner-title" th:text="${b.title}">标题</span>
|
||||
</a>
|
||||
</div>
|
||||
<button class="an-banner-arrow prev" type="button" aria-label="上一张" th:if="${#lists.size(banners) > 1}">‹</button>
|
||||
<button class="an-banner-arrow next" type="button" aria-label="下一张" th:if="${#lists.size(banners) > 1}">›</button>
|
||||
<div class="an-banner-dots" th:if="${#lists.size(banners) > 1}">
|
||||
<button th:each="b, stat : ${banners}" th:classappend="${stat.first} ? 'active'"
|
||||
th:aria-label="${'第' + (stat.count) + '张'}"></button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 首页布局模式(docs/16 §4.4):区块形态由本主题 fragments/home-blocks 承载——换肤必须连首页正文一起换,
|
||||
只换页头页脚会被判「皮肤不生效」。未知类型仍回落 common 骨架(渲染端已过滤未知 type,正常不触发)。
|
||||
注意:th:replace(优先级100)先于 th:case(300)执行——case 必须放父元素、replace 放子元素 -->
|
||||
<th:block th:if="${layoutBlocks != null}">
|
||||
<th:block th:each="block : ${layoutBlocks}">
|
||||
<th:block th:switch="${block.type}">
|
||||
<th:block th:case="'article_list'">
|
||||
<th:block th:switch="${block.homeStyle}">
|
||||
<th:block th:case="'news_grid'"><th:block th:replace="~{${themeBase} + '/fragments/home-blocks' :: news_grid(${block})}" /></th:block>
|
||||
<th:block th:case="'media_focus'"><th:block th:replace="~{${themeBase} + '/fragments/home-blocks' :: media_focus(${block})}" /></th:block>
|
||||
<th:block th:case="'compact'"><th:block th:replace="~{${themeBase} + '/fragments/home-blocks' :: compact(${block})}" /></th:block>
|
||||
<th:block th:case="'image_wall'"><th:block th:replace="~{${themeBase} + '/fragments/home-blocks' :: image_wall(${block})}" /></th:block>
|
||||
<th:block th:case="*"><th:block th:replace="~{${themeBase} + '/fragments/home-blocks' :: card(${block})}" /></th:block>
|
||||
</th:block>
|
||||
</th:block>
|
||||
<th:block th:case="'ticker'"><th:block th:replace="~{${themeBase} + '/fragments/home-blocks' :: ticker(${block})}" /></th:block>
|
||||
<th:block th:case="'gallery'"><th:block th:replace="~{${themeBase} + '/fragments/home-blocks' :: gallery(${block})}" /></th:block>
|
||||
<th:block th:case="'links'"><th:block th:replace="~{${themeBase} + '/fragments/home-blocks' :: links(${block})}" /></th:block>
|
||||
<th:block th:case="*"><th:block th:replace="~{'themes/common/fragments/home-blocks' :: render(${block})}" /></th:block>
|
||||
</th:block>
|
||||
</th:block>
|
||||
</th:block>
|
||||
|
||||
<!-- 回落分支(docs/16 D2):无 home_layout 时按现状渲染 -->
|
||||
<th:block th:unless="${layoutBlocks != null}">
|
||||
<section class="an-section an-container" aria-label="头条与图片新闻">
|
||||
<div class="an-headline">
|
||||
<div class="an-headline-card" th:if="${!#lists.isEmpty(topArticles)}">
|
||||
<div class="an-block-head">
|
||||
<h2>头条推荐</h2>
|
||||
<i class="an-block-tick" aria-hidden="true"></i>
|
||||
</div>
|
||||
<ul class="an-headline-list">
|
||||
<li class="an-headline-lead" th:with="lead=${topArticles[0]}">
|
||||
<a class="an-lead-title" th:href="${url.article(lead.id)}" th:text="${lead.title}">头条标题</a>
|
||||
<div class="an-lead-meta">
|
||||
<span class="an-top-badge" th:if="${lead.topmost == 1}">置顶</span>
|
||||
<time th:if="${lead.publishTime != null}"
|
||||
th:text="${#temporals.format(lead.publishTime, 'yyyy-MM-dd')}">2026-01-01</time>
|
||||
</div>
|
||||
</li>
|
||||
<li th:each="item, stat : ${topArticles}" th:if="${stat.index > 0}">
|
||||
<span class="an-top-badge" th:if="${item.topmost == 1}">置顶</span>
|
||||
<a th:href="${url.article(item.id)}" th:text="${item.title}">文章标题</a>
|
||||
<time th:if="${item.publishTime != null}"
|
||||
th:text="${#temporals.format(item.publishTime, 'yyyy-MM-dd')}">2026-01-01</time>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="an-focus-card" th:if="${!#lists.isEmpty(focusArticles)}">
|
||||
<div class="an-block-head">
|
||||
<h2>图片新闻</h2>
|
||||
<i class="an-block-tick" aria-hidden="true"></i>
|
||||
</div>
|
||||
<a class="an-focus-lead" th:with="cover=${focusArticles[0]}"
|
||||
th:href="${url.article(cover.id)}">
|
||||
<span class="an-focus-cover">
|
||||
<img th:src="${url.upload(cover.coverImage)}" th:alt="${cover.title}">
|
||||
</span>
|
||||
<span class="an-focus-title" th:text="${cover.title}">图片新闻标题</span>
|
||||
</a>
|
||||
<ul class="an-focus-rows">
|
||||
<li th:each="row, stat : ${focusArticles}" th:if="${stat.index > 0}">
|
||||
<a th:href="${url.article(row.id)}">
|
||||
<span class="an-row-cover"><img th:src="${url.upload(row.coverImage)}" th:alt="${row.title}"></span>
|
||||
<span class="an-row-title" th:text="${row.title}">图片新闻标题</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 一级栏目区块(聚合列表型子孙栏目 top 8) -->
|
||||
<section class="an-section an-container" aria-label="栏目区块">
|
||||
<div class="an-blocks">
|
||||
<div class="an-block" th:each="block : ${categoryBlocks}">
|
||||
<div class="an-block-head an-block-head--bar">
|
||||
<h2 th:text="${block.category.name}">栏目名</h2>
|
||||
<a class="an-more" th:href="${block.moreUrl}">更多 ›</a>
|
||||
</div>
|
||||
<ul class="an-block-list" th:if="${!#lists.isEmpty(block.items)}">
|
||||
<!-- 区块头条:有封面则图文,无封面则带摘要纯文 -->
|
||||
<li class="an-block-featured" th:with="feat=${block.items[0]}">
|
||||
<a th:href="${url.article(feat.id)}">
|
||||
<span class="an-feat-cover" th:if="${feat.coverImage != null}">
|
||||
<img th:src="${url.upload(feat.coverImage)}" th:alt="${feat.title}">
|
||||
</span>
|
||||
<span class="an-feat-body">
|
||||
<span class="an-feat-title" th:text="${feat.title}">头条标题</span>
|
||||
<span class="an-feat-summary" th:if="${!#strings.isEmpty(feat.summary)}"
|
||||
th:text="${feat.summary}">摘要</span>
|
||||
<span class="an-feat-meta">
|
||||
<span class="an-top-badge" th:if="${feat.topmost == 1}">置顶</span>
|
||||
<time th:if="${feat.publishTime != null}"
|
||||
th:text="${#temporals.format(feat.publishTime, 'yyyy-MM-dd')}">2026-01-01</time>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="an-block-item" th:each="item, stat : ${block.items}" th:if="${stat.index > 0}">
|
||||
<span class="an-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="an-block-item-title" th:href="${url.article(item.id)}" th:text="${item.title}">文章标题</a>
|
||||
<span class="an-top-badge" th:if="${item.topmost == 1}">置顶</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="an-empty" th:if="${#lists.isEmpty(block.items)}">
|
||||
<p>栏目暂无内容</p>
|
||||
<p class="an-empty-hint">内容发布后将在此展示</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</th:block>
|
||||
</main>
|
||||
|
||||
<footer th:replace="~{${themeBase} + '/fragments/footer' :: footer}"></footer>
|
||||
<script th:src="${url.asset('js/main.js')}"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user