Files
YschcmsService/src/main/resources/templates/themes/academic-navy/home.html
T
chenxing d2df12b91e
build / build-push (push) Successful in 51s
feat(theme)!: 下线粉/紫两包,改以学院蓝为基座交付 academic-navy/campus-azure 两套高校官网风皮肤
线上 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 项通过
2026-09-19 20:21:30 +08:00

150 lines
9.3 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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>