diff --git a/sql/edge_seed_visual.sql b/sql/edge_seed_visual.sql index 93cf330..a023f81 100644 --- a/sql/edge_seed_visual.sql +++ b/sql/edge_seed_visual.sql @@ -21,10 +21,10 @@ WHERE code = 'demo'; UPDATE cms_banner SET status = 0 WHERE id = 5; -- 2) 院系子站(demo 的子站:header 出"专业院系"下拉;不配 public_base_url=按请求派生) --- 主题应用:jdx→cherry-bloom(樱语粉韵)、xxgc→violet-iris(鸢尾紫韵)——两套新皮肤随种子落地可视验收 +-- 主题应用:jdx→academic-navy(学府藏蓝)、xxgc→campus-azure(青蓝校园)——两套高校官网风皮肤随种子落地可视验收 INSERT INTO cms_site (id, code, parent_site_id, sch_id, sch_name, name, theme, config_json, status, create_by, create_time) VALUES -(6, 'jdx', 1, 990001, '云海大学', '机电学院', 'cherry-bloom', '{"copyright": "机电学院"}', 1, 'cmssuper', NOW()), -(7, 'xxgc', 1, 990001, '云海大学', '信息工程学院', 'violet-iris', '{"copyright": "信息工程学院"}', 1, 'cmssuper', NOW()); +(6, 'jdx', 1, 990001, '云海大学', '机电学院', 'academic-navy', '{"copyright": "机电学院"}', 1, 'cmssuper', NOW()), +(7, 'xxgc', 1, 990001, '云海大学', '信息工程学院', 'campus-azure', '{"copyright": "信息工程学院"}', 1, 'cmssuper', NOW()); INSERT INTO cms_category (id, site_id, parent_id, name, type, link_url, content, sort_no, status, create_by, create_time) VALUES (61, 6, 0, '学院概况', 2, NULL, '

机电学院(视觉精修种子)。

', 1, 1, 'cmssuper', NOW()), (62, 6, 0, '学院新闻', 1, NULL, NULL, 2, 1, 'cmssuper', NOW()), diff --git a/sql/upgrade_theme_academic_skins.sql b/sql/upgrade_theme_academic_skins.sql new file mode 100644 index 0000000..cd059fc --- /dev/null +++ b/sql/upgrade_theme_academic_skins.sql @@ -0,0 +1,21 @@ +-- ============================================================ +-- 皮肤升级脚本:粉/紫两包下线 → 高校官网风两包接管(幂等,存量库执行一次即可) +-- 背景:cherry-bloom(樱语粉韵)/ violet-iris(鸢尾紫韵)不符合高校门户视觉,已从模板库删除; +-- 存量站点若仍指向这两个主题,ThemeRegistry 匹配不到会静默回落 default(皮肤全丢), +-- 故必须把 theme 列改到新包:academic-navy(学府藏蓝)/ campus-azure(青蓝校园)。 +-- 目标库:存量 bsch_cms(生产/预发);干净环境无需本脚本 +-- 顺序:备份查询 → 回填 → 校验 +-- ============================================================ + +-- 1) 先看受影响站点(人工确认后再执行第 2 步) +SELECT id, path, name, theme FROM cms_site WHERE theme IN ('cherry-bloom', 'violet-iris'); + +-- 2) 回填:粉韵→学府藏蓝,紫韵→青蓝校园(仅命中旧值,重复执行无副作用) +UPDATE cms_site SET theme = 'academic-navy' WHERE theme = 'cherry-bloom'; +UPDATE cms_site SET theme = 'campus-azure' WHERE theme = 'violet-iris'; + +-- 3) 校验(应返回 0 行) +SELECT id, path, theme FROM cms_site WHERE theme IN ('cherry-bloom', 'violet-iris'); + +-- 备注:站点快照走 Caffeine CacheNames.SITE,TTL 30 分钟(±60s 抖动,见 CacheConfig), +-- 实测改库后 400s 门户仍是旧皮肤——改库后请重启服务,或在管理端重新保存一次站点(换肤会主动清缓存)。 diff --git a/src/main/resources/templates/themes/academic-navy/README.md b/src/main/resources/templates/themes/academic-navy/README.md new file mode 100644 index 0000000..7562bf7 --- /dev/null +++ b/src/main/resources/templates/themes/academic-navy/README.md @@ -0,0 +1,33 @@ +# academic-navy(学府藏蓝) + +高校官网风皮肤包:以 `scholar-blue` 为基座派生,配色与首页区块形态按综合类大学官网(浙江理工大学一类)重做。 +配了 `home_layout` 的站点,首页正文区块由本主题 `fragments/home-blocks.html` 渲染——派生包不自带区块形态时, +正文会落到 `themes/common` 的通用骨架,换肤就只剩页头页脚(线上事故根因,见《审计验收清单-20260919》)。 + +## 风格定位 + +- 主色藏蓝 `#0b4a8f` + 加深 `#063161` + 点缀金 `#b8934a`(标题左竖条与居中线脚走金线,区别于基座的全蓝语言)。 +- 圆角收紧到 4/8px、页脚 `#0a2b4d`,行高压到 40px 一档,整体比基座更「机关报」而非「校园活动页」。 +- 全部 11 个颜色参数可在站点 theme_params 微调(`primary/primaryDark/primaryLight/accent/navBg/footerBg/text/textLight/bg/cardBg/border`)。 + +## 首页区块形态(布局模式与回落模式同构) + +| homeStyle | 呈现 | 类名 | +| -- | -- | -- | +| `news_grid` | 双栏要闻:左 3 份图文头条(16:7 封面 + 摘要 + 元信息)、右 2 份紧凑行;区块背景可取站点 `news_bg_image` | `an-split` / `an-feat-*` | +| `compact` | 日期方块行:46px 见方的「日 + 年月」方块,hover 反白 | `an-date-chip` | +| `media_focus` | 媒体聚焦:来源徽标 + 双列密行 | `an-src` / `an-block-list--two` | +| `image_wall` | 四列图片墙:封面 + 底部渐变遮罩标题,hover 抬图 | `an-wall` | +| (空/未知) | 整宽区块卡:头条 + 行式列表 | `an-block--flow` | +| `ticker` | 藏蓝要闻速递横条(单行不换行、超长省略,中文不塌列) | `an-ticker*` | +| `gallery` | 线上展厅:居中双语文区头 + 四列图墙 | `an-gallery` | +| `links` | 友情链接分组(胶囊标签行) | `an-links` | + +## 契约要点 + +- 结构类前缀 `an-`,CSS 变量前缀 `--an-`;`:root` 把 `--an-*` 别名到皮肤契约变量(`--primary`/`--text`/`--card-bg`/`--border`…), + 使 `themes/common` 兜底骨架也能跟随本皮肤着色。 +- 空态文案「栏目暂无内容」只在 `block.items` 为空时出现;守卫必须写在 `th:block` 外层—— + `th:replace`(优先级 100)先于 `th:if`(300)求值,写在同一标签上等于没写。 +- 回归防线:`AcademicSkinContractTest`(自带区块 fragment + 中文不塌列 + 契约变量别名 + 主色必须学术蓝)、 + `PortalHomeLayoutIT#academicNavy_layoutUsesOwnFragments`(布局模式不落通用骨架)、`ThemeContractIT`(7 条路由)。 diff --git a/src/main/resources/templates/themes/academic-navy/assets/css/main.css b/src/main/resources/templates/themes/academic-navy/assets/css/main.css new file mode 100644 index 0000000..4b6886f --- /dev/null +++ b/src/main/resources/templates/themes/academic-navy/assets/css/main.css @@ -0,0 +1,1497 @@ +/* ===================================================================== + academic-navy · 学府藏蓝(基座 scholar-blue,参考语言:综合类高校官网门户) + 设计语言:藏蓝顶栏/品牌行 + 白底主导航(吸顶反蓝) + 金线标题 + 日期方块行 + + 布局模式双栏要闻(左图文右密行)+ 藏蓝要闻速递条 + 深色圆环页脚 + 色彩令牌全部走 CSS 变量,theme.json color 参数经 :root 覆盖真实生效。 + ===================================================================== */ + +/* --------------------------------------------------------------------- + 1. 设计令牌(与 theme.json params 一一对应,禁止出现未消费的声明) + --------------------------------------------------------------------- */ +:root { + --an-primary: #0b4a8f; /* 主色:藏蓝,顶栏/品牌行/标题/强调 */ + --an-primary-dark: #063161; /* 主色加深:hover/渐变端/页脚深端 */ + --an-primary-light: #1a6cb5; /* 主色提亮:亮线条/hover 链接/角标 */ + --an-accent: #b8934a; /* 点缀金:标题下划细线/日期方块描边(高校官网常见金线) */ + --an-nav-bg: #ffffff; /* 主导航条底色(白条学院风) */ + --an-footer-bg: #0a2b4d; /* 页脚主底 */ + --an-text: #2b3644; /* 正文 */ + --an-text-light: #6b7889; /* 次要文字 */ + --an-bg: #f1f4f9; /* 页面底色 */ + --an-card-bg: #ffffff; /* 卡片底 */ + --an-border: #dbe3ee; /* 边框/分隔线 */ + + --an-ink-on-dark: #ffffff; /* 深底主文字 */ + --an-ink-on-dark-soft: rgba(255,255,255,.74); /* 深底次要文字 */ + --an-ink-on-dark-faint: rgba(255,255,255,.46); + --an-panel: rgba(11,74,143,.95); /* 下拉面板(主色半透明) */ + --an-shadow-sm: 0 1px 4px rgba(6,49,97,.08); + --an-shadow-md: 0 6px 18px rgba(6,49,97,.12); + --an-shadow-lg: 0 14px 34px rgba(6,49,97,.18); + --an-radius: 4px; + --an-radius-lg: 8px; + --an-speed: .28s; + --an-font: "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Helvetica Neue", Arial, sans-serif; + + /* 皮肤契约别名:站点配了 home_layout 时首页走 common/fragments/home-blocks 骨架, + 该骨架只认契约变量(--primary/--text/…)。不别名过去,换肤就只换了页头页脚、正文区块仍是骨架内置色。 */ + --primary: var(--an-primary); + --primary-dark: var(--an-primary-dark); + --primary-light: var(--an-primary-light); + --text: var(--an-text); + --text-light: var(--an-text-light); + --bg: var(--an-bg); + --card-bg: var(--an-card-bg); + --border: var(--an-border); + --radius: var(--an-radius-lg); +} + +/* --------------------------------------------------------------------- + 2. Reset 与基础 + --------------------------------------------------------------------- */ +* { margin: 0; padding: 0; box-sizing: border-box; } + +html { -webkit-text-size-adjust: 100%; } + +body { + font-family: var(--an-font); + font-size: 14px; + line-height: 1.7; + color: var(--an-text); + background: var(--an-bg); + word-break: break-word; +} + +ul, ol { list-style: none; } + +a { color: var(--an-primary); text-decoration: none; transition: color var(--an-speed) ease; } +a:hover { color: var(--an-primary-light); } + +img { max-width: 100%; display: block; border: 0; } + +button { font-family: inherit; cursor: pointer; } + +input, button, select, textarea { font-size: inherit; color: inherit; } + +::selection { background: var(--an-primary); color: #fff; } + +/* 跳转链接:仅键盘聚焦时可见 */ +.skip-link { + position: absolute; left: -999px; top: 0; z-index: 300; + background: var(--an-primary-dark); color: #fff; + padding: 8px 18px; border-radius: 0 0 var(--an-radius) 0; +} +.skip-link:focus { left: 0; color: #fff; } + +/* 通用容器 */ +.an-container { + width: 1200px; max-width: calc(100% - 40px); + margin: 0 auto; +} + +/* 屏幕阅读器专用 */ +.an-visually-hidden { + position: absolute; width: 1px; height: 1px; + margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; +} + +/* 置顶徽章 */ +.an-top-badge { + display: inline-block; flex: none; + padding: 0 7px; line-height: 18px; + font-size: 12px; color: #fff; + background: var(--an-primary-light); + border-radius: 2px; + letter-spacing: 1px; +} + +/* 空状态 */ +.an-empty { + padding: 56px 20px; + text-align: center; + color: var(--an-text-light); +} +.an-empty p:first-child { font-size: 16px; margin-bottom: 8px; } +.an-empty-hint { font-size: 13px; color: var(--an-text-light); opacity: .75; } + +/* --------------------------------------------------------------------- + 3. 顶栏(深蓝细条:日期 + 院系切换) + --------------------------------------------------------------------- */ +.an-topbar { + background: linear-gradient(90deg, var(--an-primary-dark), var(--an-primary)); + color: var(--an-ink-on-dark-soft); + font-size: 13px; +} + +.an-topbar-inner { + width: 1200px; max-width: calc(100% - 40px); + margin: 0 auto; + display: flex; align-items: center; justify-content: space-between; + min-height: 36px; +} + +.an-topbar-date { letter-spacing: .5px; } + +.an-topbar-side { position: relative; } + +/* 院系切换:细字 + 箭头,展开为深蓝面板 */ +.an-colleges { position: relative; } + +.an-colleges-toggle { + display: inline-flex; align-items: center; gap: 6px; + padding: 4px 2px; + cursor: pointer; +} +.an-colleges-toggle a { color: var(--an-ink-on-dark-soft); } +.an-colleges-toggle a:hover { color: #fff; } + +.an-colleges-arrow { + font-style: normal; font-size: 10px; + transition: transform var(--an-speed) ease; +} +.an-colleges:hover .an-colleges-arrow { transform: rotate(180deg); } + +.an-colleges-menu { + position: absolute; right: 0; top: calc(100% + 6px); + min-width: 180px; max-height: 280px; overflow: auto; + background: var(--an-panel); + backdrop-filter: blur(4px); + border-top: 2px solid var(--an-primary-light); + border-radius: 0 0 var(--an-radius) var(--an-radius); + box-shadow: var(--an-shadow-lg); + padding: 6px 0; + opacity: 0; visibility: hidden; transform: translateY(-6px); + transition: opacity var(--an-speed) ease, transform var(--an-speed) ease, visibility var(--an-speed); + z-index: 120; +} +.an-colleges:hover .an-colleges-menu, +.an-colleges:focus-within .an-colleges-menu { + opacity: 1; visibility: visible; transform: translateY(0); +} + +.an-colleges-menu li a { + display: block; + padding: 7px 16px; + color: var(--an-ink-on-dark); + font-size: 13px; + border-left: 2px solid transparent; + transition: background var(--an-speed) ease, border-color var(--an-speed) ease, padding-left var(--an-speed) ease; +} +.an-colleges-menu li a:hover { + background: var(--an-primary-dark); + border-left-color: var(--an-primary-light); + padding-left: 20px; + color: #fff; +} +.an-colleges-menu li.active a { + background: rgba(255,255,255,.08); + border-left-color: var(--an-primary-light); + color: #fff; +} + +/* --------------------------------------------------------------------- + 4. 品牌行(深蓝主区:徽标 + 站名 + 圆角搜索) + --------------------------------------------------------------------- */ +.an-brandbar { + background: linear-gradient(100deg, var(--an-primary-dark) 0%, var(--an-primary) 62%, var(--an-primary-light) 130%); + color: var(--an-ink-on-dark); + position: relative; + overflow: hidden; +} +/* 品牌行右侧淡圆环装饰(CSS 绘制,无图片依赖) */ +.an-brandbar::before, +.an-brandbar::after { + content: ""; + position: absolute; + border-radius: 50%; + border: 26px solid rgba(255,255,255,.05); + pointer-events: none; +} +.an-brandbar::before { width: 320px; height: 320px; right: -80px; top: -170px; } +.an-brandbar::after { width: 200px; height: 200px; right: 180px; bottom: -140px; border-width: 16px; } + +.an-brandbar-inner { + width: 1200px; max-width: calc(100% - 40px); + margin: 0 auto; + display: flex; align-items: center; justify-content: space-between; + gap: 24px; + padding: 22px 0; + position: relative; z-index: 1; +} + +.an-brand { + display: flex; align-items: center; gap: 14px; + min-width: 0; +} + +/* 徽标容器:白衬圆角方块,校徽任何底色都干净 */ +.an-brand-badge { + flex: none; + width: 58px; height: 58px; + display: inline-flex; align-items: center; justify-content: center; + background: rgba(255,255,255,.94); + border-radius: var(--an-radius); + box-shadow: 0 2px 8px rgba(6,49,97,.35); + overflow: hidden; +} +.an-brand-badge--plain { color: var(--an-primary); } +.an-logo { width: 100%; height: 100%; object-fit: contain; } + +.an-site-name { + color: var(--an-ink-on-dark); + font-size: 27px; + font-weight: 700; + letter-spacing: 2px; + line-height: 1.3; + text-shadow: 0 2px 6px rgba(6,49,97,.4); + white-space: nowrap; + overflow: hidden; text-overflow: ellipsis; + max-width: 560px; +} +.an-site-name:hover { color: #fff; } + +/* 搜索:白色胶囊(学院风记忆点) */ +.an-search { + flex: none; + display: flex; align-items: center; + width: 300px; max-width: 36%; + background: #fff; + border-radius: 999px; + padding: 3px 4px 3px 18px; + box-shadow: 0 3px 10px rgba(6,49,97,.28); +} +.an-search input { + flex: 1; min-width: 0; + border: 0; outline: none; + background: transparent; + font-size: 13px; color: var(--an-text); + line-height: 30px; +} +.an-search input::placeholder { color: #a9b3c0; } +.an-search button { + flex: none; + width: 32px; height: 32px; + border: 0; border-radius: 50%; + background: var(--an-primary); + color: #fff; + display: inline-flex; align-items: center; justify-content: center; + transition: background var(--an-speed) ease, transform var(--an-speed) ease; +} +.an-search button:hover { background: var(--an-primary-light); transform: scale(1.06); } + +/* --------------------------------------------------------------------- + 5. 主导航(白底条 + 主蓝半透明下拉面板 + 吸顶反蓝) + --------------------------------------------------------------------- */ +.an-nav { + background: var(--an-nav-bg); + box-shadow: var(--an-shadow-sm); + position: relative; + z-index: 100; +} +/* 顶部主蓝细线:把导航条与品牌行缝在一起 */ +.an-nav::before { + content: ""; + display: block; height: 3px; + background: linear-gradient(90deg, var(--an-primary), var(--an-primary-light)); +} + +.an-nav-inner { + width: 1200px; max-width: calc(100% - 40px); + margin: 0 auto; + position: relative; +} + +.an-nav-toggle { display: none; } + +.an-nav-list { + display: flex; + justify-content: space-between; +} + +.an-nav-list > li { position: relative; flex: 1 1 auto; text-align: center; } + +.an-nav-list > li > a { + display: flex; align-items: center; justify-content: center; gap: 5px; + position: relative; + padding: 0 10px; + height: 52px; + font-size: 16px; + font-weight: 600; + letter-spacing: 1px; + color: var(--an-primary-dark); + white-space: nowrap; + transition: color var(--an-speed) ease, background var(--an-speed) ease; +} +/* 底部指示条:hover/当前项 */ +.an-nav-list > li > a::after { + content: ""; + position: absolute; left: 50%; bottom: 0; + width: 0; height: 3px; + background: var(--an-primary); + transform: translateX(-50%); + transition: width var(--an-speed) ease; +} +.an-nav-list > li:hover > a, +.an-nav-list > li.active > a { + color: var(--an-primary); + background: rgba(11,74,143,.06); +} +.an-nav-list > li:hover > a::after, +.an-nav-list > li.active > a::after { width: 56%; } + +.an-caret { + font-style: normal; font-size: 11px; + opacity: .65; + transition: transform var(--an-speed) ease; +} +.an-nav-list > li:hover .an-caret { transform: rotate(180deg); } + +/* —— 下拉面板:主蓝半透明、顶部亮线、项 hover 加深 + 左亮条(红线第一优先级) —— */ +.an-sub { + position: absolute; left: 50%; top: 100%; + transform: translate(-50%, 10px); + min-width: 176px; + background: var(--an-panel); + backdrop-filter: blur(6px); + border-top: 3px solid var(--an-primary-light); + border-radius: 0 0 var(--an-radius) var(--an-radius); + box-shadow: var(--an-shadow-lg); + padding: 6px 0; + text-align: left; + opacity: 0; visibility: hidden; + transition: opacity var(--an-speed) ease, transform var(--an-speed) ease, visibility var(--an-speed); + z-index: 110; +} +.an-nav-list > li:hover .an-sub, +.an-nav-list > li:focus-within .an-sub { + opacity: 1; visibility: visible; + transform: translate(-50%, 0); +} + +/* 面板小尖角 */ +.an-sub::before { + content: ""; + position: absolute; left: 50%; top: -8px; + width: 12px; height: 12px; + background: var(--an-primary); + transform: translateX(-50%) rotate(45deg); + border-radius: 2px 0 0 0; +} + +.an-sub li a { + display: block; + position: relative; + margin: 2px 8px; + padding: 9px 14px 9px 22px; + font-size: 14px; + color: var(--an-ink-on-dark); + border-radius: 4px; + white-space: nowrap; + transition: background var(--an-speed) ease, padding-left var(--an-speed) ease, color var(--an-speed) ease; +} +/* 项前细刻度线 */ +.an-sub li a::before { + content: ""; + position: absolute; left: 10px; top: 50%; + width: 4px; height: 4px; + margin-top: -2px; + background: rgba(255,255,255,.5); + border-radius: 50%; + transition: background var(--an-speed) ease, box-shadow var(--an-speed) ease; +} +.an-sub li a:hover { + background: var(--an-primary-dark); + padding-left: 26px; + color: #fff; +} +.an-sub li a:hover::before { + background: var(--an-primary-light); + box-shadow: 0 0 6px var(--an-primary-light); +} + +/* —— 吸顶反蓝(.an-nav--fixed 由 main.js 滚动加挂) —— */ +.an-nav--fixed { + position: fixed; left: 0; right: 0; top: 0; + background: linear-gradient(90deg, var(--an-primary-dark), var(--an-primary)); + box-shadow: var(--an-shadow-md); + animation: an-nav-drop var(--an-speed) ease; +} +@keyframes an-nav-drop { + from { transform: translateY(-100%); } + to { transform: translateY(0); } +} +.an-nav--fixed::before { height: 0; } +.an-nav--fixed .an-nav-list > li > a { color: var(--an-ink-on-dark); } +.an-nav--fixed .an-nav-list > li:hover > a, +.an-nav--fixed .an-nav-list > li.active > a { + color: #fff; + background: rgba(255,255,255,.1); +} +.an-nav--fixed .an-nav-list > li > a::after { background: var(--an-primary-light); } +/* 吸顶态下拉:底色更沉,与蓝条衔接 */ +.an-nav--fixed .an-sub { background: rgba(6,49,97,.96); } +.an-nav--fixed .an-sub::before { background: var(--an-primary-dark); } + +/* 吸顶占位,防跳变(main.js 加挂时同步高度,解锁即移除) */ +.an-nav-holder { display: block; } + +/* --------------------------------------------------------------------- + 6. 首页 banner(通栏轮播 + 圆形箭头 + 底部圆点) + --------------------------------------------------------------------- */ +.an-banner { + position: relative; + overflow: hidden; + background: var(--an-primary-dark); +} +.an-banner-slides { position: relative; height: 520px; } + +.an-banner-slide { + position: absolute; inset: 0; + opacity: 0; visibility: hidden; + transition: opacity .8s ease, visibility .8s ease; +} +.an-banner-slide.active { opacity: 1; visibility: visible; z-index: 1; } + +.an-banner-slide img { + width: 100%; height: 100%; + object-fit: cover; +} +/* 底部渐隐遮罩上承标题 */ +.an-banner-slide::after { + content: ""; + position: absolute; left: 0; right: 0; bottom: 0; + height: 120px; + background: linear-gradient(180deg, transparent, rgba(6,49,97,.66)); + pointer-events: none; +} +.an-banner-slide.active img { animation: an-banner-zoom 6.5s ease forwards; } +@keyframes an-banner-zoom { + from { transform: scale(1); } + to { transform: scale(1.045); } +} + +.an-banner-title { + position: absolute; left: 6%; bottom: 34px; + z-index: 2; + max-width: 70%; + color: #fff; + font-size: 24px; + font-weight: 600; + letter-spacing: 1px; + text-shadow: 0 2px 8px rgba(6,49,97,.6); + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; +} + +/* 圆形半透明箭头(建平语言) */ +.an-banner-arrow { + position: absolute; top: 50%; z-index: 3; + width: 52px; height: 52px; margin-top: -26px; + border: 2px solid rgba(255,255,255,.75); + border-radius: 50%; + background: rgba(6,49,97,.25); + color: #fff; + font-size: 26px; line-height: 1; + display: inline-flex; align-items: center; justify-content: center; + padding-bottom: 4px; + opacity: 0; + transition: opacity var(--an-speed) ease, background var(--an-speed) ease, border-color var(--an-speed) ease; +} +.an-banner:hover .an-banner-arrow { opacity: 1; } +.an-banner-arrow:hover { background: var(--an-primary); border-color: #fff; } +.an-banner-arrow.prev { left: 26px; } +.an-banner-arrow.next { right: 26px; } + +/* 圆点指示器 */ +.an-banner-dots { + position: absolute; left: 0; right: 0; bottom: 18px; + z-index: 3; + display: flex; justify-content: center; gap: 10px; +} +.an-banner-dots button { + width: 10px; height: 10px; + border: 0; border-radius: 50%; + background: rgba(255,255,255,.55); + transition: background var(--an-speed) ease, transform var(--an-speed) ease; +} +.an-banner-dots button:hover { background: #fff; } +.an-banner-dots button.active { + background: #fff; + transform: scale(1.25); + box-shadow: 0 0 0 3px rgba(255,255,255,.25); +} + +/* --------------------------------------------------------------------- + 7. 区块标题(居中双线 + 下短横)与区块通用 + --------------------------------------------------------------------- */ +.an-section { margin-top: 40px; } + +.an-block-head { + position: relative; + text-align: center; + padding: 14px 0 12px; + border-top: 1px solid var(--an-border); + border-bottom: 1px solid var(--an-border); +} +.an-block-head h2 { + display: inline-block; + position: relative; + font-size: 22px; + font-weight: 700; + letter-spacing: 3px; + color: var(--an-primary-dark); +} +/* 标题下短横:主蓝 3px */ +.an-block-tick { + position: absolute; left: 50%; bottom: -2px; + width: 42px; height: 3px; + transform: translateX(-50%); + background: var(--an-primary); + border-radius: 2px; +} + +/* 栏目卡头变体:左右布局(标题居左 + 更多居右) */ +.an-block-head--bar { + text-align: left; + padding: 0 0 10px; + border-top: 0; + border-bottom: 2px solid var(--an-primary); + display: flex; align-items: baseline; justify-content: space-between; +} +.an-block-head--bar h2 { + font-size: 19px; + letter-spacing: 1px; + padding-left: 12px; + position: relative; +} +/* 标题左侧竖条 */ +.an-block-head--bar h2::before { + content: ""; + position: absolute; left: 0; top: 6px; bottom: 6px; + width: 4px; + background: var(--an-primary); + border-radius: 2px; +} +.an-block-head--bar .an-block-tick { display: none; } + +.an-more { + font-size: 13px; + font-weight: 400; + color: var(--an-text-light); + letter-spacing: 0; + transition: color var(--an-speed) ease, transform var(--an-speed) ease; +} +.an-more:hover { color: var(--an-primary); transform: translateX(3px); } + +/* --------------------------------------------------------------------- + 8. 头条区(左:头条推荐列表 / 右:图片新闻) + --------------------------------------------------------------------- */ +.an-headline { + display: grid; + grid-template-columns: 7fr 5fr; + gap: 28px; +} +/* grid 项允许收缩到轨道内:否则 nowrap 省略文本会把 auto 最小宽撑破容器(移动端横向溢出) */ +.an-headline > * { min-width: 0; } + +.an-headline-card, +.an-focus-card { + background: var(--an-card-bg); + border-radius: var(--an-radius-lg); + box-shadow: var(--an-shadow-sm); + padding: 6px 22px 18px; +} + +/* 头条推荐 */ +.an-headline-list li { + display: flex; align-items: center; gap: 10px; + padding: 10px 2px; + border-bottom: 1px dashed var(--an-border); +} +.an-headline-list li:last-child { border-bottom: 0; } +.an-headline-list li a { + flex: 1; min-width: 0; + color: var(--an-text); + font-size: 15px; + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + transition: color var(--an-speed) ease, padding-left var(--an-speed) ease; +} +.an-headline-list li a:hover { color: var(--an-primary); padding-left: 4px; } +.an-headline-list time { flex: none; color: var(--an-text-light); font-size: 13px; } + +/* 头条头条:大标题 + 元信息 */ +.an-headline-lead { + display: block !important; + padding: 14px 2px 16px !important; +} +.an-lead-title { + display: block; + font-size: 19px !important; + font-weight: 700; + color: var(--an-primary-dark) !important; + line-height: 1.5; + white-space: normal !important; + overflow: hidden; + max-height: 3em; +} +.an-lead-title:hover { color: var(--an-primary) !important; padding-left: 0 !important; } +.an-lead-meta { + display: flex; align-items: center; gap: 10px; + margin-top: 8px; + font-size: 13px; color: var(--an-text-light); +} + +/* 图片新闻 */ +.an-focus-lead { + display: block; + margin-top: 14px; + border-radius: var(--an-radius); + overflow: hidden; + position: relative; +} +.an-focus-cover { display: block; aspect-ratio: 16/9; overflow: hidden; background: var(--an-bg); } +.an-focus-cover img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s ease; } +.an-focus-lead:hover .an-focus-cover img { transform: scale(1.05); } +.an-focus-title { + position: absolute; left: 0; right: 0; bottom: 0; + padding: 26px 14px 10px; + color: #fff; font-size: 15px; font-weight: 600; + background: linear-gradient(180deg, transparent, rgba(6,49,97,.82)); + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; +} + +.an-focus-rows { margin-top: 10px; } +.an-focus-rows li a { + display: flex; align-items: center; gap: 12px; + padding: 8px 0; +} +.an-row-cover { + flex: none; + width: 104px; height: 66px; + border-radius: 4px; + overflow: hidden; + background: var(--an-bg); +} +.an-row-cover img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s ease; } +.an-focus-rows li a:hover .an-row-cover img { transform: scale(1.06); } +.an-row-title { + flex: 1; min-width: 0; + color: var(--an-text); + font-size: 14px; + line-height: 1.55; + max-height: 3.1em; + overflow: hidden; + transition: color var(--an-speed) ease; +} +.an-focus-rows li a:hover .an-row-title { color: var(--an-primary); } + +/* --------------------------------------------------------------------- + 9. 栏目区块(白卡三列 + 日期方块行) + --------------------------------------------------------------------- */ +.an-blocks { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 24px; + margin-top: 14px; +} + +.an-block { + background: var(--an-card-bg); + border-radius: var(--an-radius-lg); + box-shadow: var(--an-shadow-sm); + padding: 18px 20px 14px; + display: flex; flex-direction: column; + min-width: 0; + transition: box-shadow var(--an-speed) ease, transform var(--an-speed) ease; +} +.an-block:hover { box-shadow: var(--an-shadow-md); transform: translateY(-2px); } + +.an-block-list { flex: 1; } + +/* 区块头条(有封面图文 / 无封面纯文) */ +.an-block-featured { border-bottom: 1px solid var(--an-border); } +.an-block-featured > a { + display: block; + padding: 12px 0 14px; +} +.an-feat-cover { + display: block; + aspect-ratio: 16/9; + border-radius: var(--an-radius); + overflow: hidden; + background: var(--an-bg); + margin-bottom: 10px; +} +.an-feat-cover img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s ease; } +.an-block-featured > a:hover .an-feat-cover img { transform: scale(1.04); } + +.an-feat-title { + display: block; + font-size: 16px; font-weight: 700; + color: var(--an-primary-dark); + line-height: 1.5; + max-height: 3em; + overflow: hidden; + transition: color var(--an-speed) ease; +} +.an-block-featured > a:hover .an-feat-title { color: var(--an-primary); } +.an-feat-summary { + display: block; + margin-top: 6px; + font-size: 13px; color: var(--an-text-light); + line-height: 1.65; + max-height: 3.3em; + overflow: hidden; +} +.an-feat-meta { + display: flex; align-items: center; gap: 10px; + margin-top: 8px; + font-size: 12px; color: var(--an-text-light); +} + +/* 列表行:日期方块 + 标题(建平通知公告语言) */ +.an-block-item { + display: flex; align-items: center; gap: 12px; + padding: 8px 0; + border-bottom: 1px dashed var(--an-border); +} +.an-block-item:last-child { border-bottom: 0; } + +.an-date-chip { + flex: none; + width: 46px; + text-align: center; + border-radius: 4px; + overflow: hidden; + background: var(--an-bg); + border: 1px solid var(--an-border); + transition: background var(--an-speed) ease, border-color var(--an-speed) ease; +} +.an-date-chip b { + display: block; + font-size: 17px; line-height: 24px; + font-weight: 700; + color: var(--an-primary); + background: rgba(11,74,143,.08); + transition: color var(--an-speed) ease; +} +.an-date-chip i { + display: block; + font-style: normal; + font-size: 10px; line-height: 16px; + color: var(--an-text-light); + transform: scale(.92); +} +.an-block-item:hover .an-date-chip { + background: var(--an-primary); + border-color: var(--an-primary); +} +.an-block-item:hover .an-date-chip b { color: #fff; background: transparent; } +.an-block-item:hover .an-date-chip i { color: rgba(255,255,255,.8); } + +.an-block-item-title { + flex: 1; min-width: 0; + font-size: 14px; + color: var(--an-text); + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + transition: color var(--an-speed) ease; +} +.an-block-item:hover .an-block-item-title { color: var(--an-primary); } + +/* --------------------------------------------------------------------- + 10. 列表/详情/单页公共:页面卡 + 面包屑 + 页题 + --------------------------------------------------------------------- */ +.an-page { padding: 28px 0 48px; } + +.an-page-card { + background: var(--an-card-bg); + border-radius: var(--an-radius-lg); + box-shadow: var(--an-shadow-sm); + padding: 26px 32px 34px; + min-height: 420px; +} + +.an-crumb { + display: flex; align-items: center; flex-wrap: wrap; gap: 8px; + font-size: 13px; + color: var(--an-text-light); + padding-bottom: 14px; + border-bottom: 1px solid var(--an-border); + margin-bottom: 20px; +} +.an-crumb a { color: var(--an-text-light); } +.an-crumb a:hover { color: var(--an-primary); } +.an-crumb-sep { color: #c3cad4; } +.an-crumb-current { color: var(--an-primary); } + +.an-page-title { + position: relative; + font-size: 23px; + font-weight: 700; + letter-spacing: 1px; + color: var(--an-primary-dark); + padding-left: 16px; + margin-bottom: 22px; +} +.an-page-title::before { + content: ""; + position: absolute; left: 0; top: 5px; bottom: 5px; + width: 5px; + border-radius: 3px; + background: linear-gradient(180deg, var(--an-primary), var(--an-primary-light)); +} + +/* --------------------------------------------------------------------- + 11. 列表页:图文行 + 行式列表 + 分页 + --------------------------------------------------------------------- */ +.an-article-list > li { border-bottom: 1px solid var(--an-border); } +.an-article-list > li:last-child { border-bottom: 0; } + +/* 图文行 */ +.an-article-row { + display: flex; gap: 20px; + padding: 18px 2px; +} +.an-item-cover { + flex: none; + width: 216px; aspect-ratio: 3/2; + border-radius: var(--an-radius); + overflow: hidden; + background: var(--an-bg); +} +.an-item-cover img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s ease; } +.an-article-row:hover .an-item-cover img { transform: scale(1.05); } + +.an-item-body { flex: 1; min-width: 0; padding-top: 4px; } +.an-item-title a { + font-size: 17px; font-weight: 600; + color: var(--an-text); + line-height: 1.5; + transition: color var(--an-speed) ease; +} +.an-item-title a:hover { color: var(--an-primary); } +.an-item-summary { + margin-top: 8px; + font-size: 13px; color: var(--an-text-light); + line-height: 1.7; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} +.an-item-meta { + display: flex; align-items: center; gap: 12px; + margin-top: 10px; + font-size: 12px; color: var(--an-text-light); +} + +/* 行式(无封面):刻度符号 + 标题 + 右侧日期 */ +.an-article-line { + display: flex; align-items: center; gap: 12px; + padding: 13px 2px; +} +.an-line-tick { + flex: none; + width: 6px; height: 6px; + background: var(--an-primary); + transform: rotate(45deg); + opacity: .85; + transition: transform var(--an-speed) ease; +} +.an-article-line:hover .an-line-tick { transform: rotate(45deg) scale(1.35); } + +.an-line-main { flex: 1; min-width: 0; } +.an-line-title { + display: block; + font-size: 15px; + color: var(--an-text); + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + transition: color var(--an-speed) ease, padding-left var(--an-speed) ease; +} +.an-article-line:hover .an-line-title { color: var(--an-primary); padding-left: 4px; } +.an-article-line .an-item-summary { margin-top: 4px; -webkit-line-clamp: 1; } + +.an-line-side { + flex: none; + display: flex; align-items: center; gap: 10px; + font-size: 13px; color: var(--an-text-light); +} + +/* 分页 */ +.an-pagination { + display: flex; justify-content: center; flex-wrap: wrap; gap: 8px; + margin-top: 30px; +} +.an-pagination a { + min-width: 36px; height: 36px; + display: inline-flex; align-items: center; justify-content: center; + padding: 0 10px; + font-size: 14px; + color: var(--an-text); + background: var(--an-bg); + border: 1px solid var(--an-border); + border-radius: 4px; + transition: all var(--an-speed) ease; +} +.an-pagination a:hover { + color: #fff; + background: var(--an-primary); + border-color: var(--an-primary); +} +.an-pagination a.active { + color: #fff; + background: var(--an-primary); + border-color: var(--an-primary); + font-weight: 700; + box-shadow: 0 3px 8px rgba(11,74,143,.35); +} +.an-pagination a.disabled { + opacity: .45; pointer-events: none; +} + +/* --------------------------------------------------------------------- + 12. 详情/单页:文章排版 + --------------------------------------------------------------------- */ +.an-article-title { + text-align: center; + font-size: 24px; + font-weight: 700; + color: var(--an-primary-dark); + line-height: 1.5; + padding: 6px 20px 0; +} + +.an-article-meta { + display: flex; justify-content: center; flex-wrap: wrap; gap: 6px 22px; + padding: 14px 0 18px; + margin-bottom: 24px; + font-size: 13px; color: var(--an-text-light); + border-bottom: 1px solid var(--an-border); + position: relative; +} +/* 标题下居中短横(与区块标题语言呼应) */ +.an-article-meta::before { + content: ""; + position: absolute; left: 50%; top: -12px; + width: 42px; height: 3px; + transform: translateX(-50%); + background: var(--an-primary); + border-radius: 2px; +} + +.an-article-content { + font-size: 15px; + line-height: 1.9; + color: var(--an-text); +} +.an-article-content p { margin: 0 0 14px; } +.an-article-content h1, .an-article-content h2, +.an-article-content h3, .an-article-content h4 { + margin: 22px 0 12px; + color: var(--an-primary-dark); + font-weight: 700; +} +.an-article-content h2 { font-size: 19px; } +.an-article-content h3 { font-size: 17px; } +.an-article-content h4 { font-size: 15px; } +.an-article-content img { + max-width: 100%; + height: auto; + border-radius: var(--an-radius); + margin: 6px auto 14px; + box-shadow: var(--an-shadow-sm); +} +.an-article-content ul, .an-article-content ol { + margin: 0 0 14px; padding-left: 26px; +} +.an-article-content ul li { list-style: disc; } +.an-article-content ol li { list-style: decimal; } +.an-article-content li { margin-bottom: 6px; } +.an-article-content blockquote { + margin: 14px 0; + padding: 12px 18px; + background: rgba(11,74,143,.05); + border-left: 4px solid var(--an-primary); + border-radius: 0 var(--an-radius) var(--an-radius) 0; + color: var(--an-text-light); +} +.an-article-content table { + width: 100%; + border-collapse: collapse; + margin: 14px 0; + font-size: 14px; +} +.an-article-content th, .an-article-content td { + border: 1px solid var(--an-border); + padding: 8px 12px; + text-align: left; +} +.an-article-content th { + background: rgba(11,74,143,.06); + color: var(--an-primary-dark); + font-weight: 600; +} +.an-article-content a { color: var(--an-primary-light); text-decoration: underline; } +.an-article-content hr { + border: 0; height: 1px; + background: var(--an-border); + margin: 20px 0; +} +.an-article-content video, .an-article-content audio { max-width: 100%; } + +/* 上下篇 */ +.an-sibling { + display: grid; grid-template-columns: 1fr 1fr; gap: 16px; + margin-top: 34px; + padding-top: 22px; + border-top: 1px solid var(--an-border); +} +.an-sib, .an-sib-none { + display: block; + padding: 12px 16px; + border: 1px solid var(--an-border); + border-radius: var(--an-radius); + background: var(--an-bg); + min-width: 0; + transition: border-color var(--an-speed) ease, background var(--an-speed) ease, box-shadow var(--an-speed) ease; +} +.an-sib:hover { + border-color: var(--an-primary); + background: var(--an-card-bg); + box-shadow: var(--an-shadow-sm); +} +.an-sib.next { text-align: right; } +.an-sib-label { + display: block; + font-size: 12px; + color: var(--an-primary); + letter-spacing: 2px; + margin-bottom: 4px; +} +.an-sib-title { + display: block; + font-size: 14px; + color: var(--an-text); + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; +} +.an-sib-none { opacity: .6; } +.an-sib-none .an-sib-label { color: var(--an-text-light); } + +/* --------------------------------------------------------------------- + 13. 页脚(深蓝渐变 + 同心圆环纹理 + 三栏 + 底条) + --------------------------------------------------------------------- */ +.an-footer { + position: relative; + margin-top: 56px; + color: var(--an-ink-on-dark); + background: linear-gradient(160deg, var(--an-footer-bg) 0%, var(--an-primary-dark) 100%); + overflow: hidden; +} + +/* 同心圆环纹理(CSS 绘制,呼应校徽环形语言) */ +.an-ring { + position: absolute; + border-radius: 50%; + border: 2px solid rgba(255,255,255,.06); + pointer-events: none; +} +.an-ring--1 { width: 460px; height: 460px; right: -140px; top: -160px; border-width: 30px; } +.an-ring--2 { width: 300px; height: 300px; right: -60px; top: -80px; border-width: 18px; } +.an-ring--3 { width: 520px; height: 520px; left: -200px; bottom: -300px; border-width: 34px; } + +.an-footer-inner { + width: 1200px; max-width: calc(100% - 40px); + margin: 0 auto; + position: relative; z-index: 1; + display: grid; + grid-template-columns: 1.1fr 1.2fr 1.4fr; + gap: 40px; + padding: 44px 0 36px; +} +.an-footer-inner > * { min-width: 0; } + +.an-footer-brand { + display: flex; align-items: center; gap: 14px; +} +.an-footer-badge { + flex: none; + width: 66px; height: 66px; + display: inline-flex; align-items: center; justify-content: center; + background: rgba(255,255,255,.94); + border-radius: var(--an-radius); + overflow: hidden; + box-shadow: 0 2px 10px rgba(6,49,97,.4); +} +.an-footer-badge--plain { color: var(--an-primary); } +.an-footer-logo { width: 100%; height: 100%; object-fit: contain; } +.an-footer-site { + font-size: 21px; + font-weight: 700; + letter-spacing: 1.5px; + line-height: 1.45; + text-shadow: 0 2px 6px rgba(6,49,97,.4); +} + +.an-footer-info { padding-top: 6px; } +.an-footer-line { + display: flex; align-items: baseline; gap: 10px; + margin-bottom: 10px; + font-size: 13px; + color: var(--an-ink-on-dark-soft); + line-height: 1.7; +} +.an-dot { + flex: none; + width: 6px; height: 6px; + background: var(--an-primary-light); + border-radius: 50%; + transform: translateY(-2px); + box-shadow: 0 0 6px rgba(17,103,187,.9); +} + +.an-footer-links { + padding-top: 6px; + display: grid; grid-template-columns: 1fr 1fr; gap: 8px 28px; +} +.an-group-name { + display: inline-block; + font-size: 13px; font-weight: 600; + color: #fff; + padding-bottom: 4px; + margin-bottom: 6px; + border-bottom: 2px solid var(--an-primary-light); +} +.an-links-row { + display: flex; flex-wrap: wrap; gap: 4px 14px; + margin-bottom: 10px; +} +.an-links-row a { + font-size: 13px; + color: var(--an-ink-on-dark-soft); + transition: color var(--an-speed) ease, padding-left var(--an-speed) ease; +} +.an-links-row a:hover { color: #fff; padding-left: 2px; } + +.an-footer-bottom { + position: relative; z-index: 1; + border-top: 1px solid rgba(255,255,255,.14); + text-align: center; + padding: 16px 0; + font-size: 13px; + color: var(--an-ink-on-dark-faint); + letter-spacing: 1px; +} + +/* --------------------------------------------------------------------- + 14. 响应式:1200 / 992 / 768 三档 + --------------------------------------------------------------------- */ +@media (max-width: 1240px) { + .an-container, .an-topbar-inner, .an-brandbar-inner, .an-nav-inner { max-width: calc(100% - 40px); } +} + +@media (max-width: 992px) { + .an-site-name { font-size: 22px; max-width: 380px; } + .an-search { width: 240px; } + + .an-nav-list { flex-wrap: wrap; justify-content: flex-start; } + .an-nav-list > li { flex: 0 0 auto; } + .an-nav-list > li > a { height: 46px; font-size: 15px; padding: 0 12px; } + + .an-headline { grid-template-columns: 1fr; } + .an-blocks { grid-template-columns: repeat(2, 1fr); } + + .an-banner-slides { height: 380px; } + .an-banner-title { font-size: 19px; bottom: 28px; } + + .an-footer-inner { grid-template-columns: 1fr 1fr; } +} + +@media (max-width: 768px) { + /* —— 品牌行收纳 —— */ + .an-brandbar-inner { flex-direction: column; align-items: stretch; gap: 14px; padding: 16px 0; } + .an-site-name { font-size: 19px; max-width: none; white-space: normal; } + .an-brand { justify-content: center; } + .an-search { width: 100%; max-width: none; } + + /* —— 移动端导航:抽屉展开 —— */ + .an-nav-toggle { + display: inline-flex; flex-direction: column; justify-content: center; gap: 5px; + width: 44px; height: 40px; + padding: 8px; + border: 1px solid var(--an-border); + border-radius: var(--an-radius); + background: var(--an-card-bg); + } + .an-nav-toggle span { + display: block; height: 2px; + background: var(--an-primary); + border-radius: 2px; + transition: transform var(--an-speed) ease, opacity var(--an-speed) ease; + } + .an-nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); } + .an-nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; } + .an-nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); } + + .an-nav-inner { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 0; } + /* 吸顶在移动端直接融入文档流配色 */ + .an-nav--fixed { position: sticky; animation: none; } + + .an-nav-list { + display: none; + position: absolute; left: -20px; right: -20px; top: 100%; + flex-direction: column; + background: var(--an-card-bg); + box-shadow: var(--an-shadow-md); + border-top: 1px solid var(--an-border); + padding: 6px 16px 12px; + max-height: 68vh; overflow: auto; + } + .an-nav-list.an-open { display: flex; } + .an-nav-list > li { text-align: left; border-bottom: 1px solid var(--an-border); } + .an-nav-list > li:last-child { border-bottom: 0; } + .an-nav-list > li > a { height: 46px; justify-content: flex-start; } + .an-nav-list > li > a::after { display: none; } + + /* 移动端二级:手风琴内联展开(.an-sub-open 由 main.js 控制) */ + .an-sub { + position: static; + display: none; + transform: none; + opacity: 1; visibility: visible; + background: var(--an-bg); + border-top: 0; + box-shadow: none; + border-radius: 0; + margin: 0 -8px 8px; + padding: 4px 0; + } + .an-sub::before { display: none; } + .an-sub li a { color: var(--an-text); margin: 0 8px; } + .an-sub li a:hover { background: rgba(11,74,143,.08); color: var(--an-primary); } + .an-sub li a::before { background: var(--an-primary); opacity: .5; } + .an-nav-list li.an-sub-open .an-sub { display: block; } + .an-nav-list li.has-sub > a { cursor: pointer; } + + /* —— 内容收纳 —— */ + .an-banner-slides { height: 240px; } + .an-banner-title { font-size: 15px; left: 16px; bottom: 20px; max-width: 85%; } + .an-banner-arrow { display: none; } + + .an-section { margin-top: 28px; } + .an-blocks { grid-template-columns: 1fr; } + .an-block-head h2 { font-size: 19px; } + + .an-article-row { flex-direction: column; gap: 12px; } + .an-item-cover { width: 100%; } + .an-article-line { flex-wrap: wrap; } + .an-line-side { width: 100%; padding-left: 18px; } + + .an-page-card { padding: 18px 16px 24px; } + .an-article-title { font-size: 20px; padding: 0; } + .an-article-meta { gap: 4px 14px; } + .an-sibling { grid-template-columns: 1fr; } + .an-sib-title { white-space: normal; } + + .an-footer-inner { grid-template-columns: 1fr; gap: 26px; padding: 32px 0 26px; } + .an-footer-links { grid-template-columns: 1fr; } + .an-ring--1 { width: 300px; height: 300px; border-width: 20px; } +} + +/* --------------------------------------------------------------------- + 15. 动效偏好:尊重系统减弱动态设置 + --------------------------------------------------------------------- */ +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: .01ms !important; + animation-iteration-count: 1 !important; + transition-duration: .01ms !important; + } +} + +/* B4 友链 Logo:与名称并排小图(高度 ~20px,垂直居中;过宽截断不撑破页脚) */ +.links-group a .link-logo, +.an-links-row a .link-logo { + height: 20px; + width: auto; + max-width: 64px; + object-fit: contain; + vertical-align: middle; + margin-right: 4px; + border-radius: 2px; +} + +/* ===== 左侧栏目导航(docs/11):有二级节点的模块,栏目/文章页左侧展示兄弟目录 ===== */ +.an-cols { display: flex; align-items: flex-start; gap: 28px; } +.an-cols .an-page-card { flex: 1; min-width: 0; } +.an-side-nav { + flex: none; + width: 230px; + background: var(--an-card-bg); + border: 1px solid var(--an-border); + border-top: 3px solid var(--an-primary); + box-shadow: var(--an-shadow-md); +} +.an-side-nav-head { + background: var(--an-primary); + color: #fff; + font-size: 20px; + font-weight: 700; + text-align: center; + padding: 18px 12px; + letter-spacing: 3px; +} +.an-side-nav-list { list-style: none; margin: 0; padding: 0; } +.an-side-nav-item + .an-side-nav-item { border-top: 1px solid var(--an-border); } +.an-side-nav-item a { + display: block; + padding: 13px 18px; + color: var(--an-text); + text-decoration: none; + font-size: 15px; + border-left: 3px solid transparent; + transition: background .15s ease, color .15s ease, border-color .15s ease; +} +.an-side-nav-item a:hover { color: var(--an-primary); background: var(--an-panel); } +.an-side-nav-item.active a { + background: var(--an-primary); + color: #fff; + font-weight: 600; + text-decoration: none; + border-left-color: var(--an-primary-light); +} +/* 顶导航二级下拉当前项高亮(docs/11 顺带) */ +.an-nav-list ul.an-sub li.active a { color: var(--an-primary-light); } +@media (max-width: 768px) { + .an-cols { flex-direction: column; gap: 16px; } + .an-side-nav { width: 100%; } + .an-side-nav-list { display: flex; overflow-x: auto; -webkit-overflow-scrolling: touch; } + .an-side-nav-item { flex: none; } + .an-side-nav-item + .an-side-nav-item { border-top: none; border-left: 1px solid var(--an-border); } + .an-side-nav-item a { white-space: nowrap; border-left: none; } +} +@media print { + .an-side-nav { display: none; } + .an-cols { display: block; } +} + +/* --------------------------------------------------------------------- + 16. 首页布局模式区块(fragments/home-blocks.html) + 站点配了 home_layout 时首页正文由本主题渲染:整宽区块卡 + 双栏要闻 + 藏蓝速递条。 + --------------------------------------------------------------------- */ +/* 藏蓝配金线:标题左竖条与居中线脚改用点缀金,区别于基座 scholar-blue 的全蓝语言 */ +.an-block-head--bar h2::before { background: var(--an-accent); } +.an-block-tick { background: var(--an-accent); } + +.an-section { position: relative; } +.an-sec-bg { + position: absolute; inset: 0; z-index: 0; + background-size: cover; background-position: center; + opacity: .45; pointer-events: none; +} +/* 定位元素默认画在普通流之上:不抬 z-index,背景带会糊在卡片正面(截图实测整卡泛绿)。 + 带图时卡片转半透明白,让底图若隐若现——高校官网新闻带的通行做法 */ +.an-sec-bg + .an-block { position: relative; z-index: 1; background: rgba(255, 255, 255, .88); } +.an-block--flow { padding: 20px 26px 12px; } +.an-block--flow:hover { transform: none; box-shadow: var(--an-shadow-sm); } +/* grid 轨道用 minmax(0,1fr):否则 nowrap 省略行的 min-content 会撑破轨道(中文标题尤其明显) */ +.an-split { + display: grid; + grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); + gap: 26px; + padding-top: 14px; +} +.an-split > * { min-width: 0; } +/* 头条图限高 + 右列行均分:否则左图按 16/9 撑到 400px,右列 5 行挤在顶部留大片空白 */ +.an-split .an-feat-cover { max-height: 300px; } +.an-split .an-block-list { display: flex; flex-direction: column; justify-content: space-between; } +.an-split .an-block-item { padding: 12px 0; } +.an-block-list--two { columns: 2; column-gap: 34px; } +.an-block-list--two .an-block-item { break-inside: avoid; } +.an-feat-cover--row { aspect-ratio: 16 / 7; } +.an-item-date { + flex: none; + font-size: 12px; color: var(--an-text-light); + font-variant-numeric: tabular-nums; +} +.an-src { + flex: none; + font-size: 11px; line-height: 18px; padding: 0 6px; + color: var(--an-primary); border: 1px solid var(--an-primary); + border-radius: 2px; +} +.an-wall { + display: grid; + grid-template-columns: repeat(4, minmax(0, 1fr)); + gap: 14px; + margin: 16px 0 0; +} +.an-wall-item { + position: relative; + border-radius: var(--an-radius); + overflow: hidden; + box-shadow: var(--an-shadow-sm); +} +.an-wall-item img { width: 100%; height: 158px; object-fit: cover; transition: transform .5s ease; } +.an-wall-item:hover img { transform: scale(1.05); } +.an-wall-cap { + position: absolute; left: 0; right: 0; bottom: 0; + padding: 26px 10px 9px; + font-size: 13px; color: #fff; + background: linear-gradient(transparent, rgba(6, 49, 97, .85)); + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; +} +.an-gallery-sub { + margin: -4px 0 4px; + text-align: center; + font-size: 12px; letter-spacing: .18em; text-transform: uppercase; + color: var(--an-text-light); +} +/* 速递条:flex 项自动最小尺寸 = min-content,中文可按字断行 → 不锁 nowrap/不收缩就会塌成一字一列 */ +.an-ticker { + position: relative; + background: var(--an-primary); + color: #fff; + padding: 11px 0; + font-size: 14px; +} +.an-ticker-inner { display: flex; align-items: center; gap: 16px; min-width: 0; } +.an-ticker-label { + flex: none; + font-weight: 700; letter-spacing: .06em; + padding-right: 14px; + border-right: 1px solid rgba(255, 255, 255, .35); +} +.an-ticker-list { + display: flex; gap: 28px; + flex: 1; min-width: 0; + overflow: hidden; white-space: nowrap; +} +.an-ticker-list li { flex: none; min-width: 0; } +.an-ticker-item { + display: block; flex: none; + max-width: 320px; + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + color: #fff; opacity: .92; +} +.an-ticker-item:hover { opacity: 1; text-decoration: underline; } +.an-links-group { + margin: 12px 0 0; + font-size: 13px; letter-spacing: .08em; + color: var(--an-text-light); +} +.an-links { display: flex; flex-wrap: wrap; gap: 10px; margin: 8px 0 0; } +.an-links a { + font-size: 13px; color: var(--an-text-light); + padding: 4px 14px; + background: var(--an-card-bg); + border: 1px solid var(--an-border); + border-radius: 999px; +} +.an-links a:hover { color: var(--an-primary); border-color: var(--an-primary); } + +@media (max-width: 992px) { + .an-split { grid-template-columns: minmax(0, 1fr); } + .an-wall { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .an-block-list--two { columns: 1; } + .an-ticker-item { max-width: 180px; } + .an-block--flow { padding: 16px 16px 8px; } +} diff --git a/src/main/resources/templates/themes/academic-navy/assets/favicon.png b/src/main/resources/templates/themes/academic-navy/assets/favicon.png new file mode 100644 index 0000000..ac16156 Binary files /dev/null and b/src/main/resources/templates/themes/academic-navy/assets/favicon.png differ diff --git a/src/main/resources/templates/themes/academic-navy/assets/favicon.svg b/src/main/resources/templates/themes/academic-navy/assets/favicon.svg new file mode 100644 index 0000000..b64acaf --- /dev/null +++ b/src/main/resources/templates/themes/academic-navy/assets/favicon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/main/resources/templates/themes/academic-navy/assets/js/main.js b/src/main/resources/templates/themes/academic-navy/assets/js/main.js new file mode 100644 index 0000000..4d2397b --- /dev/null +++ b/src/main/resources/templates/themes/academic-navy/assets/js/main.js @@ -0,0 +1,190 @@ +/* academic-navy 主题脚本:顶栏日期 + 轮播(圆点/箭头/触摸) + 导航吸顶反蓝 + 移动端抽屉与二级手风琴。 + 与 default 同源逻辑,无任何外部依赖。 */ +(function () { + 'use strict'; + + /* ---------- 顶栏日期(本地时间,YYYY年M月D日 星期X) ---------- */ + var dateEl = document.getElementById('siteDate'); + if (dateEl) { + var now = new Date(); + var week = ['日', '一', '二', '三', '四', '五', '六'][now.getDay()]; + dateEl.textContent = now.getFullYear() + '年' + (now.getMonth() + 1) + '月' + + now.getDate() + '日 星期' + week; + } + + /* ---------- 导航吸顶反蓝(桌面端;≤768 不吸顶) ---------- */ + var nav = document.getElementById('anNav'); + var DESKTOP_MIN = 769; + var navBaseTop = 0; + var holder = null; + + function measureNavTop() { + unlockNav(); + navBaseTop = nav.getBoundingClientRect().top + window.pageYOffset; + } + + function lockNav() { + if (!nav.classList.contains('an-nav--fixed')) { + holder = document.createElement('div'); + holder.className = 'an-nav-holder'; + holder.style.height = nav.offsetHeight + 'px'; + nav.parentNode.insertBefore(holder, nav); + nav.classList.add('an-nav--fixed'); + } else if (holder) { + holder.style.height = nav.offsetHeight + 'px'; + } + } + + function unlockNav() { + nav.classList.remove('an-nav--fixed'); + if (holder && holder.parentNode) { + holder.parentNode.removeChild(holder); + } + holder = null; + } + + function onScrollNav() { + if (window.innerWidth < DESKTOP_MIN) { + unlockNav(); + return; + } + if (window.pageYOffset > navBaseTop) { + lockNav(); + } else { + unlockNav(); + } + } + + if (nav) { + measureNavTop(); + window.addEventListener('scroll', onScrollNav, { passive: true }); + var resizeTimer = null; + window.addEventListener('resize', function () { + if (resizeTimer) { window.clearTimeout(resizeTimer); } + resizeTimer = window.setTimeout(function () { + measureNavTop(); + onScrollNav(); + }, 150); + }); + } + + /* ---------- 移动端抽屉 + 二级手风琴 ---------- */ + var navToggle = document.querySelector('.an-nav-toggle'); + var navList = document.querySelector('.an-nav-list'); + + if (navToggle && navList) { + navToggle.addEventListener('click', function () { + var open = navList.classList.toggle('an-open'); + navToggle.setAttribute('aria-expanded', open ? 'true' : 'false'); + }); + } + + /* 窄屏下:点击带二级的项先展开面板,再点才跳转;宽屏交还给 CSS hover */ + if (navList) { + navList.addEventListener('click', function (e) { + if (window.innerWidth >= DESKTOP_MIN) { return; } + var link = e.target.closest ? e.target.closest('a') : null; + if (!link) { return; } + var li = link.parentElement; + if (!li || !li.classList.contains('has-sub')) { return; } + var sub = li.querySelector('.an-sub'); + if (!sub) { return; } + if (!li.classList.contains('an-sub-open')) { + e.preventDefault(); + li.classList.add('an-sub-open'); + } + }); + } + + /* ---------- 首页轮播 ---------- */ + var root = document.querySelector('.an-banner'); + var slides = document.querySelectorAll('.an-banner-slide'); + var dots = document.querySelectorAll('.an-banner-dots button'); + var prevBtn = document.querySelector('.an-banner-arrow.prev'); + var nextBtn = document.querySelector('.an-banner-arrow.next'); + if (!slides.length || !root) { + return; + } + var current = 0; + var timer = null; + var INTERVAL = 5000; + + function show(index) { + slides[current].classList.remove('active'); + if (dots[current]) { + dots[current].classList.remove('active'); + } + current = (index + slides.length) % slides.length; + slides[current].classList.add('active'); + if (dots[current]) { + dots[current].classList.add('active'); + } + } + + function start() { + stop(); + if (slides.length > 1) { + timer = window.setInterval(function () { + show(current + 1); + }, INTERVAL); + } + } + + function stop() { + if (timer) { + window.clearInterval(timer); + timer = null; + } + } + + dots.forEach(function (dot, index) { + dot.addEventListener('click', function () { + show(index); + start(); + }); + }); + if (prevBtn) { + prevBtn.addEventListener('click', function () { + show(current - 1); + start(); + }); + } + if (nextBtn) { + nextBtn.addEventListener('click', function () { + show(current + 1); + start(); + }); + } + root.addEventListener('mouseenter', stop); + root.addEventListener('mouseleave', start); + root.addEventListener('focusin', stop); + root.addEventListener('focusout', start); + root.setAttribute('tabindex', '-1'); + root.addEventListener('keydown', function (e) { + if (e.key === 'ArrowLeft') { + show(current - 1); + start(); + } else if (e.key === 'ArrowRight') { + show(current + 1); + start(); + } + }); + + /* 触摸滑动切页 */ + var touchX = null; + root.addEventListener('touchstart', function (e) { + touchX = e.changedTouches[0].clientX; + stop(); + }, { passive: true }); + root.addEventListener('touchend', function (e) { + if (touchX === null) { return; } + var dx = e.changedTouches[0].clientX - touchX; + if (Math.abs(dx) > 40) { + show(dx < 0 ? current + 1 : current - 1); + } + touchX = null; + start(); + }, { passive: true }); + + start(); +})(); diff --git a/src/main/resources/templates/themes/academic-navy/assets/screenshot.png b/src/main/resources/templates/themes/academic-navy/assets/screenshot.png new file mode 100644 index 0000000..8f21f46 Binary files /dev/null and b/src/main/resources/templates/themes/academic-navy/assets/screenshot.png differ diff --git a/src/main/resources/templates/themes/academic-navy/detail.html b/src/main/resources/templates/themes/academic-navy/detail.html new file mode 100644 index 0000000..de38ed3 --- /dev/null +++ b/src/main/resources/templates/themes/academic-navy/detail.html @@ -0,0 +1,76 @@ + + + + +
+ +
+
+ + + +
+ + +
+

文章标题

+ + +
正文
+
+ + +
+
+
+ + + + + diff --git a/src/main/resources/templates/themes/academic-navy/fragments/footer.html b/src/main/resources/templates/themes/academic-navy/fragments/footer.html new file mode 100644 index 0000000..867ca6a --- /dev/null +++ b/src/main/resources/templates/themes/academic-navy/fragments/footer.html @@ -0,0 +1,59 @@ + + + + + diff --git a/src/main/resources/templates/themes/cherry-bloom/fragments/head.html b/src/main/resources/templates/themes/academic-navy/fragments/head.html similarity index 74% rename from src/main/resources/templates/themes/cherry-bloom/fragments/head.html rename to src/main/resources/templates/themes/academic-navy/fragments/head.html index 1809482..a01c179 100644 --- a/src/main/resources/templates/themes/cherry-bloom/fragments/head.html +++ b/src/main/resources/templates/themes/academic-navy/fragments/head.html @@ -1,6 +1,6 @@ - + @@ -8,16 +8,13 @@ - - - diff --git a/src/main/resources/templates/themes/academic-navy/fragments/header.html b/src/main/resources/templates/themes/academic-navy/fragments/header.html new file mode 100644 index 0000000..f25584e --- /dev/null +++ b/src/main/resources/templates/themes/academic-navy/fragments/header.html @@ -0,0 +1,86 @@ + + + +
+ + + +
+
+ 加载日期… +
+
+ + 专业院系 + + + +
+
+
+
+ + +
+
+
+ + + + + 站点名称 +
+ +
+
+ + + +
+ diff --git a/src/main/resources/templates/themes/academic-navy/fragments/home-blocks.html b/src/main/resources/templates/themes/academic-navy/fragments/home-blocks.html new file mode 100644 index 0000000..79f1992 --- /dev/null +++ b/src/main/resources/templates/themes/academic-navy/fragments/home-blocks.html @@ -0,0 +1,205 @@ + + +
+
+
+
+

校园要闻

+ 更多 › +
+ + +
+
+
+
+ + +
+
+
+

栏目名

+ 更多 › +
+ + + +
+
+
+
+ + +
+
+
+

通知公告

+ 更多 › +
+ + +
+
+
+
+ + +
+
+
+

媒体聚焦

+ 更多 › +
+ + +
+
+
+
+ + +
+
+

图片新闻

+ 更多 › +
+ + +
+
+
+ + +
+
+ 要闻速递 + +
+
+ + + + + +
+
+
+

友情链接

+
+ + + + +
+
+ + +
+

栏目暂无内容

+

内容发布后将在此展示

+
diff --git a/src/main/resources/templates/themes/academic-navy/home.html b/src/main/resources/templates/themes/academic-navy/home.html new file mode 100644 index 0000000..9860bb0 --- /dev/null +++ b/src/main/resources/templates/themes/academic-navy/home.html @@ -0,0 +1,149 @@ + + + + +
+ +
+ +
+ + + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+

头条推荐

+ +
+ +
+ +
+
+ + +
+
+
+
+

栏目名

+ 更多 › +
+ +
+

栏目暂无内容

+

内容发布后将在此展示

+
+
+
+
+
+
+ + + + + diff --git a/src/main/resources/templates/themes/academic-navy/list.html b/src/main/resources/templates/themes/academic-navy/list.html new file mode 100644 index 0000000..71f8a4a --- /dev/null +++ b/src/main/resources/templates/themes/academic-navy/list.html @@ -0,0 +1,105 @@ + + + + +
+ +
+
+ + + +
+ + + +

栏目名

+

搜索词

+ + +
+

未找到相关内容

+

栏目暂无内容

+

换个关键词试试

+

内容发布后将在此展示

+
+ + + +
+
+
+ + + + + diff --git a/src/main/resources/templates/themes/academic-navy/single.html b/src/main/resources/templates/themes/academic-navy/single.html new file mode 100644 index 0000000..64c413f --- /dev/null +++ b/src/main/resources/templates/themes/academic-navy/single.html @@ -0,0 +1,50 @@ + + + + +
+ +
+
+ + + +
+ + +
+

栏目名

+ +
单页内容
+
+

页面暂无内容

+

内容编辑后将在此展示

+
+
+
+
+
+ + + + + diff --git a/src/main/resources/templates/themes/academic-navy/theme.json b/src/main/resources/templates/themes/academic-navy/theme.json new file mode 100644 index 0000000..2e47990 --- /dev/null +++ b/src/main/resources/templates/themes/academic-navy/theme.json @@ -0,0 +1,23 @@ +{ + "specVersion": 1, + "name": "academic-navy", + "title": "学府藏蓝(academic-navy)", + "version": "1.0.0", + "author": "yunhai", + "description": "学府藏蓝·高校官网门户:藏蓝顶栏+白底主导航+金线标题+日期方块列表+双栏要闻(左图右列)+深色三段页脚。基座 scholar-blue,配色与首页区块形态按综合类高校官网重做。", + "screenshot": "assets/screenshot.png", + "bannerSize": "1920x560", + "params": [ + { "key": "primary", "cssVar": "--an-primary", "label": "主色", "type": "color", "default": "#0b4a8f" }, + { "key": "primaryDark", "cssVar": "--an-primary-dark", "label": "主色加深", "type": "color", "default": "#063161" }, + { "key": "primaryLight", "cssVar": "--an-primary-light","label": "主色提亮", "type": "color", "default": "#1a6cb5" }, + { "key": "accent", "cssVar": "--an-accent", "label": "点缀金", "type": "color", "default": "#b8934a" }, + { "key": "navBg", "cssVar": "--an-nav-bg", "label": "导航条底色", "type": "color", "default": "#ffffff" }, + { "key": "footerBg", "cssVar": "--an-footer-bg", "label": "页脚底色", "type": "color", "default": "#0a2b4d" }, + { "key": "text", "cssVar": "--an-text", "label": "正文文字色", "type": "color", "default": "#2b3644" }, + { "key": "textLight", "cssVar": "--an-text-light", "label": "次要文字色", "type": "color", "default": "#6b7889" }, + { "key": "bg", "cssVar": "--an-bg", "label": "页面底色", "type": "color", "default": "#f1f4f9" }, + { "key": "cardBg", "cssVar": "--an-card-bg", "label": "卡片底色", "type": "color", "default": "#ffffff" }, + { "key": "border", "cssVar": "--an-border", "label": "边框色", "type": "color", "default": "#dbe3ee" } + ] +} diff --git a/src/main/resources/templates/themes/campus-azure/README.md b/src/main/resources/templates/themes/campus-azure/README.md new file mode 100644 index 0000000..e36338f --- /dev/null +++ b/src/main/resources/templates/themes/campus-azure/README.md @@ -0,0 +1,33 @@ +# campus-azure(青蓝校园) + +高校官网风皮肤包:以 `scholar-blue` 为基座派生,配色与首页区块形态按师范/农林类院校官网(信阳师范学院、 +信阳农林学院一类)重做。配了 `home_layout` 的站点,首页正文区块由本主题 `fragments/home-blocks.html` 渲染, +不落 `themes/common` 通用骨架(否则换肤只剩页头页脚)。 + +## 风格定位 + +- 主色青蓝 `#0d6a9e` + 加深 `#084c73` + 点缀青绿 `#2b9c8f`(列表圆点、标题线脚、速递标签走青绿)。 +- 圆角放宽到 6/12px、页面底 `#eef3f6`,比 academic-navy 更松更「校园」;区块头统一走居中双线 + 右置「更多」。 +- 全部 11 个颜色参数可在站点 theme_params 微调(`primary/primaryDark/primaryLight/accent/navBg/footerBg/text/textLight/bg/cardBg/border`)。 + +## 首页区块形态(布局模式与回落模式同构) + +| homeStyle | 呈现 | 类名 | +| -- | -- | -- | +| `news_grid` | 图文要闻:全宽封面头条(底部渐变遮罩标题 + 元信息)+ 双列圆点密行;区块背景可取站点 `news_bg_image` | `cz-hero*` / `cz-block-list--two` | +| `compact` | 圆点密行:青绿圆点 + 标题 + 右对齐完整日期 | `cz-item-bullet` / `cz-item-date` | +| `media_focus` | 媒体聚焦:胶囊来源徽标 + 双列密行 | `cz-src` | +| `image_wall` | 三列大格图墙:圆角卡 + 底部遮罩标题 | `cz-wall` | +| (空/未知) | 整宽区块卡:左 2 份图文头条 + 右 3 份圆点行 | `cz-block--flow` / `cz-split` | +| `ticker` | 青蓝要闻速递横条,标签为青绿胶囊(单行不换行、超长省略,中文不塌列) | `cz-ticker*` | +| `gallery` | 线上展厅:居中双语文区头 + 四列图墙 | `cz-gallery` | +| `links` | 友情链接分组(胶囊标签行) | `cz-links` | + +## 契约要点 + +- 结构类前缀 `cz-`,CSS 变量前缀 `--cz-`;`:root` 把 `--cz-*` 别名到皮肤契约变量(`--primary`/`--text`/`--card-bg`/`--border`…), + 使 `themes/common` 兜底骨架也能跟随本皮肤着色。 +- 空态文案「栏目暂无内容」只在 `block.items` 为空时出现;守卫必须写在 `th:block` 外层—— + `th:replace`(优先级 100)先于 `th:if`(300)求值,写在同一标签上等于没写。 +- 回归防线:`AcademicSkinContractTest`(自带区块 fragment + 中文不塌列 + 契约变量别名 + 主色必须学术蓝)、 + `PortalHomeLayoutIT#campusAzure_fallbackAndAssets`(回落分支 + 资产通道)、`ThemeContractIT`(7 条路由)。 diff --git a/src/main/resources/templates/themes/campus-azure/assets/css/main.css b/src/main/resources/templates/themes/campus-azure/assets/css/main.css new file mode 100644 index 0000000..5bea869 --- /dev/null +++ b/src/main/resources/templates/themes/campus-azure/assets/css/main.css @@ -0,0 +1,1535 @@ +/* ===================================================================== + campus-azure · 青蓝校园(基座 scholar-blue,参考语言:师范/农林类高校官网门户) + 设计语言:青蓝顶栏/品牌行 + 白底主导航(吸顶反蓝) + 青绿描边双线标题 + + 布局模式全宽封面头条 + 圆点密行 + 胶囊速递标签 + 深色圆环页脚 + 色彩令牌全部走 CSS 变量,theme.json color 参数经 :root 覆盖真实生效。 + ===================================================================== */ + +/* --------------------------------------------------------------------- + 1. 设计令牌(与 theme.json params 一一对应,禁止出现未消费的声明) + --------------------------------------------------------------------- */ +:root { + --cz-primary: #0d6a9e; /* 主色:青蓝,顶栏/品牌行/标题/强调 */ + --cz-primary-dark: #084c73; /* 主色加深:hover/渐变端/页脚深端 */ + --cz-primary-light: #1d87bd; /* 主色提亮:亮线条/hover 链接/角标 */ + --cz-accent: #2b9c8f; /* 点缀青绿:标题下划细线/日期方块描边(农林院校常见) */ + --cz-nav-bg: #ffffff; /* 主导航条底色(白条学院风) */ + --cz-footer-bg: #0b3348; /* 页脚主底 */ + --cz-text: #303c46; /* 正文 */ + --cz-text-light: #6e7d88; /* 次要文字 */ + --cz-bg: #eef3f6; /* 页面底色 */ + --cz-card-bg: #ffffff; /* 卡片底 */ + --cz-border: #d9e5ec; /* 边框/分隔线 */ + + --cz-ink-on-dark: #ffffff; /* 深底主文字 */ + --cz-ink-on-dark-soft: rgba(255,255,255,.75); /* 深底次要文字 */ + --cz-ink-on-dark-faint: rgba(255,255,255,.47); + --cz-panel: rgba(13,106,158,.95); /* 下拉面板(主色半透明) */ + --cz-shadow-sm: 0 1px 4px rgba(8,76,115,.08); + --cz-shadow-md: 0 6px 18px rgba(8,76,115,.12); + --cz-shadow-lg: 0 14px 34px rgba(8,76,115,.18); + --cz-radius: 6px; + --cz-radius-lg: 12px; + --cz-speed: .28s; + --cz-font: "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Helvetica Neue", Arial, sans-serif; + + /* 皮肤契约别名:站点配了 home_layout 时首页走 common/fragments/home-blocks 骨架, + 该骨架只认契约变量(--primary/--text/…)。不别名过去,换肤就只换了页头页脚、正文区块仍是骨架内置色。 */ + --primary: var(--cz-primary); + --primary-dark: var(--cz-primary-dark); + --primary-light: var(--cz-primary-light); + --text: var(--cz-text); + --text-light: var(--cz-text-light); + --bg: var(--cz-bg); + --card-bg: var(--cz-card-bg); + --border: var(--cz-border); + --radius: var(--cz-radius-lg); +} + +/* --------------------------------------------------------------------- + 2. Reset 与基础 + --------------------------------------------------------------------- */ +* { margin: 0; padding: 0; box-sizing: border-box; } + +html { -webkit-text-size-adjust: 100%; } + +body { + font-family: var(--cz-font); + font-size: 14px; + line-height: 1.7; + color: var(--cz-text); + background: var(--cz-bg); + word-break: break-word; +} + +ul, ol { list-style: none; } + +a { color: var(--cz-primary); text-decoration: none; transition: color var(--cz-speed) ease; } +a:hover { color: var(--cz-primary-light); } + +img { max-width: 100%; display: block; border: 0; } + +button { font-family: inherit; cursor: pointer; } + +input, button, select, textarea { font-size: inherit; color: inherit; } + +::selection { background: var(--cz-primary); color: #fff; } + +/* 跳转链接:仅键盘聚焦时可见 */ +.skip-link { + position: absolute; left: -999px; top: 0; z-index: 300; + background: var(--cz-primary-dark); color: #fff; + padding: 8px 18px; border-radius: 0 0 var(--cz-radius) 0; +} +.skip-link:focus { left: 0; color: #fff; } + +/* 通用容器 */ +.cz-container { + width: 1200px; max-width: calc(100% - 40px); + margin: 0 auto; +} + +/* 屏幕阅读器专用 */ +.cz-visually-hidden { + position: absolute; width: 1px; height: 1px; + margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; +} + +/* 置顶徽章 */ +.cz-top-badge { + display: inline-block; flex: none; + padding: 0 7px; line-height: 18px; + font-size: 12px; color: #fff; + background: var(--cz-primary-light); + border-radius: 2px; + letter-spacing: 1px; +} + +/* 空状态 */ +.cz-empty { + padding: 56px 20px; + text-align: center; + color: var(--cz-text-light); +} +.cz-empty p:first-child { font-size: 16px; margin-bottom: 8px; } +.cz-empty-hint { font-size: 13px; color: var(--cz-text-light); opacity: .75; } + +/* --------------------------------------------------------------------- + 3. 顶栏(深蓝细条:日期 + 院系切换) + --------------------------------------------------------------------- */ +.cz-topbar { + background: linear-gradient(90deg, var(--cz-primary-dark), var(--cz-primary)); + color: var(--cz-ink-on-dark-soft); + font-size: 13px; +} + +.cz-topbar-inner { + width: 1200px; max-width: calc(100% - 40px); + margin: 0 auto; + display: flex; align-items: center; justify-content: space-between; + min-height: 36px; +} + +.cz-topbar-date { letter-spacing: .5px; } + +.cz-topbar-side { position: relative; } + +/* 院系切换:细字 + 箭头,展开为深蓝面板 */ +.cz-colleges { position: relative; } + +.cz-colleges-toggle { + display: inline-flex; align-items: center; gap: 6px; + padding: 4px 2px; + cursor: pointer; +} +.cz-colleges-toggle a { color: var(--cz-ink-on-dark-soft); } +.cz-colleges-toggle a:hover { color: #fff; } + +.cz-colleges-arrow { + font-style: normal; font-size: 10px; + transition: transform var(--cz-speed) ease; +} +.cz-colleges:hover .cz-colleges-arrow { transform: rotate(180deg); } + +.cz-colleges-menu { + position: absolute; right: 0; top: calc(100% + 6px); + min-width: 180px; max-height: 280px; overflow: auto; + background: var(--cz-panel); + backdrop-filter: blur(4px); + border-top: 2px solid var(--cz-primary-light); + border-radius: 0 0 var(--cz-radius) var(--cz-radius); + box-shadow: var(--cz-shadow-lg); + padding: 6px 0; + opacity: 0; visibility: hidden; transform: translateY(-6px); + transition: opacity var(--cz-speed) ease, transform var(--cz-speed) ease, visibility var(--cz-speed); + z-index: 120; +} +.cz-colleges:hover .cz-colleges-menu, +.cz-colleges:focus-within .cz-colleges-menu { + opacity: 1; visibility: visible; transform: translateY(0); +} + +.cz-colleges-menu li a { + display: block; + padding: 7px 16px; + color: var(--cz-ink-on-dark); + font-size: 13px; + border-left: 2px solid transparent; + transition: background var(--cz-speed) ease, border-color var(--cz-speed) ease, padding-left var(--cz-speed) ease; +} +.cz-colleges-menu li a:hover { + background: var(--cz-primary-dark); + border-left-color: var(--cz-primary-light); + padding-left: 20px; + color: #fff; +} +.cz-colleges-menu li.active a { + background: rgba(255,255,255,.08); + border-left-color: var(--cz-primary-light); + color: #fff; +} + +/* --------------------------------------------------------------------- + 4. 品牌行(深蓝主区:徽标 + 站名 + 圆角搜索) + --------------------------------------------------------------------- */ +.cz-brandbar { + background: linear-gradient(100deg, var(--cz-primary-dark) 0%, var(--cz-primary) 62%, var(--cz-primary-light) 130%); + color: var(--cz-ink-on-dark); + position: relative; + overflow: hidden; +} +/* 品牌行右侧淡圆环装饰(CSS 绘制,无图片依赖) */ +.cz-brandbar::before, +.cz-brandbar::after { + content: ""; + position: absolute; + border-radius: 50%; + border: 26px solid rgba(255,255,255,.05); + pointer-events: none; +} +.cz-brandbar::before { width: 320px; height: 320px; right: -80px; top: -170px; } +.cz-brandbar::after { width: 200px; height: 200px; right: 180px; bottom: -140px; border-width: 16px; } + +.cz-brandbar-inner { + width: 1200px; max-width: calc(100% - 40px); + margin: 0 auto; + display: flex; align-items: center; justify-content: space-between; + gap: 24px; + padding: 22px 0; + position: relative; z-index: 1; +} + +.cz-brand { + display: flex; align-items: center; gap: 14px; + min-width: 0; +} + +/* 徽标容器:白衬圆角方块,校徽任何底色都干净 */ +.cz-brand-badge { + flex: none; + width: 58px; height: 58px; + display: inline-flex; align-items: center; justify-content: center; + background: rgba(255,255,255,.94); + border-radius: var(--cz-radius); + box-shadow: 0 2px 8px rgba(8,76,115,.35); + overflow: hidden; +} +.cz-brand-badge--plain { color: var(--cz-primary); } +.cz-logo { width: 100%; height: 100%; object-fit: contain; } + +.cz-site-name { + color: var(--cz-ink-on-dark); + font-size: 27px; + font-weight: 700; + letter-spacing: 2px; + line-height: 1.3; + text-shadow: 0 2px 6px rgba(8,76,115,.4); + white-space: nowrap; + overflow: hidden; text-overflow: ellipsis; + max-width: 560px; +} +.cz-site-name:hover { color: #fff; } + +/* 搜索:白色胶囊(学院风记忆点) */ +.cz-search { + flex: none; + display: flex; align-items: center; + width: 300px; max-width: 36%; + background: #fff; + border-radius: 999px; + padding: 3px 4px 3px 18px; + box-shadow: 0 3px 10px rgba(8,76,115,.28); +} +.cz-search input { + flex: 1; min-width: 0; + border: 0; outline: none; + background: transparent; + font-size: 13px; color: var(--cz-text); + line-height: 30px; +} +.cz-search input::placeholder { color: #a9b3c0; } +.cz-search button { + flex: none; + width: 32px; height: 32px; + border: 0; border-radius: 50%; + background: var(--cz-primary); + color: #fff; + display: inline-flex; align-items: center; justify-content: center; + transition: background var(--cz-speed) ease, transform var(--cz-speed) ease; +} +.cz-search button:hover { background: var(--cz-primary-light); transform: scale(1.06); } + +/* --------------------------------------------------------------------- + 5. 主导航(白底条 + 主蓝半透明下拉面板 + 吸顶反蓝) + --------------------------------------------------------------------- */ +.cz-nav { + background: var(--cz-nav-bg); + box-shadow: var(--cz-shadow-sm); + position: relative; + z-index: 100; +} +/* 顶部主蓝细线:把导航条与品牌行缝在一起 */ +.cz-nav::before { + content: ""; + display: block; height: 3px; + background: linear-gradient(90deg, var(--cz-primary), var(--cz-primary-light)); +} + +.cz-nav-inner { + width: 1200px; max-width: calc(100% - 40px); + margin: 0 auto; + position: relative; +} + +.cz-nav-toggle { display: none; } + +.cz-nav-list { + display: flex; + justify-content: space-between; +} + +.cz-nav-list > li { position: relative; flex: 1 1 auto; text-align: center; } + +.cz-nav-list > li > a { + display: flex; align-items: center; justify-content: center; gap: 5px; + position: relative; + padding: 0 10px; + height: 52px; + font-size: 16px; + font-weight: 600; + letter-spacing: 1px; + color: var(--cz-primary-dark); + white-space: nowrap; + transition: color var(--cz-speed) ease, background var(--cz-speed) ease; +} +/* 底部指示条:hover/当前项 */ +.cz-nav-list > li > a::after { + content: ""; + position: absolute; left: 50%; bottom: 0; + width: 0; height: 3px; + background: var(--cz-primary); + transform: translateX(-50%); + transition: width var(--cz-speed) ease; +} +.cz-nav-list > li:hover > a, +.cz-nav-list > li.active > a { + color: var(--cz-primary); + background: rgba(13,106,158,.06); +} +.cz-nav-list > li:hover > a::after, +.cz-nav-list > li.active > a::after { width: 56%; } + +.cz-caret { + font-style: normal; font-size: 11px; + opacity: .65; + transition: transform var(--cz-speed) ease; +} +.cz-nav-list > li:hover .cz-caret { transform: rotate(180deg); } + +/* —— 下拉面板:主蓝半透明、顶部亮线、项 hover 加深 + 左亮条(红线第一优先级) —— */ +.cz-sub { + position: absolute; left: 50%; top: 100%; + transform: translate(-50%, 10px); + min-width: 176px; + background: var(--cz-panel); + backdrop-filter: blur(6px); + border-top: 3px solid var(--cz-primary-light); + border-radius: 0 0 var(--cz-radius) var(--cz-radius); + box-shadow: var(--cz-shadow-lg); + padding: 6px 0; + text-align: left; + opacity: 0; visibility: hidden; + transition: opacity var(--cz-speed) ease, transform var(--cz-speed) ease, visibility var(--cz-speed); + z-index: 110; +} +.cz-nav-list > li:hover .cz-sub, +.cz-nav-list > li:focus-within .cz-sub { + opacity: 1; visibility: visible; + transform: translate(-50%, 0); +} + +/* 面板小尖角 */ +.cz-sub::before { + content: ""; + position: absolute; left: 50%; top: -8px; + width: 12px; height: 12px; + background: var(--cz-primary); + transform: translateX(-50%) rotate(45deg); + border-radius: 2px 0 0 0; +} + +.cz-sub li a { + display: block; + position: relative; + margin: 2px 8px; + padding: 9px 14px 9px 22px; + font-size: 14px; + color: var(--cz-ink-on-dark); + border-radius: 4px; + white-space: nowrap; + transition: background var(--cz-speed) ease, padding-left var(--cz-speed) ease, color var(--cz-speed) ease; +} +/* 项前细刻度线 */ +.cz-sub li a::before { + content: ""; + position: absolute; left: 10px; top: 50%; + width: 4px; height: 4px; + margin-top: -2px; + background: rgba(255,255,255,.5); + border-radius: 50%; + transition: background var(--cz-speed) ease, box-shadow var(--cz-speed) ease; +} +.cz-sub li a:hover { + background: var(--cz-primary-dark); + padding-left: 26px; + color: #fff; +} +.cz-sub li a:hover::before { + background: var(--cz-primary-light); + box-shadow: 0 0 6px var(--cz-primary-light); +} + +/* —— 吸顶反蓝(.cz-nav--fixed 由 main.js 滚动加挂) —— */ +.cz-nav--fixed { + position: fixed; left: 0; right: 0; top: 0; + background: linear-gradient(90deg, var(--cz-primary-dark), var(--cz-primary)); + box-shadow: var(--cz-shadow-md); + animation: cz-nav-drop var(--cz-speed) ease; +} +@keyframes cz-nav-drop { + from { transform: translateY(-100%); } + to { transform: translateY(0); } +} +.cz-nav--fixed::before { height: 0; } +.cz-nav--fixed .cz-nav-list > li > a { color: var(--cz-ink-on-dark); } +.cz-nav--fixed .cz-nav-list > li:hover > a, +.cz-nav--fixed .cz-nav-list > li.active > a { + color: #fff; + background: rgba(255,255,255,.1); +} +.cz-nav--fixed .cz-nav-list > li > a::after { background: var(--cz-primary-light); } +/* 吸顶态下拉:底色更沉,与蓝条衔接 */ +.cz-nav--fixed .cz-sub { background: rgba(8,76,115,.96); } +.cz-nav--fixed .cz-sub::before { background: var(--cz-primary-dark); } + +/* 吸顶占位,防跳变(main.js 加挂时同步高度,解锁即移除) */ +.cz-nav-holder { display: block; } + +/* --------------------------------------------------------------------- + 6. 首页 banner(通栏轮播 + 圆形箭头 + 底部圆点) + --------------------------------------------------------------------- */ +.cz-banner { + position: relative; + overflow: hidden; + background: var(--cz-primary-dark); +} +.cz-banner-slides { position: relative; height: 520px; } + +.cz-banner-slide { + position: absolute; inset: 0; + opacity: 0; visibility: hidden; + transition: opacity .8s ease, visibility .8s ease; +} +.cz-banner-slide.active { opacity: 1; visibility: visible; z-index: 1; } + +.cz-banner-slide img { + width: 100%; height: 100%; + object-fit: cover; +} +/* 底部渐隐遮罩上承标题 */ +.cz-banner-slide::after { + content: ""; + position: absolute; left: 0; right: 0; bottom: 0; + height: 120px; + background: linear-gradient(180deg, transparent, rgba(8,76,115,.66)); + pointer-events: none; +} +.cz-banner-slide.active img { animation: cz-banner-zoom 6.5s ease forwards; } +@keyframes cz-banner-zoom { + from { transform: scale(1); } + to { transform: scale(1.045); } +} + +.cz-banner-title { + position: absolute; left: 6%; bottom: 34px; + z-index: 2; + max-width: 70%; + color: #fff; + font-size: 24px; + font-weight: 600; + letter-spacing: 1px; + text-shadow: 0 2px 8px rgba(8,76,115,.6); + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; +} + +/* 圆形半透明箭头(建平语言) */ +.cz-banner-arrow { + position: absolute; top: 50%; z-index: 3; + width: 52px; height: 52px; margin-top: -26px; + border: 2px solid rgba(255,255,255,.75); + border-radius: 50%; + background: rgba(8,76,115,.25); + color: #fff; + font-size: 26px; line-height: 1; + display: inline-flex; align-items: center; justify-content: center; + padding-bottom: 4px; + opacity: 0; + transition: opacity var(--cz-speed) ease, background var(--cz-speed) ease, border-color var(--cz-speed) ease; +} +.cz-banner:hover .cz-banner-arrow { opacity: 1; } +.cz-banner-arrow:hover { background: var(--cz-primary); border-color: #fff; } +.cz-banner-arrow.prev { left: 26px; } +.cz-banner-arrow.next { right: 26px; } + +/* 圆点指示器 */ +.cz-banner-dots { + position: absolute; left: 0; right: 0; bottom: 18px; + z-index: 3; + display: flex; justify-content: center; gap: 10px; +} +.cz-banner-dots button { + width: 10px; height: 10px; + border: 0; border-radius: 50%; + background: rgba(255,255,255,.55); + transition: background var(--cz-speed) ease, transform var(--cz-speed) ease; +} +.cz-banner-dots button:hover { background: #fff; } +.cz-banner-dots button.active { + background: #fff; + transform: scale(1.25); + box-shadow: 0 0 0 3px rgba(255,255,255,.25); +} + +/* --------------------------------------------------------------------- + 7. 区块标题(居中双线 + 下短横)与区块通用 + --------------------------------------------------------------------- */ +.cz-section { margin-top: 40px; } + +.cz-block-head { + position: relative; + text-align: center; + padding: 14px 0 12px; + border-top: 1px solid var(--cz-border); + border-bottom: 1px solid var(--cz-border); +} +.cz-block-head h2 { + display: inline-block; + position: relative; + font-size: 22px; + font-weight: 700; + letter-spacing: 3px; + color: var(--cz-primary-dark); +} +/* 标题下短横:主蓝 3px */ +.cz-block-tick { + position: absolute; left: 50%; bottom: -2px; + width: 42px; height: 3px; + transform: translateX(-50%); + background: var(--cz-primary); + border-radius: 2px; +} + +/* 栏目卡头变体:左右布局(标题居左 + 更多居右) */ +.cz-block-head--bar { + text-align: left; + padding: 0 0 10px; + border-top: 0; + border-bottom: 2px solid var(--cz-primary); + display: flex; align-items: baseline; justify-content: space-between; +} +.cz-block-head--bar h2 { + font-size: 19px; + letter-spacing: 1px; + padding-left: 12px; + position: relative; +} +/* 标题左侧竖条 */ +.cz-block-head--bar h2::before { + content: ""; + position: absolute; left: 0; top: 6px; bottom: 6px; + width: 4px; + background: var(--cz-primary); + border-radius: 2px; +} +.cz-block-head--bar .cz-block-tick { display: none; } + +.cz-more { + font-size: 13px; + font-weight: 400; + color: var(--cz-text-light); + letter-spacing: 0; + transition: color var(--cz-speed) ease, transform var(--cz-speed) ease; +} +.cz-more:hover { color: var(--cz-primary); transform: translateX(3px); } + +/* --------------------------------------------------------------------- + 8. 头条区(左:头条推荐列表 / 右:图片新闻) + --------------------------------------------------------------------- */ +.cz-headline { + display: grid; + grid-template-columns: 7fr 5fr; + gap: 28px; +} +/* grid 项允许收缩到轨道内:否则 nowrap 省略文本会把 auto 最小宽撑破容器(移动端横向溢出) */ +.cz-headline > * { min-width: 0; } + +.cz-headline-card, +.cz-focus-card { + background: var(--cz-card-bg); + border-radius: var(--cz-radius-lg); + box-shadow: var(--cz-shadow-sm); + padding: 6px 22px 18px; +} + +/* 头条推荐 */ +.cz-headline-list li { + display: flex; align-items: center; gap: 10px; + padding: 10px 2px; + border-bottom: 1px dashed var(--cz-border); +} +.cz-headline-list li:last-child { border-bottom: 0; } +.cz-headline-list li a { + flex: 1; min-width: 0; + color: var(--cz-text); + font-size: 15px; + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + transition: color var(--cz-speed) ease, padding-left var(--cz-speed) ease; +} +.cz-headline-list li a:hover { color: var(--cz-primary); padding-left: 4px; } +.cz-headline-list time { flex: none; color: var(--cz-text-light); font-size: 13px; } + +/* 头条头条:大标题 + 元信息 */ +.cz-headline-lead { + display: block !important; + padding: 14px 2px 16px !important; +} +.cz-lead-title { + display: block; + font-size: 19px !important; + font-weight: 700; + color: var(--cz-primary-dark) !important; + line-height: 1.5; + white-space: normal !important; + overflow: hidden; + max-height: 3em; +} +.cz-lead-title:hover { color: var(--cz-primary) !important; padding-left: 0 !important; } +.cz-lead-meta { + display: flex; align-items: center; gap: 10px; + margin-top: 8px; + font-size: 13px; color: var(--cz-text-light); +} + +/* 图片新闻 */ +.cz-focus-lead { + display: block; + margin-top: 14px; + border-radius: var(--cz-radius); + overflow: hidden; + position: relative; +} +.cz-focus-cover { display: block; aspect-ratio: 16/9; overflow: hidden; background: var(--cz-bg); } +.cz-focus-cover img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s ease; } +.cz-focus-lead:hover .cz-focus-cover img { transform: scale(1.05); } +.cz-focus-title { + position: absolute; left: 0; right: 0; bottom: 0; + padding: 26px 14px 10px; + color: #fff; font-size: 15px; font-weight: 600; + background: linear-gradient(180deg, transparent, rgba(8,76,115,.82)); + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; +} + +.cz-focus-rows { margin-top: 10px; } +.cz-focus-rows li a { + display: flex; align-items: center; gap: 12px; + padding: 8px 0; +} +.cz-row-cover { + flex: none; + width: 104px; height: 66px; + border-radius: 4px; + overflow: hidden; + background: var(--cz-bg); +} +.cz-row-cover img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s ease; } +.cz-focus-rows li a:hover .cz-row-cover img { transform: scale(1.06); } +.cz-row-title { + flex: 1; min-width: 0; + color: var(--cz-text); + font-size: 14px; + line-height: 1.55; + max-height: 3.1em; + overflow: hidden; + transition: color var(--cz-speed) ease; +} +.cz-focus-rows li a:hover .cz-row-title { color: var(--cz-primary); } + +/* --------------------------------------------------------------------- + 9. 栏目区块(白卡三列 + 日期方块行) + --------------------------------------------------------------------- */ +.cz-blocks { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 24px; + margin-top: 14px; +} + +.cz-block { + background: var(--cz-card-bg); + border-radius: var(--cz-radius-lg); + box-shadow: var(--cz-shadow-sm); + padding: 18px 20px 14px; + display: flex; flex-direction: column; + min-width: 0; + transition: box-shadow var(--cz-speed) ease, transform var(--cz-speed) ease; +} +.cz-block:hover { box-shadow: var(--cz-shadow-md); transform: translateY(-2px); } + +.cz-block-list { flex: 1; } + +/* 区块头条(有封面图文 / 无封面纯文) */ +.cz-block-featured { border-bottom: 1px solid var(--cz-border); } +.cz-block-featured > a { + display: block; + padding: 12px 0 14px; +} +.cz-feat-cover { + display: block; + aspect-ratio: 16/9; + border-radius: var(--cz-radius); + overflow: hidden; + background: var(--cz-bg); + margin-bottom: 10px; +} +.cz-feat-cover img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s ease; } +.cz-block-featured > a:hover .cz-feat-cover img { transform: scale(1.04); } + +.cz-feat-title { + display: block; + font-size: 16px; font-weight: 700; + color: var(--cz-primary-dark); + line-height: 1.5; + max-height: 3em; + overflow: hidden; + transition: color var(--cz-speed) ease; +} +.cz-block-featured > a:hover .cz-feat-title { color: var(--cz-primary); } +.cz-feat-summary { + display: block; + margin-top: 6px; + font-size: 13px; color: var(--cz-text-light); + line-height: 1.65; + max-height: 3.3em; + overflow: hidden; +} +.cz-feat-meta { + display: flex; align-items: center; gap: 10px; + margin-top: 8px; + font-size: 12px; color: var(--cz-text-light); +} + +/* 列表行:日期方块 + 标题(建平通知公告语言) */ +.cz-block-item { + display: flex; align-items: center; gap: 12px; + padding: 8px 0; + border-bottom: 1px dashed var(--cz-border); +} +.cz-block-item:last-child { border-bottom: 0; } + +.cz-date-chip { + flex: none; + width: 46px; + text-align: center; + border-radius: 4px; + overflow: hidden; + background: var(--cz-bg); + border: 1px solid var(--cz-border); + transition: background var(--cz-speed) ease, border-color var(--cz-speed) ease; +} +.cz-date-chip b { + display: block; + font-size: 17px; line-height: 24px; + font-weight: 700; + color: var(--cz-primary); + background: rgba(13,106,158,.08); + transition: color var(--cz-speed) ease; +} +.cz-date-chip i { + display: block; + font-style: normal; + font-size: 10px; line-height: 16px; + color: var(--cz-text-light); + transform: scale(.92); +} +.cz-block-item:hover .cz-date-chip { + background: var(--cz-primary); + border-color: var(--cz-primary); +} +.cz-block-item:hover .cz-date-chip b { color: #fff; background: transparent; } +.cz-block-item:hover .cz-date-chip i { color: rgba(255,255,255,.8); } + +.cz-block-item-title { + flex: 1; min-width: 0; + font-size: 14px; + color: var(--cz-text); + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + transition: color var(--cz-speed) ease; +} +.cz-block-item:hover .cz-block-item-title { color: var(--cz-primary); } + +/* --------------------------------------------------------------------- + 10. 列表/详情/单页公共:页面卡 + 面包屑 + 页题 + --------------------------------------------------------------------- */ +.cz-page { padding: 28px 0 48px; } + +.cz-page-card { + background: var(--cz-card-bg); + border-radius: var(--cz-radius-lg); + box-shadow: var(--cz-shadow-sm); + padding: 26px 32px 34px; + min-height: 420px; +} + +.cz-crumb { + display: flex; align-items: center; flex-wrap: wrap; gap: 8px; + font-size: 13px; + color: var(--cz-text-light); + padding-bottom: 14px; + border-bottom: 1px solid var(--cz-border); + margin-bottom: 20px; +} +.cz-crumb a { color: var(--cz-text-light); } +.cz-crumb a:hover { color: var(--cz-primary); } +.cz-crumb-sep { color: #c3cad4; } +.cz-crumb-current { color: var(--cz-primary); } + +.cz-page-title { + position: relative; + font-size: 23px; + font-weight: 700; + letter-spacing: 1px; + color: var(--cz-primary-dark); + padding-left: 16px; + margin-bottom: 22px; +} +.cz-page-title::before { + content: ""; + position: absolute; left: 0; top: 5px; bottom: 5px; + width: 5px; + border-radius: 3px; + background: linear-gradient(180deg, var(--cz-primary), var(--cz-primary-light)); +} + +/* --------------------------------------------------------------------- + 11. 列表页:图文行 + 行式列表 + 分页 + --------------------------------------------------------------------- */ +.cz-article-list > li { border-bottom: 1px solid var(--cz-border); } +.cz-article-list > li:last-child { border-bottom: 0; } + +/* 图文行 */ +.cz-article-row { + display: flex; gap: 20px; + padding: 18px 2px; +} +.cz-item-cover { + flex: none; + width: 216px; aspect-ratio: 3/2; + border-radius: var(--cz-radius); + overflow: hidden; + background: var(--cz-bg); +} +.cz-item-cover img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s ease; } +.cz-article-row:hover .cz-item-cover img { transform: scale(1.05); } + +.cz-item-body { flex: 1; min-width: 0; padding-top: 4px; } +.cz-item-title a { + font-size: 17px; font-weight: 600; + color: var(--cz-text); + line-height: 1.5; + transition: color var(--cz-speed) ease; +} +.cz-item-title a:hover { color: var(--cz-primary); } +.cz-item-summary { + margin-top: 8px; + font-size: 13px; color: var(--cz-text-light); + line-height: 1.7; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} +.cz-item-meta { + display: flex; align-items: center; gap: 12px; + margin-top: 10px; + font-size: 12px; color: var(--cz-text-light); +} + +/* 行式(无封面):刻度符号 + 标题 + 右侧日期 */ +.cz-article-line { + display: flex; align-items: center; gap: 12px; + padding: 13px 2px; +} +.cz-line-tick { + flex: none; + width: 6px; height: 6px; + background: var(--cz-primary); + transform: rotate(45deg); + opacity: .85; + transition: transform var(--cz-speed) ease; +} +.cz-article-line:hover .cz-line-tick { transform: rotate(45deg) scale(1.35); } + +.cz-line-main { flex: 1; min-width: 0; } +.cz-line-title { + display: block; + font-size: 15px; + color: var(--cz-text); + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + transition: color var(--cz-speed) ease, padding-left var(--cz-speed) ease; +} +.cz-article-line:hover .cz-line-title { color: var(--cz-primary); padding-left: 4px; } +.cz-article-line .cz-item-summary { margin-top: 4px; -webkit-line-clamp: 1; } + +.cz-line-side { + flex: none; + display: flex; align-items: center; gap: 10px; + font-size: 13px; color: var(--cz-text-light); +} + +/* 分页 */ +.cz-pagination { + display: flex; justify-content: center; flex-wrap: wrap; gap: 8px; + margin-top: 30px; +} +.cz-pagination a { + min-width: 36px; height: 36px; + display: inline-flex; align-items: center; justify-content: center; + padding: 0 10px; + font-size: 14px; + color: var(--cz-text); + background: var(--cz-bg); + border: 1px solid var(--cz-border); + border-radius: 4px; + transition: all var(--cz-speed) ease; +} +.cz-pagination a:hover { + color: #fff; + background: var(--cz-primary); + border-color: var(--cz-primary); +} +.cz-pagination a.active { + color: #fff; + background: var(--cz-primary); + border-color: var(--cz-primary); + font-weight: 700; + box-shadow: 0 3px 8px rgba(13,106,158,.35); +} +.cz-pagination a.disabled { + opacity: .45; pointer-events: none; +} + +/* --------------------------------------------------------------------- + 12. 详情/单页:文章排版 + --------------------------------------------------------------------- */ +.cz-article-title { + text-align: center; + font-size: 24px; + font-weight: 700; + color: var(--cz-primary-dark); + line-height: 1.5; + padding: 6px 20px 0; +} + +.cz-article-meta { + display: flex; justify-content: center; flex-wrap: wrap; gap: 6px 22px; + padding: 14px 0 18px; + margin-bottom: 24px; + font-size: 13px; color: var(--cz-text-light); + border-bottom: 1px solid var(--cz-border); + position: relative; +} +/* 标题下居中短横(与区块标题语言呼应) */ +.cz-article-meta::before { + content: ""; + position: absolute; left: 50%; top: -12px; + width: 42px; height: 3px; + transform: translateX(-50%); + background: var(--cz-primary); + border-radius: 2px; +} + +.cz-article-content { + font-size: 15px; + line-height: 1.9; + color: var(--cz-text); +} +.cz-article-content p { margin: 0 0 14px; } +.cz-article-content h1, .cz-article-content h2, +.cz-article-content h3, .cz-article-content h4 { + margin: 22px 0 12px; + color: var(--cz-primary-dark); + font-weight: 700; +} +.cz-article-content h2 { font-size: 19px; } +.cz-article-content h3 { font-size: 17px; } +.cz-article-content h4 { font-size: 15px; } +.cz-article-content img { + max-width: 100%; + height: auto; + border-radius: var(--cz-radius); + margin: 6px auto 14px; + box-shadow: var(--cz-shadow-sm); +} +.cz-article-content ul, .cz-article-content ol { + margin: 0 0 14px; padding-left: 26px; +} +.cz-article-content ul li { list-style: disc; } +.cz-article-content ol li { list-style: decimal; } +.cz-article-content li { margin-bottom: 6px; } +.cz-article-content blockquote { + margin: 14px 0; + padding: 12px 18px; + background: rgba(13,106,158,.05); + border-left: 4px solid var(--cz-primary); + border-radius: 0 var(--cz-radius) var(--cz-radius) 0; + color: var(--cz-text-light); +} +.cz-article-content table { + width: 100%; + border-collapse: collapse; + margin: 14px 0; + font-size: 14px; +} +.cz-article-content th, .cz-article-content td { + border: 1px solid var(--cz-border); + padding: 8px 12px; + text-align: left; +} +.cz-article-content th { + background: rgba(13,106,158,.06); + color: var(--cz-primary-dark); + font-weight: 600; +} +.cz-article-content a { color: var(--cz-primary-light); text-decoration: underline; } +.cz-article-content hr { + border: 0; height: 1px; + background: var(--cz-border); + margin: 20px 0; +} +.cz-article-content video, .cz-article-content audio { max-width: 100%; } + +/* 上下篇 */ +.cz-sibling { + display: grid; grid-template-columns: 1fr 1fr; gap: 16px; + margin-top: 34px; + padding-top: 22px; + border-top: 1px solid var(--cz-border); +} +.cz-sib, .cz-sib-none { + display: block; + padding: 12px 16px; + border: 1px solid var(--cz-border); + border-radius: var(--cz-radius); + background: var(--cz-bg); + min-width: 0; + transition: border-color var(--cz-speed) ease, background var(--cz-speed) ease, box-shadow var(--cz-speed) ease; +} +.cz-sib:hover { + border-color: var(--cz-primary); + background: var(--cz-card-bg); + box-shadow: var(--cz-shadow-sm); +} +.cz-sib.next { text-align: right; } +.cz-sib-label { + display: block; + font-size: 12px; + color: var(--cz-primary); + letter-spacing: 2px; + margin-bottom: 4px; +} +.cz-sib-title { + display: block; + font-size: 14px; + color: var(--cz-text); + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; +} +.cz-sib-none { opacity: .6; } +.cz-sib-none .cz-sib-label { color: var(--cz-text-light); } + +/* --------------------------------------------------------------------- + 13. 页脚(深蓝渐变 + 同心圆环纹理 + 三栏 + 底条) + --------------------------------------------------------------------- */ +.cz-footer { + position: relative; + margin-top: 56px; + color: var(--cz-ink-on-dark); + background: linear-gradient(160deg, var(--cz-footer-bg) 0%, var(--cz-primary-dark) 100%); + overflow: hidden; +} + +/* 同心圆环纹理(CSS 绘制,呼应校徽环形语言) */ +.cz-ring { + position: absolute; + border-radius: 50%; + border: 2px solid rgba(255,255,255,.06); + pointer-events: none; +} +.cz-ring--1 { width: 460px; height: 460px; right: -140px; top: -160px; border-width: 30px; } +.cz-ring--2 { width: 300px; height: 300px; right: -60px; top: -80px; border-width: 18px; } +.cz-ring--3 { width: 520px; height: 520px; left: -200px; bottom: -300px; border-width: 34px; } + +.cz-footer-inner { + width: 1200px; max-width: calc(100% - 40px); + margin: 0 auto; + position: relative; z-index: 1; + display: grid; + grid-template-columns: 1.1fr 1.2fr 1.4fr; + gap: 40px; + padding: 44px 0 36px; +} +.cz-footer-inner > * { min-width: 0; } + +.cz-footer-brand { + display: flex; align-items: center; gap: 14px; +} +.cz-footer-badge { + flex: none; + width: 66px; height: 66px; + display: inline-flex; align-items: center; justify-content: center; + background: rgba(255,255,255,.94); + border-radius: var(--cz-radius); + overflow: hidden; + box-shadow: 0 2px 10px rgba(8,76,115,.4); +} +.cz-footer-badge--plain { color: var(--cz-primary); } +.cz-footer-logo { width: 100%; height: 100%; object-fit: contain; } +.cz-footer-site { + font-size: 21px; + font-weight: 700; + letter-spacing: 1.5px; + line-height: 1.45; + text-shadow: 0 2px 6px rgba(8,76,115,.4); +} + +.cz-footer-info { padding-top: 6px; } +.cz-footer-line { + display: flex; align-items: baseline; gap: 10px; + margin-bottom: 10px; + font-size: 13px; + color: var(--cz-ink-on-dark-soft); + line-height: 1.7; +} +.cz-dot { + flex: none; + width: 6px; height: 6px; + background: var(--cz-primary-light); + border-radius: 50%; + transform: translateY(-2px); + box-shadow: 0 0 6px rgba(17,103,187,.9); +} + +.cz-footer-links { + padding-top: 6px; + display: grid; grid-template-columns: 1fr 1fr; gap: 8px 28px; +} +.cz-group-name { + display: inline-block; + font-size: 13px; font-weight: 600; + color: #fff; + padding-bottom: 4px; + margin-bottom: 6px; + border-bottom: 2px solid var(--cz-primary-light); +} +.cz-links-row { + display: flex; flex-wrap: wrap; gap: 4px 14px; + margin-bottom: 10px; +} +.cz-links-row a { + font-size: 13px; + color: var(--cz-ink-on-dark-soft); + transition: color var(--cz-speed) ease, padding-left var(--cz-speed) ease; +} +.cz-links-row a:hover { color: #fff; padding-left: 2px; } + +.cz-footer-bottom { + position: relative; z-index: 1; + border-top: 1px solid rgba(255,255,255,.14); + text-align: center; + padding: 16px 0; + font-size: 13px; + color: var(--cz-ink-on-dark-faint); + letter-spacing: 1px; +} + +/* --------------------------------------------------------------------- + 14. 响应式:1200 / 992 / 768 三档 + --------------------------------------------------------------------- */ +@media (max-width: 1240px) { + .cz-container, .cz-topbar-inner, .cz-brandbar-inner, .cz-nav-inner { max-width: calc(100% - 40px); } +} + +@media (max-width: 992px) { + .cz-site-name { font-size: 22px; max-width: 380px; } + .cz-search { width: 240px; } + + .cz-nav-list { flex-wrap: wrap; justify-content: flex-start; } + .cz-nav-list > li { flex: 0 0 auto; } + .cz-nav-list > li > a { height: 46px; font-size: 15px; padding: 0 12px; } + + .cz-headline { grid-template-columns: 1fr; } + .cz-blocks { grid-template-columns: repeat(2, 1fr); } + + .cz-banner-slides { height: 380px; } + .cz-banner-title { font-size: 19px; bottom: 28px; } + + .cz-footer-inner { grid-template-columns: 1fr 1fr; } +} + +@media (max-width: 768px) { + /* —— 品牌行收纳 —— */ + .cz-brandbar-inner { flex-direction: column; align-items: stretch; gap: 14px; padding: 16px 0; } + .cz-site-name { font-size: 19px; max-width: none; white-space: normal; } + .cz-brand { justify-content: center; } + .cz-search { width: 100%; max-width: none; } + + /* —— 移动端导航:抽屉展开 —— */ + .cz-nav-toggle { + display: inline-flex; flex-direction: column; justify-content: center; gap: 5px; + width: 44px; height: 40px; + padding: 8px; + border: 1px solid var(--cz-border); + border-radius: var(--cz-radius); + background: var(--cz-card-bg); + } + .cz-nav-toggle span { + display: block; height: 2px; + background: var(--cz-primary); + border-radius: 2px; + transition: transform var(--cz-speed) ease, opacity var(--cz-speed) ease; + } + .cz-nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); } + .cz-nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; } + .cz-nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); } + + .cz-nav-inner { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 0; } + /* 吸顶在移动端直接融入文档流配色 */ + .cz-nav--fixed { position: sticky; animation: none; } + + .cz-nav-list { + display: none; + position: absolute; left: -20px; right: -20px; top: 100%; + flex-direction: column; + background: var(--cz-card-bg); + box-shadow: var(--cz-shadow-md); + border-top: 1px solid var(--cz-border); + padding: 6px 16px 12px; + max-height: 68vh; overflow: auto; + } + .cz-nav-list.cz-open { display: flex; } + .cz-nav-list > li { text-align: left; border-bottom: 1px solid var(--cz-border); } + .cz-nav-list > li:last-child { border-bottom: 0; } + .cz-nav-list > li > a { height: 46px; justify-content: flex-start; } + .cz-nav-list > li > a::after { display: none; } + + /* 移动端二级:手风琴内联展开(.cz-sub-open 由 main.js 控制) */ + .cz-sub { + position: static; + display: none; + transform: none; + opacity: 1; visibility: visible; + background: var(--cz-bg); + border-top: 0; + box-shadow: none; + border-radius: 0; + margin: 0 -8px 8px; + padding: 4px 0; + } + .cz-sub::before { display: none; } + .cz-sub li a { color: var(--cz-text); margin: 0 8px; } + .cz-sub li a:hover { background: rgba(13,106,158,.08); color: var(--cz-primary); } + .cz-sub li a::before { background: var(--cz-primary); opacity: .5; } + .cz-nav-list li.cz-sub-open .cz-sub { display: block; } + .cz-nav-list li.has-sub > a { cursor: pointer; } + + /* —— 内容收纳 —— */ + .cz-banner-slides { height: 240px; } + .cz-banner-title { font-size: 15px; left: 16px; bottom: 20px; max-width: 85%; } + .cz-banner-arrow { display: none; } + + .cz-section { margin-top: 28px; } + .cz-blocks { grid-template-columns: 1fr; } + .cz-block-head h2 { font-size: 19px; } + + .cz-article-row { flex-direction: column; gap: 12px; } + .cz-item-cover { width: 100%; } + .cz-article-line { flex-wrap: wrap; } + .cz-line-side { width: 100%; padding-left: 18px; } + + .cz-page-card { padding: 18px 16px 24px; } + .cz-article-title { font-size: 20px; padding: 0; } + .cz-article-meta { gap: 4px 14px; } + .cz-sibling { grid-template-columns: 1fr; } + .cz-sib-title { white-space: normal; } + + .cz-footer-inner { grid-template-columns: 1fr; gap: 26px; padding: 32px 0 26px; } + .cz-footer-links { grid-template-columns: 1fr; } + .cz-ring--1 { width: 300px; height: 300px; border-width: 20px; } +} + +/* --------------------------------------------------------------------- + 15. 动效偏好:尊重系统减弱动态设置 + --------------------------------------------------------------------- */ +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: .01ms !important; + animation-iteration-count: 1 !important; + transition-duration: .01ms !important; + } +} + +/* B4 友链 Logo:与名称并排小图(高度 ~20px,垂直居中;过宽截断不撑破页脚) */ +.links-group a .link-logo, +.cz-links-row a .link-logo { + height: 20px; + width: auto; + max-width: 64px; + object-fit: contain; + vertical-align: middle; + margin-right: 4px; + border-radius: 2px; +} + +/* ===== 左侧栏目导航(docs/11):有二级节点的模块,栏目/文章页左侧展示兄弟目录 ===== */ +.cz-cols { display: flex; align-items: flex-start; gap: 28px; } +.cz-cols .cz-page-card { flex: 1; min-width: 0; } +.cz-side-nav { + flex: none; + width: 230px; + background: var(--cz-card-bg); + border: 1px solid var(--cz-border); + border-top: 3px solid var(--cz-primary); + box-shadow: var(--cz-shadow-md); +} +.cz-side-nav-head { + background: var(--cz-primary); + color: #fff; + font-size: 20px; + font-weight: 700; + text-align: center; + padding: 18px 12px; + letter-spacing: 3px; +} +.cz-side-nav-list { list-style: none; margin: 0; padding: 0; } +.cz-side-nav-item + .cz-side-nav-item { border-top: 1px solid var(--cz-border); } +.cz-side-nav-item a { + display: block; + padding: 13px 18px; + color: var(--cz-text); + text-decoration: none; + font-size: 15px; + border-left: 3px solid transparent; + transition: background .15s ease, color .15s ease, border-color .15s ease; +} +.cz-side-nav-item a:hover { color: var(--cz-primary); background: var(--cz-panel); } +.cz-side-nav-item.active a { + background: var(--cz-primary); + color: #fff; + font-weight: 600; + text-decoration: none; + border-left-color: var(--cz-primary-light); +} +/* 顶导航二级下拉当前项高亮(docs/11 顺带) */ +.cz-nav-list ul.cz-sub li.active a { color: var(--cz-primary-light); } +@media (max-width: 768px) { + .cz-cols { flex-direction: column; gap: 16px; } + .cz-side-nav { width: 100%; } + .cz-side-nav-list { display: flex; overflow-x: auto; -webkit-overflow-scrolling: touch; } + .cz-side-nav-item { flex: none; } + .cz-side-nav-item + .cz-side-nav-item { border-top: none; border-left: 1px solid var(--cz-border); } + .cz-side-nav-item a { white-space: nowrap; border-left: none; } +} +@media print { + .cz-side-nav { display: none; } + .cz-cols { display: block; } +} + +/* --------------------------------------------------------------------- + 16. 首页布局模式区块(fragments/home-blocks.html) + 站点配了 home_layout 时首页正文由本主题渲染:整宽区块卡 + 全宽封面头条 + 圆点密行。 + --------------------------------------------------------------------- */ +/* 青蓝配青绿:标题竖条/线脚与列表圆点走 accent,圆角比基座更大(卡片更柔和) */ +.cz-block-head--bar h2::before { background: var(--cz-accent); } +.cz-block-tick { background: var(--cz-accent); } +/* 居中双线标题下的「更多」钉在标题行右端(师范/农林官网惯例:标题居中、更多右置) */ +.cz-block-head .cz-more { + position: absolute; right: 0; bottom: 13px; + font-size: 13px; +} + +.cz-section { position: relative; } +.cz-sec-bg { + position: absolute; inset: 0; z-index: 0; + background-size: cover; background-position: center; + opacity: .45; pointer-events: none; +} +/* 定位元素默认画在普通流之上:不抬 z-index,背景带会糊在卡片正面(截图实测整卡泛色)。 + 带图时卡片转半透明白,让底图若隐若现——高校官网新闻带的通行做法 */ +.cz-sec-bg + .cz-block { position: relative; z-index: 1; background: rgba(255, 255, 255, .88); } +.cz-block--flow { padding: 20px 26px 14px; } +.cz-block--flow:hover { transform: none; box-shadow: var(--cz-shadow-sm); } +/* grid 轨道用 minmax(0,1fr):否则 nowrap 省略行的 min-content 会撑破轨道(中文标题尤其明显) */ +.cz-split { + display: grid; + grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); + gap: 26px; + padding-top: 14px; +} +.cz-split > * { min-width: 0; } +.cz-hero { + position: relative; + display: block; + margin-top: 14px; + border-radius: var(--cz-radius-lg); + overflow: hidden; + background: var(--cz-bg); +} +.cz-hero img { width: 100%; height: 300px; object-fit: cover; transition: transform .5s ease; } +.cz-hero:hover img { transform: scale(1.03); } +.cz-hero-plain { display: block; height: 8px; background: var(--cz-accent); } +.cz-hero-mask { + position: absolute; left: 0; right: 0; bottom: 0; + padding: 40px 22px 16px; + background: linear-gradient(transparent, rgba(8, 76, 115, .88)); + color: #fff; +} +.cz-hero-plain + .cz-hero-mask { + position: static; + padding: 16px 22px; + background: var(--cz-card-bg); + color: var(--cz-text); +} +.cz-hero-title { + display: block; + font-size: 20px; font-weight: 700; line-height: 1.5; + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; +} +.cz-hero-meta { display: flex; align-items: center; gap: 10px; margin-top: 6px; font-size: 12px; opacity: .9; } +.cz-block-list--two { columns: 2; column-gap: 34px; } +.cz-block-list--two .cz-block-item { break-inside: avoid; } +.cz-item-bullet { + flex: none; + width: 5px; height: 5px; + border-radius: 50%; + background: var(--cz-accent); +} +.cz-item-date { + flex: none; + font-size: 12px; color: var(--cz-text-light); + font-variant-numeric: tabular-nums; +} +.cz-src { + flex: none; + font-size: 11px; line-height: 18px; padding: 0 6px; + color: var(--cz-primary); border: 1px solid var(--cz-primary); + border-radius: 999px; +} +.cz-wall { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 16px; + margin: 16px 0 0; +} +.cz-wall--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } +.cz-wall-item { + position: relative; + border-radius: var(--cz-radius-lg); + overflow: hidden; + box-shadow: var(--cz-shadow-sm); +} +.cz-wall-item img { width: 100%; height: 168px; object-fit: cover; transition: transform .5s ease; } +.cz-wall-item:hover img { transform: scale(1.05); } +.cz-wall-cap { + position: absolute; left: 0; right: 0; bottom: 0; + padding: 28px 12px 10px; + font-size: 13px; color: #fff; + background: linear-gradient(transparent, rgba(11, 51, 72, .85)); + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; +} +.cz-gallery-sub { + margin: -4px 0 4px; + text-align: center; + font-size: 12px; letter-spacing: .18em; text-transform: uppercase; + color: var(--cz-text-light); +} +/* 速递条:flex 项自动最小尺寸 = min-content,中文可按字断行 → 不锁 nowrap/不收缩就会塌成一字一列 */ +.cz-ticker { + position: relative; + background: var(--cz-primary); + color: #fff; + padding: 11px 0; + font-size: 14px; +} +.cz-ticker-inner { display: flex; align-items: center; gap: 16px; min-width: 0; } +.cz-ticker-label { + flex: none; + font-weight: 700; letter-spacing: .06em; + padding: 2px 14px; + border-radius: 999px; + background: var(--cz-accent); +} +.cz-ticker-list { + display: flex; gap: 28px; + flex: 1; min-width: 0; + overflow: hidden; white-space: nowrap; +} +.cz-ticker-list li { flex: none; min-width: 0; } +.cz-ticker-item { + display: block; flex: none; + max-width: 320px; + overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + color: #fff; opacity: .92; +} +.cz-ticker-item:hover { opacity: 1; text-decoration: underline; } +.cz-links-group { + margin: 12px 0 0; + font-size: 13px; letter-spacing: .08em; + color: var(--cz-text-light); +} +.cz-links { display: flex; flex-wrap: wrap; gap: 10px; margin: 8px 0 0; } +.cz-links a { + font-size: 13px; color: var(--cz-text-light); + padding: 4px 14px; + background: var(--cz-card-bg); + border: 1px solid var(--cz-border); + border-radius: 999px; +} +.cz-links a:hover { color: var(--cz-primary); border-color: var(--cz-primary); } + +@media (max-width: 992px) { + .cz-split { grid-template-columns: minmax(0, 1fr); } + .cz-wall, .cz-wall--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .cz-block-list--two { columns: 1; } + .cz-hero img { height: 190px; } + .cz-ticker-item { max-width: 180px; } + .cz-block--flow { padding: 16px 16px 8px; } +} diff --git a/src/main/resources/templates/themes/campus-azure/assets/favicon.png b/src/main/resources/templates/themes/campus-azure/assets/favicon.png new file mode 100644 index 0000000..ac16156 Binary files /dev/null and b/src/main/resources/templates/themes/campus-azure/assets/favicon.png differ diff --git a/src/main/resources/templates/themes/campus-azure/assets/favicon.svg b/src/main/resources/templates/themes/campus-azure/assets/favicon.svg new file mode 100644 index 0000000..b64acaf --- /dev/null +++ b/src/main/resources/templates/themes/campus-azure/assets/favicon.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/main/resources/templates/themes/campus-azure/assets/js/main.js b/src/main/resources/templates/themes/campus-azure/assets/js/main.js new file mode 100644 index 0000000..3ef9a19 --- /dev/null +++ b/src/main/resources/templates/themes/campus-azure/assets/js/main.js @@ -0,0 +1,190 @@ +/* campus-azure 主题脚本:顶栏日期 + 轮播(圆点/箭头/触摸) + 导航吸顶反蓝 + 移动端抽屉与二级手风琴。 + 与 default 同源逻辑,无任何外部依赖。 */ +(function () { + 'use strict'; + + /* ---------- 顶栏日期(本地时间,YYYY年M月D日 星期X) ---------- */ + var dateEl = document.getElementById('siteDate'); + if (dateEl) { + var now = new Date(); + var week = ['日', '一', '二', '三', '四', '五', '六'][now.getDay()]; + dateEl.textContent = now.getFullYear() + '年' + (now.getMonth() + 1) + '月' + + now.getDate() + '日 星期' + week; + } + + /* ---------- 导航吸顶反蓝(桌面端;≤768 不吸顶) ---------- */ + var nav = document.getElementById('czNav'); + var DESKTOP_MIN = 769; + var navBaseTop = 0; + var holder = null; + + function measureNavTop() { + unlockNav(); + navBaseTop = nav.getBoundingClientRect().top + window.pageYOffset; + } + + function lockNav() { + if (!nav.classList.contains('cz-nav--fixed')) { + holder = document.createElement('div'); + holder.className = 'cz-nav-holder'; + holder.style.height = nav.offsetHeight + 'px'; + nav.parentNode.insertBefore(holder, nav); + nav.classList.add('cz-nav--fixed'); + } else if (holder) { + holder.style.height = nav.offsetHeight + 'px'; + } + } + + function unlockNav() { + nav.classList.remove('cz-nav--fixed'); + if (holder && holder.parentNode) { + holder.parentNode.removeChild(holder); + } + holder = null; + } + + function onScrollNav() { + if (window.innerWidth < DESKTOP_MIN) { + unlockNav(); + return; + } + if (window.pageYOffset > navBaseTop) { + lockNav(); + } else { + unlockNav(); + } + } + + if (nav) { + measureNavTop(); + window.addEventListener('scroll', onScrollNav, { passive: true }); + var resizeTimer = null; + window.addEventListener('resize', function () { + if (resizeTimer) { window.clearTimeout(resizeTimer); } + resizeTimer = window.setTimeout(function () { + measureNavTop(); + onScrollNav(); + }, 150); + }); + } + + /* ---------- 移动端抽屉 + 二级手风琴 ---------- */ + var navToggle = document.querySelector('.cz-nav-toggle'); + var navList = document.querySelector('.cz-nav-list'); + + if (navToggle && navList) { + navToggle.addEventListener('click', function () { + var open = navList.classList.toggle('cz-open'); + navToggle.setAttribute('aria-expanded', open ? 'true' : 'false'); + }); + } + + /* 窄屏下:点击带二级的项先展开面板,再点才跳转;宽屏交还给 CSS hover */ + if (navList) { + navList.addEventListener('click', function (e) { + if (window.innerWidth >= DESKTOP_MIN) { return; } + var link = e.target.closest ? e.target.closest('a') : null; + if (!link) { return; } + var li = link.parentElement; + if (!li || !li.classList.contains('has-sub')) { return; } + var sub = li.querySelector('.cz-sub'); + if (!sub) { return; } + if (!li.classList.contains('cz-sub-open')) { + e.preventDefault(); + li.classList.add('cz-sub-open'); + } + }); + } + + /* ---------- 首页轮播 ---------- */ + var root = document.querySelector('.cz-banner'); + var slides = document.querySelectorAll('.cz-banner-slide'); + var dots = document.querySelectorAll('.cz-banner-dots button'); + var prevBtn = document.querySelector('.cz-banner-arrow.prev'); + var nextBtn = document.querySelector('.cz-banner-arrow.next'); + if (!slides.length || !root) { + return; + } + var current = 0; + var timer = null; + var INTERVAL = 5000; + + function show(index) { + slides[current].classList.remove('active'); + if (dots[current]) { + dots[current].classList.remove('active'); + } + current = (index + slides.length) % slides.length; + slides[current].classList.add('active'); + if (dots[current]) { + dots[current].classList.add('active'); + } + } + + function start() { + stop(); + if (slides.length > 1) { + timer = window.setInterval(function () { + show(current + 1); + }, INTERVAL); + } + } + + function stop() { + if (timer) { + window.clearInterval(timer); + timer = null; + } + } + + dots.forEach(function (dot, index) { + dot.addEventListener('click', function () { + show(index); + start(); + }); + }); + if (prevBtn) { + prevBtn.addEventListener('click', function () { + show(current - 1); + start(); + }); + } + if (nextBtn) { + nextBtn.addEventListener('click', function () { + show(current + 1); + start(); + }); + } + root.addEventListener('mouseenter', stop); + root.addEventListener('mouseleave', start); + root.addEventListener('focusin', stop); + root.addEventListener('focusout', start); + root.setAttribute('tabindex', '-1'); + root.addEventListener('keydown', function (e) { + if (e.key === 'ArrowLeft') { + show(current - 1); + start(); + } else if (e.key === 'ArrowRight') { + show(current + 1); + start(); + } + }); + + /* 触摸滑动切页 */ + var touchX = null; + root.addEventListener('touchstart', function (e) { + touchX = e.changedTouches[0].clientX; + stop(); + }, { passive: true }); + root.addEventListener('touchend', function (e) { + if (touchX === null) { return; } + var dx = e.changedTouches[0].clientX - touchX; + if (Math.abs(dx) > 40) { + show(dx < 0 ? current + 1 : current - 1); + } + touchX = null; + start(); + }, { passive: true }); + + start(); +})(); diff --git a/src/main/resources/templates/themes/campus-azure/assets/screenshot.png b/src/main/resources/templates/themes/campus-azure/assets/screenshot.png new file mode 100644 index 0000000..e278e48 Binary files /dev/null and b/src/main/resources/templates/themes/campus-azure/assets/screenshot.png differ diff --git a/src/main/resources/templates/themes/campus-azure/detail.html b/src/main/resources/templates/themes/campus-azure/detail.html new file mode 100644 index 0000000..6cd44f7 --- /dev/null +++ b/src/main/resources/templates/themes/campus-azure/detail.html @@ -0,0 +1,76 @@ + + + + +
+ +
+
+ + + +
+ + +
+

文章标题

+ + +
正文
+
+ + +
+
+
+ + + + + diff --git a/src/main/resources/templates/themes/campus-azure/fragments/footer.html b/src/main/resources/templates/themes/campus-azure/fragments/footer.html new file mode 100644 index 0000000..2a9e163 --- /dev/null +++ b/src/main/resources/templates/themes/campus-azure/fragments/footer.html @@ -0,0 +1,59 @@ + + + + + diff --git a/src/main/resources/templates/themes/violet-iris/fragments/head.html b/src/main/resources/templates/themes/campus-azure/fragments/head.html similarity index 74% rename from src/main/resources/templates/themes/violet-iris/fragments/head.html rename to src/main/resources/templates/themes/campus-azure/fragments/head.html index 1809482..f9395a0 100644 --- a/src/main/resources/templates/themes/violet-iris/fragments/head.html +++ b/src/main/resources/templates/themes/campus-azure/fragments/head.html @@ -1,6 +1,6 @@ - + @@ -8,16 +8,13 @@ - - - diff --git a/src/main/resources/templates/themes/campus-azure/fragments/header.html b/src/main/resources/templates/themes/campus-azure/fragments/header.html new file mode 100644 index 0000000..b2a9883 --- /dev/null +++ b/src/main/resources/templates/themes/campus-azure/fragments/header.html @@ -0,0 +1,86 @@ + + + +
+ + + +
+
+ 加载日期… +
+
+ + 专业院系 + + + +
+
+
+
+ + +
+
+
+ + + + + 站点名称 +
+ +
+
+ + + +
+ diff --git a/src/main/resources/templates/themes/campus-azure/fragments/home-blocks.html b/src/main/resources/templates/themes/campus-azure/fragments/home-blocks.html new file mode 100644 index 0000000..a252e1a --- /dev/null +++ b/src/main/resources/templates/themes/campus-azure/fragments/home-blocks.html @@ -0,0 +1,209 @@ + + +
+
+
+
+

校园要闻

+ +
+ + + + + 头条标题 + + 置顶 + + + + + + +
+
+
+
+ + +
+
+
+

栏目名

+ 更多 › + +
+ + +
+
+
+
+ + +
+
+
+

通知公告

+ 更多 › + +
+ + +
+
+
+
+ + +
+
+
+

媒体聚焦

+ 更多 › + +
+ + +
+
+
+
+ + +
+
+

图片新闻

+ 更多 › + +
+ + +
+
+
+ + +
+
+ 要闻速递 + +
+
+ + + + + +
+
+
+

友情链接

+ +
+ + + + +
+
+ + +
+

栏目暂无内容

+

内容发布后将在此展示

+
diff --git a/src/main/resources/templates/themes/campus-azure/home.html b/src/main/resources/templates/themes/campus-azure/home.html new file mode 100644 index 0000000..037a873 --- /dev/null +++ b/src/main/resources/templates/themes/campus-azure/home.html @@ -0,0 +1,149 @@ + + + + +
+ +
+ +
+ + + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+

头条推荐

+ +
+ +
+ +
+
+ + +
+
+
+
+

栏目名

+ 更多 › +
+ +
+

栏目暂无内容

+

内容发布后将在此展示

+
+
+
+
+
+
+ + + + + diff --git a/src/main/resources/templates/themes/campus-azure/list.html b/src/main/resources/templates/themes/campus-azure/list.html new file mode 100644 index 0000000..1a58292 --- /dev/null +++ b/src/main/resources/templates/themes/campus-azure/list.html @@ -0,0 +1,105 @@ + + + + +
+ +
+
+ + + +
+ + + +

栏目名

+

搜索词

+ + +
+

未找到相关内容

+

栏目暂无内容

+

换个关键词试试

+

内容发布后将在此展示

+
+ + + +
+
+
+ + + + + diff --git a/src/main/resources/templates/themes/campus-azure/single.html b/src/main/resources/templates/themes/campus-azure/single.html new file mode 100644 index 0000000..86422bf --- /dev/null +++ b/src/main/resources/templates/themes/campus-azure/single.html @@ -0,0 +1,50 @@ + + + + +
+ +
+
+ + + +
+ + +
+

栏目名

+ +
单页内容
+
+

页面暂无内容

+

内容编辑后将在此展示

+
+
+
+
+
+ + + + + diff --git a/src/main/resources/templates/themes/campus-azure/theme.json b/src/main/resources/templates/themes/campus-azure/theme.json new file mode 100644 index 0000000..e72758d --- /dev/null +++ b/src/main/resources/templates/themes/campus-azure/theme.json @@ -0,0 +1,23 @@ +{ + "specVersion": 1, + "name": "campus-azure", + "title": "青蓝校园(campus-azure)", + "version": "1.0.0", + "author": "yunhai", + "description": "青蓝校园·师范/农林类官网门户:青蓝顶栏+白底主导航+青绿描边标题+大日期数字列表+图文左列右栏+圆角卡片区块+深色页脚带二维码位。基座 scholar-blue,配色与首页区块形态按师范/农林类高校官网重做。", + "screenshot": "assets/screenshot.png", + "bannerSize": "1920x560", + "params": [ + { "key": "primary", "cssVar": "--cz-primary", "label": "主色", "type": "color", "default": "#0d6a9e" }, + { "key": "primaryDark", "cssVar": "--cz-primary-dark", "label": "主色加深", "type": "color", "default": "#084c73" }, + { "key": "primaryLight", "cssVar": "--cz-primary-light","label": "主色提亮", "type": "color", "default": "#1d87bd" }, + { "key": "accent", "cssVar": "--cz-accent", "label": "点缀青绿", "type": "color", "default": "#2b9c8f" }, + { "key": "navBg", "cssVar": "--cz-nav-bg", "label": "导航条底色", "type": "color", "default": "#ffffff" }, + { "key": "footerBg", "cssVar": "--cz-footer-bg", "label": "页脚底色", "type": "color", "default": "#0b3348" }, + { "key": "text", "cssVar": "--cz-text", "label": "正文文字色", "type": "color", "default": "#303c46" }, + { "key": "textLight", "cssVar": "--cz-text-light", "label": "次要文字色", "type": "color", "default": "#6e7d88" }, + { "key": "bg", "cssVar": "--cz-bg", "label": "页面底色", "type": "color", "default": "#eef3f6" }, + { "key": "cardBg", "cssVar": "--cz-card-bg", "label": "卡片底色", "type": "color", "default": "#ffffff" }, + { "key": "border", "cssVar": "--cz-border", "label": "边框色", "type": "color", "default": "#d9e5ec" } + ] +} diff --git a/src/main/resources/templates/themes/cherry-bloom/README.md b/src/main/resources/templates/themes/cherry-bloom/README.md deleted file mode 100644 index ebf6e2e..0000000 --- a/src/main/resources/templates/themes/cherry-bloom/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# cherry-bloom 皮肤包 - -由 mint-breeze 派生的温柔樱粉风皮肤(docs/10 契约 §5 工作流)。**升级随版本发布,不在站点间复制目录**。 - - -## 风格定位 - -温柔、甜美,适合幼儿园与小学门户:樱花粉品牌色、大圆角 16px、卡片纯白+暖粉柔和阴影、细线顶栏+居中 logo(桌面吸顶,移动端取消)、列表卡片网格(一行 3 张)、花瓣式圆点装饰与暖白留白;无纹理、无通栏大图、无复杂动效。 - -## 数据契约(模板可取用的 model 属性全集,契约 §3) - -| 页面 | 属性 | -| --- | --- | -| 全部 | `site`(快照:config/分类树/轮播/友链组/院系子站)、`url`(PortalUrlHelper:home/category/article/asset/search/siteHome)、`themeBase`(=`themes/cherry-bloom`)、`navCategories`、`pageTitle`、`seoDescription`、`canonical`、`themeParams`(微调参数,仅 theme.json 声明过的 key)、`themeVarsCss`(服务端预构建 `:root{}` 覆盖串,无参数为空) | -| home | `banners`(时间窗内 home 位)、`topArticles`(置顶+最新 6)、`categoryBlocks`(LIST 型根栏目块,各 top 8) | -| list / search | `listData`(分页)、`category`(list)、`q` + `searchMode`(search) | -| detail | `article`、`prevArticle`、`nextArticle`、`currentCategoryId` | -| single | `category`(含 `content`)、`currentCategoryId` | - -原则:皮肤不得假设"首页一定有轮播",对空集合优雅降级。 - -## 轮播图尺寸约定(bannerSize) - -1920x560(管理端上传端按此引导裁剪,D2 决策点)。 - -## 可调参数 - -见 `theme.json` 的 `params`: - -- **color**(进 CSS 变量):`primary` 主色 / `primaryDark` 主色加深(即强调色,用于品牌文字与悬停加深)/ `primaryLight` 主色提亮 / `bg` 页面底色 / `cardBg` 卡片底色 / `border` 边框色 / `text` 正文文字 / `textLight` 次要文字。 -- **select**:`listLayout` = `grid`(默认,卡片网格一行 3 张)或 `row`(传统行列表)。 - -换色建议:主色系三值同步调整(primary / primaryDark / primaryLight),bg 与 border、primaryLight 保持同族浅色调,避免底色与卡片对比失衡。 - -优先级:`custom_css` > `themeVarsCss` > `main.css`。 diff --git a/src/main/resources/templates/themes/cherry-bloom/assets/css/main.css b/src/main/resources/templates/themes/cherry-bloom/assets/css/main.css deleted file mode 100644 index 3362d07..0000000 --- a/src/main/resources/templates/themes/cherry-bloom/assets/css/main.css +++ /dev/null @@ -1,616 +0,0 @@ -/* ============================================================ - * cherry-bloom 皮肤包样式(mint-breeze 派生,docs/10 皮肤包契约 §5) - * 温柔樱粉风:樱花粉品牌色 + 大圆角 16px + 柔和暖粉阴影 + 卡片网格列表 - * + 花瓣式圆点装饰与暖白留白;无纹理、无通栏大图、无复杂动效。 - * 轮播图设计尺寸约定:1920×560(管理端上传端按此引导裁剪)。 - * ============================================================ */ -:root { - --primary: #DB5A8B; - --primary-dark: #B23A68; - --primary-light: #FDECF3; - --text: #43333B; - --text-light: #9C828C; - --bg: #FFFAFB; - --card-bg: #FFFFFF; - --border: #F7DDE7; - --radius: 16px; - --radius-sm: 10px; - --radius-pill: 999px; - --shadow-soft: 0 6px 18px rgba(178, 58, 104, .08); - --shadow-hover: 0 12px 28px rgba(178, 58, 104, .14); - --max-width: 1200px; -} - -* { box-sizing: border-box; } -body { - margin: 0; - font-family: "PingFang SC", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif; - color: var(--text); - background: var(--bg); -} -a { color: inherit; text-decoration: none; transition: color .15s ease; } -a:hover { color: var(--primary); } -img { max-width: 100%; } -time, .item-meta { color: var(--text-light); font-size: 13px; } -.empty { color: var(--text-light); text-align: center; padding: 32px 0; } - -/* ===== 头部:细线顶栏 + 居中品牌行 + 白底导航(桌面吸顶) ===== */ -.site-header { - position: sticky; - top: 0; - z-index: 90; - background: var(--card-bg); - box-shadow: var(--shadow-soft); -} -/* 细线顶栏:院系切换在左、站内搜索在右;院系未配置时搜索仍靠右 */ -.top-bar { - display: flex; - align-items: center; - justify-content: flex-end; - gap: 16px; - padding: 7px max(16px, calc((100% - var(--max-width)) / 2 + 16px)); - border-bottom: 1px solid var(--border); - font-size: 13px; -} -.top-bar .site-colleges { margin-right: auto; } -/* 居中品牌行:两侧几何圆点装饰(纯 CSS,无纹理) */ -.brand-row { - position: relative; - display: flex; - justify-content: center; - padding: 18px 16px 14px; -} -.brand-row::before, .brand-row::after { - content: ""; - position: absolute; - top: 50%; - transform: translateY(-50%); - width: 46px; - height: 10px; - opacity: .45; - background-image: - radial-gradient(circle, var(--primary) 2.2px, transparent 2.8px), - radial-gradient(circle, var(--primary) 1.6px, transparent 2.2px), - radial-gradient(circle, var(--primary) 1.1px, transparent 1.7px); - background-repeat: no-repeat; - background-position: left center, center center, right center; -} -.brand-row::before { left: 6%; } -.brand-row::after { right: 6%; } -.site-brand { display: flex; align-items: center; gap: 14px; } -.site-logo { height: 52px; } -.site-name { - font-size: 28px; - font-weight: 600; - letter-spacing: .02em; - color: var(--primary-dark); -} -.site-name:hover { color: var(--primary); } -.site-search { display: flex; align-items: center; } -.site-search input { - border: 1px solid var(--border); - border-right: none; - border-radius: var(--radius-pill) 0 0 var(--radius-pill); - padding: 7px 16px; - width: 220px; - outline: none; - background: var(--card-bg); - color: var(--text); - transition: border-color .15s ease, box-shadow .15s ease; -} -.site-search input:focus { - border-color: var(--primary); - box-shadow: 0 0 0 3px var(--primary-light); -} -.site-search button { - border: none; - background: var(--primary); - color: #fff; - padding: 7px 20px; - border-radius: 0 var(--radius-pill) var(--radius-pill) 0; - cursor: pointer; - transition: background .15s ease; -} -.site-search button:hover { background: var(--primary-dark); } - -/* ===== 专业院系下拉(院系子站切换):胶囊开关 + 圆角白卡面板 ===== */ -.site-colleges { position: relative; } -.site-colleges .colleges-toggle { - display: inline-flex; - align-items: center; - gap: 6px; - border: 1px solid var(--border); - border-radius: var(--radius-pill); - padding: 5px 14px; - cursor: pointer; - color: var(--primary-dark); - font-weight: 600; - white-space: nowrap; - transition: border-color .15s ease, background .15s ease; -} -.site-colleges:hover .colleges-toggle { - border-color: var(--primary); - background: var(--primary-light); -} -.site-colleges .colleges-arrow { font-style: normal; font-size: 12px; color: var(--primary); } -.site-colleges .colleges-menu { - display: none; - position: absolute; - top: 100%; - left: 0; - min-width: 200px; - max-height: 420px; - overflow-y: auto; - margin: 0; - padding: 8px 0; - list-style: none; - background: var(--card-bg); - border: 1px solid var(--border); - border-radius: var(--radius); - box-shadow: var(--shadow-hover); - z-index: 110; -} -.site-colleges:hover .colleges-menu { display: block; } -.site-colleges .colleges-menu li a { - display: block; - margin: 0 8px; - padding: 9px 12px; - border-radius: var(--radius-sm); - color: var(--text); - white-space: nowrap; -} -.site-colleges .colleges-menu li a:hover { background: var(--primary-light); color: var(--primary-dark); } -.site-colleges .colleges-menu li.active a { color: var(--primary); font-weight: 600; } - -/* ===== 主导航:白底 + 柔和文字层级,当前项圆点标记 ===== */ -.site-nav { background: var(--card-bg); border-top: 1px solid var(--border); } -.site-nav ul { - max-width: var(--max-width); - margin: 0 auto; - padding: 0 16px; - list-style: none; - display: flex; - justify-content: center; - flex-wrap: wrap; -} -.site-nav li a { - position: relative; - display: block; - padding: 13px 20px; - color: var(--text); - font-weight: 500; -} -.site-nav li a:hover { color: var(--primary); } -.site-nav li.active a { color: var(--primary); font-weight: 600; } -/* 几何圆点:当前栏目下方的小圆点 */ -.site-nav li.active > a::before { - content: ""; - position: absolute; - left: 50%; - bottom: 5px; - width: 5px; - height: 5px; - transform: translateX(-50%); - border-radius: 50%; - background: var(--primary); -} - -/* ===== 二级下拉导航:圆角白卡面板 ===== */ -.site-nav > ul > li { position: relative; } -.site-nav li.has-sub > a::after { content: " ▾"; font-size: 12px; opacity: .7; } -.site-nav ul.sub-nav { - display: none; - position: absolute; - top: 100%; - left: 0; - min-width: 168px; - max-width: none; - margin: 0; - padding: 8px 0; - list-style: none; - background: var(--card-bg); - border: 1px solid var(--border); - border-radius: var(--radius); - box-shadow: var(--shadow-hover); - z-index: 100; -} -.site-nav li:hover > .sub-nav { display: block; } -.site-nav .sub-nav li a { - margin: 0 8px; - padding: 9px 12px; - border-radius: var(--radius-sm); - color: var(--text); - font-weight: 400; - white-space: nowrap; - background: none; -} -.site-nav .sub-nav li a:hover { background: var(--primary-light); color: var(--primary-dark); } -.site-nav .sub-nav li.active a { color: var(--primary); font-weight: 600; } - -/* ===== 首页轮播:容器内圆角卡片(非通栏),几何圆点指示器 ===== */ -.banner { - position: relative; - width: min(var(--max-width), calc(100% - 32px)); - margin: 24px auto 0; - border-radius: var(--radius); - overflow: hidden; - box-shadow: var(--shadow-soft); -} -.banner-slides { position: relative; height: 400px; } -.banner-slide { display: none; position: absolute; inset: 0; } -.banner-slide.active { display: block; } -.banner-slide img { width: 100%; height: 100%; object-fit: cover; } -.banner-title { - position: absolute; - left: 0; right: 0; bottom: 0; - padding: 14px 20px; - color: #fff; - font-weight: 500; - background: linear-gradient(transparent, rgba(20, 63, 61, .62)); -} -.banner-dots { - position: absolute; - bottom: 14px; right: 20px; - display: flex; gap: 8px; -} -.banner-dots button { - width: 9px; height: 9px; - border-radius: 50%; - border: none; - background: rgba(255, 255, 255, .55); - cursor: pointer; - transition: background .15s ease, transform .15s ease; -} -.banner-dots button.active { background: #fff; transform: scale(1.25); } - -/* ===== 首页区块:卡片网格(头条通栏 + 栏目块一行 3 张) ===== */ -.home-main { - max-width: var(--max-width); - margin: 0 auto; - padding: 28px 16px 56px; - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 24px; -} -.top-strip, .category-block { - background: var(--card-bg); - border-radius: var(--radius); - box-shadow: var(--shadow-soft); - padding: 24px; -} -.top-strip { grid-column: 1 / -1; } -/* 栏目标题:几何圆点标记(环形点),标题中等粗细、层级柔和 */ -.section-title, .block-head h2 { - margin: 0 0 14px; - font-size: 19px; - font-weight: 600; - letter-spacing: .02em; - display: flex; - align-items: center; - gap: 10px; -} -.section-title::before, .block-head h2::before { - content: ""; - flex: none; - width: 9px; - height: 9px; - border-radius: 50%; - background: var(--primary); - box-shadow: 0 0 0 3.5px var(--primary-light); -} -.block-head { display: flex; align-items: center; justify-content: space-between; } -.more { - flex: none; - font-size: 13px; - color: var(--text-light); - padding: 4px 12px; - border: 1px solid var(--border); - border-radius: var(--radius-pill); - transition: color .15s ease, border-color .15s ease, background .15s ease; -} -.more:hover { - color: var(--primary-dark); - border-color: var(--primary); - background: var(--primary-light); -} -.top-strip ul, .block-list { list-style: none; margin: 0; padding: 0; } -.top-strip li, .block-list li { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - padding: 10px 0; - border-bottom: 1px dashed var(--border); -} -.top-strip li:last-child, .block-list li:last-child { border-bottom: none; } -.top-strip li a, .block-list li a { - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.top-badge { - flex: none; - color: var(--primary-dark); - background: var(--primary-light); - font-size: 12px; - font-weight: 500; - padding: 2px 10px; - border-radius: var(--radius-pill); -} - -/* ===== 列表页:卡片网格(默认一行 3 张;listLayout=row 切换行列表) ===== */ -.list-main { - max-width: var(--max-width); - margin: 0 auto; - padding: 28px 16px 56px; -} -.breadcrumb { color: var(--text-light); font-size: 13px; margin-bottom: 14px; } -.breadcrumb a { margin-right: 8px; } -.breadcrumb span { margin-left: 8px; } -.page-title { - font-size: 24px; - font-weight: 600; - letter-spacing: .02em; - margin: 0 0 22px; - display: flex; - align-items: center; - gap: 10px; -} -.page-title::before { - content: ""; - flex: none; - width: 9px; - height: 9px; - border-radius: 50%; - background: var(--primary); - box-shadow: 0 0 0 3.5px var(--primary-light); -} -.article-list { - list-style: none; - margin: 0; - padding: 0; - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 20px; -} -.article-list li { - background: var(--card-bg); - border-radius: var(--radius); - box-shadow: var(--shadow-soft); - padding: 22px 20px; - display: flex; - flex-direction: column; - gap: 10px; - transition: transform .18s ease, box-shadow .18s ease; -} -.article-list li:hover { transform: translateY(-3px); box-shadow: var(--shadow-hover); } -.item-title { - font-size: 16px; - font-weight: 600; - line-height: 1.55; - overflow: hidden; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; -} -.article-list li:hover .item-title { color: var(--primary); } -.item-summary { - color: var(--text-light); - font-size: 13.5px; - line-height: 1.7; - margin: 0; - flex: 1; - overflow: hidden; - display: -webkit-box; - -webkit-line-clamp: 3; - -webkit-box-orient: vertical; -} -.item-meta { - display: flex; - gap: 10px; - align-items: center; - margin-top: auto; - padding-top: 10px; - border-top: 1px dashed var(--border); -} -/* 布局变体:传统行列表(themeParams listLayout=row,见 list.html classappend) */ -.list-rows .article-list { display: block; } -.list-rows .article-list li { display: block; margin-bottom: 16px; } -.list-rows .item-summary { -webkit-line-clamp: 2; flex: none; } -.list-rows .item-meta { border-top: none; padding-top: 0; margin-top: 6px; } - -/* ===== 分页:胶囊页码 ===== */ -.pagination { - display: flex; - gap: 8px; - justify-content: center; - flex-wrap: wrap; - padding: 28px 0 4px; -} -.pagination a { - min-width: 36px; - text-align: center; - padding: 6px 12px; - border: 1px solid var(--border); - border-radius: var(--radius-pill); - background: var(--card-bg); - font-size: 14px; - transition: color .15s ease, border-color .15s ease, background .15s ease; -} -.pagination a:hover { color: var(--primary-dark); border-color: var(--primary); } -.pagination a.active { background: var(--primary); color: #fff; border-color: var(--primary); } - -/* ===== 详情/单页:整卡承载,正文舒适行距 ===== */ -.detail-main { - max-width: var(--max-width); - margin: 0 auto; - padding: 28px 16px 56px; -} -.detail-main .article { - background: var(--card-bg); - border-radius: var(--radius); - box-shadow: var(--shadow-soft); - padding: 36px 40px; -} -.article-title { - margin: 4px 0 14px; - font-size: 26px; - font-weight: 600; - letter-spacing: .02em; - text-align: center; -} -.article-meta { - display: flex; - gap: 18px; - justify-content: center; - flex-wrap: wrap; - color: var(--text-light); - font-size: 13px; - padding-bottom: 18px; - border-bottom: 1px solid var(--border); -} -.article-content { line-height: 1.9; font-size: 16px; padding-top: 22px; word-break: break-word; } -.article-content img { height: auto; border-radius: var(--radius-sm); } -.article-content table { border-collapse: collapse; width: 100%; } -.article-content table td, .article-content table th { border: 1px solid var(--border); padding: 6px 10px; } -.article-sibling { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 20px; font-size: 14px; } -.article-sibling a { - flex: 1 1 320px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - background: var(--card-bg); - border-radius: var(--radius); - box-shadow: var(--shadow-soft); - padding: 14px 18px; - transition: transform .18s ease, box-shadow .18s ease, color .15s ease; -} -.article-sibling a:hover { - color: var(--primary-dark); - transform: translateY(-2px); - box-shadow: var(--shadow-hover); -} - -/* ===== 页脚:浅樱粉底 + 圆点分隔条(三态渲染逻辑不动) ===== */ -.site-footer { - margin-top: 40px; - background: var(--primary-light); - color: var(--text); - border-top: 1px solid var(--border); -} -.site-footer::before { - content: ""; - display: block; - height: 8px; - width: min(var(--max-width), calc(100% - 32px)); - margin: 18px auto 0; - background-image: radial-gradient(circle, var(--primary) 1.6px, transparent 2.2px); - background-size: 16px 8px; - background-repeat: repeat-x; - opacity: .4; -} -.footer-links { - max-width: var(--max-width); - margin: 0 auto; - padding: 18px 16px 0; - display: flex; - flex-direction: column; - gap: 10px; -} -.links-group { display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: center; font-size: 13px; } -.group-name { font-weight: 600; margin-right: 4px; color: var(--primary-dark); } -.links-group a { - padding: 3px 10px; - border-radius: var(--radius-pill); - transition: background .15s ease, color .15s ease; -} -.links-group a:hover { background: rgba(255, 255, 255, .75); color: var(--primary-dark); } -.footer-info { - max-width: var(--max-width); - margin: 0 auto; - padding: 14px 16px 24px; - text-align: center; - font-size: 13px; - color: var(--text-light); -} -.footer-info p { margin: 4px 0; } - -/* B4 友链 Logo:与名称并排小图(高度 ~20px,垂直居中;过宽截断不撑破页脚) */ -.links-group a .link-logo { - height: 20px; - width: auto; - max-width: 64px; - object-fit: contain; - vertical-align: middle; - margin-right: 4px; - border-radius: 4px; -} - -/* ===== 平板(≤992px):网格降为两列,隐藏品牌行圆点装饰 ===== */ -@media (max-width: 992px) { - .home-main { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .article-list { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .brand-row::before, .brand-row::after { display: none; } -} - -/* ===== 移动端(≤768px):取消吸顶,网格单列,导航横向滑动 ===== */ -@media (max-width: 768px) { - .site-header { position: static; } - .top-bar { padding: 8px 14px; justify-content: flex-start; flex-wrap: wrap; } - .top-bar .site-colleges { margin-right: 0; } - .site-search { flex: 1; } - .site-search input { flex: 1; width: auto; min-width: 0; } - .brand-row { padding: 14px 16px 10px; } - .site-logo { height: 40px; } - .site-name { font-size: 22px; } - .site-nav ul { - justify-content: flex-start; - flex-wrap: nowrap; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - .banner-slides { height: 200px; } - .home-main { grid-template-columns: minmax(0, 1fr); padding: 18px 14px 40px; } - .article-list { grid-template-columns: minmax(0, 1fr); } - .detail-main .article { padding: 22px 16px; } - .article-title { font-size: 21px; } - /* 触屏无 hover:子菜单静态平铺(缩进列表),不做浮层 */ - .site-nav ul.sub-nav { - display: block; - position: static; - border: none; - box-shadow: none; - background: transparent; - min-width: 0; - padding: 0 0 6px; - } - .site-nav .sub-nav li a { margin: 0; padding: 8px 16px 8px 34px; border-radius: 0; color: var(--text-light); } - .site-nav .sub-nav li a:hover { background: var(--primary-light); color: var(--primary-dark); } -} - - -/* ===== 左侧栏目导航(docs/11):有二级节点的模块,栏目/文章页左侧展示兄弟目录 ===== */ -.list-main.has-sidenav, .detail-main.has-sidenav { display: flex; align-items: flex-start; gap: 24px; } -.has-sidenav .list-body { flex: 1; min-width: 0; } -.side-nav { flex: none; width: 220px; background: var(--card-bg, #fff); border: 1px solid var(--border); overflow: hidden; } -.side-nav-head { background: var(--primary); color: #fff; font-size: 20px; font-weight: 700; text-align: center; padding: 18px 12px; letter-spacing: 2px; } -.side-nav-list { list-style: none; margin: 0; padding: 0; } -.side-nav-item + .side-nav-item { border-top: 1px dashed var(--border); } -.side-nav-item a { display: block; padding: 12px 16px; color: var(--text); text-decoration: none; font-size: 15px; transition: background .15s ease, color .15s ease; } -.side-nav-item a:hover { color: var(--primary); background: var(--bg); } -.side-nav-item.active a { background: var(--primary); color: #fff; font-weight: 600; text-decoration: none; } -/* 顶导航二级下拉当前项高亮(docs/11 顺带) */ -.site-nav ul.sub-nav li.active a { color: var(--primary); } -@media (max-width: 768px) { - .list-main.has-sidenav, .detail-main.has-sidenav { flex-direction: column; gap: 12px; } - .side-nav { width: 100%; } - .side-nav-list { display: flex; overflow-x: auto; -webkit-overflow-scrolling: touch; } - .side-nav-item { flex: none; } - .side-nav-item + .side-nav-item { border-top: none; border-left: 1px dashed var(--border); } - .side-nav-item a { white-space: nowrap; } -} -@media print { - .side-nav { display: none; } - .list-main.has-sidenav, .detail-main.has-sidenav { display: block; } -} diff --git a/src/main/resources/templates/themes/cherry-bloom/assets/favicon.png b/src/main/resources/templates/themes/cherry-bloom/assets/favicon.png deleted file mode 100644 index e451454..0000000 Binary files a/src/main/resources/templates/themes/cherry-bloom/assets/favicon.png and /dev/null differ diff --git a/src/main/resources/templates/themes/cherry-bloom/assets/favicon.svg b/src/main/resources/templates/themes/cherry-bloom/assets/favicon.svg deleted file mode 100644 index 56ed83c..0000000 --- a/src/main/resources/templates/themes/cherry-bloom/assets/favicon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/src/main/resources/templates/themes/cherry-bloom/assets/js/main.js b/src/main/resources/templates/themes/cherry-bloom/assets/js/main.js deleted file mode 100644 index a077c85..0000000 --- a/src/main/resources/templates/themes/cherry-bloom/assets/js/main.js +++ /dev/null @@ -1,40 +0,0 @@ -/* cherry-bloom 皮肤包脚本:仅首页轮播自动播放与圆点切换,无任何外部依赖 */ -(function () { - var slides = document.querySelectorAll('.banner-slide'); - var dots = document.querySelectorAll('.banner-dots button'); - if (!slides.length) { - return; - } - var current = 0; - var timer = null; - - function show(index) { - slides[current].classList.remove('active'); - if (dots[current]) { - dots[current].classList.remove('active'); - } - current = (index + slides.length) % slides.length; - slides[current].classList.add('active'); - if (dots[current]) { - dots[current].classList.add('active'); - } - } - - function start() { - if (slides.length > 1) { - timer = window.setInterval(function () { - show(current + 1); - }, 5000); - } - } - - dots.forEach(function (dot, index) { - dot.addEventListener('click', function () { - window.clearInterval(timer); - show(index); - start(); - }); - }); - - start(); -})(); diff --git a/src/main/resources/templates/themes/cherry-bloom/assets/screenshot.png b/src/main/resources/templates/themes/cherry-bloom/assets/screenshot.png deleted file mode 100644 index 8f58dfb..0000000 Binary files a/src/main/resources/templates/themes/cherry-bloom/assets/screenshot.png and /dev/null differ diff --git a/src/main/resources/templates/themes/cherry-bloom/detail.html b/src/main/resources/templates/themes/cherry-bloom/detail.html deleted file mode 100644 index 8eab71f..0000000 --- a/src/main/resources/templates/themes/cherry-bloom/detail.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - -
- -
- - -
- - -
-

文章标题

- - -
正文
-
- - -
-
- - - - diff --git a/src/main/resources/templates/themes/cherry-bloom/fragments/footer.html b/src/main/resources/templates/themes/cherry-bloom/fragments/footer.html deleted file mode 100644 index 1f3f83d..0000000 --- a/src/main/resources/templates/themes/cherry-bloom/fragments/footer.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - diff --git a/src/main/resources/templates/themes/cherry-bloom/fragments/header.html b/src/main/resources/templates/themes/cherry-bloom/fragments/header.html deleted file mode 100644 index 903691c..0000000 --- a/src/main/resources/templates/themes/cherry-bloom/fragments/header.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - - diff --git a/src/main/resources/templates/themes/cherry-bloom/home.html b/src/main/resources/templates/themes/cherry-bloom/home.html deleted file mode 100644 index 8d4f072..0000000 --- a/src/main/resources/templates/themes/cherry-bloom/home.html +++ /dev/null @@ -1,68 +0,0 @@ - - - - -
- -
- - - - - - - - - - - - - - -
-

头条推荐

- -
- - -
-
-

栏目名

- 更多 > -
- -

暂无内容

-
-
-
- - - - - diff --git a/src/main/resources/templates/themes/cherry-bloom/list.html b/src/main/resources/templates/themes/cherry-bloom/list.html deleted file mode 100644 index 14c47c4..0000000 --- a/src/main/resources/templates/themes/cherry-bloom/list.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - -
- - -
- - -
- - - -

栏目名

-

搜索词

- - -

暂无内容

- - - -
-
- - - - - diff --git a/src/main/resources/templates/themes/cherry-bloom/single.html b/src/main/resources/templates/themes/cherry-bloom/single.html deleted file mode 100644 index 65292dc..0000000 --- a/src/main/resources/templates/themes/cherry-bloom/single.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - -
- -
- - -
- - -
-

栏目名

- -
单页内容
-

暂无内容

-
-
-
- - - - diff --git a/src/main/resources/templates/themes/cherry-bloom/theme.json b/src/main/resources/templates/themes/cherry-bloom/theme.json deleted file mode 100644 index 5d24a11..0000000 --- a/src/main/resources/templates/themes/cherry-bloom/theme.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "specVersion": 1, - "name": "cherry-bloom", - "title": "樱语粉韵", - "version": "1.0.0", - "author": "yunhai", - "description": "温柔樱粉风门户:细线顶栏+居中 logo 吸顶、樱花粉品牌色、大圆角 16px 卡片网格列表、花瓣式圆点装饰与暖白留白,甜美亲和,适合幼儿园与小学门户。", - "screenshot": "assets/screenshot.png", - "bannerSize": "1920x560", - "params": [ - { "key": "primary", "cssVar": "--primary", "label": "主色", "type": "color", "default": "#DB5A8B" }, - { "key": "primaryDark", "cssVar": "--primary-dark", "label": "主色加深(强调色)", "type": "color", "default": "#B23A68" }, - { "key": "primaryLight", "cssVar": "--primary-light", "label": "主色提亮", "type": "color", "default": "#FDECF3" }, - { "key": "bg", "cssVar": "--bg", "label": "页面底色", "type": "color", "default": "#FFFAFB" }, - { "key": "cardBg", "cssVar": "--card-bg", "label": "卡片底色", "type": "color", "default": "#FFFFFF" }, - { "key": "border", "cssVar": "--border", "label": "边框色", "type": "color", "default": "#F7DDE7" }, - { "key": "text", "cssVar": "--text", "label": "正文文字色", "type": "color", "default": "#43333B" }, - { "key": "textLight", "cssVar": "--text-light", "label": "次要文字色", "type": "color", "default": "#9C828C" }, - { "key": "listLayout", "label": "列表布局", "type": "select", "default": "grid", - "options": [ { "value": "grid", "label": "卡片网格(一行 3 张)" }, { "value": "row", "label": "传统行列表" } ] } - ] -} diff --git a/src/main/resources/templates/themes/common/fragments/home-blocks.html b/src/main/resources/templates/themes/common/fragments/home-blocks.html index 952b001..54dd804 100644 --- a/src/main/resources/templates/themes/common/fragments/home-blocks.html +++ b/src/main/resources/templates/themes/common/fragments/home-blocks.html @@ -8,56 +8,76 @@ --> @@ -92,7 +112,7 @@ 更多 > - +

