feat: 图库重做/瞬间详情/重要日子适配页/全站灯箱,修复豆瓣页白屏(v0.2.0)
- 图库页重做:首屏按相册分组展示,封面自动取相册第一张图,可在设置中按相册手动配置封面;点击相册进入 ?group= 瀑布流视图(一行三张、页面加宽),支持预览模式(lightgallery 放大,默认值可配置、前台可切换)
- 瞬间页:新增 /moments/{name} 详情页(完整内容、媒体、标签、作者信息、评论区);列表页发布人与发布时间同行对齐;补充单条标签
- 新增 plugin-important-dates 适配页 important-dates.html(即将到来提醒、全部日期倒计时、重要标记、人员生日)
- 全站集成 lightgallery.js 灯箱(动态模式、按需加载、挂载 body 避免层叠上下文遮挡),可在设置中关闭
- 友链页:「全部」视图按分组展示并显示分组标题(空分组自动隐藏);卡片简介限制两行,统一卡片高度与尺寸
- 修复 /douban 白屏:SpEL 不支持 th:with 中的嵌套 map 字面量(EL1041E),改为四段显式区块
- 版本号升至 0.2.0;README 同步更新
Co-Authored-By: WorkBuddy <noreply@workbuddy.ai>
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
- **页脚可关闭的主题署名**:默认「Theme by WhiteSpace」并链接到主题仓库,可在设置中修改名称 / 链接或整体关闭
|
- **页脚可关闭的主题署名**:默认「Theme by WhiteSpace」并链接到主题仓库,可在设置中修改名称 / 链接或整体关闭
|
||||||
- **页脚全站数据统计**:文章 / 分类 / 标签 / 评论 / 访问量 / 字数,可按项开关,整体可关闭
|
- **页脚全站数据统计**:文章 / 分类 / 标签 / 评论 / 访问量 / 字数,可按项开关,整体可关闭
|
||||||
- **文章卡片字数 + 阅读时长**:可在设置中关闭(需 `halo-plugin-extra-api` 提供数据)
|
- **文章卡片字数 + 阅读时长**:可在设置中关闭(需 `halo-plugin-extra-api` 提供数据)
|
||||||
|
- **全站 lightgallery.js 灯箱**:文章、页面、瞬间、图库等页面的图片点击放大预览(按需懒加载,可在设置中整体关闭)
|
||||||
- 多语言:`i18n/default.properties`(回退)+ `i18n/zh_CN.properties`
|
- 多语言:`i18n/default.properties`(回退)+ `i18n/zh_CN.properties`
|
||||||
- 提供 `layout.html` 布局契约,供使用布局契约的插件页面复用主题导航 / 页脚 / 暗色样式
|
- 提供 `layout.html` 布局契约,供使用布局契约的插件页面复用主题导航 / 页脚 / 暗色样式
|
||||||
|
|
||||||
@@ -25,9 +26,10 @@
|
|||||||
|
|
||||||
主题已为以下插件提供适配页面(在 `src/` 下以同名模板提供,构建后进入 `templates/`):
|
主题已为以下插件提供适配页面(在 `src/` 下以同名模板提供,构建后进入 `templates/`):
|
||||||
|
|
||||||
- **plugin-moments**:`/moments` 瞬间页
|
- **plugin-moments**:`/moments` 瞬间列表页 + `/moments/{name}` 瞬间详情页(完整内容、媒体、标签、作者信息与评论区);列表页发布人与发布时间同行对齐
|
||||||
- **plugin-photos**:`/photos` 图库页
|
- **plugin-photos**:`/photos` 图库页 —— 首屏按相册分组展示(封面默认自动取相册内第一张图,可在主题设置「图库」中为每个相册手动配置封面);点击相册进入 `?group=photo-group-*` 瀑布流视图(一行三张、页面加宽),支持**预览模式**(lightgallery 点击放大,默认值可在设置中配置,访客可在前台自行切换)
|
||||||
- **plugin-links**:`/links` 友链页;友链申请表单已**直接内嵌**在友链页底部(点击「申请友链」展开,调用插件公共 REST API,含图形验证码);并额外提供
|
- **plugin-important-dates**:`/important-dates` 重要日子页(即将到来提醒、全部日期倒计时、重要标记、人员生日)
|
||||||
|
- **plugin-links**:`/links` 友链页 —— 「全部」视图按分组展示并显示分组标题,卡片简介最多两行、卡片高度统一;友链申请表单已**直接内嵌**在友链页底部(点击「申请友链」展开,调用插件公共 REST API,含图形验证码);并额外提供
|
||||||
- `links-subscribe.html`:RSS 订阅动态聚合页(使用 `linkFeedFinder`);每条仅显示作者与发布时间,作者名链接到来源博客,摘要默认折叠为 5 行
|
- `links-subscribe.html`:RSS 订阅动态聚合页(使用 `linkFeedFinder`);每条仅显示作者与发布时间,作者名链接到来源博客,摘要默认折叠为 5 行
|
||||||
- **halo-weread-plugin**:`weread.html` 微信读书书架页(前端拉取插件 `/shelf-html` 并注入)
|
- **halo-weread-plugin**:`weread.html` 微信读书书架页(前端拉取插件 `/shelf-html` 并注入)
|
||||||
- **plugin-douban**:`douban.html` 豆瓣收藏页(按「读过 / 在读 / 看过 / 在追」分栏展示书影音,使用 `doubanFinder.getMovies(...)`;未安装插件时显示提示)。在控制台新建单页并选择「豆瓣页面」模板即可启用
|
- **plugin-douban**:`douban.html` 豆瓣收藏页(按「读过 / 在读 / 看过 / 在追」分栏展示书影音,使用 `doubanFinder.getMovies(...)`;未安装插件时显示提示)。在控制台新建单页并选择「豆瓣页面」模板即可启用
|
||||||
@@ -88,8 +90,10 @@ pnpm check # 格式化 + Lint 修复
|
|||||||
| basic | color_scheme | 默认配色:跟随系统 / 浅色 / 深色 |
|
| basic | color_scheme | 默认配色:跟随系统 / 浅色 / 深色 |
|
||||||
| basic | footer_copyright | 页脚自定义版权(支持 HTML,留空不显示) |
|
| basic | footer_copyright | 页脚自定义版权(支持 HTML,留空不显示) |
|
||||||
| basic | show_theme_credit | 页脚是否显示「Theme by」署名(默认开) |
|
| basic | show_theme_credit | 页脚是否显示「Theme by」署名(默认开) |
|
||||||
| basic | theme_credit_text | 署名名称(默认「暖心向阳335」) |
|
| basic | theme_credit_text | 署名名称(默认「WhiteSpace」,可改) |
|
||||||
|
| basic | theme_credit_url | 署名跳转链接(默认主题仓库地址) |
|
||||||
| basic | show_word_count | 文章卡片是否显示字数与阅读时长(需 extra-api)|
|
| basic | show_word_count | 文章卡片是否显示字数与阅读时长(需 extra-api)|
|
||||||
|
| basic | enable_lightbox | 全站 lightgallery 图片灯箱(默认开) |
|
||||||
| featured| enable | 首页是否展示置顶文章 |
|
| featured| enable | 首页是否展示置顶文章 |
|
||||||
| about | avatar / bio / github_url / twitter_url / contact_email | 关于页(自定义模板 `page_about.html`)内容 |
|
| about | avatar / bio / github_url / twitter_url / contact_email | 关于页(自定义模板 `page_about.html`)内容 |
|
||||||
| seo | enable_metadata | 输出 canonical / Open Graph / Twitter Card |
|
| seo | enable_metadata | 输出 canonical / Open Graph / Twitter Card |
|
||||||
@@ -99,6 +103,8 @@ pnpm check # 格式化 + Lint 修复
|
|||||||
| stats | enable | 页脚是否显示全站数据统计(默认关) |
|
| stats | enable | 页脚是否显示全站数据统计(默认关) |
|
||||||
| stats | show_posts / show_categories / show_tags / show_comments / show_visits | 各项统计的显隐开关 |
|
| stats | show_posts / show_categories / show_tags / show_comments / show_visits | 各项统计的显隐开关 |
|
||||||
| stats | show_words | 全站总字数(需 extra-api 插件,默认开) |
|
| stats | show_words | 全站总字数(需 extra-api 插件,默认开) |
|
||||||
|
| photos | preview_default | 相册瀑布流是否默认开启预览模式(默认开) |
|
||||||
|
| photos | album_covers | 各相册手动封面(「相册名称 + 封面图」列表,留空自动取第一张) |
|
||||||
|
|
||||||
## 约定与说明
|
## 约定与说明
|
||||||
|
|
||||||
|
|||||||
@@ -47,3 +47,6 @@ stats.comments={0} comments
|
|||||||
stats.visits={0} visits
|
stats.visits={0} visits
|
||||||
stats.words={0} words
|
stats.words={0} words
|
||||||
douban.plugin_required=Douban plugin (plugin-douban) not detected. Please install and enable it, then set your Douban ID in the plugin settings.
|
douban.plugin_required=Douban plugin (plugin-douban) not detected. Please install and enable it, then set your Douban ID in the plugin settings.
|
||||||
|
photos.photo_count={0} photos
|
||||||
|
moment.back_to_list=Back to moments
|
||||||
|
moment.tags=Tags
|
||||||
|
|||||||
@@ -47,3 +47,6 @@ stats.comments={0} 条评论
|
|||||||
stats.visits={0} 次访问
|
stats.visits={0} 次访问
|
||||||
stats.words={0} 字
|
stats.words={0} 字
|
||||||
douban.plugin_required=未检测到豆瓣插件(plugin-douban),请安装并启用后在插件设置中填写豆瓣 ID。
|
douban.plugin_required=未检测到豆瓣插件(plugin-douban),请安装并启用后在插件设置中填写豆瓣 ID。
|
||||||
|
photos.photo_count={0} 张照片
|
||||||
|
moment.back_to_list=返回瞬间列表
|
||||||
|
moment.tags=标签
|
||||||
|
|||||||
@@ -25,5 +25,8 @@
|
|||||||
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
|
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
|
||||||
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
|
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"lightgallery": "^2.9.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+10
@@ -11,6 +11,10 @@ overrides:
|
|||||||
importers:
|
importers:
|
||||||
|
|
||||||
.:
|
.:
|
||||||
|
dependencies:
|
||||||
|
lightgallery:
|
||||||
|
specifier: ^2.9.0
|
||||||
|
version: 2.9.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@halo-dev/theme-package-cli':
|
'@halo-dev/theme-package-cli':
|
||||||
specifier: ^1.0.1
|
specifier: ^1.0.1
|
||||||
@@ -761,6 +765,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==}
|
resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==}
|
||||||
engines: {node: '>= 0.6.3'}
|
engines: {node: '>= 0.6.3'}
|
||||||
|
|
||||||
|
lightgallery@2.9.0:
|
||||||
|
resolution: {integrity: sha512-58Ud1DyhD2ao58t+kPEqSZrjFxg23tGd5ZKr75erm7q31g5xhUtWUJH3sTUkhHzlyJAKHj5eTrJ37HQRXG4Wbg==}
|
||||||
|
engines: {node: '>=6.0.0'}
|
||||||
|
|
||||||
lightningcss-android-arm64@1.32.0:
|
lightningcss-android-arm64@1.32.0:
|
||||||
resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
|
resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
|
||||||
engines: {node: '>= 12.0.0'}
|
engines: {node: '>= 12.0.0'}
|
||||||
@@ -1629,6 +1637,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
readable-stream: 2.3.8
|
readable-stream: 2.3.8
|
||||||
|
|
||||||
|
lightgallery@2.9.0: {}
|
||||||
|
|
||||||
lightningcss-android-arm64@1.32.0:
|
lightningcss-android-arm64@1.32.0:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
|||||||
@@ -47,6 +47,11 @@ spec:
|
|||||||
label: 文章卡片显示字数
|
label: 文章卡片显示字数
|
||||||
value: true
|
value: true
|
||||||
help: 在文章卡片与置顶文章上展示字数及阅读时长(需 extra-api 插件提供数据)
|
help: 在文章卡片与置顶文章上展示字数及阅读时长(需 extra-api 插件提供数据)
|
||||||
|
- $formkit: checkbox
|
||||||
|
name: enable_lightbox
|
||||||
|
label: 全站图片灯箱
|
||||||
|
value: true
|
||||||
|
help: 使用 lightgallery 为文章、瞬间、图库等页面的图片提供点击放大预览
|
||||||
- group: featured
|
- group: featured
|
||||||
label: 置顶文章
|
label: 置顶文章
|
||||||
formSchema:
|
formSchema:
|
||||||
@@ -153,3 +158,25 @@ spec:
|
|||||||
name: show_words
|
name: show_words
|
||||||
label: 全站总字数(需 extra-api 插件)
|
label: 全站总字数(需 extra-api 插件)
|
||||||
value: true
|
value: true
|
||||||
|
- group: photos
|
||||||
|
label: 图库
|
||||||
|
formSchema:
|
||||||
|
- $formkit: checkbox
|
||||||
|
name: preview_default
|
||||||
|
label: 相册默认开启预览模式
|
||||||
|
value: true
|
||||||
|
help: 进入相册瀑布流时默认开启点击放大预览,访客可在前台自行切换
|
||||||
|
- $formkit: repeater
|
||||||
|
name: album_covers
|
||||||
|
label: 相册封面
|
||||||
|
value: []
|
||||||
|
help: 为相册手动指定封面;不配置时自动使用相册内第一张图片。「相册名称」填写图库页面显示的相册名
|
||||||
|
children:
|
||||||
|
- $formkit: text
|
||||||
|
name: group
|
||||||
|
label: 相册名称
|
||||||
|
validation: required
|
||||||
|
- $formkit: attachment
|
||||||
|
name: cover
|
||||||
|
label: 封面图
|
||||||
|
validation: required
|
||||||
|
|||||||
+122
-10
@@ -80,19 +80,133 @@
|
|||||||
|
|
||||||
<th:block th:if="${pluginFinder.available('plugin-douban')}">
|
<th:block th:if="${pluginFinder.available('plugin-douban')}">
|
||||||
<th:block
|
<th:block
|
||||||
th:with="collections=${{ {'type':'book','status':'done','label':'读过'}, {'type':'book','status':'doing','label':'在读'}, {'type':'movie','status':'done','label':'看过'}, {'type':'drama','status':'doing','label':'在追'} }}"
|
th:with="movies=${doubanFinder.getMovies(24, 0, 'book', 'done')}"
|
||||||
>
|
|
||||||
<th:block th:each="col : ${collections}">
|
|
||||||
<th:block
|
|
||||||
th:with="movies=${doubanFinder.getMovies(24, 0, col.type, col.status)}"
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="douban-section"
|
class="douban-section"
|
||||||
th:if="${not #lists.isEmpty(movies)}"
|
th:if="${not #lists.isEmpty(movies)}"
|
||||||
>
|
>
|
||||||
<h2 class="douban-section-title" th:text="${col.label}">
|
<h2 class="douban-section-title">读过</h2>
|
||||||
读过
|
<div class="douban-grid">
|
||||||
</h2>
|
<a
|
||||||
|
th:each="m : ${movies}"
|
||||||
|
class="douban-card"
|
||||||
|
th:href="${m.spec.link}"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="douban-cover"
|
||||||
|
th:src="${m.spec.poster}"
|
||||||
|
th:alt="${m.spec.name}"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
<p class="douban-name" th:text="${m.spec.name}">名称</p>
|
||||||
|
<p class="douban-meta">
|
||||||
|
<span
|
||||||
|
th:if="${not #strings.isEmpty(m.spec.year)}"
|
||||||
|
th:text="${m.spec.year}"
|
||||||
|
>年份</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
th:if="${not #strings.isEmpty(m.spec.douban_score)}"
|
||||||
|
class="douban-score"
|
||||||
|
th:text="'★ ' + ${m.spec.douban_score}"
|
||||||
|
>评分</span
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
<th:block
|
||||||
|
th:with="movies=${doubanFinder.getMovies(24, 0, 'book', 'doing')}"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="douban-section"
|
||||||
|
th:if="${not #lists.isEmpty(movies)}"
|
||||||
|
>
|
||||||
|
<h2 class="douban-section-title">在读</h2>
|
||||||
|
<div class="douban-grid">
|
||||||
|
<a
|
||||||
|
th:each="m : ${movies}"
|
||||||
|
class="douban-card"
|
||||||
|
th:href="${m.spec.link}"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="douban-cover"
|
||||||
|
th:src="${m.spec.poster}"
|
||||||
|
th:alt="${m.spec.name}"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
<p class="douban-name" th:text="${m.spec.name}">名称</p>
|
||||||
|
<p class="douban-meta">
|
||||||
|
<span
|
||||||
|
th:if="${not #strings.isEmpty(m.spec.year)}"
|
||||||
|
th:text="${m.spec.year}"
|
||||||
|
>年份</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
th:if="${not #strings.isEmpty(m.spec.douban_score)}"
|
||||||
|
class="douban-score"
|
||||||
|
th:text="'★ ' + ${m.spec.douban_score}"
|
||||||
|
>评分</span
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
<th:block
|
||||||
|
th:with="movies=${doubanFinder.getMovies(24, 0, 'movie', 'done')}"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="douban-section"
|
||||||
|
th:if="${not #lists.isEmpty(movies)}"
|
||||||
|
>
|
||||||
|
<h2 class="douban-section-title">看过</h2>
|
||||||
|
<div class="douban-grid">
|
||||||
|
<a
|
||||||
|
th:each="m : ${movies}"
|
||||||
|
class="douban-card"
|
||||||
|
th:href="${m.spec.link}"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="douban-cover"
|
||||||
|
th:src="${m.spec.poster}"
|
||||||
|
th:alt="${m.spec.name}"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
<p class="douban-name" th:text="${m.spec.name}">名称</p>
|
||||||
|
<p class="douban-meta">
|
||||||
|
<span
|
||||||
|
th:if="${not #strings.isEmpty(m.spec.year)}"
|
||||||
|
th:text="${m.spec.year}"
|
||||||
|
>年份</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
th:if="${not #strings.isEmpty(m.spec.douban_score)}"
|
||||||
|
class="douban-score"
|
||||||
|
th:text="'★ ' + ${m.spec.douban_score}"
|
||||||
|
>评分</span
|
||||||
|
>
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
<th:block
|
||||||
|
th:with="movies=${doubanFinder.getMovies(24, 0, 'drama', 'doing')}"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="douban-section"
|
||||||
|
th:if="${not #lists.isEmpty(movies)}"
|
||||||
|
>
|
||||||
|
<h2 class="douban-section-title">在追</h2>
|
||||||
<div class="douban-grid">
|
<div class="douban-grid">
|
||||||
<a
|
<a
|
||||||
th:each="m : ${movies}"
|
th:each="m : ${movies}"
|
||||||
@@ -126,8 +240,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</th:block>
|
</th:block>
|
||||||
</th:block>
|
</th:block>
|
||||||
</th:block>
|
|
||||||
</th:block>
|
|
||||||
|
|
||||||
<p
|
<p
|
||||||
class="douban-empty"
|
class="douban-empty"
|
||||||
|
|||||||
@@ -0,0 +1,237 @@
|
|||||||
|
<include src="partials/layout.html">
|
||||||
|
<template name="head">
|
||||||
|
<title th:text="|${title} - ${site.title}|">记得</title>
|
||||||
|
<style>
|
||||||
|
.dates-intro {
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
.dates-section {
|
||||||
|
margin: 2rem 0;
|
||||||
|
}
|
||||||
|
.dates-section-title {
|
||||||
|
font-size: 1.15rem;
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
.dates-reminders {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(min(15rem, 100%), 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
.dates-reminder {
|
||||||
|
padding: 1rem 1.25rem;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-left: 3px solid var(--color-primary);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
}
|
||||||
|
.dates-reminder-name {
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.dates-reminder-text {
|
||||||
|
margin: 0.35rem 0 0;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
.dates-list {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.dates-item {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem 0.75rem;
|
||||||
|
padding: 0.9rem 0;
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
.dates-item-name {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.dates-item-date {
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
.dates-badge {
|
||||||
|
margin-left: auto;
|
||||||
|
padding: 0.15rem 0.6rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 0.78rem;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.dates-badge.soon {
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
.dates-badge.today {
|
||||||
|
background: var(--color-primary);
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
color: var(--color-bg);
|
||||||
|
}
|
||||||
|
.dates-badge.important {
|
||||||
|
border-color: #e5a50a;
|
||||||
|
color: #e5a50a;
|
||||||
|
}
|
||||||
|
.dates-persons {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.35rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
}
|
||||||
|
.people-list {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(min(14rem, 100%), 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
.people-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.9rem 1rem;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
}
|
||||||
|
.people-item img {
|
||||||
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.people-name {
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
.people-meta {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
}
|
||||||
|
.dates-empty {
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
padding: 1rem 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<section class="important-dates">
|
||||||
|
<h1 th:text="${title}">记得</h1>
|
||||||
|
<p class="dates-intro">重要的日子,值得被记住。</p>
|
||||||
|
|
||||||
|
<!-- 即将到来的提醒 -->
|
||||||
|
<th:block th:if="${not #lists.isEmpty(reminders)}">
|
||||||
|
<div class="dates-section">
|
||||||
|
<h2 class="dates-section-title">即将到来</h2>
|
||||||
|
<div class="dates-reminders">
|
||||||
|
<div class="dates-reminder" th:each="r : ${reminders}">
|
||||||
|
<p class="dates-reminder-name" th:text="${r.title}">名称</p>
|
||||||
|
<p class="dates-reminder-text">
|
||||||
|
<span
|
||||||
|
th:if="${not #strings.isEmpty(r.dateText)}"
|
||||||
|
th:text="${r.dateText}"
|
||||||
|
></span>
|
||||||
|
<span
|
||||||
|
th:if="${not #strings.isEmpty(r.stageText)}"
|
||||||
|
th:text="${r.stageText}"
|
||||||
|
></span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
|
||||||
|
<!-- 全部日期 -->
|
||||||
|
<div class="dates-section">
|
||||||
|
<h2 class="dates-section-title">全部日期</h2>
|
||||||
|
<ul class="dates-list" th:if="${not #lists.isEmpty(dates)}">
|
||||||
|
<li class="dates-item" th:each="d : ${dates}">
|
||||||
|
<span class="dates-item-name" th:text="${d.title}">名称</span>
|
||||||
|
<span
|
||||||
|
class="dates-badge important"
|
||||||
|
th:if="${showImportantTag and d.important}"
|
||||||
|
>重要</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="dates-item-date"
|
||||||
|
th:if="${not #strings.isEmpty(d.dateText)}"
|
||||||
|
th:text="${d.dateText}"
|
||||||
|
></span>
|
||||||
|
<span
|
||||||
|
class="dates-item-date"
|
||||||
|
th:if="${d.daysValid and not #strings.isEmpty(d.nextSolarDate)}"
|
||||||
|
th:text="${d.nextSolarDate}"
|
||||||
|
></span>
|
||||||
|
<span
|
||||||
|
class="dates-persons"
|
||||||
|
th:if="${not #lists.isEmpty(d.personNames)}"
|
||||||
|
>
|
||||||
|
<span th:each="pn : ${d.personNames}" th:text="'@' + ${pn}"></span>
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="dates-badge today"
|
||||||
|
th:if="${d.daysValid and d.daysUntil == 0}"
|
||||||
|
>今天</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="dates-badge soon"
|
||||||
|
th:if="${d.daysValid and d.daysUntil > 0}"
|
||||||
|
th:text="|还有 ${d.daysUntil} 天|"
|
||||||
|
>还有 N 天</span
|
||||||
|
>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p class="dates-empty" th:if="${#lists.isEmpty(dates)}">暂无重要日期</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 人员与生日 -->
|
||||||
|
<th:block th:if="${not #lists.isEmpty(people)}">
|
||||||
|
<div class="dates-section">
|
||||||
|
<h2 class="dates-section-title">人员</h2>
|
||||||
|
<ul class="people-list">
|
||||||
|
<li class="people-item" th:each="p : ${people}">
|
||||||
|
<img
|
||||||
|
th:if="${showAvatar and not #strings.isEmpty(p.avatar)}"
|
||||||
|
th:src="${p.avatar}"
|
||||||
|
th:alt="${p.displayName}"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
<span class="people-name" th:text="${p.displayName}">姓名</span>
|
||||||
|
<p class="people-meta">
|
||||||
|
<span
|
||||||
|
th:if="${not #strings.isEmpty(p.relation)}"
|
||||||
|
th:text="${p.relation}"
|
||||||
|
></span>
|
||||||
|
<span
|
||||||
|
th:if="${not #strings.isEmpty(p.nextSolarDate) and p.daysUntil == 0}"
|
||||||
|
th:text="'生日 · 今天'"
|
||||||
|
></span>
|
||||||
|
<span
|
||||||
|
th:if="${not #strings.isEmpty(p.nextSolarDate) and p.daysUntil > 0}"
|
||||||
|
th:text="|生日还有 ${p.daysUntil} 天|"
|
||||||
|
></span>
|
||||||
|
<span
|
||||||
|
th:if="${not #strings.isEmpty(p.nextSolarDate)}"
|
||||||
|
th:text="${p.nextSolarDate}"
|
||||||
|
></span>
|
||||||
|
</p>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</include>
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* whitespace 留白主题 - lightgallery 灯箱
|
||||||
|
* 动态模式:扫描带 data-lightbox 属性的容器内的图片,点击放大预览。
|
||||||
|
*/
|
||||||
|
import lightGallery from "lightgallery";
|
||||||
|
import lgZoom from "lightgallery/plugins/zoom";
|
||||||
|
import "lightgallery/css/lightgallery.css";
|
||||||
|
import "lightgallery/css/lg-zoom.css";
|
||||||
|
|
||||||
|
export function initLightbox(): void {
|
||||||
|
const containers = document.querySelectorAll<HTMLElement>("[data-lightbox]");
|
||||||
|
if (!containers.length) return;
|
||||||
|
|
||||||
|
containers.forEach((container) => {
|
||||||
|
const images = Array.from(container.querySelectorAll("img"));
|
||||||
|
if (!images.length) return;
|
||||||
|
|
||||||
|
const items = images
|
||||||
|
.map((img) => {
|
||||||
|
const src = img.getAttribute("data-full") || img.currentSrc || img.src;
|
||||||
|
if (!src) return null;
|
||||||
|
return { src, thumb: src, alt: img.alt || "" };
|
||||||
|
})
|
||||||
|
.filter((item): item is { src: string; thumb: string; alt: string } =>
|
||||||
|
Boolean(item),
|
||||||
|
);
|
||||||
|
if (!items.length) return;
|
||||||
|
|
||||||
|
// 挂载到 body,避免父级 stacking context 遮挡灯箱
|
||||||
|
const gallery = lightGallery(document.body, {
|
||||||
|
dynamic: true,
|
||||||
|
dynamicEl: items,
|
||||||
|
plugins: [lgZoom],
|
||||||
|
speed: 400,
|
||||||
|
});
|
||||||
|
|
||||||
|
images.forEach((img, index) => {
|
||||||
|
img.style.cursor = "zoom-in";
|
||||||
|
img.addEventListener("click", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
gallery.openGallery(index);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -252,6 +252,20 @@ function initKeyboardShortcuts(): void {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// lightgallery 灯箱(按需加载)
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
async function initLightbox(): Promise<void> {
|
||||||
|
if (!document.querySelector("[data-lightbox]")) return;
|
||||||
|
try {
|
||||||
|
const { initLightbox } = await import("./lightbox");
|
||||||
|
initLightbox();
|
||||||
|
} catch {
|
||||||
|
/* 加载失败时静默降级为普通图片展示 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// 初始化
|
// 初始化
|
||||||
// ========================================
|
// ========================================
|
||||||
@@ -270,6 +284,7 @@ function init(): void {
|
|||||||
initSmoothScroll();
|
initSmoothScroll();
|
||||||
initExternalLinks();
|
initExternalLinks();
|
||||||
initKeyboardShortcuts();
|
initKeyboardShortcuts();
|
||||||
|
void initLightbox();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.readyState === "loading") {
|
if (document.readyState === "loading") {
|
||||||
|
|||||||
+108
-6
@@ -26,12 +26,15 @@
|
|||||||
}
|
}
|
||||||
.links-list li {
|
.links-list li {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
.links-list a {
|
.links-list a {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
background: var(--color-bg-secondary);
|
background: var(--color-bg-secondary);
|
||||||
@@ -48,6 +51,11 @@
|
|||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
.links-list .links-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
.links-list .links-name {
|
.links-list .links-name {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
@@ -55,6 +63,19 @@
|
|||||||
margin: 0.25rem 0 0;
|
margin: 0.25rem 0 0;
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.links-group-title {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
margin: 2rem 0 1rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
.links-section:first-of-type .links-group-title {
|
||||||
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
.links-apply {
|
.links-apply {
|
||||||
margin-top: 2rem;
|
margin-top: 2rem;
|
||||||
@@ -138,16 +159,28 @@
|
|||||||
></a>
|
></a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<ul th:if="${not #lists.isEmpty(links)}" class="links-list">
|
<th:block th:if="${group == null}">
|
||||||
<li th:each="link : ${links}">
|
<section class="links-section" th:each="grp : ${simpleGroups}">
|
||||||
<a th:href="@{${link.spec.url}}" target="_blank" rel="noopener noreferrer">
|
<h2 class="links-group-title" th:text="${grp.spec.displayName}">
|
||||||
|
分组
|
||||||
|
</h2>
|
||||||
|
<ul class="links-list">
|
||||||
|
<li
|
||||||
|
th:each="link : ${links}"
|
||||||
|
th:if="${link.spec.groupName == grp.metadata.name}"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
th:href="@{${link.spec.url}}"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
<img
|
<img
|
||||||
th:if="${not #strings.isEmpty(link.spec.logo)}"
|
th:if="${not #strings.isEmpty(link.spec.logo)}"
|
||||||
th:src="@{${link.spec.logo}}"
|
th:src="@{${link.spec.logo}}"
|
||||||
alt=""
|
alt=""
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
<span>
|
<span class="links-body">
|
||||||
<span class="links-name" th:text="${link.spec.displayName}"
|
<span class="links-name" th:text="${link.spec.displayName}"
|
||||||
>链接名称</span
|
>链接名称</span
|
||||||
>
|
>
|
||||||
@@ -160,7 +193,69 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p th:unless="${not #lists.isEmpty(links)}">暂无链接</p>
|
</section>
|
||||||
|
<section class="links-section">
|
||||||
|
<h2 class="links-group-title">未分组</h2>
|
||||||
|
<ul class="links-list">
|
||||||
|
<li
|
||||||
|
th:each="link : ${links}"
|
||||||
|
th:if="${#strings.isEmpty(link.spec.groupName)}"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
th:href="@{${link.spec.url}}"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
th:if="${not #strings.isEmpty(link.spec.logo)}"
|
||||||
|
th:src="@{${link.spec.logo}}"
|
||||||
|
alt=""
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
<span class="links-body">
|
||||||
|
<span class="links-name" th:text="${link.spec.displayName}"
|
||||||
|
>链接名称</span
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="links-desc"
|
||||||
|
th:if="${not #strings.isEmpty(link.spec.description)}"
|
||||||
|
th:text="${link.spec.description}"
|
||||||
|
></p>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</th:block>
|
||||||
|
<th:block th:unless="${group == null}">
|
||||||
|
<ul th:if="${not #lists.isEmpty(links)}" class="links-list">
|
||||||
|
<li th:each="link : ${links}">
|
||||||
|
<a
|
||||||
|
th:href="@{${link.spec.url}}"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
th:if="${not #strings.isEmpty(link.spec.logo)}"
|
||||||
|
th:src="@{${link.spec.logo}}"
|
||||||
|
alt=""
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
<span class="links-body">
|
||||||
|
<span class="links-name" th:text="${link.spec.displayName}"
|
||||||
|
>链接名称</span
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
class="links-desc"
|
||||||
|
th:if="${not #strings.isEmpty(link.spec.description)}"
|
||||||
|
th:text="${link.spec.description}"
|
||||||
|
></p>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p th:if="${#lists.isEmpty(links)}">暂无链接</p>
|
||||||
|
</th:block>
|
||||||
|
|
||||||
<div class="links-apply">
|
<div class="links-apply">
|
||||||
<button
|
<button
|
||||||
@@ -221,6 +316,13 @@
|
|||||||
|
|
||||||
<script th:inline="none">
|
<script th:inline="none">
|
||||||
(function () {
|
(function () {
|
||||||
|
// 收起没有链接的分组区块
|
||||||
|
document.querySelectorAll(".links-section").forEach(function (sec) {
|
||||||
|
if (!sec.querySelector(".links-list li")) {
|
||||||
|
sec.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
var API = "/apis/api.link.halo.run/v1alpha1/link-applications";
|
var API = "/apis/api.link.halo.run/v1alpha1/link-applications";
|
||||||
var CAPTCHA = "/apis/api.link.halo.run/v1alpha1/link-applications/captcha";
|
var CAPTCHA = "/apis/api.link.halo.run/v1alpha1/link-applications/captcha";
|
||||||
var toggle = document.getElementById("link-apply-toggle");
|
var toggle = document.getElementById("link-apply-toggle");
|
||||||
|
|||||||
+185
@@ -0,0 +1,185 @@
|
|||||||
|
<include src="partials/layout.html">
|
||||||
|
<template name="head">
|
||||||
|
<title th:text="|${title} - ${site.title}|">瞬间</title>
|
||||||
|
<style>
|
||||||
|
.moment-detail {
|
||||||
|
max-width: 46rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.moment-back {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
color: var(--color-link);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
.moment-back:hover {
|
||||||
|
color: var(--color-link-hover);
|
||||||
|
}
|
||||||
|
.moment-card {
|
||||||
|
padding: 1.5rem 0;
|
||||||
|
}
|
||||||
|
.moment-card header {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
.moment-owner {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
}
|
||||||
|
.moment-owner img {
|
||||||
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
.moment-owner-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
.moment-owner-name {
|
||||||
|
color: var(--color-text);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.moment-owner-bio {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
}
|
||||||
|
.moment-meta {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
.moment-meta a {
|
||||||
|
color: var(--color-link);
|
||||||
|
}
|
||||||
|
.moment-tags {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
.moment-tags a {
|
||||||
|
color: var(--color-link);
|
||||||
|
}
|
||||||
|
.moment-media img,
|
||||||
|
.moment-media video {
|
||||||
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
margin-top: 1rem;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
}
|
||||||
|
.moment-media audio {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
.comments-section {
|
||||||
|
margin-top: 2rem;
|
||||||
|
padding-top: 1.5rem;
|
||||||
|
border-top: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<section class="moment-detail" th:if="${moment != null}">
|
||||||
|
<a class="moment-back" th:href="@{/moments}" th:text="#{moment.back_to_list}"
|
||||||
|
>返回瞬间列表</a
|
||||||
|
>
|
||||||
|
|
||||||
|
<article class="moment-card" th:with="content=${moment.spec.content}">
|
||||||
|
<header>
|
||||||
|
<span class="moment-owner" th:if="${moment.owner != null}">
|
||||||
|
<img
|
||||||
|
th:if="${not #strings.isEmpty(moment.owner.avatar)}"
|
||||||
|
th:src="${moment.owner.avatar}"
|
||||||
|
th:alt="${moment.owner.displayName}"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
<span class="moment-owner-info">
|
||||||
|
<span
|
||||||
|
class="moment-owner-name"
|
||||||
|
th:text="${moment.owner.displayName}"
|
||||||
|
>用户名</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="moment-owner-bio"
|
||||||
|
th:if="${not #strings.isEmpty(moment.owner.bio)}"
|
||||||
|
th:text="${moment.owner.bio}"
|
||||||
|
></span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="moment-meta">
|
||||||
|
<time
|
||||||
|
th:if="${moment.spec.releaseTime != null}"
|
||||||
|
th:datetime="${moment.spec.releaseTime}"
|
||||||
|
th:text="${#temporals.format(moment.spec.releaseTime, 'yyyy-MM-dd HH:mm')}"
|
||||||
|
>发布时间</time
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="moment-tags"
|
||||||
|
th:if="${not #lists.isEmpty(moment.spec.tags)}"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
th:each="tag : ${moment.spec.tags}"
|
||||||
|
th:href="@{/moments(tag=${tag})}"
|
||||||
|
th:text="'#' + ${tag}"
|
||||||
|
></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="article-content"
|
||||||
|
th:if="${not #strings.isEmpty(content.html)}"
|
||||||
|
th:attr="data-lightbox=${theme.config?.basic?.enable_lightbox != false ? 'true' : null}"
|
||||||
|
th:utext="${content.html}"
|
||||||
|
></div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="moment-media"
|
||||||
|
th:attr="data-lightbox=${theme.config?.basic?.enable_lightbox != false ? 'true' : null}"
|
||||||
|
th:if="${not #lists.isEmpty(content.medium)}"
|
||||||
|
>
|
||||||
|
<th:block th:each="medium : ${content.medium}">
|
||||||
|
<img
|
||||||
|
th:if="${medium.type.name == 'PHOTO'}"
|
||||||
|
th:src="${medium.url}"
|
||||||
|
alt="瞬间图片"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
<video
|
||||||
|
th:if="${medium.type.name == 'VIDEO'}"
|
||||||
|
th:src="${medium.url}"
|
||||||
|
controls
|
||||||
|
></video>
|
||||||
|
<audio
|
||||||
|
th:if="${medium.type.name == 'AUDIO'}"
|
||||||
|
th:src="${medium.url}"
|
||||||
|
controls
|
||||||
|
></audio>
|
||||||
|
</th:block>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<div class="comments-section" th:if="${haloCommentEnabled}">
|
||||||
|
<halo:comment
|
||||||
|
group="moment.halo.run"
|
||||||
|
kind="Moment"
|
||||||
|
th:attr="name=${moment.metadata.name}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<p th:if="${moment == null}">瞬间不存在或不可见。</p>
|
||||||
|
</div>
|
||||||
|
</include>
|
||||||
@@ -26,11 +26,17 @@
|
|||||||
.moments article header {
|
.moments article header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
opacity: 0.75;
|
opacity: 0.75;
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
}
|
}
|
||||||
|
.moments article header > a {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 2rem;
|
||||||
|
}
|
||||||
.moments .moment-owner {
|
.moments .moment-owner {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -42,6 +48,16 @@
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
.moments .moment-tags {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
.moments .moment-tags a {
|
||||||
|
color: var(--color-link);
|
||||||
|
}
|
||||||
.moments .moment-media img,
|
.moments .moment-media img,
|
||||||
.moments .moment-media video {
|
.moments .moment-media video {
|
||||||
display: block;
|
display: block;
|
||||||
@@ -102,13 +118,26 @@
|
|||||||
</th:block>
|
</th:block>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="moment-tags"
|
||||||
|
th:if="${not #lists.isEmpty(moment.spec.tags)}"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
th:each="tag : ${moment.spec.tags}"
|
||||||
|
th:href="@{/moments(tag=${tag})}"
|
||||||
|
th:text="'#' + ${tag}"
|
||||||
|
></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
th:if="${not #strings.isEmpty(content.html)}"
|
th:if="${not #strings.isEmpty(content.html)}"
|
||||||
|
th:attr="data-lightbox=${theme.config?.basic?.enable_lightbox != false ? 'true' : null}"
|
||||||
th:utext="${content.html}"
|
th:utext="${content.html}"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="moment-media"
|
class="moment-media"
|
||||||
|
th:attr="data-lightbox=${theme.config?.basic?.enable_lightbox != false ? 'true' : null}"
|
||||||
th:if="${not #lists.isEmpty(content.medium)}"
|
th:if="${not #lists.isEmpty(content.medium)}"
|
||||||
>
|
>
|
||||||
<th:block th:each="medium : ${content.medium}">
|
<th:block th:each="medium : ${content.medium}">
|
||||||
|
|||||||
+5
-1
@@ -26,7 +26,11 @@
|
|||||||
<img th:src="${singlePage.spec.cover}" th:alt="${singlePage.spec.title}" />
|
<img th:src="${singlePage.spec.cover}" th:alt="${singlePage.spec.title}" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="article-content" th:utext="${singlePage.content.content}"></div>
|
<div
|
||||||
|
class="article-content"
|
||||||
|
th:attr="data-lightbox=${theme.config?.basic?.enable_lightbox != false ? 'true' : null}"
|
||||||
|
th:utext="${singlePage.content.content}"
|
||||||
|
></div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
<!-- 评论区 -->
|
<!-- 评论区 -->
|
||||||
|
|||||||
+234
-55
@@ -2,6 +2,10 @@
|
|||||||
<template name="head">
|
<template name="head">
|
||||||
<title th:text="|${title} - ${site.title}|">图库</title>
|
<title th:text="|${title} - ${site.title}|">图库</title>
|
||||||
<style>
|
<style>
|
||||||
|
/* 图库页面适当加宽 */
|
||||||
|
.container {
|
||||||
|
max-width: 75rem;
|
||||||
|
}
|
||||||
:where(.photos-groups) {
|
:where(.photos-groups) {
|
||||||
margin: 1.5rem 0;
|
margin: 1.5rem 0;
|
||||||
}
|
}
|
||||||
@@ -27,30 +31,121 @@
|
|||||||
:where(.photos-group.is-active .photos-group__link) {
|
:where(.photos-group.is-active .photos-group__link) {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
:where(.photos-grid) {
|
|
||||||
|
/* 相册网格 */
|
||||||
|
:where(.albums-grid) {
|
||||||
|
list-style: none;
|
||||||
|
margin: 1.5rem 0 0;
|
||||||
|
padding: 0;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(min(16rem, 100%), 1fr));
|
grid-template-columns: repeat(
|
||||||
gap: 1.25rem;
|
auto-fill,
|
||||||
|
minmax(min(16rem, 100%), 1fr)
|
||||||
|
);
|
||||||
|
gap: 1.5rem;
|
||||||
}
|
}
|
||||||
:where(.photos-card__image) {
|
:where(.albums-card) {
|
||||||
display: block;
|
display: block;
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
:where(.albums-card:hover .albums-card__name) {
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
:where(.albums-card__cover) {
|
||||||
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
aspect-ratio: 4 / 3;
|
aspect-ratio: 4 / 3;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
:where(.albums-card__cover img) {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
:where(.albums-card__cover img.album-cover-custom) {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
:where(.albums-card__name) {
|
||||||
|
margin: 0.75rem 0 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
:where(.albums-card__count) {
|
||||||
|
margin: 0.25rem 0 0;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 瀑布流(一行三张) */
|
||||||
|
:where(.photos-masonry) {
|
||||||
|
list-style: none;
|
||||||
|
margin: 1.5rem 0 0;
|
||||||
|
padding: 0;
|
||||||
|
columns: 3;
|
||||||
|
column-gap: 1rem;
|
||||||
|
}
|
||||||
|
:where(.photos-masonry > li) {
|
||||||
|
break-inside: avoid;
|
||||||
|
margin: 0 0 1rem;
|
||||||
|
}
|
||||||
|
:where(.photos-masonry .photos-card__media) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
:where(.photos-masonry img) {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
background: var(--color-bg-secondary);
|
background: var(--color-bg-secondary);
|
||||||
}
|
}
|
||||||
:where(.photos-card__title) {
|
:where(.photos-masonry .photos-card__caption) {
|
||||||
margin: 0.75rem 0 0.25rem;
|
margin: 0.4rem 0 0;
|
||||||
font-size: 1rem;
|
font-size: 0.85rem;
|
||||||
}
|
|
||||||
:where(.photos-card__description, .photos-card__tags) {
|
|
||||||
margin-top: 0.5rem;
|
|
||||||
}
|
|
||||||
:where(.photos-card__tags) {
|
|
||||||
gap: 0.5rem;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
:where(.photos-masonry) {
|
||||||
|
columns: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 560px) {
|
||||||
|
:where(.photos-masonry) {
|
||||||
|
columns: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 预览模式切换 */
|
||||||
|
:where(.photos-toolbar) {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
:where(.photos-preview-toggle) {
|
||||||
|
padding: 0.4rem 0.9rem;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
color: var(--color-text);
|
||||||
|
font: inherit;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
:where(.photos-preview-toggle)[aria-pressed="true"] {
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
:where(.photos-empty) {
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
padding: 2rem 0;
|
||||||
}
|
}
|
||||||
:where(.photos-pagination) {
|
:where(.photos-pagination) {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -63,10 +158,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<section
|
<section class="photos-page">
|
||||||
class="photos-page"
|
|
||||||
th:attr="data-template-id=${_templateId},data-group=${group},data-page=${photos.page},data-size=${photos.size},data-total=${photos.total}"
|
|
||||||
>
|
|
||||||
<header class="photos-header">
|
<header class="photos-header">
|
||||||
<h1 class="photos-header__title" th:text="${title}">图库</h1>
|
<h1 class="photos-header__title" th:text="${title}">图库</h1>
|
||||||
</header>
|
</header>
|
||||||
@@ -81,14 +173,13 @@
|
|||||||
class="photos-group__link"
|
class="photos-group__link"
|
||||||
th:href="${photoUrl.list()}"
|
th:href="${photoUrl.list()}"
|
||||||
th:attr="aria-current=${#strings.isEmpty(group) ? 'page' : null}"
|
th:attr="aria-current=${#strings.isEmpty(group) ? 'page' : null}"
|
||||||
>全部</a
|
>全部相册</a
|
||||||
>
|
>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
class="photos-group"
|
class="photos-group"
|
||||||
th:each="photoGroup : ${groups}"
|
th:each="photoGroup : ${groups}"
|
||||||
th:classappend="${group == photoGroup.metadata.name} ? 'is-active' : ''"
|
th:classappend="${group == photoGroup.metadata.name} ? 'is-active' : ''"
|
||||||
th:attr="data-group=${photoGroup.metadata.name}"
|
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
class="photos-group__link"
|
class="photos-group__link"
|
||||||
@@ -98,11 +189,11 @@
|
|||||||
<span
|
<span
|
||||||
class="photos-group__name"
|
class="photos-group__name"
|
||||||
th:text="${photoGroup.spec.displayName}"
|
th:text="${photoGroup.spec.displayName}"
|
||||||
>分组</span
|
>相册</span
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="photos-group__count"
|
class="photos-group__count"
|
||||||
th:if="${photoGroup.status != null && photoGroup.status.photoCount != null}"
|
th:if="${photoGroup.status != null and photoGroup.status.photoCount != null}"
|
||||||
th:text="|(${photoGroup.status.photoCount})|"
|
th:text="|(${photoGroup.status.photoCount})|"
|
||||||
></span>
|
></span>
|
||||||
</a>
|
</a>
|
||||||
@@ -110,48 +201,94 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<ul class="photos-grid" th:if="${!#lists.isEmpty(photos.items)}">
|
<!-- 相册视图:按分组展示各相册 -->
|
||||||
<li
|
<th:block th:if="${#strings.isEmpty(group)}">
|
||||||
class="photos-grid__item"
|
<th:block th:with="albums=${photoFinder.groupBy()}">
|
||||||
th:each="photo : ${photos.items}"
|
<ul class="albums-grid" th:if="${not #lists.isEmpty(albums)}">
|
||||||
th:attr="data-photo-name=${photo.metadata.name},data-group=${photo.spec.groupName},data-priority=${photo.spec.priority}"
|
<li th:each="album : ${albums}">
|
||||||
|
<a
|
||||||
|
class="albums-card"
|
||||||
|
th:href="${photoUrl.list(album.metadata.name)}"
|
||||||
|
>
|
||||||
|
<span class="albums-card__cover">
|
||||||
|
<img
|
||||||
|
th:if="${not #lists.isEmpty(album.photos)}"
|
||||||
|
th:src="${album.photos[0].spec.cover != null and !#strings.isEmpty(album.photos[0].spec.cover) ? album.photos[0].spec.cover : album.photos[0].spec.url}"
|
||||||
|
th:alt="${album.spec.displayName}"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
<th:block
|
||||||
|
th:if="${not #lists.isEmpty(theme.config?.photos?.album_covers)}"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="album-cover-custom"
|
||||||
|
th:each="cv : ${theme.config.photos.album_covers}"
|
||||||
|
th:if="${cv != null and cv.group == album.spec.displayName and not #strings.isEmpty(cv.cover)}"
|
||||||
|
th:src="${cv.cover}"
|
||||||
|
th:alt="${album.spec.displayName}"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
</th:block>
|
||||||
|
</span>
|
||||||
|
<p
|
||||||
|
class="albums-card__name"
|
||||||
|
th:text="${album.spec.displayName}"
|
||||||
|
>
|
||||||
|
相册
|
||||||
|
</p>
|
||||||
|
<p
|
||||||
|
class="albums-card__count"
|
||||||
|
th:if="${album.status != null and album.status.photoCount != null}"
|
||||||
|
th:text="#{photos.photo_count(${album.status.photoCount})}"
|
||||||
|
>
|
||||||
|
12 张照片
|
||||||
|
</p>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p class="photos-empty" th:if="${#lists.isEmpty(albums)}" role="status">
|
||||||
|
暂无相册
|
||||||
|
</p>
|
||||||
|
</th:block>
|
||||||
|
</th:block>
|
||||||
|
|
||||||
|
<!-- 相册内瀑布流视图 -->
|
||||||
|
<th:block th:unless="${#strings.isEmpty(group)}">
|
||||||
|
<div class="photos-toolbar" th:if="${not #lists.isEmpty(photos.items)}">
|
||||||
|
<button
|
||||||
|
class="photos-preview-toggle"
|
||||||
|
id="photos-preview-toggle"
|
||||||
|
type="button"
|
||||||
|
th:attr="data-default=${theme.config?.photos?.preview_default != false ? 'on' : 'off'}"
|
||||||
|
>
|
||||||
|
预览模式
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul
|
||||||
|
class="photos-masonry"
|
||||||
|
th:if="${not #lists.isEmpty(photos.items)}"
|
||||||
|
th:attr="data-lightbox=${theme.config?.basic?.enable_lightbox != false ? 'true' : null}"
|
||||||
|
>
|
||||||
|
<li th:each="photo : ${photos.items}">
|
||||||
|
<a
|
||||||
|
class="photos-card__media"
|
||||||
|
th:href="${photoUrl.detail(photo)}"
|
||||||
|
th:title="${photo.spec.displayName}"
|
||||||
>
|
>
|
||||||
<article class="photos-card">
|
|
||||||
<a class="photos-card__media" th:href="${photoUrl.detail(photo)}">
|
|
||||||
<img
|
<img
|
||||||
class="photos-card__image"
|
|
||||||
th:src="${#strings.isEmpty(photo.spec.cover) ? photo.spec.url : photo.spec.cover}"
|
th:src="${#strings.isEmpty(photo.spec.cover) ? photo.spec.url : photo.spec.cover}"
|
||||||
th:alt="${photo.spec.displayName}"
|
th:alt="${photo.spec.displayName}"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
</a>
|
</a>
|
||||||
<div class="photos-card__content">
|
|
||||||
<h2 class="photos-card__title">
|
|
||||||
<a
|
|
||||||
class="photos-card__title-link"
|
|
||||||
th:href="${photoUrl.detail(photo)}"
|
|
||||||
th:text="${photo.spec.displayName}"
|
|
||||||
>照片</a
|
|
||||||
>
|
|
||||||
</h2>
|
|
||||||
<p
|
<p
|
||||||
class="photos-card__description"
|
class="photos-card__caption"
|
||||||
th:if="${!#strings.isEmpty(photo.spec.description)}"
|
th:if="${not #strings.isEmpty(photo.spec.displayName)}"
|
||||||
th:text="${photo.spec.description}"
|
th:text="${photo.spec.displayName}"
|
||||||
></p>
|
|
||||||
<ul
|
|
||||||
class="photos-card__tags"
|
|
||||||
th:if="${!#lists.isEmpty(photo.spec.tags)}"
|
|
||||||
aria-label="照片标签"
|
|
||||||
>
|
>
|
||||||
<li
|
照片
|
||||||
class="photos-card__tag"
|
</p>
|
||||||
th:each="tag : ${photo.spec.tags}"
|
|
||||||
th:text="${tag}"
|
|
||||||
></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p
|
<p
|
||||||
@@ -184,6 +321,48 @@
|
|||||||
>下一页</a
|
>下一页</a
|
||||||
>
|
>
|
||||||
</nav>
|
</nav>
|
||||||
|
</th:block>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script th:inline="none">
|
||||||
|
(function () {
|
||||||
|
var btn = document.getElementById("photos-preview-toggle");
|
||||||
|
var masonry = document.querySelector(".photos-masonry");
|
||||||
|
if (!btn || !masonry) return;
|
||||||
|
|
||||||
|
var KEY = "whitespace-photo-preview";
|
||||||
|
var defaultOn = btn.getAttribute("data-default") === "on";
|
||||||
|
var stored = null;
|
||||||
|
try {
|
||||||
|
stored = localStorage.getItem(KEY);
|
||||||
|
} catch (e) {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
var mode = stored === null ? defaultOn : stored === "on";
|
||||||
|
|
||||||
|
function apply() {
|
||||||
|
if (mode) {
|
||||||
|
masonry.setAttribute("data-lightbox", "true");
|
||||||
|
} else {
|
||||||
|
masonry.removeAttribute("data-lightbox");
|
||||||
|
}
|
||||||
|
btn.textContent = mode ? "预览模式:开" : "预览模式:关";
|
||||||
|
btn.setAttribute("aria-pressed", mode ? "true" : "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
btn.addEventListener("click", function () {
|
||||||
|
mode = !mode;
|
||||||
|
try {
|
||||||
|
localStorage.setItem(KEY, mode ? "on" : "off");
|
||||||
|
} catch (e) {
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
// 重新加载以按新模式初始化灯箱
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
apply();
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
</include>
|
</include>
|
||||||
|
|||||||
+5
-1
@@ -67,7 +67,11 @@
|
|||||||
<img th:src="${post.spec.cover}" th:alt="${post.spec.title}" />
|
<img th:src="${post.spec.cover}" th:alt="${post.spec.title}" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="article-content" th:utext="${post.content.content}"></div>
|
<div
|
||||||
|
class="article-content"
|
||||||
|
th:attr="data-lightbox=${theme.config?.basic?.enable_lightbox != false ? 'true' : null}"
|
||||||
|
th:utext="${post.content.content}"
|
||||||
|
></div>
|
||||||
|
|
||||||
<!-- 文章标签 -->
|
<!-- 文章标签 -->
|
||||||
<div class="article-tags" th:if="${not #lists.isEmpty(post.tags)}">
|
<div class="article-tags" th:if="${not #lists.isEmpty(post.tags)}">
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@ spec:
|
|||||||
issues: https://git.nxxy335.top/nxxy335/halo-theme-whitespace/issues
|
issues: https://git.nxxy335.top/nxxy335/halo-theme-whitespace/issues
|
||||||
settingName: "whitespace-setting"
|
settingName: "whitespace-setting"
|
||||||
configMapName: "whitespace-configMap"
|
configMapName: "whitespace-configMap"
|
||||||
version: 0.1.0
|
version: 0.2.0
|
||||||
requires: ">=2.26.0"
|
requires: ">=2.26.0"
|
||||||
license:
|
license:
|
||||||
- name: "GPL-3.0"
|
- name: "GPL-3.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user