该区块暂无内容

@@ -121,7 +141,9 @@ -
+ +
+
@@ -138,7 +160,9 @@ -
+ +
+
@@ -158,7 +182,9 @@ 置顶 -
+ +
+
@@ -176,7 +202,10 @@ -
+ + +
+
@@ -193,7 +222,9 @@ -
+ +
+
diff --git a/src/main/resources/templates/themes/mint-breeze/assets/css/main.css b/src/main/resources/templates/themes/mint-breeze/assets/css/main.css index e65d638..73fc718 100644 --- a/src/main/resources/templates/themes/mint-breeze/assets/css/main.css +++ b/src/main/resources/templates/themes/mint-breeze/assets/css/main.css @@ -230,6 +230,7 @@ time, .item-meta { color: var(--text-light); font-size: 13px; } /* ===== 首页轮播:容器内圆角卡片(非通栏),几何圆点指示器 ===== */ .banner { position: relative; + grid-column: 1 / -1; /* .home-main 是三列网格,轮播必须跨满整行,否则被塞进首列变窄条 */ width: min(var(--max-width), calc(100% - 32px)); margin: 24px auto 0; border-radius: var(--radius); diff --git a/src/main/resources/templates/themes/scholar-blue/assets/css/main.css b/src/main/resources/templates/themes/scholar-blue/assets/css/main.css index 7822fa6..cc02a36 100644 --- a/src/main/resources/templates/themes/scholar-blue/assets/css/main.css +++ b/src/main/resources/templates/themes/scholar-blue/assets/css/main.css @@ -31,6 +31,18 @@ --sp-radius-lg: 10px; --sp-speed: .28s; --sp-font: "Microsoft YaHei", "PingFang SC", "Hiragino Sans GB", "Helvetica Neue", Arial, sans-serif; + + /* 皮肤契约别名:站点配了 home_layout 时首页走 common/fragments/home-blocks 骨架, + 该骨架只认契约变量(--primary/--text/…)。不别名过去,换肤就只换了页头页脚、正文区块仍是骨架内置色。 */ + --primary: var(--sp-primary); + --primary-dark: var(--sp-primary-dark); + --primary-light: var(--sp-primary-light); + --text: var(--sp-text); + --text-light: var(--sp-text-light); + --bg: var(--sp-bg); + --card-bg: var(--sp-card-bg); + --border: var(--sp-border); + --radius: var(--sp-radius-lg); } /* --------------------------------------------------------------------- diff --git a/src/main/resources/templates/themes/violet-iris/README.md b/src/main/resources/templates/themes/violet-iris/README.md deleted file mode 100644 index 747398a..0000000 --- a/src/main/resources/templates/themes/violet-iris/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# violet-iris 皮肤包 - -由 mint-breeze 派生的雅致鸢尾紫皮肤(docs/10 契约 §5 工作流)。**升级随版本发布,不在站点间复制目录**。 - - -## 风格定位 - -沉静、书卷气,适合高校与教科研机构门户:鸢尾紫品牌色、圆角 12px、卡片纯白+淡紫柔和阴影、细线顶栏+居中 logo(桌面吸顶,移动端取消)、列表卡片网格(一行 3 张)、几何圆点装饰与大留白;无纹理、无通栏大图、无复杂动效。 - -## 数据契约(模板可取用的 model 属性全集,契约 §3) - -| 页面 | 属性 | -| --- | --- | -| 全部 | `site`(快照:config/分类树/轮播/友链组/院系子站)、`url`(PortalUrlHelper:home/category/article/asset/search/siteHome)、`themeBase`(=`themes/violet-iris`)、`navCategories`、`pageTitle`、`seoDescription`、`canonical`、`themeParams`(微调参数,仅 theme.json 声明过的 key)、`themeVarsCss`(服务端预构建 `:root{}` 覆盖串,无参数为空) | -| home | `banners`(时间窗内 home 位)、`topArticles`(置顶+最新 6)、`categoryBlocks`(LIST 型根栏目块,各 top 8) | -| list / search | `listData`(分页)、`category`(list)、`q` + `searchMode`(search) | -| detail | `article`、`prevArticle`、`nextArticle`、`currentCategoryId` | -| single | `category`(含 `content`)、`currentCategoryId` | - -原则:皮肤不得假设"首页一定有轮播",对空集合优雅降级。 - -## 轮播图尺寸约定(bannerSize) - -1920x560(管理端上传端按此引导裁剪,D2 决策点)。 - -## 可调参数 - -见 `theme.json` 的 `params`: - -- **color**(进 CSS 变量):`primary` 主色 / `primaryDark` 主色加深(即强调色,用于品牌文字与悬停加深)/ `primaryLight` 主色提亮 / `bg` 页面底色 / `cardBg` 卡片底色 / `border` 边框色 / `text` 正文文字 / `textLight` 次要文字。 -- **select**:`listLayout` = `grid`(默认,卡片网格一行 3 张)或 `row`(传统行列表)。 - -换色建议:主色系三值同步调整(primary / primaryDark / primaryLight),bg 与 border、primaryLight 保持同族浅色调,避免底色与卡片对比失衡。 - -优先级:`custom_css` > `themeVarsCss` > `main.css`。 diff --git a/src/main/resources/templates/themes/violet-iris/assets/css/main.css b/src/main/resources/templates/themes/violet-iris/assets/css/main.css deleted file mode 100644 index 8fffbe5..0000000 --- a/src/main/resources/templates/themes/violet-iris/assets/css/main.css +++ /dev/null @@ -1,616 +0,0 @@ -/* ============================================================ - * violet-iris 皮肤包样式(mint-breeze 派生,docs/10 皮肤包契约 §5) - * 雅致鸢尾紫风:鸢尾紫品牌色 + 圆角 12px + 柔和紫阴影 + 卡片网格列表 - * + 几何圆点装饰与淡薰衣草留白;无纹理、无通栏大图、无复杂动效。 - * 轮播图设计尺寸约定:1920×560(管理端上传端按此引导裁剪)。 - * ============================================================ */ -:root { - --primary: #6552D9; - --primary-dark: #4A39B0; - --primary-light: #EFECFC; - --text: #332F45; - --text-light: #847E9C; - --bg: #F8F7FD; - --card-bg: #FFFFFF; - --border: #E4DFF6; - --radius: 12px; - --radius-sm: 8px; - --radius-pill: 999px; - --shadow-soft: 0 6px 18px rgba(74, 57, 176, .08); - --shadow-hover: 0 12px 28px rgba(74, 57, 176, .14); - --max-width: 1200px; -} - -* { box-sizing: border-box; } -body { - margin: 0; - font-family: "PingFang SC", "Microsoft YaHei", "Helvetica Neue", Arial, sans-serif; - color: var(--text); - background: var(--bg); -} -a { color: inherit; text-decoration: none; transition: color .15s ease; } -a:hover { color: var(--primary); } -img { max-width: 100%; } -time, .item-meta { color: var(--text-light); font-size: 13px; } -.empty { color: var(--text-light); text-align: center; padding: 32px 0; } - -/* ===== 头部:细线顶栏 + 居中品牌行 + 白底导航(桌面吸顶) ===== */ -.site-header { - position: sticky; - top: 0; - z-index: 90; - background: var(--card-bg); - box-shadow: var(--shadow-soft); -} -/* 细线顶栏:院系切换在左、站内搜索在右;院系未配置时搜索仍靠右 */ -.top-bar { - display: flex; - align-items: center; - justify-content: flex-end; - gap: 16px; - padding: 7px max(16px, calc((100% - var(--max-width)) / 2 + 16px)); - border-bottom: 1px solid var(--border); - font-size: 13px; -} -.top-bar .site-colleges { margin-right: auto; } -/* 居中品牌行:两侧几何圆点装饰(纯 CSS,无纹理) */ -.brand-row { - position: relative; - display: flex; - justify-content: center; - padding: 18px 16px 14px; -} -.brand-row::before, .brand-row::after { - content: ""; - position: absolute; - top: 50%; - transform: translateY(-50%); - width: 46px; - height: 10px; - opacity: .45; - background-image: - radial-gradient(circle, var(--primary) 2.2px, transparent 2.8px), - radial-gradient(circle, var(--primary) 1.6px, transparent 2.2px), - radial-gradient(circle, var(--primary) 1.1px, transparent 1.7px); - background-repeat: no-repeat; - background-position: left center, center center, right center; -} -.brand-row::before { left: 6%; } -.brand-row::after { right: 6%; } -.site-brand { display: flex; align-items: center; gap: 14px; } -.site-logo { height: 52px; } -.site-name { - font-size: 28px; - font-weight: 600; - letter-spacing: .02em; - color: var(--primary-dark); -} -.site-name:hover { color: var(--primary); } -.site-search { display: flex; align-items: center; } -.site-search input { - border: 1px solid var(--border); - border-right: none; - border-radius: var(--radius-pill) 0 0 var(--radius-pill); - padding: 7px 16px; - width: 220px; - outline: none; - background: var(--card-bg); - color: var(--text); - transition: border-color .15s ease, box-shadow .15s ease; -} -.site-search input:focus { - border-color: var(--primary); - box-shadow: 0 0 0 3px var(--primary-light); -} -.site-search button { - border: none; - background: var(--primary); - color: #fff; - padding: 7px 20px; - border-radius: 0 var(--radius-pill) var(--radius-pill) 0; - cursor: pointer; - transition: background .15s ease; -} -.site-search button:hover { background: var(--primary-dark); } - -/* ===== 专业院系下拉(院系子站切换):胶囊开关 + 圆角白卡面板 ===== */ -.site-colleges { position: relative; } -.site-colleges .colleges-toggle { - display: inline-flex; - align-items: center; - gap: 6px; - border: 1px solid var(--border); - border-radius: var(--radius-pill); - padding: 5px 14px; - cursor: pointer; - color: var(--primary-dark); - font-weight: 600; - white-space: nowrap; - transition: border-color .15s ease, background .15s ease; -} -.site-colleges:hover .colleges-toggle { - border-color: var(--primary); - background: var(--primary-light); -} -.site-colleges .colleges-arrow { font-style: normal; font-size: 12px; color: var(--primary); } -.site-colleges .colleges-menu { - display: none; - position: absolute; - top: 100%; - left: 0; - min-width: 200px; - max-height: 420px; - overflow-y: auto; - margin: 0; - padding: 8px 0; - list-style: none; - background: var(--card-bg); - border: 1px solid var(--border); - border-radius: var(--radius); - box-shadow: var(--shadow-hover); - z-index: 110; -} -.site-colleges:hover .colleges-menu { display: block; } -.site-colleges .colleges-menu li a { - display: block; - margin: 0 8px; - padding: 9px 12px; - border-radius: var(--radius-sm); - color: var(--text); - white-space: nowrap; -} -.site-colleges .colleges-menu li a:hover { background: var(--primary-light); color: var(--primary-dark); } -.site-colleges .colleges-menu li.active a { color: var(--primary); font-weight: 600; } - -/* ===== 主导航:白底 + 柔和文字层级,当前项圆点标记 ===== */ -.site-nav { background: var(--card-bg); border-top: 1px solid var(--border); } -.site-nav ul { - max-width: var(--max-width); - margin: 0 auto; - padding: 0 16px; - list-style: none; - display: flex; - justify-content: center; - flex-wrap: wrap; -} -.site-nav li a { - position: relative; - display: block; - padding: 13px 20px; - color: var(--text); - font-weight: 500; -} -.site-nav li a:hover { color: var(--primary); } -.site-nav li.active a { color: var(--primary); font-weight: 600; } -/* 几何圆点:当前栏目下方的小圆点 */ -.site-nav li.active > a::before { - content: ""; - position: absolute; - left: 50%; - bottom: 5px; - width: 5px; - height: 5px; - transform: translateX(-50%); - border-radius: 50%; - background: var(--primary); -} - -/* ===== 二级下拉导航:圆角白卡面板 ===== */ -.site-nav > ul > li { position: relative; } -.site-nav li.has-sub > a::after { content: " ▾"; font-size: 12px; opacity: .7; } -.site-nav ul.sub-nav { - display: none; - position: absolute; - top: 100%; - left: 0; - min-width: 168px; - max-width: none; - margin: 0; - padding: 8px 0; - list-style: none; - background: var(--card-bg); - border: 1px solid var(--border); - border-radius: var(--radius); - box-shadow: var(--shadow-hover); - z-index: 100; -} -.site-nav li:hover > .sub-nav { display: block; } -.site-nav .sub-nav li a { - margin: 0 8px; - padding: 9px 12px; - border-radius: var(--radius-sm); - color: var(--text); - font-weight: 400; - white-space: nowrap; - background: none; -} -.site-nav .sub-nav li a:hover { background: var(--primary-light); color: var(--primary-dark); } -.site-nav .sub-nav li.active a { color: var(--primary); font-weight: 600; } - -/* ===== 首页轮播:容器内圆角卡片(非通栏),几何圆点指示器 ===== */ -.banner { - position: relative; - width: min(var(--max-width), calc(100% - 32px)); - margin: 24px auto 0; - border-radius: var(--radius); - overflow: hidden; - box-shadow: var(--shadow-soft); -} -.banner-slides { position: relative; height: 400px; } -.banner-slide { display: none; position: absolute; inset: 0; } -.banner-slide.active { display: block; } -.banner-slide img { width: 100%; height: 100%; object-fit: cover; } -.banner-title { - position: absolute; - left: 0; right: 0; bottom: 0; - padding: 14px 20px; - color: #fff; - font-weight: 500; - background: linear-gradient(transparent, rgba(20, 63, 61, .62)); -} -.banner-dots { - position: absolute; - bottom: 14px; right: 20px; - display: flex; gap: 8px; -} -.banner-dots button { - width: 9px; height: 9px; - border-radius: 50%; - border: none; - background: rgba(255, 255, 255, .55); - cursor: pointer; - transition: background .15s ease, transform .15s ease; -} -.banner-dots button.active { background: #fff; transform: scale(1.25); } - -/* ===== 首页区块:卡片网格(头条通栏 + 栏目块一行 3 张) ===== */ -.home-main { - max-width: var(--max-width); - margin: 0 auto; - padding: 28px 16px 56px; - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 24px; -} -.top-strip, .category-block { - background: var(--card-bg); - border-radius: var(--radius); - box-shadow: var(--shadow-soft); - padding: 24px; -} -.top-strip { grid-column: 1 / -1; } -/* 栏目标题:几何圆点标记(环形点),标题中等粗细、层级柔和 */ -.section-title, .block-head h2 { - margin: 0 0 14px; - font-size: 19px; - font-weight: 600; - letter-spacing: .02em; - display: flex; - align-items: center; - gap: 10px; -} -.section-title::before, .block-head h2::before { - content: ""; - flex: none; - width: 9px; - height: 9px; - border-radius: 50%; - background: var(--primary); - box-shadow: 0 0 0 3.5px var(--primary-light); -} -.block-head { display: flex; align-items: center; justify-content: space-between; } -.more { - flex: none; - font-size: 13px; - color: var(--text-light); - padding: 4px 12px; - border: 1px solid var(--border); - border-radius: var(--radius-pill); - transition: color .15s ease, border-color .15s ease, background .15s ease; -} -.more:hover { - color: var(--primary-dark); - border-color: var(--primary); - background: var(--primary-light); -} -.top-strip ul, .block-list { list-style: none; margin: 0; padding: 0; } -.top-strip li, .block-list li { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - padding: 10px 0; - border-bottom: 1px dashed var(--border); -} -.top-strip li:last-child, .block-list li:last-child { border-bottom: none; } -.top-strip li a, .block-list li a { - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.top-badge { - flex: none; - color: var(--primary-dark); - background: var(--primary-light); - font-size: 12px; - font-weight: 500; - padding: 2px 10px; - border-radius: var(--radius-pill); -} - -/* ===== 列表页:卡片网格(默认一行 3 张;listLayout=row 切换行列表) ===== */ -.list-main { - max-width: var(--max-width); - margin: 0 auto; - padding: 28px 16px 56px; -} -.breadcrumb { color: var(--text-light); font-size: 13px; margin-bottom: 14px; } -.breadcrumb a { margin-right: 8px; } -.breadcrumb span { margin-left: 8px; } -.page-title { - font-size: 24px; - font-weight: 600; - letter-spacing: .02em; - margin: 0 0 22px; - display: flex; - align-items: center; - gap: 10px; -} -.page-title::before { - content: ""; - flex: none; - width: 9px; - height: 9px; - border-radius: 50%; - background: var(--primary); - box-shadow: 0 0 0 3.5px var(--primary-light); -} -.article-list { - list-style: none; - margin: 0; - padding: 0; - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 20px; -} -.article-list li { - background: var(--card-bg); - border-radius: var(--radius); - box-shadow: var(--shadow-soft); - padding: 22px 20px; - display: flex; - flex-direction: column; - gap: 10px; - transition: transform .18s ease, box-shadow .18s ease; -} -.article-list li:hover { transform: translateY(-3px); box-shadow: var(--shadow-hover); } -.item-title { - font-size: 16px; - font-weight: 600; - line-height: 1.55; - overflow: hidden; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; -} -.article-list li:hover .item-title { color: var(--primary); } -.item-summary { - color: var(--text-light); - font-size: 13.5px; - line-height: 1.7; - margin: 0; - flex: 1; - overflow: hidden; - display: -webkit-box; - -webkit-line-clamp: 3; - -webkit-box-orient: vertical; -} -.item-meta { - display: flex; - gap: 10px; - align-items: center; - margin-top: auto; - padding-top: 10px; - border-top: 1px dashed var(--border); -} -/* 布局变体:传统行列表(themeParams listLayout=row,见 list.html classappend) */ -.list-rows .article-list { display: block; } -.list-rows .article-list li { display: block; margin-bottom: 16px; } -.list-rows .item-summary { -webkit-line-clamp: 2; flex: none; } -.list-rows .item-meta { border-top: none; padding-top: 0; margin-top: 6px; } - -/* ===== 分页:胶囊页码 ===== */ -.pagination { - display: flex; - gap: 8px; - justify-content: center; - flex-wrap: wrap; - padding: 28px 0 4px; -} -.pagination a { - min-width: 36px; - text-align: center; - padding: 6px 12px; - border: 1px solid var(--border); - border-radius: var(--radius-pill); - background: var(--card-bg); - font-size: 14px; - transition: color .15s ease, border-color .15s ease, background .15s ease; -} -.pagination a:hover { color: var(--primary-dark); border-color: var(--primary); } -.pagination a.active { background: var(--primary); color: #fff; border-color: var(--primary); } - -/* ===== 详情/单页:整卡承载,正文舒适行距 ===== */ -.detail-main { - max-width: var(--max-width); - margin: 0 auto; - padding: 28px 16px 56px; -} -.detail-main .article { - background: var(--card-bg); - border-radius: var(--radius); - box-shadow: var(--shadow-soft); - padding: 36px 40px; -} -.article-title { - margin: 4px 0 14px; - font-size: 26px; - font-weight: 600; - letter-spacing: .02em; - text-align: center; -} -.article-meta { - display: flex; - gap: 18px; - justify-content: center; - flex-wrap: wrap; - color: var(--text-light); - font-size: 13px; - padding-bottom: 18px; - border-bottom: 1px solid var(--border); -} -.article-content { line-height: 1.9; font-size: 16px; padding-top: 22px; word-break: break-word; } -.article-content img { height: auto; border-radius: var(--radius-sm); } -.article-content table { border-collapse: collapse; width: 100%; } -.article-content table td, .article-content table th { border: 1px solid var(--border); padding: 6px 10px; } -.article-sibling { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 20px; font-size: 14px; } -.article-sibling a { - flex: 1 1 320px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - background: var(--card-bg); - border-radius: var(--radius); - box-shadow: var(--shadow-soft); - padding: 14px 18px; - transition: transform .18s ease, box-shadow .18s ease, color .15s ease; -} -.article-sibling a:hover { - color: var(--primary-dark); - transform: translateY(-2px); - box-shadow: var(--shadow-hover); -} - -/* ===== 页脚:浅鸢尾底 + 圆点分隔条(三态渲染逻辑不动) ===== */ -.site-footer { - margin-top: 40px; - background: var(--primary-light); - color: var(--text); - border-top: 1px solid var(--border); -} -.site-footer::before { - content: ""; - display: block; - height: 8px; - width: min(var(--max-width), calc(100% - 32px)); - margin: 18px auto 0; - background-image: radial-gradient(circle, var(--primary) 1.6px, transparent 2.2px); - background-size: 16px 8px; - background-repeat: repeat-x; - opacity: .4; -} -.footer-links { - max-width: var(--max-width); - margin: 0 auto; - padding: 18px 16px 0; - display: flex; - flex-direction: column; - gap: 10px; -} -.links-group { display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: center; font-size: 13px; } -.group-name { font-weight: 600; margin-right: 4px; color: var(--primary-dark); } -.links-group a { - padding: 3px 10px; - border-radius: var(--radius-pill); - transition: background .15s ease, color .15s ease; -} -.links-group a:hover { background: rgba(255, 255, 255, .75); color: var(--primary-dark); } -.footer-info { - max-width: var(--max-width); - margin: 0 auto; - padding: 14px 16px 24px; - text-align: center; - font-size: 13px; - color: var(--text-light); -} -.footer-info p { margin: 4px 0; } - -/* B4 友链 Logo:与名称并排小图(高度 ~20px,垂直居中;过宽截断不撑破页脚) */ -.links-group a .link-logo { - height: 20px; - width: auto; - max-width: 64px; - object-fit: contain; - vertical-align: middle; - margin-right: 4px; - border-radius: 4px; -} - -/* ===== 平板(≤992px):网格降为两列,隐藏品牌行圆点装饰 ===== */ -@media (max-width: 992px) { - .home-main { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .article-list { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .brand-row::before, .brand-row::after { display: none; } -} - -/* ===== 移动端(≤768px):取消吸顶,网格单列,导航横向滑动 ===== */ -@media (max-width: 768px) { - .site-header { position: static; } - .top-bar { padding: 8px 14px; justify-content: flex-start; flex-wrap: wrap; } - .top-bar .site-colleges { margin-right: 0; } - .site-search { flex: 1; } - .site-search input { flex: 1; width: auto; min-width: 0; } - .brand-row { padding: 14px 16px 10px; } - .site-logo { height: 40px; } - .site-name { font-size: 22px; } - .site-nav ul { - justify-content: flex-start; - flex-wrap: nowrap; - overflow-x: auto; - -webkit-overflow-scrolling: touch; - } - .banner-slides { height: 200px; } - .home-main { grid-template-columns: minmax(0, 1fr); padding: 18px 14px 40px; } - .article-list { grid-template-columns: minmax(0, 1fr); } - .detail-main .article { padding: 22px 16px; } - .article-title { font-size: 21px; } - /* 触屏无 hover:子菜单静态平铺(缩进列表),不做浮层 */ - .site-nav ul.sub-nav { - display: block; - position: static; - border: none; - box-shadow: none; - background: transparent; - min-width: 0; - padding: 0 0 6px; - } - .site-nav .sub-nav li a { margin: 0; padding: 8px 16px 8px 34px; border-radius: 0; color: var(--text-light); } - .site-nav .sub-nav li a:hover { background: var(--primary-light); color: var(--primary-dark); } -} - - -/* ===== 左侧栏目导航(docs/11):有二级节点的模块,栏目/文章页左侧展示兄弟目录 ===== */ -.list-main.has-sidenav, .detail-main.has-sidenav { display: flex; align-items: flex-start; gap: 24px; } -.has-sidenav .list-body { flex: 1; min-width: 0; } -.side-nav { flex: none; width: 220px; background: var(--card-bg, #fff); border: 1px solid var(--border); overflow: hidden; } -.side-nav-head { background: var(--primary); color: #fff; font-size: 20px; font-weight: 700; text-align: center; padding: 18px 12px; letter-spacing: 2px; } -.side-nav-list { list-style: none; margin: 0; padding: 0; } -.side-nav-item + .side-nav-item { border-top: 1px dashed var(--border); } -.side-nav-item a { display: block; padding: 12px 16px; color: var(--text); text-decoration: none; font-size: 15px; transition: background .15s ease, color .15s ease; } -.side-nav-item a:hover { color: var(--primary); background: var(--bg); } -.side-nav-item.active a { background: var(--primary); color: #fff; font-weight: 600; text-decoration: none; } -/* 顶导航二级下拉当前项高亮(docs/11 顺带) */ -.site-nav ul.sub-nav li.active a { color: var(--primary); } -@media (max-width: 768px) { - .list-main.has-sidenav, .detail-main.has-sidenav { flex-direction: column; gap: 12px; } - .side-nav { width: 100%; } - .side-nav-list { display: flex; overflow-x: auto; -webkit-overflow-scrolling: touch; } - .side-nav-item { flex: none; } - .side-nav-item + .side-nav-item { border-top: none; border-left: 1px dashed var(--border); } - .side-nav-item a { white-space: nowrap; } -} -@media print { - .side-nav { display: none; } - .list-main.has-sidenav, .detail-main.has-sidenav { display: block; } -} diff --git a/src/main/resources/templates/themes/violet-iris/assets/favicon.png b/src/main/resources/templates/themes/violet-iris/assets/favicon.png deleted file mode 100644 index 5fa2755..0000000 Binary files a/src/main/resources/templates/themes/violet-iris/assets/favicon.png and /dev/null differ diff --git a/src/main/resources/templates/themes/violet-iris/assets/favicon.svg b/src/main/resources/templates/themes/violet-iris/assets/favicon.svg deleted file mode 100644 index eed749f..0000000 --- a/src/main/resources/templates/themes/violet-iris/assets/favicon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/src/main/resources/templates/themes/violet-iris/assets/js/main.js b/src/main/resources/templates/themes/violet-iris/assets/js/main.js deleted file mode 100644 index 8261c6b..0000000 --- a/src/main/resources/templates/themes/violet-iris/assets/js/main.js +++ /dev/null @@ -1,40 +0,0 @@ -/* violet-iris 皮肤包脚本:仅首页轮播自动播放与圆点切换,无任何外部依赖 */ -(function () { - var slides = document.querySelectorAll('.banner-slide'); - var dots = document.querySelectorAll('.banner-dots button'); - if (!slides.length) { - return; - } - var current = 0; - var timer = null; - - function show(index) { - slides[current].classList.remove('active'); - if (dots[current]) { - dots[current].classList.remove('active'); - } - current = (index + slides.length) % slides.length; - slides[current].classList.add('active'); - if (dots[current]) { - dots[current].classList.add('active'); - } - } - - function start() { - if (slides.length > 1) { - timer = window.setInterval(function () { - show(current + 1); - }, 5000); - } - } - - dots.forEach(function (dot, index) { - dot.addEventListener('click', function () { - window.clearInterval(timer); - show(index); - start(); - }); - }); - - start(); -})(); diff --git a/src/main/resources/templates/themes/violet-iris/assets/screenshot.png b/src/main/resources/templates/themes/violet-iris/assets/screenshot.png deleted file mode 100644 index 908a26e..0000000 Binary files a/src/main/resources/templates/themes/violet-iris/assets/screenshot.png and /dev/null differ diff --git a/src/main/resources/templates/themes/violet-iris/detail.html b/src/main/resources/templates/themes/violet-iris/detail.html deleted file mode 100644 index 8eab71f..0000000 --- a/src/main/resources/templates/themes/violet-iris/detail.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - -
- -
- - -
- - -
-

文章标题

- - -
正文
-
- - -
-
- - - - diff --git a/src/main/resources/templates/themes/violet-iris/fragments/footer.html b/src/main/resources/templates/themes/violet-iris/fragments/footer.html deleted file mode 100644 index 1f3f83d..0000000 --- a/src/main/resources/templates/themes/violet-iris/fragments/footer.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - diff --git a/src/main/resources/templates/themes/violet-iris/fragments/header.html b/src/main/resources/templates/themes/violet-iris/fragments/header.html deleted file mode 100644 index 462364e..0000000 --- a/src/main/resources/templates/themes/violet-iris/fragments/header.html +++ /dev/null @@ -1,56 +0,0 @@ - - - - - diff --git a/src/main/resources/templates/themes/violet-iris/home.html b/src/main/resources/templates/themes/violet-iris/home.html deleted file mode 100644 index 8d4f072..0000000 --- a/src/main/resources/templates/themes/violet-iris/home.html +++ /dev/null @@ -1,68 +0,0 @@ - - - - -
- -
- - - - - - - - - - - - - - -
-

头条推荐

- -
- - -
-
-

栏目名

- 更多 > -
- -

暂无内容

-
-
-
- - - - - diff --git a/src/main/resources/templates/themes/violet-iris/list.html b/src/main/resources/templates/themes/violet-iris/list.html deleted file mode 100644 index 14c47c4..0000000 --- a/src/main/resources/templates/themes/violet-iris/list.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - -
- - -
- - -
- - - -

栏目名

-

搜索词

- - -

暂无内容

- - - -
-
- - - - - diff --git a/src/main/resources/templates/themes/violet-iris/single.html b/src/main/resources/templates/themes/violet-iris/single.html deleted file mode 100644 index 65292dc..0000000 --- a/src/main/resources/templates/themes/violet-iris/single.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - -
- -
- - -
- - -
-

栏目名

- -
单页内容
-

暂无内容

-
-
-
- - - - diff --git a/src/main/resources/templates/themes/violet-iris/theme.json b/src/main/resources/templates/themes/violet-iris/theme.json deleted file mode 100644 index 607917f..0000000 --- a/src/main/resources/templates/themes/violet-iris/theme.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "specVersion": 1, - "name": "violet-iris", - "title": "鸢尾紫韵", - "version": "1.0.0", - "author": "yunhai", - "description": "雅致鸢尾紫门户:细线顶栏+居中 logo 吸顶、鸢尾紫品牌色、圆角 12px 卡片网格列表、几何圆点装饰与淡薰衣草留白,沉静书卷气,适合高校与教科研机构。", - "screenshot": "assets/screenshot.png", - "bannerSize": "1920x560", - "params": [ - { "key": "primary", "cssVar": "--primary", "label": "主色", "type": "color", "default": "#6552D9" }, - { "key": "primaryDark", "cssVar": "--primary-dark", "label": "主色加深(强调色)", "type": "color", "default": "#4A39B0" }, - { "key": "primaryLight", "cssVar": "--primary-light", "label": "主色提亮", "type": "color", "default": "#EFECFC" }, - { "key": "bg", "cssVar": "--bg", "label": "页面底色", "type": "color", "default": "#F8F7FD" }, - { "key": "cardBg", "cssVar": "--card-bg", "label": "卡片底色", "type": "color", "default": "#FFFFFF" }, - { "key": "border", "cssVar": "--border", "label": "边框色", "type": "color", "default": "#E4DFF6" }, - { "key": "text", "cssVar": "--text", "label": "正文文字色", "type": "color", "default": "#332F45" }, - { "key": "textLight", "cssVar": "--text-light", "label": "次要文字色", "type": "color", "default": "#847E9C" }, - { "key": "listLayout", "label": "列表布局", "type": "select", "default": "grid", - "options": [ { "value": "grid", "label": "卡片网格(一行 3 张)" }, { "value": "row", "label": "传统行列表" } ] } - ] -} diff --git a/src/test/java/com/yunhai/cms/portal/PortalHomeLayoutIT.java b/src/test/java/com/yunhai/cms/portal/PortalHomeLayoutIT.java index b55deec..fe46edf 100644 --- a/src/test/java/com/yunhai/cms/portal/PortalHomeLayoutIT.java +++ b/src/test/java/com/yunhai/cms/portal/PortalHomeLayoutIT.java @@ -35,12 +35,13 @@ class PortalHomeLayoutIT extends CmsIntegrationTestBase { private Integer catNews; private Integer catNotice; + private Integer siteId; @BeforeEach void seed() { purgeTables(); CmsSite site = persistSite("layhome", 1, "布局测试门户"); - Integer siteId = site.getId(); + siteId = site.getId(); catNews = persistCategory(siteId, 0, "校园时讯", CategoryTypeEnum.LIST).getId(); catNotice = persistCategory(siteId, 0, "通知公告", CategoryTypeEnum.LIST).getId(); persistCategory(siteId, 0, "学校概况", CategoryTypeEnum.SINGLE); @@ -118,7 +119,7 @@ class PortalHomeLayoutIT extends CmsIntegrationTestBase { assertThat(html).doesNotContain("headline-list").doesNotContain("category-block"); // ticker:置顶优先+发布时间倒序取 size=2 条(无置顶 → 最新两篇) assertThat(html).contains("plb-ticker").contains("要闻速递"); - assertThat(count(html, "plb-ticker-item")).as("ticker 取 size=2 条").isEqualTo(2); + assertThat(count(html, " { + s.setTheme("academic-navy"); + siteRepository.saveAndFlush(s); + }); + clearCaches(); + + String html = body(mockMvc.perform(get("/portal/layhome/")).andExpect(status().isOk()).andReturn()); + + // 皮肤自有区块:an-* 结构类;news_grid/compact 不再落 plb 骨架 + assertThat(html).contains("an-ticker").contains("an-split").contains("an-date-chip"); + assertThat(html).as("已实现形态不得回落通用骨架").doesNotContain("plb-"); + assertThat(html).contains("校园要闻").contains("通知公告").contains("要闻速递"); + assertThat(count(html, " { + s.setTheme("campus-azure"); + siteRepository.saveAndFlush(s); + }); + clearCaches(); + + String html = body(mockMvc.perform(get("/portal/layhome/")).andExpect(status().isOk()).andReturn()); + assertThat(html).contains("cz-header").contains("cz-footer").contains("校园时讯"); + assertThat(html).as("无 home_layout 时不得出现布局骨架").doesNotContain("plb-section"); + + String css = body(mockMvc.perform(get("/portal/layhome/assets/campus-azure/css/main.css")) + .andExpect(status().isOk()).andReturn()); + // CSS 里是 "--primary: var(--cz-primary);",声明带空格,断言前先去空白 + assertThat(css.replaceAll("\\s+", "")) + .as("资产通道按主题名服务 CSS,且私有变量已别名到契约变量") + .contains("--cz-primary").contains("--primary:var(--cz-primary)"); + } + + @Test + @DisplayName("空态守卫:有内容的区块不得渲染「该区块暂无内容」,空区块恰渲染一次") + void emptyState_onlyForEmptyBlocks() throws Exception { + Integer emptyCat = persistCategory(siteId, 0, "空置栏目", CategoryTypeEnum.LIST).getId(); + clearCaches(); + setConfig("layhome", "{\"home_layout\":{\"version\":1,\"blocks\":[" + + "{\"id\":\"b1\",\"type\":\"article_list\",\"title\":\"校园时讯\",\"categoryIds\":[" + catNews + "],\"style\":\"compact\",\"size\":3}," + + "{\"id\":\"b2\",\"type\":\"article_list\",\"title\":\"校园要闻\",\"categoryIds\":[" + catNews + "],\"style\":\"news_grid\",\"size\":3}," + + "{\"id\":\"b3\",\"type\":\"article_list\",\"title\":\"空置栏目\",\"categoryIds\":[" + emptyCat + "],\"style\":\"compact\",\"size\":3}" + + "]}}"); + + String html = body(mockMvc.perform(get("/portal/layhome/")).andExpect(status().isOk()).andReturn()); + + // th:replace(优先级 100)先于 th:if(300)求值:守卫写在同一标签上等于没写, + // 空态块会被无条件插入——线上表现为每个有内容的栏目卡下面都挂一句「该区块暂无内容」。 + assertThat(count(html, "该区块暂无内容")) + .as("三块中仅空置栏目该显示空态") + .isEqualTo(1); + assertThat(html).contains("校园时讯").contains("校园要闻"); + } } diff --git a/src/test/java/com/yunhai/cms/theme/AcademicSkinContractTest.java b/src/test/java/com/yunhai/cms/theme/AcademicSkinContractTest.java new file mode 100644 index 0000000..c80e882 --- /dev/null +++ b/src/test/java/com/yunhai/cms/theme/AcademicSkinContractTest.java @@ -0,0 +1,194 @@ +package com.yunhai.cms.theme; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 高校官网风皮肤契约(academic-navy / campus-azure,以 scholar-blue 为基座派生)。 + * + * 线上事故复盘:站点配了 home_layout 时首页走 common 骨架,派生包若只改色不改区块形态, + * 首页正文就是骨架内置的蓝灰卡片——用户看到的"皮肤"只剩页头页脚,且中文标题被 flex 压塌。 + * 故这里要求派生包必须自带 home-blocks fragment,并钉住三条渲染不变量。 + */ +@DisplayName("高校官网风皮肤契约 - 自带首页区块 + 学术蓝配色") +class AcademicSkinContractTest { + + /** 皮肤名:结构类名前缀,两处必须一致(拷贝忘改前缀会让两套皮肤互相串样式)。 */ + private static final String[] SKINS = {"academic-navy:an", "campus-azure:cz"}; + + private static String skin(String spec) { + return spec.substring(0, spec.indexOf(':')); + } + + private static String prefix(String spec) { + return spec.substring(spec.indexOf(':') + 1); + } + + private static String text(String classpath) { + try (InputStream in = AcademicSkinContractTest.class.getResourceAsStream(classpath)) { + assertThat(in).as("classpath 资源必须存在:%s", classpath).isNotNull(); + return new String(readAll(in), StandardCharsets.UTF_8); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + private static byte[] readAll(InputStream in) throws IOException { + java.io.ByteArrayOutputStream out = new java.io.ByteArrayOutputStream(); + byte[] buf = new byte[8192]; + int n; + while ((n = in.read(buf)) > 0) { + out.write(buf, 0, n); + } + return out.toByteArray(); + } + + private static String ruleOf(String css, String selector) { + Matcher m = Pattern.compile(Pattern.quote(selector) + "\\s*\\{([^}]*)\\}").matcher(css); + assertThat(m.find()).as("main.css 应含选择器 %s 的规则", selector).isTrue(); + return m.group(1).replaceAll("\\s+", ""); + } + + /** 抽出所有含 marker 的开始标签,用于检查同一标签上的属性组合。 */ + private static java.util.List tagsContaining(String html, String marker) { + java.util.List tags = new java.util.ArrayList<>(); + Matcher m = Pattern.compile("<[a-zA-Z][^>]*>").matcher(html); + while (m.find()) { + if (m.group().contains(marker)) { + tags.add(m.group()); + } + } + return tags; + } + + @Test + @DisplayName("派生包自带首页区块 fragment:布局模式下区块由皮肤渲染,不落通用骨架") + void skinOwnsHomeBlockFragments() { + for (String spec : SKINS) { + String html = text("/templates/themes/" + skin(spec) + "/fragments/home-blocks.html"); + for (String fragment : new String[] {"news_grid", "media_focus", "compact", "image_wall", + "card", "ticker", "gallery", "links", "empty"}) { + assertThat(html).as("%s 区块 fragment 必须自带", fragment) + .contains("th:fragment=\"" + fragment + "(block)\""); + } + // 空态:th:replace(优先级 100)先于 th:if(300),守卫写在同一标签上等于没写 + for (String tag : tagsContaining(html, ":: empty(")) { + assertThat(tag).as("%s 的空态守卫必须在外层 th:block 上", skin(spec)).doesNotContain("th:if"); + } + assertThat(html).contains("th:block th:if=\"${#lists.isEmpty(block.items)}\""); + } + } + + @Test + @DisplayName("home.html 把布局区块分发到本皮肤 fragment,区块头带右置「更多」") + void homeDispatchesToOwnFragments() { + for (String spec : SKINS) { + String html = text("/templates/themes/" + skin(spec) + "/home.html"); + assertThat(html).as("须按 layoutBlocks 分支渲染").contains("layoutBlocks != null"); + for (String style : new String[] {"news_grid", "media_focus", "compact", "image_wall"}) { + assertThat(html).as("%s 形态 %s 应走本皮肤 fragment", skin(spec), style) + .contains("'/fragments/home-blocks' :: " + style + "(${block})"); + } + assertThat(html).as("区块标题行须有右侧「更多」样式钩子").contains(prefix(spec) + "-more"); + } + } + + @Test + @DisplayName("中文行不被压塌:速递条单行可收缩、列表标题超长省略") + void rowsMustNotCollapseCjkText() { + for (String spec : SKINS) { + String css = text("/templates/themes/" + skin(spec) + "/assets/css/main.css"); + String p = prefix(spec); + String list = ruleOf(css, "." + p + "-ticker-list"); + assertThat(list).as("flex 列表须让出收缩权并锁单行").contains("min-width:0").contains("white-space:nowrap"); + String item = ruleOf(css, "." + p + "-ticker-item"); + assertThat(item).as("条目不被压缩且超长省略").contains("flex:none").contains("text-overflow:ellipsis"); + String title = ruleOf(css, "." + p + "-block-item-title"); + assertThat(title).contains("min-width:0").contains("overflow:hidden").contains("text-overflow:ellipsis"); + } + } + + @Test + @DisplayName("皮肤变量别名到主题契约变量,供 common 兜底骨架继承") + void skinAliasesContractVars() { + for (String spec : SKINS) { + String css = text("/templates/themes/" + skin(spec) + "/assets/css/main.css"); + String p = prefix(spec); + String root = ruleOf(css, ":root"); + assertThat(root).contains("--primary:var(--" + p + "-primary)") + .contains("--text:var(--" + p + "-text)") + .contains("--card-bg:var(--" + p + "-card-bg)") + .contains("--border:var(--" + p + "-border)"); + } + } + + @Test + @DisplayName("新闻背景带画在卡片之下:绝对定位层必须压 z-index,卡片抬起来") + void newsBandMustPaintBehindCard() { + for (String spec : SKINS) { + String css = text("/templates/themes/" + skin(spec) + "/assets/css/main.css"); + String p = prefix(spec); + // 定位元素默认绘制在普通流之上:不带 z-index 的背景带会糊在卡片正面(实测整卡泛绿) + assertThat(ruleOf(css, "." + p + "-sec-bg")).as("%s 背景带须压到卡片之下", p).contains("z-index:0"); + assertThat(ruleOf(css, "." + p + "-sec-bg + ." + p + "-block")) + .as("%s 卡片须抬到背景带之上,否则新闻带图片盖住正文", p).contains("z-index:1"); + } + } + + @Test + @DisplayName("皮肤预览图必须是实拍图,不得沿用基座 scholar-blue 的截图") + void screenshotMustBeOwnCapture() { + byte[] base = bytes("/templates/themes/scholar-blue/assets/screenshot.png"); + for (String spec : SKINS) { + String path = "/templates/themes/" + skin(spec) + "/assets/screenshot.png"; + byte[] shot = bytes(path); + assertThat(shot).as("%s 的预览图与基座雷同,等于没换肤", skin(spec)).isNotEqualTo(base); + assertThat(shot.length).as("%s 预览图不能是空文件", skin(spec)).isGreaterThan(20 * 1024); + } + } + + private static byte[] bytes(String classpath) { + try (InputStream in = AcademicSkinContractTest.class.getResourceAsStream(classpath)) { + assertThat(in).as("classpath 资源必须存在:%s", classpath).isNotNull(); + return readAll(in); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Test + @DisplayName("配色必须是学术蓝:主色蓝通道主导、明度落在深蓝区间,不得回落粉紫") + void primaryMustBeAcademicBlue() throws IOException { + for (String spec : SKINS) { + JsonNode root = new ObjectMapper() + .readTree(text("/templates/themes/" + skin(spec) + "/theme.json")); + assertThat(root.path("name").asText()).as("name 必须等于目录名").isEqualTo(skin(spec)); + String primary = null; + for (JsonNode p : root.path("params")) { + if ("primary".equals(p.path("key").asText())) { + primary = p.path("default").asText(); + } + } + assertThat(primary).as("%s theme.json 须声明 primary 颜色参数", skin(spec)).isNotNull(); + Matcher m = Pattern.compile("#([0-9a-fA-F]{6})").matcher(primary); + assertThat(m.matches()).as("primary 需为 6 位 hex,实际 %s", primary).isTrue(); + int rgb = Integer.parseInt(m.group(1), 16); + int r = (rgb >> 16) & 0xFF, g = (rgb >> 8) & 0xFF, b = rgb & 0xFF; + assertThat(b).as("主色须蓝通道主导(粉/紫已被否决),实际 %s", primary).isGreaterThan(r); + assertThat(b).as("主色须蓝通道主导,实际 %s", primary).isGreaterThan(g); + double lightness = (Math.max(r, Math.max(g, b)) + Math.min(r, Math.min(g, b))) / 255d / 2d; + assertThat(lightness).as("官网主色应为深蓝/藏蓝而非浅粉彩,实际 %s", primary) + .isBetween(0.18d, 0.52d); + } + } +} diff --git a/src/test/java/com/yunhai/cms/theme/HomeBlocksCssContractTest.java b/src/test/java/com/yunhai/cms/theme/HomeBlocksCssContractTest.java new file mode 100644 index 0000000..911be80 --- /dev/null +++ b/src/test/java/com/yunhai/cms/theme/HomeBlocksCssContractTest.java @@ -0,0 +1,115 @@ +package com.yunhai.cms.theme; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * 通用兜底区块骨架(themes/common/fragments/home-blocks.html)的 CSS 契约。 + * + * 该片段带内联 style,任何配了 home_layout 的站点都用它渲染区块,是门户首页的实际门面; + * 它出问题时十套皮肤一起坏,且只在"站点配了首页布局"时才暴露——回落分支看不到。 + * 故把三条易被改坏的不变量钉在这里:不被中文压塌、颜色穿透皮肤、全宽区块不被网格切列。 + */ +@DisplayName("home-blocks 共享骨架 CSS 契约") +class HomeBlocksCssContractTest { + + private static final String FRAGMENT = "/templates/themes/common/fragments/home-blocks.html"; + + /** 取 ::style 片段里的 CSS 正文(第一个 ", Pattern.DOTALL).matcher(html); + assertThat(m.find()).as("片段应携带内联