21 Commits

Author SHA1 Message Date
anian 4b70da9ac3 优化界面;修复异常 2026-07-29 00:17:31 +08:00
anian d575d584cb 新增奇遇界面;修复异常 2026-07-26 21:52:40 +08:00
anian 03e5e2f529 重构课外书;修复异常;优化体验 2026-07-26 06:55:48 +08:00
anian 711cff953b 新增页码跳转;修复异常;优化效果 2026-07-25 15:34:40 +08:00
anian 7d56858616 优化导航栏、分享海报等显示效果;优化代码及性能 2026-07-25 13:33:51 +08:00
anian 32bf39f733 优化阅读体验、前台编辑、播放器等 2026-07-22 01:54:46 +08:00
anian 041fd0f7cf 更换系统图片 2026-07-20 01:13:14 +08:00
anian 6675e378d2 修复搜索相关 2026-07-19 01:51:01 +08:00
anian 1953fa86c8 重写目录样式;适配新域名 2026-07-18 00:48:46 +08:00
anian 048e4336d1 适配新接口、插件;更新附录、通知书、课外书;修复、优化部分功能 2026-06-15 20:32:43 +08:00
anian 92d591f70b 适配新接口 2026-05-09 09:45:17 +08:00
anian 061b00fc05 重构课外书;更新侧边栏;修复模态框、分享海报异常 2026-05-06 21:08:35 +08:00
anian 73dc2a1c1c 适配朋友圈插件v1.4.3-4 2026-04-21 16:06:28 +08:00
anian 565bf30ea5 朋友圈支持图片显示;手机端新增全局公告提示;更新部分页面;优化部分效果 2026-04-17 00:19:43 +08:00
anian 5a3544f8df 新增片刻删除提示框、动画;修复异常;深度优化 2026-03-20 00:15:04 +08:00
anian ee5c1a2b5b 优化文章目录位置、加载后滚动策略;修复页面恢复提示显示异常 2026-03-17 22:13:10 +08:00
anian 9c26bece1b 提前页面恢复提示;修改登出弹窗函数名 2026-03-17 00:57:07 +08:00
anian 101189a077 修复侧边栏测速组件表现异常 2026-03-15 19:07:11 +08:00
anian e716097e07 新增朋友圈作者筛选;新增前台登入登出;深度优化 2026-03-15 18:05:44 +08:00
anian b387ea885c 新增嵌入式代码块;优化通知书推送时机 2026-03-10 17:24:50 +08:00
anian 6810791637 适配日记簿 2026-03-10 17:21:05 +08:00
116 changed files with 36078 additions and 25860 deletions
Executable
+74
View File
@@ -0,0 +1,74 @@
# halo-theme-pix
> 基于原作者v1.2.5代码修改
#### v1.2.5-9补丁
1. 优化附录、奇遇页面(依赖装备插件v1.0.2-1)
2. 修复音乐播放器无法调节音量
3. 优化奇遇相关函数效果
#### v1.2.5-9
1. 修复导航栏居中显示失效、分享海报高度、页码更新、加载动画异常
2. 优化上一页动画效果
3. 新增页面底部页码跳转功能
4. 重构课外书(依赖豆瓣插件v1.2.2-3,模板端处理,支持换行)
5. 新增奇遇页面(依赖装备插件,支持换行)
6. 新增网易云音乐播放JS函数`play_netease_music(2639350114)`
#### v1.2.5-8
1. 重写作文目录、置顶样式
2. 更换(通知书等)域名
3. 修复搜索结果无法PJAX跳转(依赖搜索插件v1.7.1-1
4. 修复日记过多时无法搜索(依赖瞬间插件v1.13.1-1,基于源仓库)
5. 朋友圈内容支持搜索与跳转;大幅优化性能(依赖朋友圈插件v1.4.3-6)
6. 压缩、更换图片
7. 模板端全面支持page与tag参数(常驻显示,同步新增向上翻阅功能)
8. 取消自动恢复源内容功能
9. 前台编辑、发布日记将采用PJAX更新
10. 手机端播放器新增进度条
11. 抽象重复代码至函数、清理无效代码
12. 当前激活的标签将在导航栏中居中显示
13. 优化不同宽度下的显示效果
14. 重构分享海报布局
#### v1.2.5-7
1. 适配新接口(基于新版API-Python)
2. 更新附录页面、通知书页面
3. 朋友圈将不再显示内容为空的条目
4. 修复图文片刻中媒体库及拖拽时的图片样式
5. 修改灯箱相关UI颜色
6. 重写课外书分类获取逻辑(依赖豆瓣插件v1.2.2-2)
#### v1.2.5-6
1. 重构课外书页面
2. 音乐、背景、侧边栏等适配新接口(基于API-Python)
3. 修复分享海报空白、模态框失效等异常
#### v1.2.5-5
1. 朋友圈支持图片显示(需同步更新插件)
2. 手机端底部导航栏新增公告未读提示
3. 更新通讯录、储物柜
4. 优化操作提示、图片排版
#### v1.2.5-4
1. 修复侧边栏测速异常、拖拽片刻图片异常、手机端文章目录异常、页面提示异常
2. 优化多种场景下的提示、衔接、样式
3. 新增片刻删除提示框及删除动画
#### v1.2.5-3
1. 朋友圈支持根据作者筛选内容(需同步更新插件)
2. 支持前台弹窗登入登出
3. 附录页面功能补全
4. 对线路切换、加载异常等情况进行了深度优化
#### v1.2.5-2
1. 添加侧边栏、页面、文章等HTML内容块
2. 通知书推送时机改为RSS触发
#### v1.2.5-1
1. 修改全局样式以适配日记簿
2. 片刻、图库支持视频并接入灯箱
3. 更新remixicon
4. 片刻增加标签展示
5. 适配多线路
6. ···
+219
View File
@@ -0,0 +1,219 @@
<style>
/**
* 字体应用
*/
.poem-container,
.poem-container * {
font-family: 'MaShanZheng' !important;
/* 全局应用自定义字体 */
}
/**
* 容器整体样式
*/
.poem-container {
max-width: 860px;
margin: 2.5rem auto;
padding: 0 1rem;
line-height: 1.85;
color: #0a1f16;
}
/**
* 原文区域 (.poem-original) - 仿语文课本风格
* 注意: 侧边栏样式 (border-left) 是原始代码就存在的逻辑
*/
.poem-original {
background: #f0f9f4;
border-left: 4px solid #40916C;
/* 左侧粗边框 */
border: 1px solid #d1e7dd;
/* 其他三边细边框 */
padding: 1.4rem;
border-radius: 10px;
margin-bottom: 2rem;
}
/* 标题和作者 */
.poem-title {
text-align: center;
font-size: 1.7rem;
color: #1b4332;
margin: 0 0 0.5rem 0;
font-weight: bold;
}
.poem-author {
text-align: center;
font-size: 1.25rem;
margin: 0 0 1rem 0;
color: #2d6a4f;
}
/* 原文内容段落 */
.poem-content p {
font-size: 1.2rem;
margin: 0 0 0.7rem 0;
text-indent: 2em;
color: #081c15;
}
/**
* 赏析区域 (.appreciation)
*/
.appreciation {
margin-top: 2rem;
}
/* 赏析标题 */
.appreciation-title {
display: flex;
align-items: center;
font-size: 1.45rem;
color: #1b4332;
margin: 0 0 1rem 0;
font-weight: bold;
}
/* 赏析标题左侧短杆 */
.appreciation-title span {
width: 8px;
height: 24px;
background: #40916c;
/* 亮绿色 */
border-radius: 4px;
margin-right: 0.8rem;
}
/* 赏析内容 */
.appreciation-content {
font-size: 1.2rem;
margin-bottom: 2rem;
color: #0f3424;
}
.appreciation-content p {
margin: 0 0 1rem 0;
text-indent: 2em;
}
/**
* 响应式调整 (手机端,最大宽度 768px)
*/
@media (max-width: 768px) {
.poem-container {
padding: 0 0.5rem;
}
.poem-original {
padding: 1.1rem;
}
.poem-title {
font-size: 1.5rem;
}
.poem-author {
font-size: 1.15rem;
}
.poem-content p {
font-size: 1.1rem;
}
.appreciation-title {
font-size: 1.2rem;
}
.appreciation-content {
font-size: 1.1rem;
}
}
</style>
<div class="poem-container">
<div class="poem-original">
<h2 class="poem-title">水调歌头·明月几时有</h2>
<p class="poem-author">宋·苏轼</p>
<div class="poem-content">
<p>丙辰中秋,欢饮达旦,大醉,作此篇,兼怀子由。</p>
<p>明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。</p>
<p>转朱阁,低绮户,照无眠。不应有恨,何事长向别时圆?人有悲欢离合,月有阴晴圆缺,此事古难全。但愿人长久,千里共婵娟。</p>
</div>
</div>
<div class="appreciation">
<h3 class="appreciation-title">
<span></span>
开篇叩问:跨越时空的宇宙之思​
</h3>
<div class="appreciation-content">
<p>“明月几时有?把酒问青天。” 开篇两句,便奠定了全词开阔的意境。苏轼以 “把酒问天” 的豪迈姿态,将个人置于广袤的宇宙背景之下,抛出了一个亘古不变的哲学命题 ——
明月从何时开始存在?这种对宇宙起源的追问,并非单纯的好奇,而是蕴含着词人对人生有限与宇宙无限的深层思考。在中秋之夜,皎洁的明月高悬天际,它见证了无数人间的悲欢离合,而人类的生命却如沧海一粟般短暂。苏轼借这一问,打破了传统中秋词
“伤春悲秋” 的局限,将个人的情感升华为对宇宙人生的宏观审视,尽显其 “大江东去” 般的豪放胸襟。​</p>
</div>
<h3 class="appreciation-title">
<span></span>
中片想象:虚实交织的仙境与人间​
</h3>
<div class="appreciation-content">
<p>“不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。” 这几句词,苏轼展开了丰富的想象,将笔触从人间转向仙境。他想象着天上的宫殿此刻正处于怎样的时光,甚至产生了 “乘风归去”
的念头,想要脱离尘世的烦恼,去往那清冷高洁的仙境。然而,“又恐琼楼玉宇,高处不胜寒” 一句,却又将他拉回现实。这里的 “高处不胜寒”,既是对仙境寒冷环境的想象,更是对官场高位、人生巅峰的隐喻。苏轼一生仕途坎坷,曾经历过
“乌台诗案” 的生死考验,他深知 “高处” 的危险与孤独。这种对仙境的向往与犹豫,实则是他对现实人生的矛盾与思考 —— 既渴望摆脱世俗的束缚,又无法割舍人间的情感与责任。​​</p>
<p>“起舞弄清影,何似在人间。”
在对仙境的想象与犹豫之后,苏轼最终回归现实。他在月光下起舞,与自己的影子嬉戏,突然领悟到:与其追求虚无缥缈的仙境,不如珍惜眼前的人间生活。这句词看似平淡,却蕴含着深刻的人生智慧。它体现了苏轼乐观旷达的人生态度 ——
既然无法改变命运的坎坷,不如坦然接受现实,在平凡的生活中寻找乐趣与意义。这种从 “出世” 到 “入世” 的转变,让全词的情感更加贴近人心,也让苏轼的形象更加丰满立体。​​​</p>
</div>
<h3 class="appreciation-title">
<span></span>
下片抒情:由己及人的豁达之悟​
</h3>
<div class="appreciation-content">
<p>“转朱阁,低绮户,照无眠。不应有恨,何事长向别时圆?”
词的下片,苏轼将视角从宇宙仙境拉回人间烟火,聚焦于中秋之夜的离别之情。月光转过朱红的楼阁,低低地照进雕花的门窗,照亮了无法入眠的词人。他看着皎洁的明月,不禁心生疑问:明月本应是团圆的象征,为何总在人们离别之时显得格外圆满?这句
“何事长向别时圆”,看似是对明月的埋怨,实则是词人对离别之苦的深情抒发。苏轼与弟弟苏辙手足情深,却因仕途奔波常年分离,中秋佳节本是团圆之日,他却只能独自对月思念,这种离别之痛在明月的映衬下更显浓烈。​​​</p>
<p>然而,苏轼并未沉溺于离别之苦,而是迅速调整心态,发出了 “人有悲欢离合,月有阴晴圆缺,此事古难全”
的千古慨叹。他将人生的离别与月亮的圆缺联系起来,认识到无论是人生的悲欢离合,还是自然的阴晴圆缺,都是自古以来无法强求的事情。这种对人生无常的坦然接受,展现了苏轼超越常人的豁达与通透。他不再抱怨命运的不公,而是以一种平和的心态看待人生的起伏,这种人生态度不仅是对自己的慰藉,更是对世人的启示
—— 人生本就充满遗憾,唯有学会接受遗憾,才能获得内心的平静。​​​​</p>
<p>“但愿人长久,千里共婵娟。”
词的结尾,苏轼将个人的思念升华为对天下人的美好祝愿。他希望天下所有的亲人都能平安长久,即使相隔千里,也能共同欣赏这轮皎洁的明月。这句词没有丝毫的哀怨与悲戚,反而充满了温暖与希望。它跳出了个人情感的局限,展现了苏轼
“先天下之忧而忧,后天下之乐而乐” 的博大胸怀。“千里共婵娟” 不仅成为了中秋佳节最经典的祝福语,更成为了中华民族共同的文化记忆,每当人们在中秋之夜仰望明月时,都会想起这句充满温情与诗意的祝愿。​​​​​</p>
</div>
<h3 class="appreciation-title">
<span></span>
语言艺术:豪放与婉约的完美融合​
</h3>
<div class="appreciation-content">
<p>《水调歌头・明月几时有》在语言艺术上也达到了炉火纯青的境界。全词以 “明月” 为核心意象,贯穿始终,从 “问月” 到 “思月”,再到 “赞月”,层层递进,脉络清晰。语言风格上,既有 “把酒问青天”“我欲乘风归去”
的豪放洒脱,又有 “转朱阁,低绮户,照无眠” 的细腻婉约,豪放与婉约在词中完美融合,形成了独特的艺术魅力。​​​​</p>
<p>同时,词中运用了大量的设问、反问与感叹,如 “明月几时有?”“何事长向别时圆?”“此事古难全!”
等,这些句式不仅增强了词的节奏感与韵律感,更让词人的情感表达更加酣畅淋漓。此外,词中的比喻、拟人手法也运用得十分巧妙,如将明月比作 “婵娟”,赋予明月以人的情感与姿态,让明月更加生动形象,也让词的意境更加优美动人。​​​​​
</p>
</div>
<h3 class="appreciation-title">
<span></span>
传世价值:跨越千年的精神共鸣​
</h3>
<div class="appreciation-content">
<p>
《水调歌头・明月几时有》之所以能够流传千古,不仅在于其高超的艺术造诣,更在于它蕴含的深刻人生哲理与真挚情感。它所展现的苏轼乐观旷达的人生态度,成为了后世无数人面对困境时的精神寄托。在漫长的历史长河中,无论是仕途失意的文人墨客,还是饱受生活磨难的普通百姓,都能从这首词中获得慰藉与力量。​​​​​
</p>
<p>如今,每当中秋佳节来临,人们依然会吟诵
“但愿人长久,千里共婵娟”,这句词已经超越了时空的限制,成为了中华民族共同的文化符号。它提醒着我们,无论身处何方,无论经历怎样的坎坷,都要珍惜亲情、热爱生活,以豁达的心态面对人生的悲欢离合。​​​​​​</p>
</div>
<h3 class="appreciation-title">
<span></span>
结语:明月寄情,哲思传世
</h3>
<div class="appreciation-content">
<p>
《水调歌头・明月几时有》是一首兼具文学性、哲理性与情感性的千古佳作。它以明月为媒,将个人的情感与宇宙的奥秘、人生的智慧融为一体,展现了苏轼博大的胸襟与高超的艺术才华。这首词不仅是中国文学史上的瑰宝,更是中华民族精神文化的重要组成部分,它将永远照亮人们的心灵,陪伴我们度过每一个中秋之夜。​​​​​
</p>
</div>
</div>
</div>
+624
View File
@@ -0,0 +1,624 @@
<section class="people-list">
<style>
/* CSS 变量定义 (绿色主题) */
:root {
--green-text: #0f5132;
--green-light: #e9f7f1;
--green-border: #c3e6d3;
--green-link: #197d53;
--green-link-hover: #146343;
--green-glow: rgba(15, 81, 50, 0.2);
--avatar-w: 80px;
--avatar-h: 100px;
--radius: 12px;
--font: "Helvetica Neue", Arial, sans-serif;
--scratch-cursor-size: 100px;
/* 刮奖圆圈大小 */
--scratch-mask-color: #d1efe3;
/* 刮奖遮罩颜色 */
}
/* 整体容器样式 */
.people-list {
font-family: var(--font);
max-width: 860px;
margin: 0 auto;
padding: 20px 10px;
}
/* 标题样式 */
.category-title {
font-size: 1.4rem;
font-weight: 600;
color: var(--green-text);
margin: 0 0 16px 0;
padding-bottom: 6px;
border-bottom: 2px solid var(--green-border);
letter-spacing: 0.5px;
display: flex;
justify-content: space-between;
align-items: center;
}
/* 朋友圈链接样式 */
.people-friends-link {
font-size: 0.9rem !important;
font-weight: normal !important;
color: var(--green-link) !important;
text-decoration: none !important;
display: flex !important;
align-items: center !important;
gap: 4px !important;
transition: color 0.2s ease !important;
}
.people-friends-link:hover {
color: var(--green-link-hover) !important;
}
/* 分类区段 */
.category-section {
margin-bottom: 30px;
}
.category-section:last-child {
margin-bottom: 0;
}
/* 卡片网格布局 */
.people-grid {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
/* 单个卡片 (person) 基础样式 */
.person {
display: flex;
gap: 12px;
padding: 14px;
border-radius: 14px;
background: var(--green-light);
align-items: center;
flex: 1 1 calc(50% - 6px);
min-width: 260px;
transition: background-color 0.2s ease;
position: relative;
overflow: hidden;
cursor: default;
/* 默认光标 */
}
/* 普通卡片:鼠标光晕效果 */
.person:not(.mystery-person)::before {
content: "";
position: absolute;
width: 100px;
height: 100px;
border-radius: 50%;
background: var(--green-glow);
transform: translate(-50%, -50%) scale(0);
transition: transform 0.3s ease;
pointer-events: none;
z-index: 0;
left: 50%;
/* 默认居中 */
top: 50%;
/* 默认居中 */
}
/* 悬停时光晕放大 */
.person:not(.mystery-person):hover::before {
transform: translate(-50%, -50%) scale(1);
}
/* 悬停时头像缩放 */
.person:not(.mystery-person):hover .avatar img {
transform: scale(1.15);
}
/* 悬停时背景色变化 */
.person:not(.mystery-person):hover {
background-color: #dff4ec;
}
/* 头像区域 */
.avatar {
width: var(--avatar-w);
height: var(--avatar-h);
border-radius: var(--radius);
overflow: hidden;
background: #d1efe3;
flex-shrink: 0;
position: relative;
z-index: 1;
}
.avatar img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
transform-origin: center center;
}
/* 信息区域 */
.info {
display: flex;
flex-direction: column;
justify-content: center;
height: 100%;
position: relative;
z-index: 1;
}
.name,
.bio {
margin: 0;
padding: 0;
}
.name {
font-size: 1.15rem;
font-weight: 700;
color: var(--green-text);
}
.bio {
margin-top: 1px;
font-size: 0.92rem;
color: #234437;
line-height: 1.4;
}
.bio a {
color: var(--green-text);
text-decoration: none !important;
border-bottom: none !important;
padding-bottom: 0;
}
/* 神秘嘉宾卡片 (刮奖效果) */
.mystery-person {
cursor: none;
/* 隐藏默认光标 */
background-color: var(--scratch-mask-color);
overflow: hidden;
}
/* 刮奖遮罩 Canvas */
.mystery-mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 2;
pointer-events: auto;
/* 允许鼠标事件交互 */
border-radius: 14px;
overflow: hidden;
}
/* 刮奖提示文字 */
.mystery-hint {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 2;
pointer-events: none;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: var(--green-text);
font-size: 1.2rem;
text-align: center;
padding: 20px;
transition: opacity 0.2s ease;
}
.mystery-hint i {
font-size: 1.5rem;
margin-bottom: 8px;
}
/* 刮奖自定义光标 */
.mystery-cursor {
position: absolute;
width: var(--scratch-cursor-size);
height: var(--scratch-cursor-size);
border-radius: 50%;
border: 2px solid rgba(15, 81, 50, 0.6);
background-color: transparent;
pointer-events: none;
z-index: 3;
display: none;
/* 默认隐藏 */
}
/* 补充 CSS:普通卡片光晕位置 (用于JS鼠标追踪) */
.person:not(.mystery-person)::before {
left: var(--mouse-x, 50%);
top: var(--mouse-y, 50%);
}
/* 响应式调整 (max-width: 768px) */
@media (max-width: 768px) {
.category-title {
font-size: 1.3rem;
margin-bottom: 14px;
}
.people-friends-link {
font-size: 0.85rem !important;
}
.person {
flex: 1 1 100%;
/* 单列显示 */
padding: 18px;
gap: 18px;
overflow: visible;
}
.people-grid {
gap: 18px;
}
.avatar {
--avatar-w: 90px;
--avatar-h: 110px;
}
.name {
font-size: 1.25rem;
}
.bio {
font-size: 0.96rem;
line-height: 1.5;
}
/* 移动端隐藏普通卡片光晕 */
.person:not(.mystery-person)::before {
display: none !important;
}
}
</style>
<div class="category-section">
<h3 class="category-title">
近期联系
<a href="/friends" class="people-friends-link">
<i class="ri-group-line"></i> 前往朋友圈
</a>
</h3>
<div class="people-grid">
<div class="person">
<div class="avatar">
<img src="/upload/20260416214530_191_129.jpg">
</div>
<div class="info">
<p class="name">黄诗扶</p>
<p class="bio">
又甜又脆的大冬枣!好听!好看!好吃!黄黄的伟大无需多言!
</p>
</div>
</div>
<div class="person">
<div class="avatar">
<img src="/upload/20260416213611_190_129.jpg">
</div>
<div class="info">
<p class="name">双笙</p>
<p class="bio">
什么?你问我为什么喜欢包包?可能因为是青春吧?
</p>
</div>
</div>
<div class="person">
<div class="avatar">
<img src="/upload/005Jyswjgy1ic7xfxtlu8j33402c0npe.jpg">
</div>
<div class="info">
<p class="name">浅影阿</p>
<p class="bio">
谁能拒绝唱歌这么好看的老大呢?反正我是不行!下一位!
</p>
</div>
</div>
<div class="person">
<div class="avatar">
<img src="/upload/67f2efd2gy1i8iv8nkuqjj23344mokjp.jpg">
</div>
<div class="info">
<p class="name">玄觞</p>
<p class="bio">
你们古风圈,怎么都喊女生王子,喊男生二婶呢?
</p>
</div>
</div>
</div>
</div>
<div class="category-section">
<h3 class="category-title">神秘嘉宾</h3>
<div class="people-grid">
<div class="person mystery-person">
<div class="avatar">
<img src="/upload/69e273f8ly8fnvmu255jyj20e80e8wev.jpg">
</div>
<div class="info">
<p class="name">蔡徐坤</p>
<p class="bio">
不要问为什么坤坤会在这里!因为!黑粉也是粉!
</p>
</div>
<canvas class="mystery-mask"></canvas>
<div class="mystery-hint">
<i class="ri-hand-scratch-line"></i>
点击或悬浮,解锁神秘嘉宾
</div>
<div class="mystery-cursor"></div>
</div>
<div class="person mystery-person">
<div class="avatar">
<img src="/themes/theme-pix/assets/img/logo.webp">
</div>
<div class="info">
<p class="name">敬请期待</p>
<p class="bio">
会是谁呢?我也不知道
</p>
</div>
<canvas class="mystery-mask"></canvas>
<div class="mystery-hint">
<i class="ri-hand-scratch-line"></i>
点击或悬浮,解锁神秘嘉宾
</div>
<div class="mystery-cursor"></div>
</div>
</div>
</div>
<div class="category-section">
<h3 class="category-title">全部名单</h3>
<div class="people-grid">
<div class="person">
<div class="avatar">
<img src="/upload/62a32171jw8f2kxo5x4j1j20ai0bvt9l.jpg">
</div>
<div class="info">
<p class="name">明月庄主</p>
<p class="bio">
眼睁睁的看着庄主结婚生娃,也算是从小看到大了
</p>
</div>
</div>
<div class="person">
<div class="avatar">
<img src="/upload/80c44916gy1i6s8l132bhj20u01n14cu.jpg">
</div>
<div class="info">
<p class="name">杨馥羽</p>
<p class="bio">
剧是没看的,人是要夸可爱的,抖音是第一条推的
</p>
</div>
</div>
<div class="person">
<div class="avatar">
<img src="/upload/mugu.jpg">
</div>
<div class="info">
<p class="name">木骨</p>
<p class="bio">
事情的起因要从那节最优化理论课说起...
</p>
</div>
</div>
<div class="person">
<div class="avatar">
<img src="/upload/001U2r7Jgy1i20tzkph8gj61vk2tcx6p02.jpg">
</div>
<div class="info">
<p class="name">音频怪物</p>
<p class="bio">
你知道的,我很少粉同性的,但...老妖他不一样!
</p>
</div>
</div>
</div>
</div>
<script>
// 存储事件监听引用,用于PJAX返回时清理
const eventListeners = {
personMouseMove: [],
mysteryMouseMove: [],
mysteryMouseLeave: [],
mysteryClick: [],
resize: []
};
// 普通卡片:鼠标跟踪脚本
function initPersonCards() {
// 清理之前的事件监听
eventListeners.personMouseMove.forEach(({ el, handler }) => {
el.removeEventListener('mousemove', handler);
});
eventListeners.personMouseMove = [];
const personCards = document.querySelectorAll('.person:not(.mystery-person)');
personCards.forEach(card => {
let lastMouseX = 50;
let lastMouseY = 50;
const handleMouseMove = (e) => {
const rect = card.getBoundingClientRect();
lastMouseX = ((e.clientX - rect.left) / rect.width) * 100;
lastMouseY = ((e.clientY - rect.top) / rect.height) * 100;
card.style.setProperty('--mouse-x', `${lastMouseX}%`);
card.style.setProperty('--mouse-y', `${lastMouseY}%`);
};
card.addEventListener('mousemove', handleMouseMove);
eventListeners.personMouseMove.push({ el: card, handler: handleMouseMove });
card.addEventListener('mouseleave', () => { });
});
}
// 神秘嘉宾:刮奖效果脚本
function initMysteryCards() {
// 清理之前的事件监听
eventListeners.mysteryMouseMove.forEach(({ el, handler }) => {
el.removeEventListener('mousemove', handler);
});
eventListeners.mysteryMouseLeave.forEach(({ el, handler }) => {
el.removeEventListener('mouseleave', handler);
});
eventListeners.mysteryClick.forEach(({ el, handler }) => {
el.removeEventListener('click', handler);
});
eventListeners.resize.forEach(handler => {
window.removeEventListener('resize', handler);
});
eventListeners.mysteryMouseMove = [];
eventListeners.mysteryMouseLeave = [];
eventListeners.mysteryClick = [];
eventListeners.resize = [];
const mysteryCards = document.querySelectorAll('.mystery-person');
mysteryCards.forEach(card => {
const canvas = card.querySelector('.mystery-mask');
const ctx = canvas.getContext('2d');
const cursor = card.querySelector('.mystery-cursor');
const hintText = card.querySelector('.mystery-hint');
// 从:root获取 CSS 变量值
const computedStyle = getComputedStyle(document.documentElement);
const cursorSize = parseInt(computedStyle.getPropertyValue('--scratch-cursor-size'));
const maskColor = computedStyle.getPropertyValue('--scratch-mask-color');
// 重置/初始化 Canvas 状态
canvas.width = card.offsetWidth;
canvas.height = card.offsetHeight;
ctx.fillStyle = maskColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
hintText.style.opacity = '1';
cursor.style.display = 'none';
canvas.style.display = 'block';
hintText.style.display = 'flex';
card.style.cursor = 'none';
function resizeMysteryCanvas() {
const rect = card.getBoundingClientRect();
canvas.width = rect.width;
canvas.height = rect.height;
fillMysteryMask();
}
function fillMysteryMask() {
ctx.fillStyle = maskColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
function updateMysteryCursor(x, y) {
// 光标位置计算
cursor.style.left = `${x - cursorSize / 2}px`;
cursor.style.top = `${y - cursorSize / 2}px`;
}
function scratchMystery(x, y) {
fillMysteryMask();
ctx.globalCompositeOperation = 'destination-out';
ctx.beginPath();
// 刮奖半径同步光标大小
ctx.arc(x, y, cursorSize / 2, 0, Math.PI * 2);
ctx.fill();
ctx.globalCompositeOperation = 'source-over';
}
const handleMouseMove = (e) => {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
cursor.style.display = 'block';
hintText.style.opacity = '0';
updateMysteryCursor(x, y);
scratchMystery(x, y);
};
const handleMouseLeave = () => {
cursor.style.display = 'none';
hintText.style.opacity = '1';
fillMysteryMask(); // 鼠标移开重新遮挡
};
const handleClick = () => {
// 清除遮罩,显示内容
ctx.clearRect(0, 0, canvas.width, canvas.height);
setTimeout(() => {
canvas.style.display = 'none';
hintText.style.display = 'none';
card.style.cursor = 'default';
cursor.style.display = 'none';
}, 300);
};
// 绑定事件
canvas.addEventListener('mousemove', handleMouseMove);
canvas.addEventListener('mouseleave', handleMouseLeave);
card.addEventListener('click', handleClick);
window.addEventListener('resize', resizeMysteryCanvas);
// 存储事件监听引用
eventListeners.mysteryMouseMove.push({ el: canvas, handler: handleMouseMove });
eventListeners.mysteryMouseLeave.push({ el: canvas, handler: handleMouseLeave });
eventListeners.mysteryClick.push({ el: card, handler: handleClick });
eventListeners.resize.push(resizeMysteryCanvas);
resizeMysteryCanvas();
});
}
// 初始化所有组件
function initPeopleList() {
initPersonCards();
initMysteryCards();
}
// 页面首次加载初始化
document.addEventListener('DOMContentLoaded', initPeopleList);
// PJAX跳转成功后重新初始化
document.addEventListener('pjax:success', function () {
// 仅当页面存在people-list组件时才初始化
if (document.querySelector('.people-list')) {
initPeopleList();
}
});
// 页面隐藏时清理事件监听(避免内存泄漏)
document.addEventListener('visibilitychange', function () {
if (document.hidden) {
// 清理所有事件监听 (简化逻辑:仅清除存储的)
[...eventListeners.personMouseMove, ...eventListeners.mysteryMouseMove].forEach(({ el, handler }) => el.removeEventListener('mousemove', handler));
eventListeners.mysteryMouseLeave.forEach(({ el, handler }) => el.removeEventListener('mouseleave', handler));
eventListeners.mysteryClick.forEach(({ el, handler }) => el.removeEventListener('click', handler));
eventListeners.resize.forEach(handler => window.removeEventListener('resize', handler));
} else {
// 页面重新显示时重新初始化
if (document.querySelector('.people-list')) {
initPeopleList();
}
}
});
</script>
</section>
+332
View File
@@ -0,0 +1,332 @@
<div class="year-gallery">
<!-- Tabs -->
<div class="yg-tabs">
<button class="yg-tab active" data-target="yg-2025">2025</button>
<button class="yg-tab" data-target="yg-2026">2026</button>
</div>
<!-- ================= 2025 ================= -->
<div class="yg-panel active" id="yg-2025">
<!-- 封面:像 2026 一样两个独立卡片 -->
<div class="yg-2025-hero-row">
<div class="yg-2025-hero">
<img src="/upload/2025-0-1.jpg">
</div>
<div class="yg-2025-hero">
<img src="/upload/2025-0-2.jpg">
</div>
</div>
<!-- ========= 12 个月:4 行 3 列 ========= -->
<div class="yg-grid-2025-3col">
<div class="yg-card">
<img src="/upload/2025-1-1.jpg">
<img src="/upload/2025-1-2.jpg">
</div>
<div class="yg-card">
<img src="/upload/2025-2-1.jpg">
<img src="/upload/2025-2-2.jpg">
</div>
<div class="yg-card">
<img src="/upload/2025-3-1.jpg">
<img src="/upload/2025-3-2.jpg">
</div>
<div class="yg-card">
<img src="/upload/2025-4-1.jpg">
<img src="/upload/2025-4-2.jpg">
</div>
<div class="yg-card">
<img src="/upload/2025-5-1.jpg">
<img src="/upload/2025-5-2.jpg">
</div>
<div class="yg-card">
<img src="/upload/2025-6-1.jpg">
<img src="/upload/2025-6-2.jpg">
</div>
<div class="yg-card">
<img src="/upload/2025-7-1.jpg">
<img src="/upload/2025-7-2.jpg">
</div>
<div class="yg-card">
<img src="/upload/2025-8-1.jpg">
<img src="/upload/2025-8-2.jpg">
</div>
<div class="yg-card">
<img src="/upload/2025-9-1.jpg">
<img src="/upload/2025-9-2.jpg">
</div>
<div class="yg-card">
<img src="/upload/2025-10-1.jpg">
<img src="/upload/2025-10-2.jpg">
</div>
<div class="yg-card">
<img src="/upload/2025-11-1.jpg">
<img src="/upload/2025-11-2.jpg">
</div>
<div class="yg-card">
<img src="/upload/2025-12-1.jpg">
<img src="/upload/2025-12-2.jpg">
</div>
</div>
</div>
<!-- ================= 2026(完全未改动) ================= -->
<div class="yg-panel" id="yg-2026">
<div class="yg-hero-row">
<div class="yg-hero">
<img src="/upload/2026.jpg">
</div>
<div class="yg-hero">
<img src="/upload/2026-0.jpg">
</div>
</div>
<div class="yg-grid-2026">
<div class="yg-2026-month"><img src="/upload/2026-1.jpg"></div>
<div class="yg-2026-month"><img src="/upload/2026-2.jpg"></div>
<div class="yg-2026-month"><img src="/upload/2026-3.jpg"></div>
<div class="yg-2026-month"><img src="/upload/2026-4.jpg"></div>
<div class="yg-2026-month"><img src="/upload/2026-5.jpg"></div>
<div class="yg-2026-month"><img src="/upload/2026-6.jpg"></div>
<div class="yg-2026-month"><img src="/upload/2026-7.jpg"></div>
<div class="yg-2026-month"><img src="/upload/2026-8.jpg"></div>
<div class="yg-2026-month"><img src="/upload/2026-9.jpg"></div>
<div class="yg-2026-month"><img src="/upload/2026-10.jpg"></div>
<div class="yg-2026-month"><img src="/upload/2026-11.jpg"></div>
<div class="yg-2026-month"><img src="/upload/2026-12.jpg"></div>
</div>
</div>
</div>
<style>
.year-gallery {
width: 100%;
max-width: 1200px;
margin: 50px auto;
}
/* ---------------- Tabs ---------------- */
.yg-tabs {
display: flex;
justify-content: center;
margin-bottom: 20px;
}
.yg-tab {
padding: 10px 25px;
margin: 0 5px;
border-radius: 999px;
border: 1px solid #9ad1a1;
background: #e9f7ec;
cursor: pointer;
transition: .3s;
color: #2c6e3f;
}
.yg-tab.active {
background: #4caf50;
color: #fff;
border-color: #4caf50;
}
.yg-panel {
display: none;
}
.yg-panel.active {
display: block;
}
/* ================= 2025 ================= */
/* 封面像 2026 一样:两个独立卡片 */
.yg-2025-hero-row {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 14px;
margin-bottom: 18px;
}
.yg-2025-hero {
background: #fff;
border-radius: 16px;
box-shadow: 0 6px 18px rgba(76, 175, 80, .18);
padding: 8px;
}
.yg-2025-hero img {
width: 100%;
display: block;
border-radius: 12px;
}
/* 3 列 × 4 行 月份布局 */
.yg-grid-2025-3col {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 14px;
}
.yg-card {
border-radius: 14px;
background: #fff;
box-shadow: 0 6px 18px rgba(76, 175, 80, .18);
padding: 8px;
}
.yg-grid-2025-3col .yg-card img {
width: 100%;
border-radius: 12px;
display: block;
}
/* 月份双图之间:只加一点点 */
.yg-card img+img {
margin-top: 6px;
}
/* ================= 2026 =================(未改) */
.yg-hero-row {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
margin-bottom: 18px;
}
.yg-hero {
background: #fff;
border-radius: 16px;
box-shadow: 0 6px 22px rgba(76, 175, 80, .18);
overflow: hidden;
}
.yg-hero img {
width: 100%;
aspect-ratio: 1185/1775;
object-fit: cover;
}
.yg-hero:first-child img {
object-position: top;
}
.yg-grid-2026 {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
.yg-2026-month {
background: #fff;
border-radius: 14px;
box-shadow: 0 6px 18px rgba(76, 175, 80, .18);
padding: 8px;
}
.yg-2026-month img {
width: 100%;
aspect-ratio: 1185/1775;
object-fit: cover;
border-radius: 12px;
}
/* 手机优化 */
@media(max-width:900px) {
.yg-2025-hero-row,
.yg-grid-2025-3col {
gap: 10px;
}
.yg-hero-row,
.yg-grid-2026 {
gap: 10px;
}
.yg-card,
.yg-2026-month {
padding: 6px;
box-shadow: 0 4px 12px rgba(76, 175, 80, .15);
}
}
@media(max-width:600px) {
.year-gallery {
margin: 25px auto;
}
.yg-card,
.yg-2026-month {
padding: 5px;
}
.yg-2025-hero-row,
.yg-grid-2025-3col,
.yg-grid-2026,
.yg-hero-row {
gap: 8px;
}
}
</style>
<script>
function initYearGallery() {
document.querySelectorAll(".yg-tab").forEach(btn => {
btn.onclick = () => {
document.querySelectorAll(".yg-tab").forEach(b => b.classList.remove("active"));
document.querySelectorAll(".yg-panel").forEach(p => p.classList.remove("active"));
btn.classList.add("active");
document.getElementById(btn.dataset.target).classList.add("active");
};
});
}
document.addEventListener("DOMContentLoaded", initYearGallery);
document.addEventListener("pjax:complete", initYearGallery);
</script>
+268
View File
@@ -0,0 +1,268 @@
<style>
/* 基础样式:保留原有3D旋转卡片核心样式,修复超链接消失问题 */
.dr_card_wrapper {
width: 100%;
height: clamp(300px, 70vw, 500px);
position: relative;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
z-index: 1;
border: none;
background: none;
touch-action: pan-y;
margin: 0 0 50px 0;
/* 容器间距 */
}
.dr_card_inner {
--dr-w: clamp(60px, 18vw, 100px);
--dr-h: clamp(110px, 30vw, 170px);
--dr-translateZ: calc((var(--dr-w) + var(--dr-h)));
--dr-rotateX: -15deg;
--dr-perspective: clamp(800px, 150vw, 1000px);
--dr-rotate-direction: 1;
/* 统一顺时针旋转,可修改为-1改为逆时针 */
position: absolute;
width: var(--dr-w);
height: var(--dr-h);
top: 50%;
left: 50%;
transform: translate(-50%, -50%) perspective(var(--dr-perspective));
z-index: 2;
transform-style: preserve-3d;
animation: dr_card_rotating 25s linear infinite;
will-change: transform;
-webkit-will-change: transform;
}
@keyframes dr_card_rotating {
from {
transform: translate(-50%, -50%) perspective(var(--dr-perspective)) rotateX(var(--dr-rotateX)) rotateY(0deg);
-webkit-transform: translate(-50%, -50%) perspective(var(--dr-perspective)) rotateX(var(--dr-rotateX)) rotateY(0deg);
}
to {
transform: translate(-50%, -50%) perspective(var(--dr-perspective)) rotateX(var(--dr-rotateX)) rotateY(calc(360deg * var(--dr-rotate-direction)));
-webkit-transform: translate(-50%, -50%) perspective(var(--dr-perspective)) rotateX(var(--dr-rotateX)) rotateY(calc(360deg * var(--dr-rotate-direction)));
}
}
.dr_card_card {
position: absolute;
border-radius: 8px;
overflow: hidden;
top: 0;
right: 0;
bottom: 0;
left: 0;
transform: rotateY(calc((360deg / var(--dr-quantity)) * var(--dr-index))) translateZ(var(--dr-translateZ));
-webkit-transform: rotateY(calc((360deg / var(--dr-quantity)) * var(--dr-index))) translateZ(var(--dr-translateZ));
touch-action: manipulation;
z-index: 3;
/* 保证卡片层级高于背景 */
transition: all 0.3s ease;
/* 过渡动画,优化交互体验 */
}
.dr_card_card a {
text-decoration: none;
color: inherit;
display: block;
width: 100%;
height: 100%;
tap-highlight-color: transparent;
-webkit-tap-highlight-color: transparent;
}
.dr_card_img {
width: 100%;
height: 100%;
object-fit: cover;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
background-color: #eee;
position: relative;
/* 为视频播放图标做定位容器 */
}
/* 图片卡片专属样式(绿色系,标识静态图片) */
.dr_card_card--img {
border: 2px solid #4CAF50;
/* 绿色边框,直观区分 */
background-color: rgba(76, 175, 80, 0.1);
/* 浅绿透明底色,增加层次感 */
}
/* 视频卡片专属样式(蓝色系,标识动态视频) */
.dr_card_card--video {
border: 2px solid #2196F3;
/* 蓝色边框,直观区分 */
background-color: rgba(33, 150, 243, 0.1);
/* 浅蓝透明底色,增加层次感 */
}
/* 视频卡片专属播放图标,用户一眼识别视频类型 */
.dr_card_card--video .dr_card_img::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 30px;
height: 30px;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.9);
clip-path: polygon(50% 50%, 80% 50%, 65% 35%, 65% 65%);
/* 三角形播放按钮 */
z-index: 10;
pointer-events: none;
/* 不遮挡超链接点击 */
}
/* 卡片标签,明确标注图片/视频(强化信息) */
.dr_card_tag {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 20px;
line-height: 20px;
font-size: 12px;
color: #fff;
text-align: center;
z-index: 5;
font-family: Arial, sans-serif;
}
.dr_card_tag--img {
background-color: #4CAF50;
}
.dr_card_tag--video {
background-color: #2196F3;
}
/* 交互反馈,强化用户认知(电脑端hover/手机端active */
.dr_card_card--img:hover {
box-shadow: 0 0 18px rgba(76, 175, 80, 0.6);
/* 绿色阴影扩散 */
transform: rotateY(calc((360deg / var(--dr-quantity)) * var(--dr-index))) translateZ(var(--dr-translateZ)) scale(1.05);
/* 轻微放大 */
}
.dr_card_card--video:hover {
box-shadow: 0 0 18px rgba(33, 150, 243, 0.6);
/* 蓝色阴影扩散 */
transform: rotateY(calc((360deg / var(--dr-quantity)) * var(--dr-index))) translateZ(var(--dr-translateZ)) scale(1.05);
/* 轻微放大 */
}
.dr_card_card--img:active,
.dr_card_card--video:active {
opacity: 0.9;
/* 手机端触摸反馈,轻微透明 */
}
/* 媒体查询:小屏手机优化 - 缩小标签文字及容器尺寸 */
@media (max-width: 375px) {
.dr_card_inner {
--dr-rotateX: -10deg;
animation-duration: 30s;
}
.dr_card_card {
border-radius: 6px;
}
.dr_card_card--video .dr_card_img::after {
width: 24px;
height: 24px;
/* 小屏缩小播放图标 */
}
/* 手机端:缩小标签容器高度和文字大小 */
.dr_card_tag {
height: 16px;
/* 标签容器高度减小 */
line-height: 16px;
/* 行高与高度匹配,垂直居中 */
font-size: 10px;
/* 标签文字缩小 */
}
}
</style>
<!-- 单个旋转容器:包含5张图片 + 5个视频(总计10张卡片) -->
<div class="dr_card_wrapper">
<div class="dr_card_inner" style="--dr-quantity: 10; --dr-rotate-direction: 1;">
<!-- 图片卡片(5张,随机挑选原图片集中的5张) -->
<div class="dr_card_card dr_card_card--img" style="--dr-index: 0;">
<div class="dr_card_tag dr_card_tag--img">图片</div>
<a href="/photos">
<div class="dr_card_img" style="background-image: url('/upload/3867579473.jpg');"></div>
</a>
</div>
<div class="dr_card_card dr_card_card--img" style="--dr-index: 1;">
<div class="dr_card_tag dr_card_tag--img">图片</div>
<a href="/photos">
<div class="dr_card_img" style="background-image: url('/upload/433739173.jpg');"></div>
</a>
</div>
<div class="dr_card_card dr_card_card--img" style="--dr-index: 2;">
<div class="dr_card_tag dr_card_tag--img">图片</div>
<a href="/photos">
<div class="dr_card_img" style="background-image: url('/upload/2026-0.png');"></div>
</a>
</div>
<div class="dr_card_card dr_card_card--img" style="--dr-index: 3;">
<div class="dr_card_tag dr_card_tag--img">图片</div>
<a href="/photos">
<div class="dr_card_img" style="background-image: url('/upload/IMG_20251207_103435.jpg');"></div>
</a>
</div>
<div class="dr_card_card dr_card_card--img" style="--dr-index: 4;">
<div class="dr_card_tag dr_card_tag--img">图片</div>
<a href="/photos">
<div class="dr_card_img" style="background-image: url('/upload/26szrmrp1fm.jpg');"></div>
</a>
</div>
<!-- 视频卡片(5张,随机挑选原视频集中的5张) -->
<div class="dr_card_card dr_card_card--video" style="--dr-index: 5;">
<div class="dr_card_tag dr_card_tag--video">视频</div>
<a href="/videos">
<div class="dr_card_img" style="background-image: url('/upload/ec39c62fly1i3rextah4gj22dc35s1ky.jpg');">
</div>
</a>
</div>
<div class="dr_card_card dr_card_card--video" style="--dr-index: 6;">
<div class="dr_card_tag dr_card_tag--video">视频</div>
<a href="/videos">
<div class="dr_card_img" style="background-image: url('/upload/8aea7800gy1i2vfbxoqhdj24w07c0b2p.jpg');">
</div>
</a>
</div>
<div class="dr_card_card dr_card_card--video" style="--dr-index: 7;">
<div class="dr_card_tag dr_card_tag--video">视频</div>
<a href="/videos">
<div class="dr_card_img" style="background-image: url('/upload/IMG_20251207_161707.jpg');"></div>
</a>
</div>
<div class="dr_card_card dr_card_card--video" style="--dr-index: 8;">
<div class="dr_card_tag dr_card_tag--video">视频</div>
<a href="/videos">
<div class="dr_card_img" style="background-image: url('/upload/IMG_9900.jpg');"></div>
</a>
</div>
<div class="dr_card_card dr_card_card--video" style="--dr-index: 9;">
<div class="dr_card_tag dr_card_tag--video">视频</div>
<a href="/videos">
<div class="dr_card_img" style="background-image: url('/upload/IMG_9902.jpg');"></div>
</a>
</div>
</div>
</div>
+363
View File
@@ -0,0 +1,363 @@
<style>
/**
* 导航容器基础样式
*/
.nav-container {
font-family: 'Segoe UI', Roboto, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
color: #333;
}
/**
* Tab 导航栏
*/
.nav-tabs {
display: flex;
gap: 10px;
margin-bottom: 25px;
border-radius: 8px;
overflow: hidden;
background-color: #f9fef7;
border: 1px solid #e6f7ef;
}
.nav-tab {
flex: 1;
padding: 10px 0;
text-align: center;
font-size: 16px;
font-weight: 500;
color: #2d8659;
cursor: pointer;
transition: all 0.3s ease;
}
@media (min-width: 576px) {
.nav-tab {
padding: 10px 0;
font-size: 18px;
}
}
.nav-tab.active {
background-color: #4ade80;
/* 激活状态背景色 */
color: #ffffff;
}
.nav-tab:hover:not(.active) {
background-color: #e6f7ef;
}
/**
* 内容区标题样式
*/
.nav-section-title {
font-size: 24px;
font-weight: 600;
color: #2d8659;
margin-bottom: 25px;
padding-bottom: 10px;
border-bottom: 2px solid #e6f7ef;
display: flex;
align-items: center;
}
/* 标题前的装饰块 */
.nav-section-title::before {
content: '';
display: inline-block;
width: 8px;
height: 24px;
background-color: #4ade80;
margin-right: 10px;
border-radius: 4px;
}
/**
* 卡片布局网格
*/
.nav-grid {
display: grid;
gap: 25px;
margin-bottom: 20px;
}
@media (min-width: 576px) {
.nav-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 575px) {
.nav-grid {
grid-template-columns: 1fr;
gap: 20px;
}
}
/**
* 导航卡片样式
*/
.nav-card {
background: #ffffff;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 12px rgba(74, 222, 128, 0.1);
transition: all 0.3s ease;
border: 1px solid #e6f7ef;
cursor: pointer;
position: relative;
}
/* 隐藏的链接,用于覆盖整个卡片 */
.nav-card-link {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
opacity: 0;
}
/* 悬停效果 */
.nav-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 20px rgba(74, 222, 128, 0.2);
border-color: #4ade80;
}
/* 图片容器和图片缩放效果 */
.nav-card-img-container {
position: relative;
width: 100%;
padding-top: 56.25%;
/* 16:9 比例 */
overflow: hidden;
}
.nav-card-img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
pointer-events: none;
}
.nav-card:hover .nav-card-img {
transform: scale(1.05);
}
/* 卡片内容 */
.nav-card-content {
padding: 20px;
position: relative;
z-index: 2;
}
.nav-card-title {
font-size: 18px;
font-weight: 600;
color: #2d8659;
margin-bottom: 8px;
transition: color 0.3s ease;
}
.nav-card:hover .nav-card-title {
color: #4ade80;
}
.nav-card-desc {
font-size: 14px;
color: #666;
line-height: 1.6;
margin: 0;
}
/* 标签区卡片特殊样式 (tag-section) */
.tag-section .nav-card {
background: #f9fef7;
}
.tag-section .nav-card-title {
color: #166534;
}
.tag-section .nav-card:hover .nav-card-title {
color: #4ade80;
}
.tag-section .nav-card-desc {
color: #555;
}
/**
* 内容切换和动画
*/
.nav-content {
display: none;
}
.nav-content.active {
display: block;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
<div class="nav-container">
<div class="nav-tabs">
<div class="nav-tab active" data-target="category-content">文章分类</div>
<div class="nav-tab" data-target="tag-content">文章标签</div>
</div>
<div id="category-content" class="nav-content active">
<h2 class="nav-section-title">热门分类</h2>
<div class="nav-grid category-section">
<div class="nav-card" onclick="pjaxNavigate('/categories/shi')">
<div class="nav-card-img-container">
<img src="/upload/shi.jpg" class="nav-card-img" alt="诗">
</div>
<div class="nav-card-content">
<h3 class="nav-card-title"></h3>
<p class="nav-card-desc">笔落山河凝气韵,字藏风月寄情长。一吟一咏皆风骨,半阙半章尽流芳。</p>
</div>
</div>
<div class="nav-card" onclick="pjaxNavigate('/categories/ci')">
<div class="nav-card-img-container">
<img src="/upload/ci.jpg" class="nav-card-img" alt="词">
</div>
<div class="nav-card-content">
<h3 class="nav-card-title"></h3>
<p class="nav-card-desc">锦字填成清浅调,柔肠织就婉约声。小令长调皆入画,浅唱低吟总关情。</p>
</div>
</div>
<div class="nav-card" onclick="pjaxNavigate('/categories/qu')">
<div class="nav-card-img-container">
<img src="/upload/qu.jpg" class="nav-card-img" alt="曲">
</div>
<div class="nav-card-content">
<h3 class="nav-card-title"></h3>
<p class="nav-card-desc">俗韵唱尽人间味,清声漫染烟火色。嬉笑怒骂藏真意,浅白深醇总动人。</p>
</div>
</div>
<div class="nav-card" onclick="pjaxNavigate('/categories/fu')">
<div class="nav-card-img-container">
<img src="/upload/fu.jpg" class="nav-card-img" alt="赋">
</div>
<div class="nav-card-content">
<h3 class="nav-card-title"></h3>
<p class="nav-card-desc">铺陈万象摛文彩,墨叙今古诉慨慷。骈俪藏锋含浩气,一赋千秋韵远长。</p>
</div>
</div>
</div>
</div>
<div id="tag-content" class="nav-content">
<h2 class="nav-section-title">热门标签</h2>
<div class="nav-grid tag-section">
<div class="nav-card" onclick="pjaxNavigate('/tags/li-yu')">
<div class="nav-card-img-container">
<img src="/upload/liyu.jpg" class="nav-card-img" alt="李煜">
</div>
<div class="nav-card-content">
<h3 class="nav-card-title">李煜</h3>
<p class="nav-card-desc">雕栏玉砌成追忆,故国不堪月明中。​小楼昨夜东风起,一江春水诉愁浓。</p>
</div>
</div>
<div class="nav-card" onclick="pjaxNavigate('/tags/li-qing-zhao')">
<div class="nav-card-img-container">
<img src="/upload/liqingzhao.jpg" class="nav-card-img" alt="李清照">
</div>
<div class="nav-card-content">
<h3 class="nav-card-title">李清照</h3>
<p class="nav-card-desc">漱玉词中藏婉绪,半生烟雨半生愁。​雁字回时寻旧梦,黄花瘦尽女儿柔。</p>
</div>
</div>
<div class="nav-card" onclick="pjaxNavigate('/tags/su-shi')">
<div class="nav-card-img-container">
<img src="/upload/sushi.jpg" class="nav-card-img" alt="苏轼">
</div>
<div class="nav-card-content">
<h3 class="nav-card-title">苏轼</h3>
<p class="nav-card-desc">大江东去豪情溢,竹杖芒鞋任平生。​明月几时有把酒,豁达胸襟照汗青。</p>
</div>
</div>
</div>
</div>
</div>
<script>
/**
* PJAX 跳转核心函数
* 兼容 pjax.js 和 jquery-pjax,无 PJAX 时降级为原生跳转
*/
function pjaxNavigate(url) {
if (window.pjax) {
// 适配 pjax.js 原生库
pjax.loadUrl(url);
} else if (window.$ && $.pjax) {
// 适配 jquery-pjax 插件
$.pjax({ url: url, container: '#pjax-container' }); // 请替换为你的 PJAX 容器选择器
} else {
// 降级方案:无 PJAX 时使用原生跳转
window.location.href = url;
}
}
/**
* Tab 切换功能
*/
document.querySelectorAll('.nav-tab').forEach(tab => {
tab.addEventListener('click', function () {
// 移除所有 Tab 的 active 状态
document.querySelectorAll('.nav-tab').forEach(item => item.classList.remove('active'));
// 激活当前点击的 Tab
this.classList.add('active');
// 切换内容区
const target = this.getAttribute('data-target');
document.querySelectorAll('.nav-content').forEach(content => {
content.classList.remove('active');
});
document.getElementById(target).classList.add('active');
});
});
/**
* 优化点击体验(防止300ms延迟,依赖 FastClick 库)
*/
if ('addEventListener' in document) {
document.addEventListener('DOMContentLoaded', function () {
if (typeof FastClick !== 'undefined') {
FastClick.attach(document.body);
}
}, false);
}
</script>
+251
View File
@@ -0,0 +1,251 @@
<div id="my-links-widget" style="max-width: 1200px; margin: 20px auto; padding: 0 15px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;">
<div id="link-container" class="link-container" style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px;">
<a href="/notice" target="_self" data-is-notice="true">
<div id="notice-card" class="link-card-default">
<div class="decor-circle" style="position: absolute; top: -20px; right: -20px; width: 80px; height: 80px; background: #d8f3dc; border-radius: 50%; opacity: 0.3; transition: all 0.5s ease;"></div>
<div style="position: relative; z-index: 1;">
<div class="icon-box" style="width: 60px; height: 60px; background: #40916c; border-radius: 10px; margin: 0 auto 15px; display: flex; align-items: center; justify-content: center;">
<i class="ri-megaphone-line ri-icon" style="font-size: 30px;"></i>
</div>
<h4 class="notice-title" style="color: #2d6a4f; margin: 0 0 5px; font-size: 18px; font-weight: 500;">通知书</h4>
<p class="notice-desc" style="color: #36A28E; font-size: 13px; margin: 0; opacity: 0.8;">最新通知与提醒</p>
</div>
</div>
</a>
<a href="/about" target="_self">
<div class="link-card-default">
<div class="decor-circle" style="position: absolute; top: -20px; right: -20px; width: 80px; height: 80px; background: #d8f3dc; border-radius: 50%; opacity: 0.3; transition: all 0.5s ease;"></div>
<div style="position: relative; z-index: 1;">
<div style="width: 60px; height: 60px; background: #40916c; border-radius: 10px; margin: 0 auto 15px; display: flex; align-items: center; justify-content: center;">
<i class="ri-mail-line ri-icon" style="font-size: 30px;"></i>
</div>
<h4 style="color: #2d6a4f; margin: 0 0 5px; font-size: 18px; font-weight: 500;">告知信</h4>
<p style="color: #36A28E; font-size: 13px; margin: 0; opacity: 0.8;">翻阅日记前的须知</p>
</div>
</div>
</a>
<a href="/content" target="_self">
<div class="link-card-default">
<div class="decor-circle" style="position: absolute; top: -20px; right: -20px; width: 80px; height: 80px; background: #d8f3dc; border-radius: 50%; opacity: 0.3; transition: all 0.5s ease;"></div>
<div style="position: relative; z-index: 1;">
<div style="width: 60px; height: 60px; background: #40916c; border-radius: 10px; margin: 0 auto 15px; display: flex; align-items: center; justify-content: center;">
<i class="ri-plant-line ri-icon" style="font-size: 30px;"></i>
</div>
<h4 style="color: #2d6a4f; margin: 0 0 5px; font-size: 18px; font-weight: 500;">自命题</h4>
<p style="color: #36A28E; font-size: 13px; margin: 0; opacity: 0.8;">不同的分类与标签</p>
</div>
</div>
</a>
<a href="/photos" target="_self">
<div class="link-card-default">
<div class="decor-circle" style="position: absolute; top: -20px; right: -20px; width: 80px; height: 80px; background: #d8f3dc; border-radius: 50%; opacity: 0.3; transition: all 0.5s ease;"></div>
<div style="position: relative; z-index: 1;">
<div style="width: 60px; height: 60px; background: #40916c; border-radius: 10px; margin: 0 auto 15px; display: flex; align-items: center; justify-content: center;">
<i class="ri-folder-image-line ri-icon" style="font-size: 30px;"></i>
</div>
<h4 style="color: #2d6a4f; margin: 0 0 5px; font-size: 18px; font-weight: 500;">小插曲</h4>
<p style="color: #36A28E; font-size: 13px; margin: 0; opacity: 0.8;">值得的图片与视频</p>
</div>
</div>
</a>
<a href="/address" target="_self">
<div class="link-card-default">
<div class="decor-circle" style="position: absolute; top: -20px; right: -20px; width: 80px; height: 80px; background: #d8f3dc; border-radius: 50%; opacity: 0.3; transition: all 0.5s ease;"></div>
<div style="position: relative; z-index: 1;">
<div style="width: 60px; height: 60px; background: #40916c; border-radius: 10px; margin: 0 auto 15px; display: flex; align-items: center; justify-content: center;">
<i class="ri-contacts-line ri-icon" style="font-size: 30px;"></i>
</div>
<h4 style="color: #2d6a4f; margin: 0 0 5px; font-size: 18px; font-weight: 500;">通讯录</h4>
<p style="color: #36A28E; font-size: 13px; margin: 0; opacity: 0.8;">猜猜看会有谁呢</p>
</div>
</div>
</a>
<a href="/friends" target="_self">
<div class="link-card-default">
<div class="decor-circle" style="position: absolute; top: -20px; right: -20px; width: 80px; height: 80px; background: #d8f3dc; border-radius: 50%; opacity: 0.3; transition: all 0.5s ease;"></div>
<div style="position: relative; z-index: 1;">
<div style="width: 60px; height: 60px; background: #40916c; border-radius: 10px; margin: 0 auto 15px; display: flex; align-items: center; justify-content: center;">
<i class="ri-message-3-line ri-icon" style="font-size: 30px;"></i>
</div>
<h4 style="color: #2d6a4f; margin: 0 0 5px; font-size: 18px; font-weight: 500;">朋友圈</h4>
<p style="color: #36A28E; font-size: 13px; margin: 0; opacity: 0.8;">大家都在干什么</p>
</div>
</div>
</a>
<a href="/store" target="_self">
<div class="link-card-default">
<div class="decor-circle" style="position: absolute; top: -20px; right: -20px; width: 80px; height: 80px; background: #d8f3dc; border-radius: 50%; opacity: 0.3; transition: all 0.5s ease;"></div>
<div style="position: relative; z-index: 1;">
<div style="width: 60px; height: 60px; background: #40916c; border-radius: 10px; margin: 0 auto 15px; display: flex; align-items: center; justify-content: center;">
<i class="ri-building-4-line ri-icon" style="font-size: 30px;"></i>
</div>
<h4 style="color: #2d6a4f; margin: 0 0 5px; font-size: 18px; font-weight: 500;">储物柜</h4>
<p style="color: #36A28E; font-size: 13px; margin: 0; opacity: 0.8;">左边翻翻右边找找</p>
</div>
</div>
</a>
<a href="/qiyu" target="_self">
<div class="link-card-default">
<div class="decor-circle" style="position: absolute; top: -20px; right: -20px; width: 80px; height: 80px; background: #d8f3dc; border-radius: 50%; opacity: 0.3; transition: all 0.5s ease;"></div>
<div style="position: relative; z-index: 1;">
<div style="width: 60px; height: 60px; background: #40916c; border-radius: 10px; margin: 0 auto 15px; display: flex; align-items: center; justify-content: center;">
<i class="ri-book-3-line ri-icon" style="font-size: 30px;"></i>
</div>
<h4 style="color: #2d6a4f; margin: 0 0 5px; font-size: 18px; font-weight: 500;">奇遇书</h4>
<p style="color: #36A28E; font-size: 13px; margin: 0; opacity: 0.8;">每天都值得记录</p>
</div>
</div>
</a>
<a href="https://api.anian.net" target="_blank">
<div class="link-card-default">
<div class="decor-circle" style="position: absolute; top: -20px; right: -20px; width: 80px; height: 80px; background: #d8f3dc; border-radius: 50%; opacity: 0.3; transition: all 0.5s ease;"></div>
<div style="position: relative; z-index: 1;">
<div style="width: 60px; height: 60px; background: #40916c; border-radius: 10px; margin: 0 auto 15px; display: flex; align-items: center; justify-content: center;">
<i class="ri-git-repository-line ri-icon" style="font-size: 30px;"></i>
</div>
<h4 style="color: #2d6a4f; margin: 0 0 5px; font-size: 18px; font-weight: 500;">接口箱</h4>
<p style="color: #36A28E; font-size: 13px; margin: 0; opacity: 0.8;">运筹帷幄之中</p>
</div>
</div>
</a>
<a href="/news-2026" target="_self">
<div class="link-card-default">
<div class="decor-circle" style="position: absolute; top: -20px; right: -20px; width: 80px; height: 80px; background: #d8f3dc; border-radius: 50%; opacity: 0.3; transition: all 0.5s ease;"></div>
<div style="position: relative; z-index: 1;">
<div style="width: 60px; height: 60px; background: #40916c; border-radius: 10px; margin: 0 auto 15px; display: flex; align-items: center; justify-content: center;">
<i class="ri-file-list-3-line ri-icon" style="font-size: 30px;"></i>
</div>
<h4 style="color: #2d6a4f; margin: 0 0 5px; font-size: 18px; font-weight: 500;">每日报</h4>
<p style="color: #36A28E; font-size: 13px; margin: 0; opacity: 0.8;">决胜千里之外</p>
</div>
</div>
</a>
<a href="https://git.anian.net" target="_blank">
<div class="link-card-default">
<div class="decor-circle" style="position: absolute; top: -20px; right: -20px; width: 80px; height: 80px; background: #d8f3dc; border-radius: 50%; opacity: 0.3; transition: all 0.5s ease;"></div>
<div style="position: relative; z-index: 1;">
<div style="width: 60px; height: 60px; background: #40916c; border-radius: 10px; margin: 0 auto 15px; display: flex; align-items: center; justify-content: center;">
<i class="ri-git-pull-request-line ri-icon" style="font-size: 30px;"></i>
</div>
<h4 style="color: #2d6a4f; margin: 0 0 5px; font-size: 18px; font-weight: 500;">代码库</h4>
<p style="color: #36A28E; font-size: 13px; margin: 0; opacity: 0.8;">如何实现的呢</p>
</div>
</div>
</a>
<a href="books" target="_self">
<div class="link-card-default">
<div class="decor-circle" style="position: absolute; top: -20px; right: -20px; width: 80px; height: 80px; background: #d8f3dc; border-radius: 50%; opacity: 0.3; transition: all 0.5s ease;"></div>
<div style="position: relative; z-index: 1;">
<div style="width: 60px; height: 60px; background: #40916c; border-radius: 10px; margin: 0 auto 15px; display: flex; align-items: center; justify-content: center;">
<i class="ri-door-open-line ri-icon" style="font-size: 30px;"></i>
</div>
<h4 style="color: #2d6a4f; margin: 0 0 5px; font-size: 18px; font-weight: 500;">课外书</h4>
<p style="color: #36A28E; font-size: 13px; margin: 0; opacity: 0.8;">记忆中的锚点</p>
</div>
</div>
</a>
</div>
<style>
@media (max-width: 767px) {
#my-links-widget { margin-top: 40px !important; }
}
@media (min-width: 1367px) {
.link-container { grid-template-columns: repeat(4, 1fr) !important; }
}
.ri-icon { color: white !important; fill: white !important; }
.link-container>a { display: flex !important; flex-direction: column !important; height: 100% !important; text-decoration: none !important; }
.link-container>a>div { flex: 1 !important; display: flex !important; flex-direction: column !important; justify-content: center !important; border-radius: 12px !important; padding: 25px 15px !important; text-align: center !important; position: relative !important; overflow: hidden !important; transition: all 0.3s ease !important; border: 2px solid #e2e8f0; }
.link-card-default { background: #f0f7f4 !important; }
.notice-unread {
background: #fffbeb !important;
border-color: #fce788 !important;
}
.notice-unread .icon-box { background: #d69e2e !important; }
.notice-unread .notice-title { color: #b7791f !important; }
.notice-unread .notice-desc { color: #d69e2e !important; font-weight: 500 !important; }
.link-card-hover { transform: translateY(-5px) !important; box-shadow: 0 10px 20px rgba(72, 187, 120, 0.15) !important; border-color: #74c69d !important; }
.notice-unread-hover { transform: translateY(-5px) !important; box-shadow: 0 10px 20px rgba(214, 158, 46, 0.15) !important; border-color: #ecc94b !important; }
</style>
<script>
(function() {
const widget = document.getElementById('my-links-widget');
if (!widget) return;
async function initNoticeStatus() {
const noticeCard = document.getElementById('notice-card');
if (!noticeCard || typeof getAllLatestNoticeDatesFromRSS !== 'function') return;
const combinedDateStr = await getAllLatestNoticeDatesFromRSS();
const decorCircle = noticeCard.querySelector('.decor-circle');
const isUnread = combinedDateStr && (!getCookie('latest_notice_date') || getCookie('latest_notice_date') !== combinedDateStr);
if (isUnread) {
typeof initNoticeTip === 'function' && initNoticeTip();
$('.footer_menu a[href="/navigator"]').css('color', '#ecc94b').find('i').css('color', '#ecc94b');
noticeCard.querySelector('.notice-title').textContent = '通知书';
noticeCard.querySelector('.notice-desc').textContent = '有新通知待查看';
noticeCard.classList.add('notice-unread');
if(decorCircle) decorCircle.style.background = '#fef3c7';
const noticeLink = noticeCard.closest('a');
noticeLink.onclick = function() {
// $.cookie('latest_notice_date', combinedDateStr, { path: '/', domain: 'anian.net', expires: 365 });
$.cookie('latest_notice_date', combinedDateStr, { path: '/', expires: 365 });
typeof markAsRead === 'function' && markAsRead(combinedDateStr);
};
}
}
function initCardHover() {
document.querySelectorAll('.link-container > a').forEach(card => {
const cardDiv = card.querySelector('div');
const decorCircle = cardDiv.querySelector('.decor-circle');
card.onmouseenter = () => {
if (cardDiv.classList.contains('notice-unread')) {
cardDiv.classList.add('notice-unread-hover');
} else {
cardDiv.classList.add('link-card-hover');
}
if(decorCircle) decorCircle.style.transform = 'scale(1.5)';
};
card.onmouseleave = () => {
cardDiv.classList.remove('link-card-hover', 'notice-unread-hover');
if(decorCircle) decorCircle.style.transform = 'scale(1)';
};
});
}
function initAll() {
if (!document.getElementById('my-links-widget')) return;
initCardHover();
initNoticeStatus();
}
if (document.readyState === 'loading') {
document.addEventListener("DOMContentLoaded", initAll);
} else {
initAll();
}
})();
</script>
</div>
+213
View File
@@ -0,0 +1,213 @@
<div class="tab-container" style="width: 100%; max-width: 1200px; margin: 30px auto 0;">
<div class="tab-nav" style="display: flex; border-bottom: 1px solid #a7f3d0; margin-bottom: 15px;">
<div class="tab-nav-item active" data-tab="tab1"
style="flex: 1; padding: 8px 0; cursor: pointer; background-color: #f0fdf4; border: 1px solid #a7f3d0; border-bottom: none; border-radius: 8px 8px 0 0; font-weight: 500; text-align: center; color: #065f46; transition: all 0.3s ease-in-out;">
黄黄日历</div>
<div class="tab-nav-item" data-tab="tab2"
style="flex: 1; padding: 8px 0; cursor: pointer; background-color: #ffffff; border: 1px solid #a7f3d0; border-bottom: none; border-radius: 8px 8px 0 0; font-weight: 500; text-align: center; color: #065f46; transition: all 0.3s ease-in-out;">
近日快讯</div>
</div>
<div class="tab-content" style="width: 100%; height: auto; position: relative;">
<div class="tab-panel active" id="tab1" style="display: block; width: 100%; height: auto;">
<div style="width:100%;height:auto;position:relative;overflow:hidden;">
<div id="calendar-img-container-2025"
style="width:100%;height:auto;position:relative;margin:0 auto;max-width:1200px;">
<div id="calendar-2025-global-blur"></div>
<div id="calendar-2025-intro">
<div id="calendar-2025-intro-mask"></div>
<img id="calendar-2025-intro-img1" src="/upload/2025-0-1.jpg">
<img id="calendar-2025-intro-img2" src="/upload/2025-0-2.jpg">
</div>
<div id="calendar-2025-month-group"
style="width:100%;height:auto;position:absolute;top:0;left:0;opacity:0;transition:opacity 1s ease-in-out;z-index:1;">
<img id="calendar-2025-month-1"
style="width:100%;height:auto;display:block;margin-bottom:5px;object-fit:contain;">
<img id="calendar-2025-month-2" style="width:100%;height:auto;display:block;object-fit:contain;">
</div>
<a id="calendar-2025-link" href="/calendar"></a>
</div>
</div>
<style>
#calendar-2025-global-blur {
position: fixed;
inset: 0;
background: rgba(255, 255, 255, .35);
backdrop-filter: blur(16px);
z-index: 9997;
opacity: 1;
transition: opacity .8s ease;
pointer-events: none;
}
#calendar-2025-intro {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
z-index: 9999;
pointer-events: none;
}
#calendar-2025-intro-mask {
position: absolute;
inset: 0;
background: rgba(255, 255, 255, .25);
backdrop-filter: blur(18px);
pointer-events: none;
}
#calendar-2025-intro img {
width: 100%;
height: auto;
display: block;
object-fit: contain;
opacity: .25;
transition: opacity .9s ease, transform 1s cubic-bezier(.22, .61, .36, 1);
pointer-events: none;
}
#calendar-2025-intro-img1 {
margin-bottom: 5px;
transform: translateX(-100%);
}
#calendar-2025-intro-img2 {
transform: translateX(100%);
}
#calendar-2025-link {
position: absolute;
inset: 0;
z-index: 10000;
pointer-events: auto;
text-decoration: none;
}
.tab-nav-item:hover:not(.active) {
background-color: #dcfce7;
border-color: #86efac;
color: #047857;
}
.tab-nav-item.active {
background-color: #4ADE80 !important;
color: #ffffff !important;
border-color: #4ADE80 !important;
}
</style>
<script>
function initializeCalendar2025() {
const container = document.getElementById('calendar-img-container-2025');
if (!container) return;
const globalBlur = document.getElementById("calendar-2025-global-blur");
const intro = document.getElementById("calendar-2025-intro");
const introImg1 = document.getElementById("calendar-2025-intro-img1");
const introImg2 = document.getElementById("calendar-2025-intro-img2");
const monthGroup = document.getElementById("calendar-2025-month-group");
const monthImg1 = document.getElementById("calendar-2025-month-1");
const monthImg2 = document.getElementById("calendar-2025-month-2");
const month = new Date().getMonth() + 1;
monthImg1.src = `/upload/2025-${month}-1.jpg`;
monthImg2.src = `/upload/2025-${month}-2.jpg`;
const waitImages = imgs => new Promise(res => {
let loaded = 0;
const done = () => (++loaded >= imgs.length && res());
imgs.forEach(img => {
if (img.complete) done();
else {
img.onload = done;
img.onerror = done;
}
});
});
const setHeightByImages = (img1, img2) => {
container.style.height = (img1.offsetHeight || 0) + (img2.offsetHeight || 0) + 5 + "px";
};
const smoothScrollToContainer = () => {
const rect = container.getBoundingClientRect();
const target = window.scrollY + rect.top + rect.height / 2 - window.innerHeight / 2;
$('html, body').animate({ scrollTop: target }, 800);
};
const init = async () => {
await waitImages([introImg1, introImg2]);
setHeightByImages(introImg1, introImg2);
introImg1.style.opacity = "1";
introImg1.style.transform = "translateX(0)";
introImg2.style.opacity = "1";
introImg2.style.transform = "translateX(0)";
smoothScrollToContainer();
await waitImages([monthImg1, monthImg2]);
setTimeout(() => {
introImg1.style.transform = "translateX(100%)";
introImg2.style.transform = "translateX(-100%)";
introImg1.style.opacity = "0";
introImg2.style.opacity = "0";
intro.style.transition = "opacity .8s ease";
intro.style.opacity = 0;
globalBlur.style.opacity = 0;
setTimeout(() => {
intro.remove();
globalBlur.remove();
}, 900);
monthGroup.style.opacity = 1;
monthGroup.style.zIndex = 3;
setHeightByImages(monthImg1, monthImg2);
}, 1500);
};
init();
}
document.addEventListener("DOMContentLoaded", initializeCalendar2025);
document.addEventListener("pjax:complete", initializeCalendar2025);
</script>
</div>
<div class="tab-panel" id="tab2" style="display: none; width: 100%; height: auto;">
<iframe src="https://60s.lylme.com/" width="100%" height="800px"></iframe>
</div>
</div>
</div>
<script>
function initTabSwitch() {
const navItems = document.querySelectorAll('.tab-nav-item');
const panels = document.querySelectorAll('.tab-panel');
navItems.forEach(item => {
item.addEventListener('click', function () {
navItems.forEach(nav => nav.classList.remove('active'));
this.classList.add('active');
const targetTab = this.dataset.tab;
panels.forEach(panel => {
panel.style.display = 'none';
panel.classList.remove('active');
});
const targetPanel = document.getElementById(targetTab);
targetPanel.style.display = 'block';
targetPanel.classList.add('active');
});
});
}
document.addEventListener('DOMContentLoaded', initTabSwitch);
document.addEventListener('pjax:complete', initTabSwitch);
</script>
+187
View File
@@ -0,0 +1,187 @@
<div class="tab-container" style="width: 100%; max-width: 1200px; margin: 30px auto 0;">
<div class="tab-nav" style="display: flex; border-bottom: 1px solid #a7f3d0; margin-bottom: 15px;">
<div class="tab-nav-item active" data-tab="tab1"
style="flex: 1; padding: 8px 0; cursor: pointer; background-color: #f0fdf4; border: 1px solid #a7f3d0; border-bottom: none; border-radius: 8px 8px 0 0; font-weight: 500; text-align: center; color: #065f46; transition: all 0.3s ease-in-out;">
黄黄日历</div>
<div class="tab-nav-item" data-tab="tab2"
style="flex: 1; padding: 8px 0; cursor: pointer; background-color: #ffffff; border: 1px solid #a7f3d0; border-bottom: none; border-radius: 8px 8px 0 0; font-weight: 500; text-align: center; color: #065f46; transition: all 0.3s ease-in-out;">
近日快讯</div>
</div>
<div class="tab-content" style="width: 100%; height: auto; position: relative;">
<div class="tab-panel active" id="tab1" style="display: block; width: 100%; height: auto;">
<div style="width: 100%; height: auto; position: relative; overflow: hidden;">
<div id="calendar-img-container-2026" style="width: 100%; height: auto; position: relative;">
<div id="calendar-2026-global-blur"></div>
<div id="calendar-2026-intro">
<div id="calendar-2026-intro-mask"></div>
<img id="calendar-2026-intro-img" src="/upload/2026-0.jpg">
</div>
<img id="calendar-2026-month"
style="width:100%;height:auto;position:absolute;top:0;left:0;object-fit:contain;opacity:0;transition:opacity 1s ease-in-out;z-index:1;pointer-events:none;">
<a id="calendar-2026-link" href="/calendar"></a>
</div>
</div>
<style>
#calendar-2026-global-blur {
position: fixed;
inset: 0;
background: rgba(255, 255, 255, .35);
backdrop-filter: blur(16px);
z-index: 9997;
opacity: 1;
transition: opacity .8s ease;
pointer-events: none;
}
#calendar-2026-intro {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
z-index: 9999;
pointer-events: none;
}
#calendar-2026-intro-mask {
position: absolute;
inset: 0;
background: rgba(255, 255, 255, .25);
backdrop-filter: blur(18px);
}
#calendar-2026-intro-img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: auto;
opacity: .25;
transform: scale(.50);
transition: opacity .9s ease, transform 1s cubic-bezier(.22, .61, .36, 1);
}
#calendar-2026-link {
position: absolute;
inset: 0;
z-index: 5;
pointer-events: auto;
text-decoration: none;
}
.tab-nav-item:hover:not(.active) {
background-color: #dcfce7;
border-color: #86efac;
color: #047857;
}
.tab-nav-item.active {
background-color: #4ADE80 !important;
color: #ffffff !important;
border-color: #4ADE80 !important;
}
</style>
<script>
function initializeCalendar2026() {
const container = document.getElementById('calendar-img-container-2026');
if (!container) return;
const intro = document.getElementById("calendar-2026-intro");
const introImg = document.getElementById("calendar-2026-intro-img");
const globalBlur = document.getElementById("calendar-2026-global-blur");
const monthImg = document.getElementById('calendar-2026-month');
const month = new Date().getMonth() + 1;
monthImg.src = `/upload/2026-${month}.jpg`;
container.style.height = "100vh";
const setHeight = img => {
if (img.complete)
container.style.height = img.offsetHeight + 'px';
else
img.onload = () => container.style.height = img.offsetHeight + 'px';
};
const smoothScrollToContainer = () => {
const rect = container.getBoundingClientRect();
const target =
window.scrollY +
rect.top +
rect.height / 2 -
window.innerHeight / 2;
$('html, body').animate({ scrollTop: target }, 800);
};
const init = async () => {
await new Promise(r =>
monthImg.complete ? r() : monthImg.onload = r
);
setHeight(monthImg);
introImg.style.opacity = "1";
introImg.style.transform = "scale(1)";
smoothScrollToContainer();
setTimeout(() => {
intro.style.transition = "opacity .8s ease";
intro.style.opacity = 0;
globalBlur.style.opacity = 0;
setTimeout(() => {
intro.remove();
globalBlur.remove();
}, 800);
monthImg.style.opacity = 1;
monthImg.style.zIndex = 3;
setHeight(monthImg);
}, 1500);
};
init();
}
document.addEventListener("DOMContentLoaded", initializeCalendar2026);
document.addEventListener("pjax:complete", initializeCalendar2026);
</script>
</div>
<div class="tab-panel" id="tab2" style="display: none; width: 100%; height: auto;">
<iframe src="https://60s.lylme.com/" width="100%" height="800px"></iframe>
</div>
</div>
</div>
<script>
function initTabSwitch() {
const navItems = document.querySelectorAll('.tab-nav-item');
const panels = document.querySelectorAll('.tab-panel');
navItems.forEach(item => {
item.addEventListener('click', function () {
navItems.forEach(nav => nav.classList.remove('active'));
this.classList.add('active');
const targetTab = this.dataset.tab;
panels.forEach(panel => {
panel.style.display = 'none';
panel.classList.remove('active');
});
const targetPanel = document.getElementById(targetTab);
targetPanel.style.display = 'block';
targetPanel.classList.add('active');
});
});
}
document.addEventListener('DOMContentLoaded', initTabSwitch);
document.addEventListener('pjax:complete', initTabSwitch);
</script>
+353
View File
@@ -0,0 +1,353 @@
<div id="anian-update-wrapper-2026" class="anian-feed-root-container">
<div class="anian-feed-grid">
<div class="anian-feed-card" data-rss-url="https://git.anian.net/anian/halo-theme-pix/rss/branch/master">
<h3 class="anian-feed-caption">日记簿更新</h3>
<div class="anian-feed-list-box">
<div class="anian-loading">加载中...</div>
</div>
</div>
<div class="anian-feed-card" data-rss-url="https://git.anian.net/anian/halo-plugin-friends/rss/branch/master">
<h3 class="anian-feed-caption">朋友圈功能更新</h3>
<div class="anian-feed-list-box">
<div class="anian-loading">加载中...</div>
</div>
</div>
<div class="anian-feed-card" data-rss-url="https://git.anian.net/anian/halo-plugin-comment/rss/branch/master">
<h3 class="anian-feed-caption">评论功能更新</h3>
<div class="anian-feed-list-box">
<div class="anian-loading">加载中...</div>
</div>
</div>
<div class="anian-feed-card" data-rss-url="https://git.anian.net/anian/halo-plugin-photos/rss/branch/master">
<h3 class="anian-feed-caption">图库功能更新</h3>
<div class="anian-feed-list-box">
<div class="anian-loading">加载中...</div>
</div>
</div>
<div class="anian-feed-card" data-rss-url="https://git.anian.net/anian/halo-plugin-douban/rss/branch/master">
<h3 class="anian-feed-caption">课外书功能更新</h3>
<div class="anian-feed-list-box">
<div class="anian-loading">加载中...</div>
</div>
</div>
<div class="anian-feed-card" data-rss-url="https://git.anian.net/anian/halo-plugin-dever/rss/branch/master">
<h3 class="anian-feed-caption">插件开发容器更新</h3>
<div class="anian-feed-list-box">
<div class="anian-loading">加载中...</div>
</div>
</div>
<div class="anian-feed-card" data-rss-url="https://git.anian.net/anian/api-python/rss/branch/master">
<h3 class="anian-feed-caption">应用接口更新</h3>
<div class="anian-feed-list-box">
<div class="anian-loading">加载中...</div>
</div>
</div>
<div class="anian-feed-card" data-rss-url="https://git.anian.net/anian/anian-tool/rss/branch/master">
<h3 class="anian-feed-caption">小工具更新</h3>
<div class="anian-feed-list-box">
<div class="anian-loading">加载中...</div>
</div>
</div>
<div class="anian-feed-card" data-rss-url="https://git.anian.net/anian/memos/rss/branch/master">
<h3 class="anian-feed-caption">备忘录更新</h3>
<div class="anian-feed-list-box">
<div class="anian-loading">加载中...</div>
</div>
</div>
<div class="anian-feed-card" data-rss-url="https://git.anian.net/anian/halo-plugin-moments/rss/branch/master">
<h3 class="anian-feed-caption">日记功能更新</h3>
<div class="anian-feed-list-box">
<div class="anian-loading">加载中...</div>
</div>
</div>
<div class="anian-feed-card" data-rss-url="https://git.anian.net/anian/halo-plugin-search/rss/branch/master">
<h3 class="anian-feed-caption">搜索功能更新</h3>
<div class="anian-feed-list-box">
<div class="anian-loading">加载中...</div>
</div>
</div>
<div class="anian-feed-card" data-rss-url="https://git.anian.net/anian/weibo-rss/rss/branch/master">
<h3 class="anian-feed-caption">RSS功能更新</h3>
<div class="anian-feed-list-box">
<div class="anian-loading">加载中...</div>
</div>
</div>
<div class="anian-feed-card" data-rss-url="https://git.anian.net/anian/halo-plugin-equipments/rss/branch/master">
<h3 class="anian-feed-caption">奇遇功能更新</h3>
<div class="anian-feed-list-box">
<div class="anian-loading">加载中...</div>
</div>
</div>
</div>
</div>
<style>
/* 样式作用域锁定 */
.anian-feed-root-container {
padding: 20px;
background-color: #f9fdf8;
border-radius: 16px;
box-sizing: border-box;
}
.anian-feed-grid {
display: flex;
flex-wrap: wrap;
gap: 24px;
margin: 0;
padding: 0;
}
.anian-feed-card {
flex: 0 0 calc((100% - 24px) / 2);
max-width: calc((100% - 24px) / 2);
background: #ffffff;
border: 1px solid #e1f3d8;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(103, 194, 58, 0.06);
display: flex;
flex-direction: column;
overflow: hidden;
/* 防止内容溢出圆角 */
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.anian-feed-card:first-child {
flex-basis: 100%;
max-width: 100%;
}
.anian-feed-caption {
margin: 0;
padding: 12px 20px;
background-color: #f0f9eb;
color: #52c41a;
/* 更清新的绿色 */
font-size: 17px;
font-weight: 600;
border-bottom: 1px solid #e1f3d8;
letter-spacing: 0.2px;
text-align: center;
}
.anian-feed-list-box {
max-height: 250px;
/* 优化高度 */
overflow-y: auto;
padding: 0;
flex: 1;
}
/* 自定义滚动条 - 更精致 */
.anian-feed-list-box::-webkit-scrollbar {
width: 6px;
margin-right: 2px;
}
.anian-feed-list-box::-webkit-scrollbar-track {
background: #f8fff5;
border-radius: 10px;
}
.anian-feed-list-box::-webkit-scrollbar-thumb {
background: #b7eb8f;
border-radius: 10px;
transition: background 0.2s;
}
.anian-feed-list-box::-webkit-scrollbar-thumb:hover {
background: #95de64;
}
/* 加载状态美化 */
.anian-loading {
padding: 20px;
color: #8c8c8c;
font-size: 14px;
text-align: center;
animation: fade 1.2s infinite alternate;
}
@keyframes fade {
from {
opacity: 0.6;
}
to {
opacity: 1;
}
}
/* 列表项核心样式 - 无下划线 + 精致hover */
.anian-feed-item {
display: block;
padding: 16px 20px;
text-decoration: none !important;
/* 强制移除下划线 */
border-bottom: 1px dashed #f0f9eb;
transition: all 0.3s ease;
/* 统一过渡效果 */
position: relative;
overflow: hidden;
}
/* 移除所有状态下的下划线 */
.anian-feed-item:link,
.anian-feed-item:visited,
.anian-feed-item:active {
text-decoration: none;
}
/* 增强hover效果 */
.anian-feed-item:hover {
background-color: #f5fff0;
border-color: #d1f0b5;
}
/* 右侧小装饰 - 提升精致感 */
.anian-feed-item::after {
content: "";
position: absolute;
right: 0;
top: 0;
width: 3px;
height: 100%;
background: #67c23a;
opacity: 0;
transition: opacity 0.3s ease;
}
.anian-feed-item:hover::after {
opacity: 1;
}
.anian-feed-item-time {
display: block;
font-size: 13px;
color: #8c8c8c;
margin-bottom: 6px;
font-weight: 400;
}
.anian-feed-item-title {
display: block;
font-size: 15px;
color: #303133;
line-height: 1.5;
font-weight: 500;
transition: color 0.2s ease;
}
.anian-feed-item:hover .anian-feed-item-title {
color: #52c41a;
/* hover时标题变色 */
}
/* 空数据/错误状态样式统一 */
.anian-feed-list-box>div {
padding: 20px;
font-size: 14px;
text-align: center;
box-sizing: border-box;
}
/* 响应式优化 */
@media (max-width: 768px) {
.anian-feed-root-container {
padding: 15px;
}
.anian-feed-card {
flex: 0 0 100%;
max-width: 100%;
}
.anian-feed-caption {
padding: 14px 16px;
font-size: 16px;
}
.anian-feed-item {
padding: 14px 16px;
}
}
</style>
<script>
(function () {
async function fetchRssContent(cardElement) {
const url = cardElement.getAttribute('data-rss-url');
const listBox = cardElement.querySelector('.anian-feed-list-box');
// 1. 如果你已经开启了服务器跨域,可以尝试直接请求 url
// 2. 如果依然报错,请恢复使用代理:const targetUrl = `https://api.allorigins.win/raw?url=${encodeURIComponent(url)}`;
const targetUrl = url;
try {
const response = await fetch(targetUrl);
if (!response.ok) {
throw new Error(`HTTP 错误! 状态码: ${response.status}`);
}
const xmlText = await response.text();
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
// 检查是否有解析错误
const parseError = xmlDoc.getElementsByTagName("parsererror");
if (parseError.length > 0) {
listBox.innerHTML = '<div style="color:#f56c6c;">XML 解析错误</div>';
return;
}
const items = xmlDoc.querySelectorAll("item");
let htmlContent = "";
items.forEach((item, index) => {
const title = item.querySelector("title")?.textContent || "无标题";
const link = item.querySelector("link")?.textContent || "#";
const pubDateStr = item.querySelector("pubDate")?.textContent || "";
// 简单处理日期
const dateObj = new Date(pubDateStr);
const formattedTime = isNaN(dateObj) ? pubDateStr.split(' ').slice(0, 4).join(' ') :
`${dateObj.getFullYear()}-${(dateObj.getMonth() + 1).toString().padStart(2, '0')}-${dateObj.getDate().toString().padStart(2, '0')}`;
htmlContent += `
<a href="${link}" class="anian-feed-item" target="_blank" rel="noopener">
<span class="anian-feed-item-time">${formattedTime}</span>
<span class="anian-feed-item-title">${title}</span>
</a>
`;
});
listBox.innerHTML = htmlContent || '<div style="color:#999;">暂无更新内容</div>';
} catch (error) {
listBox.innerHTML = `<div style="color:#f56c6c;font-size:14px;">失败:超时或发生错误</div>`;
}
}
document.querySelectorAll('.anian-feed-card').forEach(card => {
fetchRssContent(card);
});
})();
</script>
+285
View File
@@ -0,0 +1,285 @@
<style>
.adventure-quest-show-wrap * {
box-sizing: border-box;
}
.adventure-quest-show-wrap {
padding: 8px;
border-radius: 10px;
--aq-show-g2: #E1E9E5;
--aq-show-g3: #8fdbb5;
--aq-show-g4: #22BB6D;
--aq-show-text: #7aa1a1;
--aq-mask-bg: rgba(143, 219, 181, 0.9);
uk-width-2-3@m;
width: 100%;
max-width: calc(100% - 20px);
margin: 0 auto;
}
.adventure-quest-show-tabs {
display: flex;
gap: 8px;
overflow-x: auto;
scrollbar-width: none;
margin-bottom: 20px;
font-size: 12px;
line-height: 18px;
padding-top: 20px;
}
.adventure-quest-show-tab {
padding: 5px 10px;
border-radius: 5px;
cursor: pointer;
user-select: none;
color: var(--aq-show-text);
background: var(--aq-show-g2);
transition: 0.2s;
}
.adventure-quest-show-tab:hover {
background: #22BB6D;
color: white;
}
.adventure-quest-show-tab.adventure-quest-show-active {
background: var(--aq-show-g4);
color: #fff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.adventure-quest-show-container {
display: flex;
flex-wrap: wrap;
gap: 16px;
justify-content: center;
padding: 6px;
margin: 0 auto;
min-height: 400px;
}
.adventure-quest-show-card {
--aq-hover-text: #fff;
background: #f7f7f8;
border-radius: 5px;
/* 卡片基础圆角(遮罩可继承此值) */
position: relative;
overflow: hidden;
aspect-ratio: 1/1 !important;
flex: 0 0 calc(50% - 8px) !important;
margin-bottom: 16px;
}
.adventure-quest-show-card__img {
width: 100%;
height: 100%;
object-fit: cover;
position: absolute;
inset: 0;
z-index: 1;
pointer-events: none;
}
.adventure-quest-show-card__hover-text {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
text-align: center;
align-items: center;
color: var(--aq-hover-text);
border-radius: 15px;
background: var(--aq-mask-bg);
font-weight: bold;
transform: scale(0);
transform-origin: center;
transition: all 0.3s cubic-bezier(0.6, 0.4, 0, 1);
z-index: 2;
}
.adventure-quest-show-card:hover .adventure-quest-show-card__hover-text {
transform: scale(1);
font-size: 1.2em;
}
@media (max-width: 639px) {
.adventure-quest-show-card {
flex: 0 0 100% !important;
aspect-ratio: 1/1 !important;
margin-bottom: 8px;
}
.adventure-quest-show-container {
gap: 8px;
}
}
.adventure-quest-show-load {
text-align: center;
padding: 16px;
margin-top: 20px;
}
.adventure-quest-show-load button {
padding: 10px 15px;
border-radius: 8px;
border: none;
cursor: pointer;
background: #22BB6D;
color: white;
transition: 0.2s;
font-size: 16px;
line-height: 24px;
}
.adventure-quest-show-load button:hover {
opacity: 0.9;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
</style>
<div class="adventure-quest-show-wrap">
<div class="adventure-quest-show-tabs" id="adventure-quest-show-tabs"></div>
<div class="adventure-quest-show-container" id="adventure-quest-show-container"></div>
<div class="adventure-quest-show-load">
<button id="adventure-quest-show-load-btn">翻阅更多</button>
</div>
</div>
<script>
let globalAdventureQuestState = {
adventureQuestCurrent: ''
};
function initializeAdventureQuestEvents() {
const wrap = document.querySelector(".adventure-quest-show-wrap");
if (!wrap) return;
const container = document.getElementById("adventure-quest-show-container");
const loadBtn = document.getElementById("adventure-quest-show-load-btn");
container.innerHTML = "";
loadBtn.style.display = "inline-block";
const adventureQuestData = {
"山林奇遇": [
{ src: "/upload/qiyu-jieqi-dahan.png", hoverText: "偶遇小鹿<br>aaa" },
{ src: "/upload/qiyu-jieqi-dahan.png", hoverText: "清凉沁心" }
],
"都市秘境": [
{ src: "/upload/qiyu-jieqi-dahan.png", hoverText: "偶遇小鹿" },
{ src: "/upload/qiyu-jieqi-dahan.png", hoverText: "清凉沁心" }
]
};
const ALL_TAB = "全部";
function getTabFromUrl() {
const url = new URL(window.location.href);
return url.searchParams.get("tab");
}
function setTabToUrl(tab) {
const url = new URL(window.location.href);
url.searchParams.set("tab", tab);
history.replaceState(null, "", url.toString());
}
function getAllList() {
let arr = [];
Object.keys(adventureQuestData).forEach(k => arr = arr.concat(adventureQuestData[k]));
return arr;
}
function getCurrentList() {
return globalAdventureQuestState.adventureQuestCurrent === ALL_TAB ? getAllList() : adventureQuestData[globalAdventureQuestState.adventureQuestCurrent];
}
const tabNames = [ALL_TAB, ...Object.keys(adventureQuestData)];
let initialTab = getTabFromUrl();
if (!globalAdventureQuestState.adventureQuestCurrent) {
globalAdventureQuestState.adventureQuestCurrent = tabNames.includes(initialTab) ? initialTab : tabNames[0];
}
let adventureQuestLoaded = 0;
const adventureQuestPer = 6;
const tabs = document.getElementById("adventure-quest-show-tabs");
tabs.innerHTML = "";
tabNames.forEach(name => {
const t = document.createElement("div");
t.className = "adventure-quest-show-tab" + (name === globalAdventureQuestState.adventureQuestCurrent ? " adventure-quest-show-active" : "");
t.innerText = name;
t.onclick = () => switchTab(name);
tabs.appendChild(t);
});
function switchTab(name) {
globalAdventureQuestState.adventureQuestCurrent = name;
adventureQuestLoaded = 0;
container.innerHTML = "";
setTabToUrl(name);
document.querySelectorAll(".adventure-quest-show-tab").forEach(t => t.classList.remove("adventure-quest-show-active"));
[...tabs.children].find(t => t.innerText === name).classList.add("adventure-quest-show-active");
loadMore();
}
function createAdventureQuestCard(item) {
const card = document.createElement("div");
card.className = "adventure-quest-show-card";
card.innerHTML = `
<img class="adventure-quest-show-card__img" src="${item.src}" alt="奇遇图片">
<span class="adventure-quest-show-card__hover-text">${item.hoverText}</span>
`;
return card;
}
function loadMore() {
const list = getCurrentList();
if (!list || list.length === 0) {
loadBtn.style.display = "none";
return;
}
const next = list.slice(adventureQuestLoaded, adventureQuestLoaded + adventureQuestPer);
if (next.length === 0) {
loadBtn.style.display = "none";
return;
}
adventureQuestLoaded += next.length;
next.forEach(item => {
const card = createAdventureQuestCard(item);
container.appendChild(card);
});
if (adventureQuestLoaded >= list.length) {
loadBtn.style.display = "none";
} else {
loadBtn.style.display = "inline-block";
}
}
loadBtn.onclick = () => {
loadMore();
};
loadMore();
window.addEventListener('resize', () => {
requestAnimationFrame(() => {
container.style.opacity = "1";
});
});
}
document.addEventListener("DOMContentLoaded", initializeAdventureQuestEvents);
document.addEventListener("pjax:complete", initializeAdventureQuestEvents);
</script>
+340
View File
@@ -0,0 +1,340 @@
<style>
.store * {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.store {
--primary: #50C878;
--primary-light: #90EE90;
--primary-dark: #3A9156;
--danger: #E74C3C;
--bg: #F9FFFB;
--shadow: 0 12px 30px rgba(80, 200, 120, .12);
--radius: 8px;
padding: 26px 14px;
background: var(--bg);
}
.store .container {
width: 100%;
max-width: 420px;
margin: 0 auto;
padding: 32px 24px;
border-radius: var(--radius);
background: #fff;
box-shadow: var(--shadow);
position: relative;
}
.store .top-bar {
height: 4px;
width: 100%;
background: linear-gradient(90deg, #8ef5b4, #50C878, #3A9156);
position: absolute;
top: 0;
left: 0;
}
.store .title {
text-align: center;
font-size: 26px;
font-weight: 800;
color: #2C3E50;
margin-bottom: 14px;
}
/* 搜索 */
.store .tag-container {
margin: 10px 0
}
.store .tag-search-wrapper {
position: relative;
margin-bottom: 6px
}
#tag-search {
width: 100%;
padding: 7px 12px 7px 34px;
border-radius: 8px;
border: 1px solid #c9efd6;
outline: none;
background: #fafffb;
font-size: 13px;
}
.store .search-icon {
position: absolute;
top: 50%;
left: 10px;
transform: translateY(-50%);
}
/* 标签居中 */
.store .tag-list {
display: flex;
flex-wrap: wrap;
gap: 8px;
padding: 8px;
border-radius: 8px;
border: 1px solid #c9efd6;
background: rgba(144, 238, 144, .08);
max-height: 100px;
overflow-y: auto;
justify-content: center;
}
.store .tag-item {
padding: 5px 7px;
border-radius: 8px;
border: 1px solid #90ee90;
font-size: 12px;
cursor: pointer;
background: #fff;
color: #2c6e49;
transition: .25s;
}
.store .tag-item:hover {
background: linear-gradient(135deg, #90ee90, #50c878);
color: #fff;
}
/* 无结果提示 */
.store .no-result {
width: 100%;
text-align: center;
align-items: center;
justify-content: center;
color: #7f8c8d;
font-size: 13px;
min-height: 100%;
padding: 0;
display: none;
}
/* 输入框 */
.store .input-group {
display: flex;
gap: 8px;
margin: 16px 0;
align-items: center;
}
.store .section-divider {
position: relative;
height: 18px;
margin: 6px 0 14px;
}
.store .section-divider::before {
content: "";
position: absolute;
top: 50%;
left: 0;
width: 100%;
height: 1px;
transform: translateY(-50%);
background: linear-gradient(90deg, rgba(80, 200, 120, 0), rgba(80, 200, 120, .55), rgba(80, 200, 120, 0));
box-shadow: 0 1px 0 rgba(80, 200, 120, .12);
}
.store .section-divider::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 10px;
height: 10px;
transform: translate(-50%, -50%) rotate(45deg);
border-radius: 2px;
background: linear-gradient(135deg, #90ee90, #50c878);
box-shadow: -14px 0 0 -3px rgba(80, 200, 120, .5), 14px 0 0 -3px rgba(80, 200, 120, .5), 0 0 0 2px #ffffff;
}
#file-path {
flex: 1;
padding: 8px 14px;
border-radius: 8px;
border: 2px solid #e8f7ec;
font-size: 14px;
outline: none;
}
#submit-btn {
padding: 10px 20px;
border-radius: 8px;
border: none;
background: linear-gradient(135deg, #50c878, #3a9156);
color: #fff;
font-size: 14px;
cursor: pointer;
}
/* 手机端 */
@media(max-width:520px) {
.store {
padding: 14px
}
.store .container {
padding: 22px 18px;
max-width: 95%
}
.store .title {
font-size: 22px
}
.store .input-group {
flex-direction: column;
align-items: stretch;
}
#submit-btn {
width: 100%
}
.store .tag-list {
max-height: 120px
}
#file-path {
width: 100%;
box-sizing: border-box;
}
}
</style>
<div class="store">
<div class="container">
<div class="top-bar"></div>
<h1 class="title">储物柜</h1>
<div class="input-group">
<input id="file-path" placeholder="请输入翻找目标..." autocomplete="off">
<button id="submit-btn">翻 找</button>
</div>
<div class="section-divider" aria-hidden="true"></div>
<div class="tag-container">
<div class="tag-search-wrapper">
<span class="search-icon"><i class="ri-search-line"></i></span>
<input id="tag-search" placeholder="搜索标签..." autocomplete="off">
</div>
<div class="tag-list">
<div class="tag-item" data-value="/themes/theme-pix/assets/img/loading.webp">加载动画</div>
<div class="tag-item" data-value="/themes/theme-pix/assets/img/logo.webp">网站Logo</div>
<div class="tag-item" data-value="/themes/theme-pix/assets/img/banner-all.webp">全部横幅</div>
<div class="tag-item" data-value="/themes/theme-pix/assets/img/banner-dong.webp">冬季横幅</div>
<div class="tag-item" data-value="/themes/theme-pix/assets/img/cover-mei.webp">封面3</div>
<div class="tag-item" data-value="/themes/theme-pix/assets/img/banner-init.webp">初始横幅</div>
<div class="tag-item" data-value="/themes/theme-pix/assets/img/sidebar-notice.webp">侧边栏通知</div>
<div class="tag-item" data-value="/themes/theme-pix/assets/img/cover-song.webp">封面1</div>
<div class="tag-item" data-value="/themes/theme-pix/assets/img/sidebar-quote.webp">侧边栏引用</div>
<div class="tag-item" data-value="/themes/theme-pix/assets/img/cover-zhu.webp">封面2</div>
<div class="no-result" id="noResult">没有匹配的结果</div>
</div>
</div>
</div>
</div>
<script>
function initializeStoreEvents() {
let closeLoading = null;
const container = document.querySelector(".store");
if (!container || container.dataset.init === "1") return;
container.dataset.init = "1";
const input = document.getElementById('file-path');
const btn = document.getElementById('submit-btn');
const tagItems = document.querySelectorAll('.tag-item');
const tagSearch = document.getElementById('tag-search');
const noResult = document.getElementById('noResult');
tagItems.forEach(t => {
t.onclick = () => {
input.value = t.dataset.value;
cocoMessage.success('已填入对应标签信息');
};
});
tagSearch.oninput = () => {
const k = tagSearch.value.toLowerCase();
let visible = 0;
tagItems.forEach(t => {
const show = t.textContent.toLowerCase().includes(k) ||
t.dataset.value.toLowerCase().includes(k);
t.style.display = show ? 'inline-flex' : 'none';
if (show) visible++;
});
noResult.style.display = visible === 0 ? 'flex' : 'none';
};
function check(url) {
return new Promise(r => {
const xhr = new XMLHttpRequest();
try {
xhr.open('HEAD', url, true);
} catch (e) {
r(0);
return;
}
xhr.timeout = 5000;
xhr.onload = () => r(xhr.status);
xhr.onerror = () => r(0);
xhr.ontimeout = () => r(0);
xhr.send();
});
}
function normalizeDestination(value) {
const trimmed = value.trim();
if (!trimmed || /\\/.test(trimmed)) return null;
const url = trimmed.charAt(0) === '/' ? trimmed : '/' + trimmed;
if (/^\/\//.test(url)) return null;
return url;
}
async function go() {
const v = input.value.trim();
if (!v) return cocoMessage.warning('请输入翻找目标');
const url = normalizeDestination(v);
if (!url) return cocoMessage.warning('翻找目标无效');
btn.textContent = '翻找中...';
closeLoading = cocoMessage.loading('正在翻找中,请稍候...');
btn.disabled = true;
try {
const status = await check(url);
if (status === 200) {
cocoMessage.success('翻找成功,即将打开目标页面');
window.open(url, '_blank');
} else if (status === 404) {
cocoMessage.warning('翻找失败,失败过多将被封禁');
} else {
cocoMessage.error('翻找异常,请重试或刷新页面');
}
} finally {
closeLoading?.();
closeLoading = null;
btn.textContent = '翻 找';
btn.disabled = false;
}
}
btn.onclick = go;
input.onkeydown = e => { if (e.key === 'Enter') go(); };
}
initializeStoreEvents();
</script>
+18
View File
@@ -0,0 +1,18 @@
<div class="nav4lite-section">
<div class="nav4lite-grid">
<a href="/address" class="nav4lite-item">
<span class="nav4lite-label">通讯录</span>
</a>
<a href="/friends" class="nav4lite-item">
<span class="nav4lite-label">朋友圈</span>
</a>
<a href="/navigator" class="nav4lite-center-dot"><i
class="sidebar-more ri-arrow-right-s-line"></i></a>
<a href="/books" class="nav4lite-item">
<span class="nav4lite-label">课外书</span>
</a>
<a href="/store" class="nav4lite-item">
<span class="nav4lite-label">储物柜</span>
</a>
</div>
</div>
+16
View File
@@ -0,0 +1,16 @@
<div class="subscribe-widget">
<div id="subscribe-message" class="subscribe-message">订阅更新</div>
<form class="subscribe-form" onsubmit="handleSubscribe(event)">
<input type="email" name="email" required class="subscribe-input" placeholder="请输入邮箱地址..." autocomplete="email">
<button type="submit" class="subscribe-btn" aria-label="Subscribe">
<i class="ri-arrow-right-line subscribe-icon"></i>
</button>
</form>
<div class="subscribe-divider"></div>
<div class="subscribe-feedback">
反馈邮箱:<a href="mailto:support@anian.net" class="feedback-email">support@anian.net</a>
</div>
</div>
+15
View File
@@ -0,0 +1,15 @@
<aside id="sideNotice" class="notice-box lotus">
<div class="notice-btn-wrap">
<span class="notice-bubble" id="noticeBubble">新功能</span>
<a href="/notice" class="notice-btn" id="noticeBtn">
<i class="remixicon ri-megaphone-line"></i>
</a>
</div>
<div class="notice-content">
<div class="content-inner">
在这里,你只能看到黄诗扶和一堆无厘头的话,但也请先仔细阅读<a href=/about>《翻阅须知》</a>
</div>
<div class="bottom-title">叽 里<br>咕 噜</div>
</div>
</aside>
+5
View File
@@ -0,0 +1,5 @@
<div class="anian-lunar-date-widget">
<div class="anian-lunar-date-text" id="anianLunarText">
一日不见 · 如隔三秋
</div>
</div>
+3
View File
@@ -0,0 +1,3 @@
<div class="card-content">
<canvas id="canvas" style="width:100%;" width="820" height="250"></canvas>
</div>
+8
View File
@@ -0,0 +1,8 @@
<div class="anianx-wrap">
<div class="anianx-group">
<button class="anianx-btn anianx-left" data-anianx-type="main">观音签</button>
<button class="anianx-btn" data-anianx-type="test">测速</button>
<button class="anianx-btn anianx-right" data-anianx-type="sub">歇后语</button>
<div class="anianx-bubble" id="anianxBubble"><span id="anianxBubbleText">一日不见 · 如隔三秋</span></div>
</div>
</div>
+12 -12
View File
@@ -10,7 +10,7 @@ spec:
- $formkit: attachment - $formkit: attachment
name: favicon name: favicon
label: favicon label: favicon
value: /themes/theme-pix/assets/img/favicon.ico value: /themes/theme-pix/assets/img/logo.webp
help: 建议制作一张400x400的png图像, 然后等比缩小到你想转换的ico尺寸,最后通过网上的工具转换成ico图标格式. help: 建议制作一张400x400的png图像, 然后等比缩小到你想转换的ico尺寸,最后通过网上的工具转换成ico图标格式.
- $formkit: menuRadio - $formkit: menuRadio
name: left_nav name: left_nav
@@ -48,7 +48,7 @@ spec:
- $formkit: attachment - $formkit: attachment
name: default_avatar name: default_avatar
label: 博主头像 label: 博主头像
value: /themes/theme-pix/assets/img/avatar.png value: /themes/theme-pix/assets/img/logo.webp
- group: base_top_set - group: base_top_set
label: 顶部设置 label: 顶部设置
@@ -56,7 +56,7 @@ spec:
- $formkit: attachment - $formkit: attachment
name: site_logo name: site_logo
label: 常规LOGO label: 常规LOGO
value: /themes/theme-pix/assets/img/avatar.png value: /themes/theme-pix/assets/img/logo.webp
- $formkit: number - $formkit: number
name: logo_height name: logo_height
label: 常规LOGO高度尺寸 label: 常规LOGO高度尺寸
@@ -66,11 +66,11 @@ spec:
name: m_site_logo name: m_site_logo
label: 方形LOGO label: 方形LOGO
help: 建议制作一张48*48的方形LOGO help: 建议制作一张48*48的方形LOGO
value: /themes/theme-pix/assets/img/avatar.png value: /themes/theme-pix/assets/img/logo.webp
- $formkit: attachment - $formkit: attachment
name: mobile_logo name: mobile_logo
label: 移动端LOGO label: 移动端LOGO
value: /themes/theme-pix/assets/img/avatar.png value: /themes/theme-pix/assets/img/logo.webp
- $formkit: checkbox - $formkit: checkbox
name: top_nav_on_web name: top_nav_on_web
id: top_nav_on_web id: top_nav_on_web
@@ -316,7 +316,7 @@ spec:
- $formkit: attachment - $formkit: attachment
name: topbg_banner name: topbg_banner
label: 顶部封面图 label: 顶部封面图
value: /themes/theme-pix/assets/img/banner.jpg value: /themes/theme-pix/assets/img/banner-all.webp
- $formkit: radio - $formkit: radio
name: home_post_type name: home_post_type
label: 首页类型模式 label: 首页类型模式
@@ -425,7 +425,7 @@ spec:
if: $get(donate_on).value if: $get(donate_on).value
name: donate_pic name: donate_pic
label: 打赏二维码 label: 打赏二维码
value: /themes/theme-pix/assets/img/avatar.png value: /themes/theme-pix/assets/img/logo.webp
- group: moments - group: moments
label: 瞬间设置 label: 瞬间设置
@@ -451,7 +451,7 @@ spec:
- $formkit: attachment - $formkit: attachment
name: banner name: banner
label: 瞬间分享背景 label: 瞬间分享背景
value: /themes/theme-pix/assets/img/banner.jpg value: /themes/theme-pix/assets/img/banner-all.webp
- $formkit: attachmentPolicySelect - $formkit: attachmentPolicySelect
name: attachmentPolicy name: attachmentPolicy
label: 附件存储策略 label: 附件存储策略
@@ -763,7 +763,7 @@ spec:
label: 一言 label: 一言
value: value:
title: 一言 title: 一言
yiyan_bg: /themes/theme-pix/assets/img/banner.jpg yiyan_bg: /themes/theme-pix/assets/img/banner-all.webp
children: children:
- $formkit: text - $formkit: text
name: title name: title
@@ -870,11 +870,11 @@ spec:
- $formkit: attachment - $formkit: attachment
name: land_logo name: land_logo
label: LOGO或头像 label: LOGO或头像
value: /themes/theme-pix/assets/img/avatar.png value: /themes/theme-pix/assets/img/logo.webp
- $formkit: attachment - $formkit: attachment
name: land_feature name: land_feature
label: 封面头图 label: 封面头图
value: /themes/theme-pix/assets/img/landimg.jpg value: /themes/theme-pix/assets/img/logo.webp
- $formkit: text - $formkit: text
name: land_title name: land_title
label: 标题或名称 label: 标题或名称
@@ -938,7 +938,7 @@ spec:
- $formkit: attachment - $formkit: attachment
name: custom_login_background name: custom_login_background
label: 背景图 label: 背景图
value: /themes/theme-pix/assets/img/default-header.png value: /themes/theme-pix/assets/img/banner-all.webp
accepts: accepts:
- "image/*" - "image/*"
+33 -22
View File
@@ -1,22 +1,33 @@
<!DOCTYPE html> <!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org" <html xmlns:th="https://www.thymeleaf.org"
th:replace="~{modules/layouts/layout :: layout(_head = null,_content = ~{::content},_title= ${'博客' + ' - ' + site.title})}"> th:replace="~{modules/layouts/layout :: layout(_head = null,_content = ~{::content},_title= ${'文章' + ' - ' + site.title})}">
<th:block th:fragment="content"> <th:block th:fragment="content">
<div class="page_main uk-width-2-3@m"> <div class="page_main uk-width-2-3@m">
<th:block th:replace="~{modules/header}"/> <th:block th:replace="~{modules/header}"/>
<div id="pjax-container" th:with="page = ${#strings.isEmpty(param.page) ? 1 : param.page}"> <div id="pjax-container" th:with="
page = ${#lists.isEmpty(param.page) or #strings.isEmpty(param.page[0]) ? 1 : param.page[0]},
<th:block th:replace="~{macro/home-blog :: home-blog(${postFinder.list(page,site.post.postPageSize)},'/archives')}"></th:block> tag = ${#lists.isEmpty(param.tag) ? null : param.tag[0]}
">
</div>
<th:block th:if="${#strings.isEmpty(tag)}">
<th:block th:replace="~{modules/footer-main}"/> <th:block th:replace="~{macro/home-blog :: home-blog(${postFinder.list(page,site.post.postPageSize)},'/archives',null,null)}"></th:block>
</th:block>
</div>
</th:block> <th:block th:unless="${#strings.isEmpty(tag)}" th:each="categoryItem : ${categoryFinder.listAll()}">
<th:block th:if="${#strings.equals(tag, categoryItem.spec.displayName) or #strings.equals(tag, categoryItem.metadata.name)}">
</html> <th:block th:replace="~{macro/home-blog :: home-blog(${postFinder.listByCategory(page,site.post.postPageSize,categoryItem.metadata.name)},'/archives',${categoryItem.spec.displayName},${categoryItem.metadata.name})}"></th:block>
</th:block>
</th:block>
</div>
<th:block th:replace="~{modules/footer-main}"/>
</div>
</th:block>
</html>
+997
View File
@@ -0,0 +1,997 @@
/* 瞬间标签与评论间距 */
.moment_tag_gap {
margin-right: 10px;
}
/* 灯箱视频格式 */
.fancybox-slide--video .fancybox-content {
width: auto !important;
height: auto !important;
background: transparent;
}
.fancybox-slide--video video.fancybox-video {
width: auto !important;
height: auto !important;
max-height: 75dvh !important;
max-width: 75dvw !important;
object-fit: contain;
}
/* 片刻视频容器鼠标样式 */
.fancybox.mo_img {
cursor: zoom-in;
}
/* 图库视频图标hover放大 */
.video-fancybox .play-icon {
transition: all 0.4s ease !important;
display: inline-flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
}
.video-fancybox:hover .play-icon {
transform: scale(1.3) !important;
opacity: 1;
}
/* 目录左边距:贴紧左侧边栏 */
.listree-box {
margin-left: 0;
}
@media (min-width: 960px) and (max-width: 1359.98px) {
.listree-box {
margin-left: calc((100vw - 860px) / 2 + 284px + 50px);
}
}
@media (min-width: 1360px) {
.listree-box {
margin-left: calc((100vw - 1400px) / 2 + 291px + 50px);
}
}
/* 登录容器 */
.custom-login-modal {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.55);
display: flex;
align-items: center;
justify-content: center;
z-index: 1999;
opacity: 0;
visibility: hidden;
transition: all .25s ease;
}
.custom-login-modal.show {
opacity: 1;
visibility: visible;
}
.login-modern-card {
width: 250px;
background: #ffffff;
padding: 30px 25px;
border-radius: 14px;
position: relative;
overflow: hidden;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.08);
}
.login-modern-card::before {
position: absolute;
content: "";
width: 360px;
height: 360px;
background: #e6f9ee;
transform: rotate(45deg);
left: -230px;
bottom: 20px;
border-radius: 40px;
z-index: 0;
}
.login-modern-form {
display: flex;
flex-direction: column;
position: relative;
z-index: 2;
}
.login-modern-title {
text-align: center;
font-size: 1.3rem;
font-weight: 600;
color: #1f2937;
margin-bottom: 15px;
}
.login-modern-input-wrapper {
width: 100%;
position: relative;
display: flex;
align-items: center;
margin-bottom: 20px;
}
.login-modern-icon {
position: absolute;
left: 2px;
font-size: 18px;
color: #6b7280;
}
.custom-login-modal input.login-modern-input {
width: 100%;
height: 38px;
padding-left: 30px;
font-size: 0.9rem;
color: #1f2937;
background: transparent !important;
border: none !important;
border-radius: 0 !important;
outline: none !important;
box-shadow: none !important;
border-bottom: 2px solid #cbd5e1 !important;
transition: border-color .2s;
}
.custom-login-modal input.login-modern-input::placeholder {
color: #94a3b8;
}
.custom-login-modal input.login-modern-input:focus {
border-bottom: 2px solid #22c55e !important;
}
.login-modern-submit {
width: 100%;
height: 35px;
border: none;
border-radius: 6px;
background: #22c55e;
color: white;
font-size: 0.9rem;
letter-spacing: .5px;
cursor: pointer;
transition: background .2s;
display: flex;
align-items: center;
justify-content: center;
padding: 0 !important;
}
.login-modern-submit:hover {
background: #16a34a;
}
.login-modern-submit:disabled {
background: #a7d7bd;
cursor: not-allowed;
color: #f0fdf4;
}
.login-modern-submit:disabled:hover {
background: #a7d7bd;
}
.close-modal-btn {
position: absolute;
right: 5px;
top: 5px;
border: none;
background: transparent;
font-size: 22px;
color: #9ca3af;
cursor: pointer;
z-index: 3;
}
/* 退出登录 */
.pixar-notice-wrapper {
display: none;
position: fixed;
inset: 0;
z-index: 1999;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, 0.35);
font-family: "Arial Rounded MT Bold", "Helvetica Rounded", Arial, sans-serif;
}
.pixar-notice-card {
--pixar-ui-blue: #5c99e2;
--pixar-ui-green: #61c470;
--pixar-ui-green-shadow: #45a253;
--pixar-ui-red: #e65e5e;
--pixar-ui-red-shadow: #c14242;
--pixar-ui-cream: #f5f1e8;
--pixar-ui-dark: #4d4c4a;
--pixar-ui-green-border: #2e7d32;
--pixar-ui-red-border: #8f2d2d;
--pixar-button-depth: 0.15em;
width: 19em;
max-width: 300px;
background: var(--pixar-ui-cream);
border-radius: 1.5em;
padding: 1.2em;
border: 0.2em solid var(--pixar-ui-dark);
box-shadow: 0.6em 0.6em 0 var(--pixar-ui-dark);
display: flex;
flex-direction: column;
transition: transform .2s cubic-bezier(.34, 1.56, .64, 1),
box-shadow .2s cubic-bezier(.34, 1.56, .64, 1);
}
.pixar-notice-card:hover {
transform: translateY(-.5em) rotate(-2deg);
box-shadow: .8em .8em 0 var(--pixar-ui-dark);
}
.pixar-notice-header {
display: flex;
align-items: center;
margin-bottom: 1em;
}
.pixar-notice-avatar {
width: 3.5em;
height: 3.5em;
border-radius: 50%;
background: linear-gradient(45deg, #f9a86d, #f48a58);
border: .2em solid var(--pixar-ui-dark);
box-shadow: .2em .2em 0 var(--pixar-ui-dark);
transition: transform .3s ease;
}
.pixar-notice-card:hover .pixar-notice-avatar {
transform: scale(1.05) rotate(5deg);
}
.pixar-notice-username {
margin-left: .8em;
font-size: 1.2em;
font-weight: 700;
color: var(--pixar-ui-dark);
}
.pixar-notice-image-area {
background: #d8d2c6;
border-radius: 1em;
padding: .8em;
border: .2em solid var(--pixar-ui-dark);
box-shadow: inset .2em .2em 0 #b3ac9f;
}
.pixar-notice-caption {
margin: 10px 5px !important;
font-size: .9em;
line-height: 1.4;
color: var(--pixar-ui-dark);
font-weight: 600;
}
.pixar-notice-actions {
display: flex;
justify-content: space-around;
margin-top: 1.5em;
}
.pixar-notice-btn {
border: .2em solid var(--pixar-ui-dark);
border-radius: 1em;
padding: .5em .8em;
font-size: 1.6em;
cursor: pointer;
background: var(--pixar-ui-green);
box-shadow:
0 var(--pixar-button-depth) 0 var(--pixar-ui-green-shadow),
0 .4em 0 var(--pixar-ui-dark);
transition: transform .1s ease,
box-shadow .1s ease;
}
.pixar-notice-btn:active {
transform: translateY(var(--pixar-button-depth));
box-shadow:
0 0 0 var(--pixar-ui-green-shadow),
0 var(--pixar-button-depth) 0 var(--pixar-ui-dark);
}
.pixar-notice-btn-close:disabled,
.pixar-notice-btn-confirm:disabled {
cursor: not-allowed;
transform: translateY(var(--pixar-button-depth));
box-shadow:
0 0 0 var(--pixar-ui-green-shadow),
0 var(--pixar-button-depth) 0 var(--pixar-ui-dark);
}
.pixar-notice-btn-close {
background: var(--pixar-ui-red);
box-shadow:
0 var(--pixar-button-depth) 0 var(--pixar-ui-red-shadow),
0 .4em 0 var(--pixar-ui-dark);
}
.pixar-notice-btn-close:active {
box-shadow:
0 0 0 var(--pixar-ui-red-shadow),
0 var(--pixar-button-depth) 0 var(--pixar-ui-dark);
}
.pixar-notice-btn-confirm:hover {
border-color: var(--pixar-ui-green-border) !important;
}
.pixar-notice-btn-close:hover {
border-color: var(--pixar-ui-red-border) !important;
}
/* 按钮禁用样式 */
.push_item:disabled {
background: #c7d4ce;
cursor: not-allowed;
color: #f0fdf4;
}
/* 删除确认弹窗样式 */
.moments-modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1999;
padding: 20px;
box-sizing: border-box;
animation: momentsFadeIn 0.2s ease;
}
.moments-card-widget {
overflow: hidden;
position: relative;
background-color: #ffffff;
text-align: left;
border-radius: 0.5rem;
width: 100%;
max-width: 400px;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
animation: momentsSlideUp 0.3s ease;
}
.moments-card-header {
padding: 1.5rem;
background-color: #ffffff;
}
.moments-header-row {
display: flex;
align-items: center;
gap: 1rem;
}
.moments-icon-wrapper {
display: flex;
flex-shrink: 0;
justify-content: center;
align-items: center;
background-color: #fee2e2;
width: 3rem;
height: 3rem;
border-radius: 9999px;
}
.moments-icon-wrapper svg {
color: #dc2626;
width: 1.5rem;
height: 1.5rem;
}
.moments-text-content {
flex: 1;
text-align: left;
}
.moments-title-text {
color: #111827;
font-size: 1.1rem;
font-weight: 600;
line-height: 1.5rem;
display: block;
}
.moments-message-text {
margin-top: 0.5rem;
margin-bottom: 0;
color: #6b7280;
font-size: 0.9rem;
line-height: 1.25rem;
}
.moments-button-group {
margin-top: 1rem;
display: flex;
gap: 0.75rem;
flex-direction: row;
}
.moments-btn-confirm {
flex: 1;
display: inline-flex;
padding: 0.5rem 1rem;
background-color: #dc2626;
color: #ffffff;
font-size: 0.9rem;
line-height: 1.3rem;
font-weight: 500;
justify-content: center;
border-radius: 0.375rem;
border-width: 1px;
border-color: transparent;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
cursor: pointer;
transition: background-color 0.2s;
white-space: nowrap;
}
.moments-btn-confirm:hover {
background-color: #b91c1c;
}
.moments-btn-cancel {
flex: 1;
display: inline-flex;
padding: 0.5rem 1rem;
background-color: #ffffff;
color: #374151;
font-size: 0.9rem;
line-height: 1.3rem;
font-weight: 500;
justify-content: center;
border-radius: 0.375rem;
border: 1px solid #d1d5db;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
cursor: pointer;
transition: background-color 0.2s;
white-space: nowrap;
}
.moments-btn-cancel:hover {
background-color: #f3f4f6;
}
.moments-btn-confirm:disabled {
background-color: #fecaca;
cursor: not-allowed;
box-shadow: none;
opacity: 0.7;
}
.moments-btn-cancel:disabled,
.push_close:disabled {
background-color: #e7f0e5;
color: #9ca3af;
border-color: #e7f0e5;
cursor: not-allowed;
box-shadow: none;
opacity: 0.7;
}
/* --- 追加:黄色警告模式 --- */
.theme-yellow .moments-icon-wrapper {
background-color: #fef3c7;
}
.theme-yellow .moments-icon-wrapper svg {
color: #d97706;
}
.theme-yellow .moments-btn-confirm {
background-color: #d97706;
}
.theme-yellow .moments-btn-confirm:hover {
background-color: #b45309;
}
/* --- 追加:绿色确认模式 --- */
.theme-green .moments-icon-wrapper {
background-color: #d1fae5;
}
.theme-green .moments-icon-wrapper svg {
color: #059669;
}
.theme-green .moments-btn-confirm {
background-color: #059669;
}
.theme-green .moments-btn-confirm:hover {
background-color: #047857;
}
/* --- 追加:红色保持默认,这里只是为了统一命名,可不加 --- */
.theme-red .moments-icon-wrapper {
background-color: #fee2e2;
}
.theme-red .moments-icon-wrapper svg {
color: #dc2626;
}
.theme-red .moments-btn-confirm {
background-color: #dc2626;
}
@keyframes momentsFadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes momentsSlideUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* --- 删除动画 --- */
/* 飘散消失动画 (持续 0.8s) */
.moments-delete-disperse {
animation: momentsDeleteDisperse 0.8s ease-in forwards;
transform-origin: center bottom;
pointer-events: none;
}
@keyframes momentsDeleteDisperse {
0% {
opacity: 1;
transform: translateY(0) rotate(0deg) scale(1);
filter: blur(0);
}
20% {
opacity: 0.9;
transform: translateY(-10px) rotate(2deg) scale(1.02);
}
100% {
opacity: 0;
transform: translateY(-120px) rotate(-8deg) scale(0.6);
filter: blur(3px);
}
}
.douban_page #post_item {
padding: 0;
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
column-gap: 0;
row-gap: 0;
align-items: center;
}
.douban_page #post_item .loading_box {
text-align: center;
grid-column: 1 / -1;
}
.douban_page #post_item > .no-results {
grid-column: 1 / -1;
width: 100%;
}
.douban_page #post_item > #prev_pagination {
grid-column: 1 / -1;
}
.douban_page #post_item .douban_card_item {
width: 100%;
margin: 0;
padding: 0 40px;
border-right: 1px solid #e5e7eb;
display: flex;
align-items: flex-start;
}
.douban_page #post_item .douban_card_item:nth-child(2n) {
border-right: none;
}
.douban_page #post_item .douban_card_inner {
display: grid !important;
grid-template-columns: 100px 1fr;
grid-template-rows: auto auto 1fr;
column-gap: 16px;
row-gap: 3px;
padding: 20px 0;
background: transparent;
width: 100%;
align-items: start;
}
.douban_page #post_item .douban_media_col {
width: 100px;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: center;
grid-column: 1;
grid-row: 2 / 4;
}
.douban_page #post_item {
align-items: stretch !important;
}
.douban_page #post_item .douban_poster_link,
.douban_page #post_item .douban_poster_img {
width: 100px;
}
.douban_page #post_item .douban_poster_img {
display: block;
height: 140px;
object-fit: cover;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.douban_page #post_item .douban_rating_wrap {
display: none;
}
.douban_page #post_item .douban_text_col {
display: contents;
}
.douban_page #post_item .douban_title_row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
grid-column: 1 / -1;
grid-row: 1;
margin-bottom: 6px;
text-align: left;
}
.douban_page #post_item .douban_title_text {
min-width: 0;
overflow: hidden;
color: var(--pix-theme);
font-size: 16px;
line-height: 1.35;
font-weight: 650;
text-decoration: none;
text-overflow: ellipsis;
white-space: nowrap;
}
.douban_page #post_item .douban_title_text:hover {
color: var(--hilight-color);
}
.douban_page #post_item .douban_title_text_plain,
.douban_page #post_item .douban_title_text_plain:hover {
color: #000;
}
.douban_page #post_item .douban_detail_link {
flex: 0 0 auto;
width: auto;
height: auto;
display: inline-flex;
align-items: center;
justify-content: center;
padding: 3px 5px;
border-radius: 4px;
color: var(--pix-theme);
font-size: 13px;
line-height: 1.4;
text-decoration: none;
visibility: hidden;
pointer-events: none;
transition: color 0.2s ease, background-color 0.2s ease;
}
.douban_page #post_item .douban_detail_link.is-visible {
visibility: visible;
pointer-events: auto;
}
.douban_page #post_item .douban_detail_link:hover {
background: var(--pix-background);
color: var(--hilight-color);
}
.douban_page #post_item .douban_sub_meta {
display: flex;
align-items: center;
gap: 0px;
margin-bottom: 6px;
flex-wrap: wrap;
grid-column: 2;
grid-row: 2;
align-self: start;
}
.douban_page #post_item .douban_time_text {
display: inline-block;
padding: 3px 8px;
border-radius: 6px;
background: rgba(168, 85, 247, 0.12);
color: #8b5cf6;
font-size: 12px;
line-height: 1.4;
font-weight: 500;
margin-right: 7px;
}
.douban_page #post_item .douban_type_badge {
display: inline-block;
padding: 3px 8px;
border-radius: 6px;
background: rgba(14, 165, 233, 0.13);
color: #0284c7;
font-size: 12px;
line-height: 1.4;
vertical-align: middle;
font-weight: 500;
margin-right: 7px;
}
.douban_page #post_item .douban_score_badge {
display: inline-block;
padding: 3px 8px;
border-radius: 6px;
background: rgba(249, 115, 22, 0.14);
color: #ea580c;
font-size: 12px;
line-height: 1.4;
vertical-align: middle;
font-weight: 500;
}
.douban_page #post_item .douban_content_box {
color: #55716a;
grid-column: 2;
grid-row: 3;
align-self: start;
}
.douban_page #post_item .douban_content_main {
margin: 0;
font-size: 14px;
line-height: 1.6;
color: #43584d;
white-space: pre-wrap;
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 5;
}
.douban_page #post_item .douban_expand_btn {
display: inline-block;
padding: 0;
color: var(--pix-theme);
font-size: 14px;
font-weight: 500;
background: transparent;
border: none;
cursor: pointer;
text-decoration: none;
line-height: 1.6;
}
.douban_page #post_item .douban_content_meta {
margin: 8px 0 0;
font-size: 12px;
color: #76937f;
line-height: 1.5;
}
.douban_detail_page #post_item {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.douban_detail_page #post_item .douban_card_item {
grid-column: 1 / -1;
border-right: none;
padding: 35px 58px;
}
.douban_detail_page #post_item .douban_detail_link {
display: none;
}
.douban_detail_page #post_item .douban_title_text {
overflow: visible;
text-overflow: clip;
white-space: normal;
overflow-wrap: anywhere;
}
.douban_detail_page #post_item .douban_content_main {
display: block;
overflow: visible;
overflow-wrap: anywhere;
-webkit-line-clamp: unset;
-webkit-box-orient: initial;
}
@media only screen and (max-width:1359px) {
.douban_page #post_item {
grid-template-columns: 1fr;
}
.douban_page #post_item .douban_card_item {
border-right: none !important;
}
}
@media only screen and (max-width:540px) {
.douban_page #post_item .douban_card_item {
padding: 0 20px;
}
.douban_page #post_item .douban_card_inner {
column-gap: 11px;
row-gap: 0px;
}
.douban_detail_page #post_item .douban_card_item {
padding: 0 20px;
}
}
.equipments_page #post_item {
padding: 0;
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
column-gap: 0;
row-gap: 0;
align-items: stretch;
}
.equipments_page #post_item .loading_box,
.equipments_page #post_item > .no-results,
.equipments_page #post_item > #prev_pagination {
grid-column: 1 / -1;
}
.equipments_page #post_item .equipment_item {
width: 100%;
margin: 0;
padding: 10px 40px;
border: 0;
display: flex;
align-items: center;
}
.equipments_page #post_item .equipment_card_inner {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.equipments_page #post_item .equipment_visual {
position: relative;
display: block;
width: 100%;
aspect-ratio: 1;
padding: 0;
overflow: hidden;
border: 0;
border-radius: 50%;
background: transparent;
color: inherit;
cursor: pointer;
font: inherit;
line-height: 1;
}
.equipments_page #post_item .equipment_visual > img {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 50%;
}
.equipments_page #post_item .equipment_overlay {
position: absolute;
inset: 0;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 30px;
overflow: auto;
border-radius: 50%;
background: radial-gradient(
circle closest-side at center,
rgba(0, 0, 0, 0.5) 0%,
rgba(0, 0, 0, 0.5) 70%,
rgba(0, 0, 0, 0.3) 85%,
rgba(0, 0, 0, 0) 100%
);
color: #fff;
text-align: center;
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: opacity 0.2s ease, visibility 0.2s ease;
}
.equipments_page #post_item .equipment_visual:hover .equipment_overlay {
opacity: 1;
visibility: visible;
pointer-events: auto;
}
.equipments_page #post_item .equipment_description {
max-width: 100%;
max-height: 100%;
margin: 0;
overflow: auto;
color: #fff;
font-size: 14px;
line-height: 1.6;
white-space: pre-wrap;
overflow-wrap: anywhere;
font-size: 16px;
}
@media only screen and (max-width:1366px) {
.equipments_page #post_item {
grid-template-columns: 1fr;
}
.equipments_page #post_item .equipment_item {
width: 70%;
margin-left: auto;
margin-right: auto;
}
}
@media only screen and (max-width:540px) {
.equipments_page #post_item {
grid-template-columns: 1fr;
}
.equipments_page #post_item .equipment_item {
width: 100%;
padding: 10px 20px;
}
.equipments_page #post_item .equipment_overlay {
padding: 24px;
}
}
+41
View File
@@ -129,6 +129,47 @@
background-color: var(--dark-ac-btn); background-color: var(--dark-ac-btn);
} }
.dark .listree-box {
color: var(--light-color);
background: rgb(27 29 48 / 94%);
border-color: var(--dark-border);
box-shadow: 0 18px 48px rgb(0 0 0 / 28%);
}
.dark .toc_nav .listree-btn:hover,
.dark .toc_nav .listree-btn.is-active {
background: transparent;
box-shadow: none;
}
.dark .toc_nav .listree-btn.is-active i {
transform: rotate(90deg);
}
.dark #listree-ol ul {
border-left-color: var(--dark-border);
}
.dark #listree-ol li a {
color: var(--text-color);
}
.dark #listree-ol li a:hover {
color: var(--light-color);
background: rgb(255 255 255 / 7%);
}
.dark #listree-ol>li.current>a,
.dark #listree-ol>li ul li.current>a {
color: var(--dark-ac-btn);
border-left-color: var(--dark-ac-btn);
background: rgb(43 72 255 / 13%);
}
.dark #listree-ol::-webkit-scrollbar-thumb {
background: rgb(43 72 255 / 48%);
}
.dark .uk-dropdown{ .dark .uk-dropdown{
background: var(--dark-border); background: var(--dark-border);
color: #666; color: #666;
+7165 -5842
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 B

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 1l8.217 1.826a1 1 0 0 1 .783.976v9.987a6 6 0 0 1-2.672 4.992L12 23l-6.328-4.219A6 6 0 0 1 3 13.79V3.802a1 1 0 0 1 .783-.976L12 1zm0 2.049L5 4.604v9.185a4 4 0 0 0 1.781 3.328L12 20.597l5.219-3.48A4 4 0 0 0 19 13.79V4.604L12 3.05zM12 7a2 2 0 0 1 1.001 3.732L13 15h-2v-4.268A2 2 0 0 1 12 7z" fill="rgba(255,255,255,1)"/></svg>

Before

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 383 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zM9 9v6h2V9H9zm4 0v6h2V9h-2z"/></svg>

Before

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zM10.622 8.415a.4.4 0 0 0-.622.332v6.506a.4.4 0 0 0 .622.332l4.879-3.252a.4.4 0 0 0 0-.666l-4.88-3.252z"/></svg>

Before

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M8 11.333l10.223-6.815a.5.5 0 0 1 .777.416v14.132a.5.5 0 0 1-.777.416L8 12.667V19a1 1 0 0 1-2 0V5a1 1 0 1 1 2 0v6.333z"/></svg>

Before

Width:  |  Height:  |  Size: 256 B

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24"><path fill="none" d="M0 0h24v24H0z"/><path d="M16 12.667L5.777 19.482A.5.5 0 0 1 5 19.066V4.934a.5.5 0 0 1 .777-.416L16 11.333V5a1 1 0 0 1 2 0v14a1 1 0 0 1-2 0v-6.333z"/></svg>

Before

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path fill="none" d="M0 0h24v24H0z"/><path d="M2 19h20v2H2v-2zM2 5l5 3.5L12 2l5 6.5L22 5v12H2V5zm2 3.841V15h16V8.841l-3.42 2.394L12 5.28l-4.58 5.955L4 8.84z" fill="rgba(255,193,116,1)"/></svg>

Before

Width:  |  Height:  |  Size: 275 B

+283
View File
@@ -0,0 +1,283 @@
// 接管手机端底部音乐按钮
function modify_m_bottom_music_button_link() {
const music = document.querySelector('a[href*="#m_bottom_music_button"]');
if (!music) return;
music.addEventListener('click', function (event) {
event.preventDefault();
if (!box_up) mu_box_show();
else mu_box_hide(0);
});
}
// 登录逻辑
let csrfToken = '';
let publicKey = '';
let loginTimer = null;
let countdownSeconds = 3;
let closeLoading = null;
const login_modal = document.getElementById('customLoginModal');
const login_closeBtn = document.getElementById('closeLoginModal');
const login_loginBtn = document.getElementById('customLoginBtn');
// 获取令牌及公钥
async function fetchCSRFToken() {
login_modal.classList.add('show');
login_loginBtn.disabled = true;
login_loginBtn.textContent = '加载中...';
closeLoading = cocoMessage.loading('正在加载署名信息...');
try {
const response = await fetch('/login', { credentials: 'include' });
if (response.url.includes('/uc')) {
return 'logged';
}
const htmlText = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString(htmlText, 'text/html');
// 获取 CSRF Token
const csrfInput = doc.querySelector('input[name="_csrf"]');
if (!csrfInput || !csrfInput.value) {
throw new Error('页面未找到 CSRF 令牌');
}
csrfToken = csrfInput.value;
// 获取 PublicKey
const publicKeyMatch = htmlText.match(/const publicKey = "([^"]+)";/);
if (!publicKeyMatch || !publicKeyMatch[1]) {
throw new Error('页面未找到 PublicKey');
}
publicKey = publicKeyMatch[1].replace(/\\\//g, '/');
return 'ok';
} catch (err) {
closeLoading?.()
cocoMessage.error('署名信息加载失败,请刷新');
if (loginTimer) { clearInterval(loginTimer) }
login_loginBtn.textContent = '错误!请刷新';
login_loginBtn.disabled = true;
return 'error';
}
}
// 打开弹窗逻辑
async function open_login_box() {
if (loginTimer) { clearInterval(loginTimer) }
countdownSeconds = 3;
const success = await fetchCSRFToken();
if (success === 'logged') {
closeLoading?.()
cocoMessage.success('您已署名成功,请刷新');
login_modal.classList.remove('show');
} else if (success === 'ok') {
loginTimer = setInterval(() => {
if (countdownSeconds > 0) {
login_loginBtn.textContent = '请稍等...' + countdownSeconds;
} else {
clearInterval(loginTimer);
closeLoading?.()
cocoMessage.success('署名信息加载完成');
login_loginBtn.textContent = '署 名';
login_loginBtn.disabled = false;
}
countdownSeconds--;
}, 1000);
} else {
closeLoading?.()
cocoMessage.error('服务器错误,请刷新');
login_modal.classList.remove('show');
}
}
// 关闭弹窗
login_closeBtn.addEventListener('click', () => {
closeLoading?.()
login_modal.classList.remove('show');
if (loginTimer) { clearInterval(loginTimer) }
});
// 加密逻辑
function encryptPassword(password) {
try {
const encrypt = new JSEncrypt();
encrypt.setPublicKey(publicKey);
return encrypt.encrypt(password);
} catch (e) {
return null;
}
}
// 提交登录
login_loginBtn.addEventListener('click', async () => {
let islogin = false;
const username = document.getElementById('customUsername').value.trim();
const password = document.getElementById('customPassword').value.trim();
if (!username || !password) {
cocoMessage.warning('请先填写信息');
return;
}
const encryptedPwd = encryptPassword(password);
login_loginBtn.disabled = true;
login_closeBtn.disabled = true;
login_loginBtn.textContent = '验证中...';
closeLoading = cocoMessage.loading('正在验证信息...');
try {
countdownSeconds = 3
const response = await fetch('/login', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
credentials: 'include',
body: new URLSearchParams({
_csrf: csrfToken,
username: username,
password: encryptedPwd,
'remember-me': 'true'
})
});
// 核心细节:判断最终落点 URL
const finalUrl = response.url;
if (loginTimer) {
clearInterval(loginTimer);
}
if (finalUrl.includes('/uc')) {
islogin = true;
closeLoading?.()
cocoMessage.success('署名成功,正在跳转...');
login_loginBtn.textContent = '跳转中...';
const currentScrollTop = window.scrollY || document.documentElement.scrollTop;
$.cookie('load_type', 'login', { path: '/' });
window.location.reload();
} else if (finalUrl.includes('error=invalid-credential')) {
closeLoading?.()
cocoMessage.warning('署名人与心上锁不匹配');
} else {
closeLoading?.()
cocoMessage.error('速度过快,请稍后重试');
}
} catch (err) {
closeLoading?.()
cocoMessage.error('服务器连接失败');
} finally {
if (!islogin) {
fetchCSRFToken();
login_closeBtn.disabled = false;
loginTimer = setInterval(() => {
if (countdownSeconds > 0) {
login_loginBtn.textContent = '请稍等...' + countdownSeconds;
} else {
clearInterval(loginTimer);
closeLoading?.()
cocoMessage.success('请重新尝试署名');
login_loginBtn.textContent = '署 名';
login_loginBtn.disabled = false;
}
countdownSeconds--;
}, 1000);
}
}
});
// 回车登录
document.addEventListener("keydown", function (e) {
if (e.key === "Enter") {
const modal = document.getElementById("customLoginModal");
if (modal.classList.contains("show")) {
document.getElementById("customLoginBtn").click();
}
}
});
// 退出登录
async function customLogout() {
$('.pixar-notice-btn-confirm, .pixar-notice-btn-close').prop('disabled', true);
let closeLoading = cocoMessage.loading('退出登录中...');
try {
const getResponse = await fetch('/logout', { credentials: 'include' });
const htmlText = await getResponse.text();
const parser = new DOMParser();
const doc = parser.parseFromString(htmlText, 'text/html');
const logoutCsrfToken = doc.querySelector('input[name="_csrf"]')?.value;
if (!logoutCsrfToken) {
cocoMessage.error('页面未找到 CSRF 令牌');
$('.pixar-notice-btn-confirm, .pixar-notice-btn-close').prop('disabled', false);
closeLoading();
return;
}
const postResponse = await fetch('/logout', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
credentials: 'include',
body: new URLSearchParams({
'_csrf': logoutCsrfToken
})
});
if (postResponse.ok) {
closeLoading();
cocoMessage.success('您已成功退出登录');
const currentScrollTop = window.scrollY || document.documentElement.scrollTop;
$.cookie('load_type', 'logout', { path: '/' });
window.location.reload();
} else {
closeLoading();
$('.pixar-notice-btn-confirm, .pixar-notice-btn-close').prop('disabled', false);
throw new Error('退出请求被拒绝');
}
} catch (err) {
closeLoading();
$('.pixar-notice-btn-confirm, .pixar-notice-btn-close').prop('disabled', false);
console.error('Logout Error:', err);
cocoMessage.error('退出失败,请手动刷新页面');
}
}
function open_logout_box() {
const wrapper = document.getElementById("pixarNoticeWrapper");
wrapper.style.display = "flex";
}
function hidePixarCard() {
const wrapper = document.getElementById("pixarNoticeWrapper");
wrapper.style.display = "none";
}
function confirmPixarAction() {
customLogout();
}
function initDoubanDetailLinks() {
document.querySelectorAll('.douban_page:not(.douban_detail_page) .douban_card_item').forEach(function (card) {
var link = card.querySelector('.douban_detail_link');
var title = card.querySelector('.douban_title_text');
var content = card.querySelector('.douban_content_main');
if (!link || !title || !content) {
return;
}
var titleOverflow = title.scrollWidth > title.clientWidth;
var contentOverflow = content.scrollHeight > content.clientHeight + 1;
link.classList.toggle('is-visible', titleOverflow || contentOverflow);
});
}
document.addEventListener('pjax:complete', initDoubanDetailLinks);
document.addEventListener('DOMContentLoaded', initDoubanDetailLinks);
window.addEventListener('resize', initDoubanDetailLinks);
// 打开确认模态框
function openModal(title, msg, type) {
var modal = $('#momentsDeleteModal');
var card = modal.find('.moments-card-widget');
modal.find('.moments-title-text').text(title);
modal.find('.moments-message-text').text(msg);
card.removeClass('theme-red theme-yellow theme-green').addClass('theme-' + type);
$('.moments-btn-cancel, .moments-btn-confirm').prop('disabled', false);
$('body').off('click', '#momentsCancelBtn');
$('body').off('click', '#momentsConfirmBtn');
modal.fadeIn(200);
}
// 主题通用交互初始化
modify_m_bottom_music_button_link();
+3265 -1594
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+241 -216
View File
@@ -1,217 +1,242 @@
var pix = { var pix = {
/** /**
* 页面運行時間 * 页面運行時間
*/ */
addRuntime: function () { addRuntime: function () {
const $runtimeCount = document.getElementById('runtimeshow'); const $runtimeCount = document.getElementById('runtimeshow');
if ($runtimeCount) { if ($runtimeCount) {
var s1 = $runtimeCount.innerText;//建站时间 var s1 = $runtimeCount.innerText;//建站时间
if (s1) { if (s1) {
s1 = new Date(s1.replace(/-/g, "/")); s1 = new Date(s1.replace(/-/g, "/"));
s2 = new Date(); s2 = new Date();
var days = s2.getTime() - s1.getTime(); var days = s2.getTime() - s1.getTime();
var number_of_days = parseInt(days / (1000 * 60 * 60 * 24)); var number_of_days = parseInt(days / (1000 * 60 * 60 * 24));
$runtimeCount.innerText = number_of_days + '天'; $runtimeCount.innerText = number_of_days + '天';
} }
} }
}, },
loadLightbox: function () { loadLightbox: function () {
/** /**
* fancybox * fancybox
*/ */
const addFancybox = function (ele) { const addFancybox = function (ele) {
const runFancybox = (ele) => { const runFancybox = (ele) => {
ele.each(function (i, o) { ele.each(function (i, o) {
const $this = $(o) const $this = $(o)
const lazyloadSrc = $this.attr('data-src') || $this.attr('src') const lazyloadSrc = $this.attr('data-src') || $this.attr('src')
const dataCaption = $this.attr('alt') || '' const dataCaption = $this.attr('alt') || ''
$this.wrap(`<a href="${lazyloadSrc}" data-fancybox="images" class="fancybox" data-srcset="${lazyloadSrc}"></a>`) $this.wrap(`<a href="${lazyloadSrc}" data-fancybox="images" class="fancybox" data-srcset="${lazyloadSrc}"></a>`)
}) })
} }
runFancybox($(ele)) runFancybox($(ele))
} }
const $fancyboxEle = document.querySelectorAll('.single-content :not(a) > img,.single-content > img') const $fancyboxEle = document.querySelectorAll('.single-content :not(a) > img,.single-content > img')
if ($fancyboxEle.length > 0) { if ($fancyboxEle.length > 0) {
addFancybox($fancyboxEle) addFancybox($fancyboxEle)
} }
}, },
initTopBar: function () { initTopBar: function () {
let topBar = $('#masthead .top_bar'); let topBar = $('#masthead .top_bar');
var placeholder = document.querySelector('#masthead .uk-sticky-placeholder'); var placeholder = document.querySelector('#masthead .uk-sticky-placeholder');
var width = $('#masthead').width(); var width = $('#masthead').width();
var height = '71.9979px;' var height = '71.9979px;'
if (window.innerWidth < 960) { if (window.innerWidth < 960) {
if (window.innerWidth <= 540) { if (window.innerWidth <= 540) {
height = '63.9957px' height = '63.9957px'
} }
topBar.addClass('uk-sticky-fixed').css({ "width": width, "top": '0px', "position": "fixed" }); topBar.addClass('uk-sticky-fixed').css({ "width": width, "top": '0px', "position": "fixed" });
$('#masthead .uk-sticky-placeholder').css({ "width": width, "height": height, "margin": "0px" }); $('#masthead .uk-sticky-placeholder').css({ "width": width, "height": height, "margin": "0px" });
placeholder.hidden = false; placeholder.hidden = false;
} else { } else {
topBar.removeAttr("style"); topBar.removeAttr("style");
$('#masthead .uk-sticky-placeholder').css({ "width": width, "height": '71.9979px;', "margin": "0px" }); $('#masthead .uk-sticky-placeholder').css({ "width": width, "height": '71.9979px;', "margin": "0px" });
placeholder.hidden = true; placeholder.hidden = true;
} }
}, },
getMsg: function () { getMsg: function () {
var com_msg_btn = $('.com_msg_btn') var com_msg_btn = $('.com_msg_btn')
var msg_modal_inner = $('.msg_modal_inner') var msg_modal_inner = $('.msg_modal_inner')
let username = Theme.username let username = Theme.username
var f_unread_num = 0; var f_unread_num = 0;
if (com_msg_btn.length > 0) { if (com_msg_btn.length > 0) {
$.ajax({ $.ajax({
type: "get", type: "get",
url: `/apis/api.notification.halo.run/v1alpha1/userspaces/${username}/notifications?fieldSelector=spec.unread=true`, url: `/apis/api.notification.halo.run/v1alpha1/userspaces/${username}/notifications?fieldSelector=spec.unread=true`,
success: function (res) { success: function (res) {
if (res.total == 0) { if (res.total == 0) {
$('.f_unread_num').remove(); $('.f_unread_num').remove();
}else { }else {
f_unread_num = res.total f_unread_num = res.total
com_msg_btn.append(`<small class="f_unread_num">${res.total}</small>`); com_msg_btn.append(`<small class="f_unread_num">${res.total}</small>`);
} }
if (msg_modal_inner.length > 0) { if (msg_modal_inner.length > 0) {
$.ajax({ $.ajax({
type: "get", type: "get",
url: `/apis/api.notification.halo.run/v1alpha1/userspaces/${username}/notifications?page=1&size=10`, url: `/apis/api.notification.halo.run/v1alpha1/userspaces/${username}/notifications?page=1&size=10`,
success: function (res) { success: function (res) {
var data = res.items var data = res.items
if (data.length > 0) { if (data.length > 0) {
var unread_box = ` var unread_box = `
<div class="unread_box"> <div class="unread_box">
<div class="unread_tip"># 您有${f_unread_num}条未读消息 # <div class="unread_tip"># 您有${f_unread_num}条未读消息 #
<a href="/uc/notifications" target="_blank">查看全部</a> <a href="/uc/notifications" target="_blank">查看全部</a>
</div> </div>
` `
var read_box = ` var read_box = `
<div class="read_box"> <div class="read_box">
` `
var unreadSum = 0 var unreadSum = 0
data.forEach((e => { data.forEach((e => {
if (e.spec.unread) { if (e.spec.unread) {
unreadSum = unreadSum + 1; unreadSum = unreadSum + 1;
unread_box += ` unread_box += `
<div class="vi_reply_item"> <div class="vi_reply_item">
<div class="box" msg-name="${e.metadata.name}"> <div class="box" msg-name="${e.metadata.name}">
<span class="mark_read">标记为已读</span> <span class="mark_read">标记为已读</span>
<div class="avatar_top"><img alt="" <div class="avatar_top"><img alt=""
src="https://cravatar.cn/avatar/?d=mp" src="https://cravatar.cn/avatar/?d=mp"
class="avatar avatar-50 photo" height="50" width="50" decoding="async"></div> class="avatar avatar-50 photo" height="50" width="50" decoding="async"></div>
${e.spec.htmlContent} ${e.spec.htmlContent}
</div> </div>
</div> </div>
` `
} else { } else {
read_box += ` read_box += `
<div class="vi_reply_item"> <div class="vi_reply_item">
<div class="box"> <div class="box">
<div class="avatar_top"><img alt="" <div class="avatar_top"><img alt=""
src="https://cravatar.cn/avatar/?d=mp" src="https://cravatar.cn/avatar/?d=mp"
class="avatar avatar-50 photo" height="50" width="50" decoding="async"></div> class="avatar avatar-50 photo" height="50" width="50" decoding="async"></div>
${e.spec.htmlContent} ${e.spec.htmlContent}
</div> </div>
</div> </div>
` `
} }
} }
)) ))
unread_box += `</div>` unread_box += `</div>`
read_box += `</div><p class="msg_limit">只显示最新10条未读和已读信息</p> ` read_box += `</div><p class="msg_limit">只显示最新10条未读和已读信息</p> `
msg_modal_inner.html(unread_box + read_box) msg_modal_inner.html(unread_box + read_box)
} else { } else {
msg_modal_inner.html(` msg_modal_inner.html(`
<p class="no_posts"><small># 暂无消息 #</small><img class="s_nodata" <p class="no_posts"><small># 暂无消息 #</small><img class="s_nodata"
src="/themes/theme-pix/assets/img/nodata.png"></p> src="/themes/theme-pix/assets/img/no-data.webp"></p>
<p class="msg_limit">只显示最新10条未读和已读信息</p> <p class="msg_limit">只显示最新10条未读和已读信息</p>
`) `)
} }
}, },
error: function () { error: function () {
msg_modal_inner.html(` msg_modal_inner.html(`
<p class="no_posts"><small># 暂无消息 #</small><img class="s_nodata" <p class="no_posts"><small># 暂无消息 #</small><img class="s_nodata"
src="/themes/theme-pix/assets/img/nodata.png"></p> src="/themes/theme-pix/assets/img/no-data.webp"></p>
<p class="msg_limit">只显示最新10条未读和已读信息</p> <p class="msg_limit">只显示最新10条未读和已读信息</p>
`) `)
}, },
}); });
} }
} }
}); });
} }
}, },
initGalleryPhotos: function () { initGalleryPhotos: function () {
var galleryPhotos = document.querySelectorAll('.gallery-photos .gallery-photo'); var galleryPhotos = document.querySelectorAll('.gallery-photos .gallery-photo');
if (galleryPhotos.length > 0) { if (galleryPhotos.length > 0) {
// 瀑布流排版 // 瀑布流排版
imgStatus.watch('.photo-img', () => { imgStatus.watch('.photo-img', () => {
waterfall('.gallery-photos'); waterfall('.gallery-photos');
}); });
} }
}, },
topCategoriesBarScroll: function () { centerCategoryNavActive: function ($link) {
if (document.getElementById("cat_nav_items")) { var $activeLink = $link && $link.length ? $link : $('.moment_cat_nav .active, .posts_cat_nav .active, .photos_cat_nav .active, .douban_cat_nav .active, .friends_cat_nav .active').first();
let xscroll = document.getElementById("cat_nav_items"); if (!$activeLink || !$activeLink.length) {
xscroll.addEventListener("mousewheel", function (e) { return;
//计算鼠标滚轮滚动的距离 }
let v = -e.wheelDelta / 2;
xscroll.scrollLeft += v; var navEl = $activeLink.closest('.moment_cat_nav, .posts_cat_nav, .photos_cat_nav, .douban_cat_nav, .friends_cat_nav').find('ul').get(0);
//阻止浏览器默认方法 if (!navEl) {
e.preventDefault(); return;
}, false); }
}
}, var linkEl = $activeLink.get(0);
var maxScrollLeft = navEl.scrollWidth - navEl.clientWidth;
topTableBarScroll: function () { if (maxScrollLeft <= 0) {
var tables = document.querySelectorAll('.single-content table') return;
if (tables.length>0) { }
tables.forEach(function (xscroll) {
xscroll.addEventListener("mousewheel", function (e) { var navRect = navEl.getBoundingClientRect();
//计算鼠标滚轮滚动的距离 var linkRect = linkEl.getBoundingClientRect();
let v = -e.wheelDelta / 2; var targetScrollLeft = navEl.scrollLeft + (linkRect.left - navRect.left) - (navRect.width / 2 - linkRect.width / 2);
xscroll.scrollLeft += v; targetScrollLeft = Math.max(0, Math.min(maxScrollLeft, targetScrollLeft));
//阻止浏览器默认方法
e.preventDefault(); $(navEl).stop(true).animate({ scrollLeft: targetScrollLeft }, 220);
}, false); },
})
} topCategoriesBarScroll: function () {
}, if (document.getElementById("cat_nav_items")) {
let xscroll = document.getElementById("cat_nav_items");
//处理瞬间权限 xscroll.addEventListener("mousewheel", function (e) {
roleMoments: async function () { //计算鼠标滚轮滚动的距离
const response = await fetch(`/apis/api.console.halo.run/v1alpha1/users/-`); let v = -e.wheelDelta / 2;
const data = await response.json(); xscroll.scrollLeft += v;
var roles = data.roles; //阻止浏览器默认方法
e.preventDefault();
if(roles.length>0) { }, false);
var name = roles[0].metadata.name }
if(name != 'super-role') { },
let dependencies = roles[0].metadata.annotations["rbac.authorization.halo.run/dependencies"]
if(dependencies != undefined) { topTableBarScroll: function () {
var targetRoles = ["role-template-uc-moments-approved","role-template-uc-moments-publish"] var tables = document.querySelectorAll('.single-content table')
const result = targetRoles.some(r => dependencies.includes(r)); if (tables.length>0) {
if(!result) { tables.forEach(function (xscroll) {
$('.normal_edit').remove(); xscroll.addEventListener("mousewheel", function (e) {
} //计算鼠标滚轮滚动的距离
}else { let v = -e.wheelDelta / 2;
$('.normal_edit').remove(); xscroll.scrollLeft += v;
} //阻止浏览器默认方法
} e.preventDefault();
}, false);
} })
}, }
},
//处理瞬间权限
roleMoments: async function () {
const response = await fetch(`/apis/api.console.halo.run/v1alpha1/users/-`);
const data = await response.json();
var roles = data.roles;
if(roles.length>0) {
var name = roles[0].metadata.name
if(name != 'super-role') {
let dependencies = roles[0].metadata.annotations["rbac.authorization.halo.run/dependencies"]
if(dependencies != undefined) {
var targetRoles = ["role-template-uc-moments-approved","role-template-uc-moments-publish"]
const result = targetRoles.some(r => dependencies.includes(r));
if(!result) {
$('.normal_edit').remove();
}
}else {
$('.normal_edit').remove();
}
}
}
},
} }
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+3234 -2583
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+74 -1
View File
@@ -1 +1,74 @@
@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}to{-webkit-transform:rotate(360deg)}}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(360deg)}}#nprogress{pointer-events:none}#nprogress .bar{background:#172eff;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #29d,0 0 5px #29d;opacity:1;-webkit-transform:rotate(3deg) translate(0,-4px);-ms-transform:rotate(3deg) translate(0,-4px);transform:rotate(3deg) translate(0,-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:solid 2px transparent;border-top-color:#29d;border-left-color:#29d;border-radius:50%;-webkit-animation:nprogress-spinner 400ms linear infinite;animation:nprogress-spinner 400ms linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute} @-webkit-keyframes nprogress-spinner {
0% {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
}
}
@keyframes nprogress-spinner {
0% {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
#nprogress {
pointer-events: none;
}
#nprogress .bar {
background: #22bb6d; /* ? 修改后的横条颜色 */
position: fixed;
z-index: 1031;
top: 0;
left: 0;
width: 100%;
height: 2px;
}
#nprogress .peg {
display: block;
position: absolute;
right: 0;
width: 100px;
height: 100%;
box-shadow: 0 0 10px #22bb6d, 0 0 5px #22bb6d;
opacity: 1;
transform: rotate(3deg) translate(0, -4px);
}
/* ? 修改 Spinner -> 使用自定义图片且居中 */
#nprogress .spinner {
display: block;
position: fixed;
z-index: 1031;
top: 15px;
left: 50%;
transform: translateX(-50%);
width: 80px;
height: 80px;
}
/* ? 不再使用旋转圆形,换成图片 */
#nprogress .spinner-icon {
width: 100%;
height: 100%;
background-image: url("/themes/theme-pix/assets/img/loading.webp");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.nprogress-custom-parent {
overflow: hidden;
position: relative;
}
.nprogress-custom-parent #nprogress .bar,
.nprogress-custom-parent #nprogress .spinner {
position: absolute;
}
+10661 -10659
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -35,7 +35,7 @@
</th:block> </th:block>
</div> </div>
<div th:if="${posts.hasNext}" class="arc_pagenav"> <div th:if="${posts.hasNext}" class="arc_pagenav post-paging">
<a th:data="@{${posts.nextUrl}}" th:text="${theme.config.base_set.site_page}"></a> <a th:data="@{${posts.nextUrl}}" th:text="${theme.config.base_set.site_page}"></a>
</div> </div>
+33
View File
@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org"
th:replace="~{modules/layouts/layout :: layout(_head = null,_content = ~{::content},_title = ${douban != null and douban.spec != null ? douban.spec.name + ' - ' + site.title : title + ' - ' + site.title})}">
<th:block th:fragment="content">
<div class="page_main uk-width-2-3@m">
<th:block th:replace="~{modules/header}" />
<div id="pjax-container">
<div class="blog_list douban_page douban_detail_page">
<div class="blog_list_inner" uk-height-viewport="offset-top: true">
<div id="post_item" class="moment_list douban_list">
<th:block th:replace="~{macro/douban-item :: douban-item(${douban})}"></th:block>
<th:block th:if="${douban == null or douban.spec == null}">
<th:block th:replace="~{macro/tpl/content-none}" />
</th:block>
</div>
<div id="comment_form_reset"></div>
</div>
</div>
</div>
<th:block th:replace="~{modules/footer-main}" />
</div>
</th:block>
</html>
+99 -61
View File
@@ -1,61 +1,99 @@
<!DOCTYPE html> <!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org" <html xmlns:th="https://www.thymeleaf.org"
th:replace="~{modules/layouts/layout :: layout(_head = null,_content = ~{::content},_title = ${title + ' - ' + site.title})}"> th:replace="~{modules/layouts/layout :: layout(_head = null,_content = ~{::content},_title = ${title + ' - ' + site.title})}">
<th:block th:fragment="content"> <th:block th:fragment="content">
<div class="page_main uk-width-2-3@m"> <div class="page_main uk-width-2-3@m">
<th:block th:replace="~{modules/header}" /> <th:block th:replace="~{modules/header}" />
<div id="pjax-container"> <div id="pjax-container" th:with="
page = ${#lists.isEmpty(param.page) or #strings.isEmpty(param.page[0]) ? 1 : param.page[0]},
<main id="primary" class="site-main"> selectedTag = ${#lists.isEmpty(param.tag) ? null : param.tag[0]},
doubanPage = ${doubanFinder.list(page, 10, selectedTag, null)}
<div class="page_content" uk-height-viewport="offset-top: true"> ">
<article class="post-276 page type-page status-publish hentry"> <div class="blog_list douban_page">
<header class="entry-header"> <div class="blog_list_inner" uk-height-viewport="offset-top: true">
<div class="left">
<i class="ri-focus-2-line"></i> <div class="friends_cat_nav douban_cat_nav">
<h1 class="entry-title" th:text="${title}"></h1> <ul id="cat_nav_items">
</div> <li>
</header> <a th:attr="data-href=@{/books}"
th:classappend="${#strings.isEmpty(selectedTag) ? 'active' : ''}"
class="cat-link">
<div class="entry-content single-content"> 全部<span></span>
<section class="db--container"> </a>
<nav class="db--nav"> </li>
<div th:each="type : ${types}" class="db--navItem JiEun" <li th:each="type : ${types}">
th:classappend="${type.key == 'movie' ? 'current' : ''}" <a th:attr="data-href=@{/books(tag=${type.key})}"
th:data-type="${type.key}" th:text="${type.key}"></div> th:classappend="${#strings.equals(selectedTag, type.key) ? 'active' : ''}"
</nav> class="cat-link">
<div class="db--genres"></div> <span th:text="${#strings.isEmpty(type.name) ? type.key : type.name}">类型</span>
<div class="db--type"> </a>
<div class="db--typeItem" data-status="mark">想看</div> </li>
<div class="db--typeItem" data-status="doing">在看</div> </ul>
<div class="db--typeItem is-active" data-status="done">看过</div> </div>
</div>
<div class="db--list db--list__card" data-type="movie"> <div id="post_item" class="moment_list douban_list">
<th:block th:each="doubanItem : ${doubanPage.items}">
</div> <th:block th:replace="~{macro/douban-item :: douban-item(${doubanItem})}"></th:block>
<div class="block-more block-more__centered"> </th:block>
<div class="lds-ripple u-hide">
</div> <th:block th:if="${#lists.isEmpty(doubanPage.items)}">
</div> <th:block th:replace="~{macro/tpl/content-none}" />
</section> </th:block>
</div>
</article> </div>
</div> <th:block th:if="${doubanPage.totalPages > 1}">
<div id="t_pagination">
</main> <div class="post-paging" th:with="_page = ${doubanPage.page + 1}">
<a th:if="${doubanPage.hasNext() and #strings.isEmpty(selectedTag)}"
</div> th:attr="data=@{/books(page=${_page})}"
th:text="${theme.config.base_set.site_page}"></a>
<th:block th:replace="~{modules/footer-main}" /> <a th:if="${doubanPage.hasNext() and not #strings.isEmpty(selectedTag)}"
th:attr="data=@{/books(tag=${selectedTag},page=${_page})}"
</div> th:text="${theme.config.base_set.site_page}"></a>
</th:block> <form class="page-jump-form" method="get" action="/books">
<span class="page-jump-label">转到</span>
</html> <input th:if="${not #strings.isEmpty(selectedTag)}" type="hidden" name="tag"
th:value="${selectedTag}" />
<span class="page-jump-input-wrap">
<button class="page-jump-step page-jump-step-down" type="button"
title="减少页码" aria-label="减少页码">
<i class="ri-subtract-line" aria-hidden="true"></i>
</button>
<input class="page-jump-input" type="number" name="page" min="1"
th:max="${doubanPage.totalPages}" th:value="${doubanPage.page}"
inputmode="numeric" aria-label="页码" autocomplete="off" required />
<button class="page-jump-step page-jump-step-up" type="button"
title="增加页码" aria-label="增加页码">
<i class="ri-add-line" aria-hidden="true"></i>
</button>
</span>
<span class="page-jump-total">/ [[${doubanPage.totalPages}]]</span>
<button class="page-jump-submit" type="submit" title="跳转到指定页"
aria-label="跳转到指定页">
<i class="ri-arrow-right-line" aria-hidden="true"></i>
</button>
</form>
</div>
</div>
</th:block>
<div id="comment_form_reset"></div>
</div>
</div>
</div>
<th:block th:replace="~{modules/footer-main}" />
</div>
</th:block>
</html>
+39
View File
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org"
th:replace="~{modules/layouts/layout :: layout(_head = null,_content = ~{::content},_title = ${title + ' - ' + site.title})}">
<th:block th:fragment="content">
<div class="page_main uk-width-2-3@m">
<th:block th:replace="~{modules/header}" />
<div id="pjax-container" th:with="
page = ${#lists.isEmpty(param.page) or #strings.isEmpty(param.page[0]) ? 1 : param.page[0]},
selectedTag = ${#lists.isEmpty(param.tag) ? null : param.tag[0]},
equipmentGroups = ${groups},
pageSize = ${equipments.size}
">
<!-- 未筛选时直接查询全部装备。 -->
<th:block th:if="${#strings.isEmpty(selectedTag)}"
th:with="equipmentPage = ${equipmentFinder.list(page, pageSize)}">
<th:block th:replace="~{macro/equipment :: equipment-page(${equipmentPage},${equipmentGroups},${selectedTag})}"></th:block>
</th:block>
<!-- URL 中的 tag 使用分组 displayNameFinder 查询使用分组 metadata.name。 -->
<th:block th:unless="${#strings.isEmpty(selectedTag)}" th:each="group : ${equipmentGroups}">
<th:block th:if="${#strings.equals(selectedTag, group.spec.displayName) or #strings.equals(selectedTag, group.metadata.name)}"
th:with="equipmentPage = ${equipmentFinder.list(page, pageSize, group.metadata.name)}">
<th:block th:replace="~{macro/equipment :: equipment-page(${equipmentPage},${equipmentGroups},${selectedTag})}"></th:block>
</th:block>
</th:block>
</div>
<th:block th:replace="~{modules/footer-main}" />
</div>
</th:block>
</html>
+31 -31
View File
@@ -1,32 +1,32 @@
<!DOCTYPE html> <!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org" <html xmlns:th="https://www.thymeleaf.org"
th:replace="~{modules/layouts/layout :: layout(_head = null,_content = ~{::content},_title=null)}"> th:replace="~{modules/layouts/layout :: layout(_head = null,_content = ~{::content},_title=null)}">
<th:block th:fragment="content"> <th:block th:fragment="content">
<div class="page_main uk-width-2-3@m"> <div class="page_main uk-width-2-3@m">
<th:block th:replace="~{modules/header}"/> <th:block th:replace="~{modules/header}"/>
<div id="pjax-container"> <div id="pjax-container">
<main id="primary" class="site-main"> <main id="primary" class="site-main">
<div class="nodata_main" uk-height-viewport="offset-top: true" > <div class="nodata_main" uk-height-viewport="offset-top: true" >
<section class="error-404 not-found"> <section class="error-404 not-found">
<div class="page-content no_resault"> <div class="page-content no_resault">
<img src="/themes/theme-pix/assets/img/404.png"> <img src="/themes/theme-pix/assets/img/not-found.webp">
</div> </div>
</section> </section>
</div> </div>
</main> </main>
</div> </div>
<th:block th:replace="~{modules/footer-main}"/> <th:block th:replace="~{modules/footer-main}"/>
</div> </div>
</th:block> </th:block>
</html> </html>
+100
View File
@@ -0,0 +1,100 @@
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org"
th:replace="~{modules/layouts/layout :: layout(_head = null,_content = ~{::content},_title = ${title + ' - ' + site.title})}">
<th:block th:fragment="content">
<div class="page_main uk-width-2-3@m">
<th:block th:replace="~{modules/header}" />
<div id="pjax-container">
<div class="blog_list">
<div class="blog_list_inner" uk-height-viewport="offset-top: true">
<div id="post_item" class="moment_list">
<!-- 作者姓名不为空才渲染朋友圈动态 -->
<div th:id="${'post-'+friend.metadata.name}"
class="loop_content p_item moment_item uk-animation-slide-bottom-small"
th:if="${friend.spec != null and not #strings.isEmpty(friend.spec.author)}"
th:with="postInfo = ${friend}, spec = ${friend.spec}">
<div class="p_item_inner">
<div class="list_user_meta">
<div class="avatar">
<img th:src="@{${spec.logo}}">
</div>
<div class="name">
<div class="avatar_title">
<a target="_blank" th:href="${spec.authorUrl}">
[[${spec.author}]]
</a>
</div>
<time itemprop="datePublished"
th:datetime="${#dates.format(spec.pubDate, 'yyyy-MM-dd HH:mm')}">
<th:block th:replace="~{modules/widgets/time :: time(${spec.pubDate})}">
</th:block>
</time>
</div>
</div>
<div class="blog_content">
<div class="entry-content">
<div class="p_title moment-collapse-container">
<a th:if="${not #strings.isEmpty(spec.postLink)}"
target="_blank" th:href="${spec.postLink}">
<div th:utext="${spec.description ?: '无内容'}"></div>
</a>
<div th:unless="${not #strings.isEmpty(spec.postLink)}"
th:utext="${spec.description ?: '无内容'}"></div>
</div>
<div class="img_list img-dynamic-container">
<div class="list_inner"
th:with="imgRaw=${spec.imageFiles},isImgList=${not #strings.isEmpty(imgRaw)}"
th:attr="num=${isImgList ? #arrays.length(imgRaw.split(',')) : 0}">
<th:block th:if="${isImgList}">
<th:block th:with="imgArray=${imgRaw.split(',')}">
<th:block th:each="url : ${imgArray}"
th:with="cleanUrl=${#strings.trim(url)}">
<span class="fancybox mo_img"
th:if="${not #strings.isEmpty(cleanUrl)}"
th:href="|/upload/image-host/${cleanUrl}|"
th:data-fancybox="${'post-' + postInfo.metadata.name}">
<img class="lazy"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
th:data-src="|/upload/image-host/${cleanUrl}|"
alt="">
</span>
</th:block>
</th:block>
</th:block>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 作者姓名为空:只展示404图片 -->
<div th:if="${friend.spec == null or #strings.isEmpty(friend.spec.author)}" style="text-align:center;padding:80px 0;">
<img src="/themes/theme-pix/assets/img/not-found.webp"style="max-width:400px;width:100%;">
</div>
</div>
<div id="comment_form_reset"></div>
</div>
</div>
</div>
<th:block th:replace="~{modules/footer-main}" />
</div>
</th:block>
</html>
Regular → Executable
+155 -76
View File
@@ -1,76 +1,155 @@
<!DOCTYPE html> <!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org" <html xmlns:th="https://www.thymeleaf.org"
th:replace="~{modules/layouts/layout :: layout(_head = null,_content = ~{::content},_title = ${title + ' - ' + site.title})}"> th:replace="~{modules/layouts/layout :: layout(_head = null,_content = ~{::content},_title = ${title + ' - ' + site.title})}">
<th:block th:fragment="content"> <th:block th:fragment="content">
<div class="page_main uk-width-2-3@m"> <div class="page_main uk-width-2-3@m">
<th:block th:replace="~{modules/header}"/> <th:block th:replace="~{modules/header}" />
<div id="pjax-container"> <div id="pjax-container" th:with="
selectedTag = ${#lists.isEmpty(param.tag) ? null : param.tag[0]},
<div class="blog_list"> friendPage = ${friends}
<div class="blog_list_inner" uk-height-viewport="offset-top: true"> ">
<div id="post_item" class="moment_list"> <div class="blog_list">
<div class="blog_list_inner" uk-height-viewport="offset-top: true">
<div th:each="postInfo, iterstat: ${friends.items}" th:id="${'post-'+postInfo.metadata.name}"
class="loop_content p_item moment_item uk-animation-slide-bottom-small" <!-- 作者姓名导航栏(参照图库风格新增) -->
th:with="spec = ${postInfo.spec}"> <div class="friends_cat_nav">
<div class="p_item_inner"> <ul id="cat_nav_items">
<div class="list_user_meta"> <li>
<div class="avatar"> <!-- 全部按钮 -->
<img th:src="@{${spec.logo}}"> <a th:attr="data-href=@{/friends}"
</div> th:classappend="${#strings.isEmpty(selectedTag) ? 'active' : ''}" class="cat-link">
<div class="name"> 全部<span></span>
<div class="avatar_title"> </a>
<a target="_blank" th:href="${spec.authorUrl}">[[${spec.author}]]</a> </li>
</div> <!-- 遍历所有作者 -->
<th:block th:each="authorLink : ${authors}">
<time itemprop="datePublished" <li>
th:datetime="${#dates.format(spec.pubDate, 'yyyy-MM-dd HH:mm')}"> <a th:attr="data-href=@{/friends(tag=${authorLink.spec.displayName})}"
<th:block th:replace="~{modules/widgets/time :: time(${spec.pubDate})}"></th:block> th:classappend="${#strings.equals(selectedTag, authorLink.spec.displayName) ? 'active' : ''}"
</time> th:text="${authorLink.spec.displayName}" class="cat-link">
</div> <span></span>
</div> </a>
</li>
<div class="blog_content"> </th:block>
<div class="entry-content"> </ul>
<div class="p_title"> </div>
<a target="_blank" th:href="${spec.postLink}"><i class="ri-at-line"></i>[[${spec.title}]]</a>
</div> <div id="post_item" class="moment_list">
<th:block th:with="excerpt = ${#strings.length(spec.description) > 100 ? 100 : #strings.length(spec.description)}">
<div class="t_content" th:text="${#strings.substring(spec.description,0,excerpt)+'...'}"> <div th:each="postInfo, iterstat: ${friendPage.items}" th:id="${'post-'+postInfo.metadata.name}"
</div> class="loop_content p_item moment_item uk-animation-slide-bottom-small"
</th:block> th:if="${postInfo.spec != null and not #strings.isEmpty(postInfo.spec.description)}"
</div> th:with="spec = ${postInfo.spec}">
</div>
</div> <div class="p_item_inner">
</div>
<div class="list_user_meta">
<th:block th:if="${#lists.isEmpty(friends.items)}"> <div class="avatar">
<th:block th:replace="~{macro/tpl/content-none}"/> <img th:src="@{${spec.logo}}">
</th:block> </div>
</div>
<th:block th:if="${friends.totalPages>1}"> <div class="name">
<div id="t_pagination"> <div class="avatar_title">
<div class="post-paging" th:if="${friends.hasNext()}" <a target="_blank" th:href="${spec.authorUrl}">
th:with="_page = ${friends.page+1}"> [[${spec.author}]]
<a th:data="${'/friends/page/'+_page+'?preview-theme=theme-pix'}" th:text="${theme.config.base_set.site_page}"></a> </a>
</div> </div>
</div>
</th:block> <time itemprop="datePublished"
th:datetime="${#dates.format(spec.pubDate, 'yyyy-MM-dd HH:mm')}">
<div id="comment_form_reset"></div> <th:block th:replace="~{modules/widgets/time :: time(${spec.pubDate})}">
</div> </th:block>
</div> </time>
</div>
</div> </div>
<th:block th:replace="~{modules/footer-main}"/> <div class="blog_content">
<div class="entry-content">
</div> <div class="p_title moment-collapse-container">
</th:block> <a target="_blank" th:href="${spec.postLink}">
<div th:utext="${spec.description}"></div>
</html> </a>
</div>
<!-- 图片列表 -->
<div class="img_list img-dynamic-container">
<div class="list_inner"
th:with="imgRaw=${spec.imageFiles},isImgList=${not #strings.isEmpty(imgRaw)}"
th:attr="num=${isImgList ? #arrays.length(imgRaw.split(',')) : 0}">
<!-- 仅使用 imageFiles;字段缺失或为空时不显示图片 -->
<th:block th:if="${isImgList}">
<th:block th:with="imgArray=${imgRaw.split(',')}">
<th:block th:each="url : ${imgArray}"
th:with="cleanUrl=${#strings.trim(url)}">
<span class="fancybox mo_img"
th:if="${not #strings.isEmpty(cleanUrl)}"
th:href="|/upload/image-host/${cleanUrl}|"
th:data-fancybox="${'post-' + postInfo.metadata.name}">
<img class="lazy"
src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
th:data-src="|/upload/image-host/${cleanUrl}|"
alt="">
</span>
</th:block>
</th:block>
</th:block>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 空数据 -->
<th:block th:if="${#lists.isEmpty(friendPage.items)}">
<th:block th:replace="~{macro/tpl/content-none}" />
</th:block>
</div>
<!-- 分页:必须保持在 blog_list_inner 里面 -->
<th:block th:if="${friendPage.totalPages>1}">
<div id="t_pagination">
<div class="post-paging">
<a th:if="${friendPage.hasNext()}" th:data="${friendPage.nextUrl}"
th:text="${theme.config.base_set.site_page}"></a>
<form class="page-jump-form" method="get" action="/friends">
<span class="page-jump-label">转到</span>
<input th:if="${not #strings.isEmpty(selectedTag)}" type="hidden" name="tag"
th:value="${selectedTag}" />
<span class="page-jump-input-wrap">
<button class="page-jump-step page-jump-step-down" type="button"
title="减少页码" aria-label="减少页码"><i class="ri-subtract-line"
aria-hidden="true"></i></button>
<input class="page-jump-input" type="number" name="page" min="1"
th:max="${friendPage.totalPages}" th:value="${friendPage.page}"
inputmode="numeric" aria-label="页码" autocomplete="off" required />
<button class="page-jump-step page-jump-step-up" type="button"
title="增加页码" aria-label="增加页码"><i class="ri-add-line"
aria-hidden="true"></i></button>
</span>
<span class="page-jump-total">/ [[${friendPage.totalPages}]]</span>
<button class="page-jump-submit" type="submit" title="跳转到指定页"
aria-label="跳转到指定页"><i class="ri-arrow-right-line"></i></button>
</form>
</div>
</div>
</th:block>
<div id="comment_form_reset"></div>
</div>
</div>
</div>
<th:block th:replace="~{modules/footer-main}" />
</div>
</th:block>
</html>
+9 -5
View File
@@ -10,16 +10,20 @@
<div id="pjax-container" th:with="page = ${#strings.isEmpty(param.page) ? 1 : param.page}"> <div id="pjax-container" th:with="
page = ${#lists.isEmpty(param.page) ? 1 : param.page[0]},
tag = ${#lists.isEmpty(param.tag) ? null : param.tag[0]}
">
<th:block th:if="${theme.config.style.home_post_type=='moment' && pluginFinder.available('PluginMoments')}"> <th:block th:if="${theme.config.style.home_post_type=='moment' && pluginFinder.available('PluginMoments')}"
th:with="momentPage = ${#strings.isEmpty(tag) ? momentFinder.list(page, site.post.postPageSize) : momentFinder.listByTag(page, site.post.postPageSize, tag)}">
<th:block <th:block
th:replace="~{macro/home-moment :: home-moment(${momentFinder.list(page, site.post.postPageSize)},${momentFinder.listAllTags()})}"> th:replace="~{macro/home-moment :: home-moment(${momentPage},${momentFinder.listAllTags()},'/')}">
</th:block> </th:block>
</th:block> </th:block>
<th:block th:if="${theme.config.style.home_post_type=='post' || (not (theme.config.style.home_post_type=='moment' && pluginFinder.available('PluginMoments'))) }"> <th:block th:if="${theme.config.style.home_post_type=='post' || (not (theme.config.style.home_post_type=='moment' && pluginFinder.available('PluginMoments'))) }">
<th:block th:replace="~{macro/home-blog :: home-blog(${posts},'/')}"></th:block> <th:block th:replace="~{macro/home-blog :: home-blog(${posts},'/',null,null)}"></th:block>
</th:block> </th:block>
</div> </div>
@@ -30,4 +34,4 @@
</div> </div>
</th:block> </th:block>
</html> </html>
+63
View File
@@ -0,0 +1,63 @@
<th:block th:fragment="douban-item(doubanItem)" th:if="${doubanItem != null and doubanItem.spec != null}"
th:with="spec = ${doubanItem.spec}, faves = ${doubanItem.faves}, score = ${faves == null ? spec.score : (not #strings.isEmpty(faves.score) ? faves.score : spec.score)}, isRead = ${faves != null and faves.status == 'done'}">
<div th:id="${'post-' + doubanItem.metadata.name}"
class="loop_content p_item moment_item douban_card_item uk-animation-slide-bottom-small">
<div class="p_item_inner douban_card_inner">
<div class="douban_text_col">
<div class="douban_title_row">
<a th:if="${isRead and not #strings.isEmpty(spec.link)}"
class="douban_title_text"
th:href="${spec.link}"
th:text="${spec.name}">
豆瓣条目
</a>
<span th:unless="${isRead and not #strings.isEmpty(spec.link)}"
class="douban_title_text douban_title_text_plain"
th:text="${spec.name}">
豆瓣条目
</span>
<a class="douban_detail_link"
th:href="${'/books/' + doubanItem.metadata.name}"
aria-label="查看详情" title="查看详情">
详情
</a>
</div>
<div class="douban_media_col">
<span class="fancybox"
th:href="${#strings.isEmpty(spec.poster) ? '/themes/theme-pix/assets/img/banner-all.webp' : spec.poster}"
th:data-fancybox="${'douban-' + doubanItem.metadata.name}">
<img class="douban_poster_img"
th:src="${#strings.isEmpty(spec.poster) ? '/themes/theme-pix/assets/img/banner-all.webp' : spec.poster}"
th:alt="${spec.name}">
</span>
</div>
<div class="douban_sub_meta">
<time class="douban_time_text"
th:if="${faves != null and faves.createTime != null}"
th:datetime="${#dates.format(faves.createTime, 'yyyy-MM-dd HH:mm')}">
<span th:text="${#dates.format(faves.createTime, 'yyyy-MM-dd')}">2025-01-01</span>
</time>
<span class="douban_type_badge" th:text="${spec.type}">类型</span>
<span class="douban_score_badge"
th:if="${not #strings.isEmpty(score)}"
th:text="|${score}分|">评分</span>
</div>
<div class="douban_content_box">
<p class="douban_content_main"
th:text="${#strings.isEmpty(spec.cardSubtitle) ? '暂无简介' : spec.cardSubtitle}">
暂无简介
</p>
<p class="douban_content_meta">
<span th:if="${not #strings.isEmpty(spec.year)}" th:text="${spec.year}"></span>
<span th:if="${not #strings.isEmpty(spec.pubdate)}" th:text="${spec.pubdate}"></span>
</p>
</div>
</div>
</div>
</div>
</th:block>
+87
View File
@@ -0,0 +1,87 @@
<th:block th:fragment="equipment-page(equipmentPage,equipmentGroups,selectedTag)">
<div class="blog_list equipments_page">
<div class="blog_list_inner" uk-height-viewport="offset-top: true">
<div class="friends_cat_nav equipments_cat_nav">
<ul id="cat_nav_items">
<li>
<a th:attr="data-href=@{/qiyu}"
th:classappend="${#strings.isEmpty(selectedTag) ? 'active' : ''}"
class="cat-link">
<span>全部</span>
</a>
</li>
<li th:each="group : ${equipmentGroups}">
<a th:attr="data-href=@{/qiyu(tag=${group.spec.displayName})}"
th:classappend="${#strings.equals(selectedTag, group.spec.displayName) or #strings.equals(selectedTag, group.metadata.name) ? 'active' : ''}"
class="cat-link">
<span th:text="${group.spec.displayName}">分组</span>
</a>
</li>
</ul>
</div>
<div id="post_item" class="moment_list equipment_list">
<article th:each="equipment : ${equipmentPage.items}"
class="loop_content p_item moment_item equipment_item uk-animation-slide-bottom-small"
th:if="${equipment.spec != null and not #strings.isEmpty(equipment.spec.cover)}">
<div class="p_item_inner equipment_card_inner">
<div class="equipment_visual">
<img loading="lazy" th:src="${equipment.spec.cover}"
th:alt="${equipment.spec.description}">
<span class="equipment_overlay"
th:if="${not #strings.isEmpty(equipment.spec.description)}">
<span class="equipment_description" th:text="${equipment.spec.description}">描述</span>
</span>
</div>
</div>
</article>
<th:block th:if="${#lists.isEmpty(equipmentPage.items)}">
<th:block th:replace="~{macro/tpl/content-none}" />
</th:block>
</div>
<th:block th:if="${equipmentPage.totalPages > 1}">
<div id="t_pagination">
<div class="post-paging" th:with="_page = ${equipmentPage.page + 1}">
<a th:if="${equipmentPage.hasNext() and #strings.isEmpty(selectedTag)}"
th:attr="data=@{/qiyu(page=${_page})}"
th:text="${theme.config.base_set.site_page}"></a>
<a th:if="${equipmentPage.hasNext() and not #strings.isEmpty(selectedTag)}"
th:attr="data=@{/qiyu(tag=${selectedTag},page=${_page})}"
th:text="${theme.config.base_set.site_page}"></a>
<form class="page-jump-form" method="get" action="/qiyu">
<span class="page-jump-label">转到</span>
<input th:if="${not #strings.isEmpty(selectedTag)}" type="hidden" name="tag"
th:value="${selectedTag}" />
<span class="page-jump-input-wrap">
<button class="page-jump-step page-jump-step-down" type="button"
title="减少页码" aria-label="减少页码">
<i class="ri-subtract-line" aria-hidden="true"></i>
</button>
<input class="page-jump-input" type="number" name="page" min="1"
th:max="${equipmentPage.totalPages}" th:value="${equipmentPage.page}"
inputmode="numeric" aria-label="页码" autocomplete="off" required />
<button class="page-jump-step page-jump-step-up" type="button"
title="增加页码" aria-label="增加页码">
<i class="ri-add-line" aria-hidden="true"></i>
</button>
</span>
<span class="page-jump-total">/ [[${equipmentPage.totalPages}]]</span>
<button class="page-jump-submit" type="submit" title="跳转到指定页"
aria-label="跳转到指定页">
<i class="ri-arrow-right-line" aria-hidden="true"></i>
</button>
</form>
</div>
</div>
</th:block>
<div id="comment_form_reset"></div>
</div>
</div>
</th:block>
+44 -12
View File
@@ -1,22 +1,28 @@
<th:block th:fragment="home-blog(_posts,path)"> <th:block th:fragment="home-blog(_posts,path,tag,selectedCategory)">
<div class="normal_list"> <div class="normal_list">
<div class="normal_list_inner" uk-height-viewport="offset-top: true"> <div class="normal_list_inner" uk-height-viewport="offset-top: true">
<div class="posts_cat_nav"> <div class="posts_cat_nav">
<ul id="cat_nav_items"> <ul id="cat_nav_items">
<li><a th:data="|${path}?preview-theme=theme-pix|" class="active">全部<span></span></a> <li><a th:data="${path}" th:classappend="${#strings.isEmpty(tag) ? 'active' : ''}">全部<span></span></a>
</li> </li>
<li th:if="${theme.config.post.post_nav == 'category'}" th:each="categoryItem,iStat : ${categoryFinder.listAll()}"> <li th:if="${theme.config.post.post_nav == 'category'}" th:each="categoryItem,iStat : ${categoryFinder.listAll()}">
<a th:data="|${categoryItem.status.permalink}?preview-theme=theme-pix|" <a th:if="${path == '/archives'}" th:data="@{/archives(tag=${categoryItem.spec.displayName})}"
class="">[[${categoryItem.spec.displayName}]]<span></span></a> th:classappend="${#strings.equals(tag, categoryItem.spec.displayName) or #strings.equals(selectedCategory, categoryItem.metadata.name) ? 'active' : ''}">[[${categoryItem.spec.displayName}]]<span></span></a>
<a th:unless="${path == '/archives'}" th:data="${categoryItem.status.permalink}"
th:classappend="${#strings.equals(tag, categoryItem.spec.displayName) or #strings.equals(selectedCategory, categoryItem.metadata.name) ? 'active' : ''}">[[${categoryItem.spec.displayName}]]<span></span></a>
</li> </li>
<li th:if="${theme.config.post.post_nav == 'custom_category'}" <li th:if="${theme.config.post.post_nav == 'custom_category'}"
th:each="category : ${theme.config.post.nav_category}"> th:each="category : ${theme.config.post.nav_category}">
<th:block th:with="categoryItem = ${categoryFinder.getByName(category)}"> <th:block th:with="categoryItem = ${categoryFinder.getByName(category)}">
<a th:if="${categoryItem!=null}" <a th:if="${categoryItem!=null and path == '/archives'}"
th:data="|${categoryItem.status.permalink}?preview-theme=theme-pix|" th:data="@{/archives(tag=${categoryItem.spec.displayName})}"
class="">[[${categoryItem.spec.displayName}]]<span></span> th:classappend="${#strings.equals(tag, categoryItem.spec.displayName) or #strings.equals(selectedCategory, categoryItem.metadata.name) ? 'active' : ''}">[[${categoryItem.spec.displayName}]]<span></span>
</a>
<a th:if="${categoryItem!=null and path != '/archives'}"
th:data="${categoryItem.status.permalink}"
th:classappend="${#strings.equals(tag, categoryItem.spec.displayName) or #strings.equals(selectedCategory, categoryItem.metadata.name) ? 'active' : ''}">[[${categoryItem.spec.displayName}]]<span></span>
</a> </a>
</th:block> </th:block>
</li> </li>
@@ -26,13 +32,39 @@
<div id="post_item" class="norpost_list " th:with='postItems=${_posts.items}' <div id="post_item" class="norpost_list " th:with='postItems=${_posts.items}'
th:classappend="${theme.config.post.post_list_type == 'grid' ? 'uk-grid-small uk-grid' : ''}"> th:classappend="${theme.config.post.post_list_type == 'grid' ? 'uk-grid-small uk-grid' : ''}">
<th:block th:replace="~{'macro/tpl/content-post'}"></th:block> <th:block th:replace="~{'macro/tpl/content-post'}"></th:block>
<th:block th:if="${#lists.isEmpty(postItems)}">
<th:block th:replace="~{macro/tpl/content-none}"/>
</th:block>
</div> </div>
<th:block th:if="${_posts.totalPages>1}"> <th:block th:if="${_posts.totalPages>1}">
<div id="pagination"> <div id="pagination">
<div class="post-paging" th:if="${_posts.hasNext()}" <div class="post-paging"
th:with="_page = ${_posts.page+1},_path = ${path == '/archives' ? path+'?preview-theme=theme-pix&page='+_page : path +'page/'+_page+'?preview-theme=theme-pix' }"> th:with="_page = ${_posts.page+1},_path = ${path +'page/'+_page }">
<a th:data="${_path}" th:text="${theme.config.base_set.site_page}"></a> <a th:if="${_posts.hasNext() and path == '/archives' and #strings.isEmpty(tag)}"
th:data="@{/archives(page=${_page})}" th:text="${theme.config.base_set.site_page}"></a>
<a th:if="${_posts.hasNext() and path == '/archives' and not #strings.isEmpty(tag)}"
th:data="@{/archives(tag=${tag},page=${_page})}" th:text="${theme.config.base_set.site_page}"></a>
<a th:if="${_posts.hasNext() and path != '/archives'}" th:data="${_path}"
th:text="${theme.config.base_set.site_page}"></a>
<form class="page-jump-form" method="get" th:action="@{${path}}">
<span class="page-jump-label">转到</span>
<input th:if="${not #strings.isEmpty(tag)}" type="hidden" name="tag" th:value="${tag}" />
<span class="page-jump-input-wrap">
<button class="page-jump-step page-jump-step-down" type="button"
title="减少页码" aria-label="减少页码"><i class="ri-subtract-line"
aria-hidden="true"></i></button>
<input class="page-jump-input" type="number" name="page" min="1"
th:max="${_posts.totalPages}" th:value="${_posts.page}"
inputmode="numeric" aria-label="页码" autocomplete="off" required />
<button class="page-jump-step page-jump-step-up" type="button"
title="增加页码" aria-label="增加页码"><i class="ri-add-line"
aria-hidden="true"></i></button>
</span>
<span class="page-jump-total">/ [[${_posts.totalPages}]]</span>
<button class="page-jump-submit" type="submit" title="跳转到指定页"
aria-label="跳转到指定页"><i class="ri-arrow-right-line"></i></button>
</form>
</div> </div>
</div> </div>
</th:block> </th:block>
@@ -41,4 +73,4 @@
</div> </div>
</div> </div>
</div> </div>
</th:block> </th:block>
+62 -39
View File
@@ -1,39 +1,62 @@
<th:block th:fragment="home-moment(_moments,tags)"> <th:block th:fragment="home-moment(_moments,tags,path)" th:with="_path = ${#strings.isEmpty(path) ? '/moments' : path}">
<div class="blog_list"> <div class="blog_list">
<div class="blog_list_inner" uk-height-viewport="offset-top: true"> <div class="blog_list_inner" uk-height-viewport="offset-top: true">
<div class="moment_cat_nav"> <div class="moment_cat_nav">
<ul id="cat_nav_items"> <ul id="cat_nav_items">
<li><a data="/moments?preview-theme=theme-pix" class="" th:classappend="${#lists.isEmpty(param.tag) ? 'active' : ''}" <li><a th:data="@{${_path}}" class="" th:classappend="${#lists.isEmpty(param.tag) ? 'active' : ''}"
><span>全部</span></a> ><span>全部</span></a>
</li> </li>
<li th:each="tag,iStat : ${tags}"> <li th:each="tag,iStat : ${tags}">
<a th:data="|/moments?preview-theme=theme-pix&tag=${tag.name}|" class="" <a th:data="@{${_path}(tag=${tag.name})}" class=""
th:classappend="${#lists.contains(param.tag, tag.name) ? 'active' : ''}" th:classappend="${#lists.contains(param.tag, tag.name) ? 'active' : ''}"
><span>[[${tag.name}]]</span></a> ><span>[[${tag.name}]]</span></a>
</li> </li>
</ul> </ul>
</div> </div>
<div id="post_item" class="moment_list"> <div id="post_item" class="moment_list">
<th:block th:replace="~{macro/tpl/content-moment :: content-moment(${_moments.items})}"></th:block> <th:block th:replace="~{macro/tpl/content-moment :: content-moment(${_moments.items})}"></th:block>
<th:block th:if="${#lists.isEmpty(_moments.items)}"> <th:block th:if="${#lists.isEmpty(_moments.items)}">
<th:block th:replace="~{macro/tpl/content-none}"/> <th:block th:replace="~{macro/tpl/content-none}"/>
</th:block> </th:block>
</div> </div>
<th:block th:if="${_moments.totalPages>1}"> <th:block th:if="${_moments.totalPages>1}">
<div id="t_pagination"> <div id="t_pagination">
<div class="post-paging" th:if="${_moments.hasNext()}" <div class="post-paging"
th:with="tag = ${not #lists.isEmpty(param.tag) ? '?tag='+param.tag : ''},_page = ${_moments.page+1}"> th:with="tag = ${#lists.isEmpty(param.tag) ? null : param.tag[0]},_page = ${_moments.page+1}">
<a th:data="${'/moments/page/'+_page+tag+'?preview-theme=theme-pix'}" th:text="${theme.config.base_set.site_page}"></a> <a th:if="${_moments.hasNext() and #strings.isEmpty(tag)}"
</div> th:data="@{${_path}(page=${_page})}"
</div> th:text="${theme.config.base_set.site_page}"></a>
</th:block> <a th:if="${_moments.hasNext() and not #strings.isEmpty(tag)}"
th:data="@{${_path}(tag=${tag},page=${_page})}"
<div id="comment_form_reset"></div> th:text="${theme.config.base_set.site_page}"></a>
</div> <form class="page-jump-form" method="get" th:action="@{${_path}}">
</div> <span class="page-jump-label">转到</span>
</th:block> <input th:if="${not #strings.isEmpty(tag)}" type="hidden" name="tag" th:value="${tag}" />
<span class="page-jump-input-wrap">
<button class="page-jump-step page-jump-step-down" type="button"
title="减少页码" aria-label="减少页码"><i class="ri-subtract-line"
aria-hidden="true"></i></button>
<input class="page-jump-input" type="number" name="page" min="1"
th:max="${_moments.totalPages}" th:value="${_moments.page}"
inputmode="numeric" aria-label="页码" autocomplete="off" required />
<button class="page-jump-step page-jump-step-up" type="button"
title="增加页码" aria-label="增加页码"><i class="ri-add-line"
aria-hidden="true"></i></button>
</span>
<span class="page-jump-total">/ [[${_moments.totalPages}]]</span>
<button class="page-jump-submit" type="submit" title="跳转到指定页"
aria-label="跳转到指定页"><i class="ri-arrow-right-line"></i></button>
</form>
</div>
</div>
</th:block>
<div id="comment_form_reset"></div>
</div>
</div>
</th:block>
+105
View File
@@ -0,0 +1,105 @@
<th:block th:fragment="home-photos(photoPage,groups,tag,selectedGroup)">
<div class="normal_list">
<div class="normal_list_inner" uk-height-viewport="offset-top: true">
<!-- 分类导航 -->
<div class="photos_cat_nav">
<ul id="cat_nav_items">
<li>
<!-- 修改:href 改为 data-href,并添加一个通用 class 如 'cat-link' -->
<a th:attr="data-href=@{/photos}"
th:classappend="${#strings.isEmpty(tag) and #strings.isEmpty(selectedGroup) ? 'active' : ''}"
class="cat-link">
全部<span></span>
</a>
</li>
<th:block th:each="photoGroup : ${groups}">
<li>
<!-- 修改:这里也改为 data-href -->
<a th:attr="data-href=@{/photos(tag=${photoGroup.spec.displayName})}"
th:classappend="${#strings.equals(tag, photoGroup.spec.displayName) or #strings.equals(selectedGroup, photoGroup.metadata.name) ? 'active' : ''}"
th:text="${photoGroup.spec.displayName}" class="cat-link">
<span></span>
</a>
</li>
</th:block>
</ul>
</div>
<!-- 相册列表 -->
<div id="photos_item" class="norpost_list">
<div th:unless="${#lists.isEmpty(photoPage.items)}" class="gallery-photos page">
<div th:each="photo : ${photoPage.items}" class="gallery-photo">
<th:block th:with="
img = ${!#strings.isEmpty(photo.spec.cover) ? photo.spec.cover : photo.spec.url},
isVideo = ${#strings.endsWith(img, '.mp4') or #strings.endsWith(img, '.webm') or #strings.endsWith(img, '.ogg')}
">
<span th:if="${isVideo}" th:href="${img}" data-fancybox="gallery"
class="fancybox video-fancybox"
style="position: relative; display: block; cursor: pointer;">
<video class="photo-img" preload="metadata" muted playsinline>
<source th:src="${img}" th:type="${#strings.endsWith(img, '.mp4') ? 'video/mp4' :
(#strings.endsWith(img, '.webm') ? 'video/webm' : 'video/ogg')}" />
</video>
<div
style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); pointer-events: none;">
<i class="ri-play-circle-line play-icon"
style="color: #41b87e; font-size: 40px; background-color: rgba(255, 255, 255, 0.8); border-radius: 50%;"></i>
</div>
</span>
<span th:unless="${isVideo}" th:href="${img}" data-fancybox="gallery"
class="fancybox" th:data-thumb="${img}"
style="position: relative; display: block;">
<img class="photo-img" loading="lazy" th:src="${img}" />
</span>
</th:block>
</div>
</div>
<!-- 空数据 -->
<th:block th:if="${#lists.isEmpty(photoPage.items)}">
<th:block th:replace="~{macro/tpl/content-none}" />
</th:block>
</div>
<!-- 分页 -->
<th:block th:if="${photoPage.hasPrevious()} or ${photoPage.hasNext()}">
<div id="p_pagination">
<div class="post-paging"
th:with="_page = ${photoPage.page+1}">
<a th:if="${photoPage.hasNext() and #strings.isEmpty(tag)}"
th:data="@{/photos(page=${_page})}"
th:text="${theme.config.base_set.site_page}"></a>
<a th:if="${photoPage.hasNext() and not #strings.isEmpty(tag)}"
th:data="@{/photos(tag=${tag},page=${_page})}"
th:text="${theme.config.base_set.site_page}"></a>
<form class="page-jump-form" method="get" action="/photos">
<span class="page-jump-label">转到</span>
<input th:if="${not #strings.isEmpty(tag)}" type="hidden" name="tag" th:value="${tag}" />
<span class="page-jump-input-wrap">
<button class="page-jump-step page-jump-step-down" type="button"
title="减少页码" aria-label="减少页码"><i class="ri-subtract-line"
aria-hidden="true"></i></button>
<input class="page-jump-input" type="number" name="page" min="1"
th:max="${photoPage.totalPages}" th:value="${photoPage.page}"
inputmode="numeric" aria-label="页码" autocomplete="off" required />
<button class="page-jump-step page-jump-step-up" type="button"
title="增加页码" aria-label="增加页码"><i class="ri-add-line"
aria-hidden="true"></i></button>
</span>
<span class="page-jump-total">/ [[${photoPage.totalPages}]]</span>
<button class="page-jump-submit" type="submit" title="跳转到指定页"
aria-label="跳转到指定页"><i class="ri-arrow-right-line"></i></button>
</form>
</div>
</div>
</th:block>
</div>
</div>
</th:block>
+51 -51
View File
@@ -1,52 +1,52 @@
<div th:id="${'post-'+post.metadata.name}" <div th:id="${'post-'+post.metadata.name}"
class="loop_content p_item uk-animation-slide-bottom-small" th:classappend="${post.spec.pinned ? 'sticky' :''}"> class="loop_content p_item uk-animation-slide-bottom-small" th:classappend="${post.spec.pinned ? 'sticky' :''}">
<div class="normal_item_inner"> <div class="normal_item_inner">
<div class="normal_card_content"> <div class="normal_card_content">
<div class="feature round12"> <div class="feature round12">
<a th:href="@{${post.status.permalink}}" <a th:href="@{${post.status.permalink}}"
th:with="cover_img=${#strings.contains(theme.config.post.post_banner,'http') ? ((#strings.contains(theme.config.post.post_banner,'?') ? theme.config.post.post_banner : theme.config.post.post_banner+'?')+','+post.spec.title): theme.config.post.post_banner}"> th:with="cover_img=${#strings.contains(theme.config.post.post_banner,'http') ? ((#strings.contains(theme.config.post.post_banner,'?') ? theme.config.post.post_banner : theme.config.post.post_banner+'?')+','+post.spec.title): theme.config.post.post_banner}">
<img class="round12 shadow lazy" <img class="round12 shadow lazy"
th:with="_cover = ${#strings.isEmpty(post.spec.cover) ? cover_img : thumbnail.gen(post.spec.cover, 's')}" th:with="_cover = ${#strings.isEmpty(post.spec.cover) ? cover_img : thumbnail.gen(post.spec.cover, 's')}"
th:data-src="${_cover}" alt=""> th:data-src="${_cover}" alt="">
</a> </a>
</div> </div>
<div class="card_right"> <div class="card_right">
<div class="entry-content"> <div class="entry-content">
<h2 class="entry-title"><a th:href="@{${post.status.permalink}}" rel="bookmark">[[${post.spec.title}]]</a></h2> <h2 class="entry-title"><a th:href="@{${post.status.permalink}}" rel="bookmark">[[${post.spec.title}]]</a></h2>
<div class="entry_meta"> <div class="entry_meta">
<span class="nickname">@[[${post.owner.displayName}]]</span> - <time itemprop="datePublished" <span class="nickname">@[[${post.owner.displayName}]]</span> - <time itemprop="datePublished"
th:datetime="${#dates.format(post.spec.publishTime, 'yyyy-MM-dd HH:mm')}"> th:datetime="${#dates.format(post.spec.publishTime, 'yyyy-MM-dd HH:mm')}">
<th:block th:replace="~{modules/widgets/time :: time(${post.spec.publishTime})}"></th:block></time> <th:block th:replace="~{modules/widgets/time :: time(${post.spec.publishTime})}"></th:block></time>
</div> </div>
<th:block th:if="${not #strings.isEmpty(post.status.excerpt)}"> <th:block th:if="${not #strings.isEmpty(post.status.excerpt)}">
<p th:with="post_word_max = ${#conversions.convert(theme.config.post.post_word_max, 'java.lang.Integer') >= 0 ? theme.config.post.post_word_max : 100 }, <p th:with="post_word_max = ${#conversions.convert(theme.config.post.post_word_max, 'java.lang.Integer') >= 0 ? theme.config.post.post_word_max : 100 },
excerpt = ${#strings.length(post.status.excerpt) > #conversions.convert(post_word_max, 'java.lang.Integer') ? post_word_max : #strings.length(post.status.excerpt)}"> excerpt = ${#strings.length(post.status.excerpt) > #conversions.convert(post_word_max, 'java.lang.Integer') ? post_word_max : #strings.length(post.status.excerpt)}">
[[${#strings.substring(post.status.excerpt,0,excerpt)+'...'}]] [[${#strings.substring(post.status.excerpt,0,excerpt)+'...'}]]
</p> </p>
</th:block> </th:block>
</div> </div>
<div class="entry-footer"> <div class="entry-footer">
<div class="post_footer_meta"> <div class="post_footer_meta">
<div class="left"> <div class="left">
<span class="post_views"><i class="ri-eye-line"></i>[[${post.stats.visit}]]</span> <span class="post_views"><i class="ri-eye-line"></i>[[${post.stats.visit}]]</span>
<span class="comnum"><i class="ri-chat-4-line"></i><small>[[${post.stats.comment}]]</small></span> <span class="comnum"><i class="ri-chat-4-line"></i>[[${post.stats.comment}]]</span>
<a class="up_like " data-action="up" data-key="post" th:data-id="${post.metadata.name}"> <a class="up_like " data-action="up" data-key="post" th:data-id="${post.metadata.name}">
<i class="ri-heart-2-line"></i> <i class="ri-heart-2-line"></i>
<span>[[${post.stats.upvote}]]</span> <span>[[${post.stats.upvote}]]</span>
</a> </a>
</div> </div>
<div class="right"> <div class="right">
<span th:if="${post.spec.pinned}" class="sticky_icon"><i class="ri-fire-line"></i> TOP</span> <span th:if="${post.spec.pinned}" class="sticky_icon"><i class="ri-fire-line"></i> TOP</span>
<th:block th:if="${not #lists.isEmpty(post.categories)}"> <th:block th:if="${not #lists.isEmpty(post.categories)}">
<div class="normal_cat"># [[${post.categories[0].spec.displayName}]]</div> <div class="normal_cat"># [[${post.categories[0].spec.displayName}]]</div>
</th:block> </th:block>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
+49 -49
View File
@@ -1,50 +1,50 @@
<div class="uk-width-1-2@l uk-width-1-1@m"> <div class="uk-width-1-2@l uk-width-1-1@m">
<div th:id="${'post-'+post.metadata.name}" class="loop_content p_item grid_item uk-animation-slide-bottom-small" <div th:id="${'post-'+post.metadata.name}" class="loop_content p_item grid_item uk-animation-slide-bottom-small"
th:classappend="${post.spec.pinned ? 'sticky' :''}"> th:classappend="${post.spec.pinned ? 'sticky' :''}">
<div class="normal_item_inner"> <div class="normal_item_inner">
<div class="normal_grid_content"> <div class="normal_grid_content">
<div class="entry-content"> <div class="entry-content">
<h2 class="entry-title"><a th:href="@{${post.status.permalink}}" rel="bookmark">[[${post.spec.title}]]</a></h2> <h2 class="entry-title"><a th:href="@{${post.status.permalink}}" rel="bookmark">[[${post.spec.title}]]</a></h2>
<div class="entry_meta"> <div class="entry_meta">
<span class="nickname">@[[${post.owner.displayName}]] <span class="nickname">@[[${post.owner.displayName}]]
</span> - <time itemprop="datePublished" th:datetime="${#dates.format(post.spec.publishTime, 'yyyy-MM-dd HH:mm')}"> </span> - <time itemprop="datePublished" th:datetime="${#dates.format(post.spec.publishTime, 'yyyy-MM-dd HH:mm')}">
<th:block th:replace="~{modules/widgets/time :: time(${post.spec.publishTime})}"></th:block> <th:block th:replace="~{modules/widgets/time :: time(${post.spec.publishTime})}"></th:block>
</time> </time>
</div> </div>
<div class="feature round12"> <div class="feature round12">
<a th:href="@{${post.status.permalink}}" <a th:href="@{${post.status.permalink}}"
th:with="cover_img=${#strings.contains(theme.config.post.post_banner,'http') ? ((#strings.contains(theme.config.post.post_banner,'?') ? theme.config.post.post_banner : theme.config.post.post_banner+'?')+','+post.spec.title): theme.config.post.post_banner}"> th:with="cover_img=${#strings.contains(theme.config.post.post_banner,'http') ? ((#strings.contains(theme.config.post.post_banner,'?') ? theme.config.post.post_banner : theme.config.post.post_banner+'?')+','+post.spec.title): theme.config.post.post_banner}">
<img class="round12 shadow lazy" <img class="round12 shadow lazy"
th:with="_cover = ${#strings.isEmpty(post.spec.cover) ? cover_img : thumbnail.gen(post.spec.cover, 's')}" th:with="_cover = ${#strings.isEmpty(post.spec.cover) ? cover_img : thumbnail.gen(post.spec.cover, 's')}"
th:data-src="${_cover}" alt=""> th:data-src="${_cover}" alt="">
</a> </a>
</div> </div>
</div> </div>
<div class="entry-footer"> <div class="entry-footer">
<div class="post_footer_meta"> <div class="post_footer_meta">
<div class="left"> <div class="left">
<span class="post_views"><i class="ri-eye-line"></i> <span class="post_views"><i class="ri-eye-line"></i>
[[${post.stats.visit}]] [[${post.stats.visit}]]
</span> </span>
<span class="comnum"><i class="ri-chat-4-line"></i> <span class="comnum"><i class="ri-chat-4-line"></i>
<small> [[${post.stats.comment}]] </small> <small>[[${post.stats.comment}]]</small>
</span> </span>
<a class="up_like " data-action="up" data-key="post" th:data-id="${post.metadata.name}"> <a class="up_like " data-action="up" data-key="post" th:data-id="${post.metadata.name}">
<i class="ri-heart-2-line"></i> <i class="ri-heart-2-line"></i>
<span>[[${post.stats.upvote}]]</span> <span>[[${post.stats.upvote}]]</span>
</a> </a>
</div> </div>
<div class="right"> <div class="right">
<span th:if="${post.spec.pinned}" class="sticky_icon"><i class="ri-fire-line"></i> TOP</span> <span th:if="${post.spec.pinned}" class="sticky_icon"><i class="ri-fire-line"></i> TOP</span>
<th:block th:if="${not #lists.isEmpty(post.categories)}"> <th:block th:if="${not #lists.isEmpty(post.categories)}">
<div class="normal_cat"># [[${post.categories[0].spec.displayName}]]</div> <div class="normal_cat"># [[${post.categories[0].spec.displayName}]]</div>
</th:block> </th:block>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
+115 -102
View File
@@ -1,103 +1,116 @@
<th:block th:fragment="content-moment(moments)"> <th:block th:fragment="content-moment(moments)">
<div th:each="moment,iStat : ${moments}" th:id="${'post-'+moment.metadata.name}"
<div th:each="moment,iStat : ${moments}" th:id="${'post-'+moment.metadata.name}" class="loop_content p_item moment_item">
class="loop_content p_item moment_item"> <div class="p_item_inner">
<div class="p_item_inner"> <!-- 帖子控制按钮(编辑/删除) -->
<div th:if="${roleName == 'super-role' || moment.owner.name == user.name}" class="post_control"> <div th:if="${roleName == 'super-role' || moment.owner.name == user.name}" class="post_control">
<a class="post_control_btn"><i class="ri-menu-3-line"></i></a> <a class="post_control_btn"><i class="ri-menu-3-line"></i></a>
<div class="post_control_box"> <div class="post_control_box">
<div class="post_control_list round8 shadow uk-animation-slide-bottom-small uk-animation-fast" <div class="post_control_list round8 shadow uk-animation-slide-bottom-small uk-animation-fast"
th:pid="${moment.metadata.name}"> th:pid="${moment.metadata.name}">
<a class="control_edit_post control_type" uk-toggle="target: #create_post_box">编辑瞬间</a> <a class="control_edit_post control_type" uk-toggle="target: #create_post_box">编辑瞬间</a>
<a class="control_delete_post control_type">删除瞬间</a> <a class="control_delete_post control_type">删除瞬间</a>
</div> </div>
</div> </div>
</div> </div>
<div class="list_user_meta"> <!-- 用户信息(头像/名称/时间) -->
<div class="avatar"> <div class="list_user_meta">
<img th:src="@{${moment.owner.avatar}}"> <div class="avatar">
</div> <img th:src="@{${moment.owner.avatar}}">
<div class="name"> </div>
[[${moment.owner.displayName}]] <div class="name">
<time itemprop="datePublished" [[${moment.owner.displayName}]]
th:datetime="${#dates.format(moment.spec.releaseTime, 'yyyy-MM-dd HH:mm')}"> <time itemprop="datePublished"
<th:block th:replace="~{modules/widgets/time :: time(${moment.spec.releaseTime})}"></th:block> th:datetime="${#dates.format(moment.spec.releaseTime, 'yyyy-MM-dd HH:mm')}">
</time> <th:block th:replace="~{modules/widgets/time :: time(${moment.spec.releaseTime})}"></th:block>
</div> </time>
</div> </div>
</div>
<div class="blog_content"> <!-- 内容区域 -->
<div class="entry-content" th:with="content=${moment.spec.content}"> <div class="blog_content">
<div class="p_title"></div> <div class="entry-content" th:with="content=${moment.spec.content}">
<div class="t_content" th:utext="${content.html}"> <div class="p_title"></div>
<th:block th:utext="${content.html}"></th:block> <!-- 内容容器(仅新增moment-collapse-container类) -->
</div> <div class="t_content moment-collapse-container" th:utext="${content.html}">
<th:block th:replace="~{'macro/tpl/moment-medium'}" /> <th:block th:utext="${content.html}"></th:block>
</div> </div>
<span class="ip_loca" th:if="${not #strings.isEmpty(moment.metadata.annotations.mylocal)}"> <th:block th:replace="~{'macro/tpl/moment-medium'}" />
<i class="ri-map-pin-2-line"></i> </div>
[[${moment.metadata.annotations.mylocal}]] <!-- IP地址显示 -->
</span> <span class="ip_loca" th:if="${not #strings.isEmpty(moment.metadata.annotations.mylocal)}">
<div class="entry-footer"> <i class="ri-map-pin-2-line"></i>
<div class="post_footer_meta"> [[${moment.metadata.annotations.mylocal}]]
<div class="left"> </span>
<a class="up_like " data-action="up" data-key="moment" th:data-id="${moment.metadata.name}"> <!-- 底部操作栏 -->
<i class="ri-heart-2-line"></i> <div class="entry-footer">
<span th:text="${moment.stats.upvote}"></span> <div class="post_footer_meta">
</a> <!-- 左侧(点赞/分享) -->
<div class="pix_share_btn" th:with="_cover = ${not #strings.isEmpty(theme.config.moments.banner) ? theme.config.moments.banner : '/themes/theme-pix/assets/img/banner.jpg'}"> <div class="left">
<a class="pix_icon share_btn_icon cr_poster" <a class="up_like " data-action="up" data-key="moment" th:data-id="${moment.metadata.name}">
th:poster-data="${moment.metadata.name}" <i class="ri-heart-2-line"></i>
th:banner = "${_cover}" <span th:text="${moment.stats.upvote}"></span>
th:uk-toggle="|target: #share_modal_${moment.metadata.name}|" </a>
uk-tooltip="title: 瞬间分享; pos: top;" tabindex="0" title="" <div class="pix_share_btn"
aria-describedby="uk-tooltip-250" aria-expanded="false"><i th:with="_cover = ${not #strings.isEmpty(theme.config.moments.banner) ? theme.config.moments.banner : '/themes/theme-pix/assets/img/banner-all.webp'}">
class="ri-share-forward-box-line"></i></a> <a class="pix_icon share_btn_icon cr_poster" th:poster-data="${moment.metadata.name}"
<div th:id="|share_modal_${moment.metadata.name}|" class="uk-flex-top poster_modal uk-modal" th:banner="${_cover}" th:uk-toggle="|target: #share_modal_${moment.metadata.name}|"
uk-modal> uk-tooltip="title: 瞬间分享; pos: top;" tabindex="0" title=""
<div class="uk-modal-dialog uk-modal-body uk-margin-auto-vertical"> aria-describedby="uk-tooltip-250" aria-expanded="false"><i
<button class="uk-modal-close-outside uk-icon uk-close" class="ri-share-forward-box-line"></i></a>
type="button" uk-close></button> <div th:id="|share_modal_${moment.metadata.name}|"
<div class="poster_box_ap"></div> class="uk-flex-top poster_modal uk-modal" uk-modal>
<div class="post_share_box hide"> <div class="uk-modal-dialog uk-modal-body uk-margin-auto-vertical">
<a th:onclick="handleShare('wb','[(${_cover})]','[(${site.title+'-瞬间'})]','[(${'/moments/'+moment.metadata.name})]')" <button class="uk-modal-close-outside uk-icon uk-close" type="button"
uk-tooltip="title: 微博分享; pos: top;" target="_blank"> uk-close></button>
<i class="ri-weibo-line"></i></a> <div class="poster_box_ap"></div>
<a th:onclick="handleShare('qzone','[(${_cover})]','[(${site.title+'-瞬间'})]','[(${'/moments/'+moment.metadata.name})]')" <div class="post_share_box hide">
uk-tooltip="title: QQ分享; pos: top;" target="_blank"> <a th:onclick="handleShare('wb','[(${_cover})]','[(${site.title+'-瞬间'})]','[(${'/moments/'+moment.metadata.name})]')"
<i class="ri-chrome-line"></i> uk-tooltip="title: 微博分享; pos: top;" target="_blank">
</a> <i class="ri-weibo-line"></i></a>
<a th:onclick="handleShare('qq','[(${_cover})]','[(${site.title+'-瞬间'})]','[(${'/moments/'+moment.metadata.name})]')" <a th:onclick="handleShare('qzone','[(${_cover})]','[(${site.title+'-瞬间'})]','[(${'/moments/'+moment.metadata.name})]')"
uk-tooltip="title: QQ好友分享; pos: top;" target="_blank"> uk-tooltip="title: QQ分享; pos: top;" target="_blank">
<i class="ri-qq-line"></i> <i class="ri-chrome-line"></i>
</a> </a>
<a th:onclick="handleShare('copy','','','[(${'/moments/'+moment.metadata.name})]')" <a th:onclick="handleShare('qq','[(${_cover})]','[(${site.title+'-瞬间'})]','[(${'/moments/'+moment.metadata.name})]')"
uk-tooltip="title: 复制链接; pos: top;"> uk-tooltip="title: QQ好友分享; pos: top;" target="_blank">
<i class="ri-file-copy-line"></i> <i class="ri-qq-line"></i>
</a> </a>
<a class="poster_download" uk-tooltip="title: 下载海报; pos: top;"> <a th:onclick="handleShare('copy','','','[(${'/moments/'+moment.metadata.name})]')"
<i class="ri-download-line"></i></a> uk-tooltip="title: 复制链接; pos: top;">
</div> <i class="ri-file-copy-line"></i>
<img id="twoCode" style="display: none;" /> </a>
</div> <a class="poster_download" uk-tooltip="title: 下载海报; pos: top;">
</div> <i class="ri-download-line"></i></a>
</div> </div>
</div> <img id="twoCode" style="display: none;" />
<div class="right" th:if="${(not theme.config.com.com_close) && haloCommentEnabled}"> </div>
<span class="comnum show_comment" th:pid="${moment.metadata.name}"><i </div>
class="ri-message-3-line"></i>[[${moment.stats.totalComment}]]</span> </div>
</div> </div>
</div> <!-- 右侧(评论数 + 新增标签显示) -->
</div> <div class="right" th:if="${(not theme.config.com.com_close) && haloCommentEnabled}">
<!-- 新增:标签显示(样式与分类一致) -->
<div th:if="${(not theme.config.com.com_close) && haloCommentEnabled}" th:id="${'halo-comment-'+moment.metadata.name}" class="topic_comments_wrap" style="display: none"> <th:block th:if="${not #lists.isEmpty(moment.spec.tags)}">
<div class="topic_comments comments comments-area" data="moment"> <div class="normal_cat"># [[${moment.spec.tags[0]}]]</div>
<div class="toi_comments_main halo-comment"> </th:block>
<th:block th:replace="~{modules/widgets/halo-comment :: halo-comment(name=${moment.metadata.name},kind='Moment',group='moment.halo.run')}"></th:block> <span class="moment_tag_gap"></span>
</div> <span class="comnum show_comment" th:pid="${moment.metadata.name}"><i
</div> class="ri-message-3-line"></i>[[${moment.stats.totalComment}]]</span>
</div> </div>
</div> </div>
</div> </div>
</div> <!-- 评论区 -->
<div th:if="${(not theme.config.com.com_close) && haloCommentEnabled}"
th:id="${'halo-comment-'+moment.metadata.name}" class="topic_comments_wrap" style="display: none">
<div class="topic_comments comments comments-area" data="moment">
<div class="toi_comments_main halo-comment">
<th:block
th:replace="~{modules/widgets/halo-comment :: halo-comment(name=${moment.metadata.name},kind='Moment',group='moment.halo.run')}">
</th:block>
</div>
</div>
</div>
</div>
</div>
</div>
</th:block> </th:block>
+4 -4
View File
@@ -1,5 +1,5 @@
<section class="no-results not-found"> <section class="no-results not-found">
<div class="page-content no_resault"> <div class="page-content no_resault">
<img src="/themes/theme-pix/assets/img/nodata.png"> <img src="/themes/theme-pix/assets/img/no-data.webp">
</div> </div>
</section> </section>
+51 -53
View File
@@ -1,54 +1,52 @@
<div th:id="${'post-'+post.metadata.name}" <div th:id="${'post-'+post.metadata.name}"
class="loop_content p_item uk-animation-slide-bottom-small" th:classappend="${post.spec.pinned ? 'sticky' :''}"> class="loop_content p_item uk-animation-slide-bottom-small" th:classappend="${post.spec.pinned ? 'sticky' :''}">
<div class="normal_item_inner"> <div class="normal_item_inner">
<div class="normal_content"> <div class="normal_card_content">
<div class="entry-content"> <div class="feature round12">
<h2 class="entry-title"><a th:href="@{${post.status.permalink}}" rel="bookmark">[[${post.spec.title}]]</a></h2> <a th:href="@{${post.status.permalink}}"
<div class="entry_meta"> th:with="cover_img=${#strings.contains(theme.config.post.post_banner,'http') ? ((#strings.contains(theme.config.post.post_banner,'?') ? theme.config.post.post_banner : theme.config.post.post_banner+'?')+','+post.spec.title): theme.config.post.post_banner}">
<span class="nickname">@[[${post.owner.displayName}]] <img class="round12 shadow lazy"
</span> - <time itemprop="datePublished" th:datetime="${#dates.format(post.spec.publishTime, 'yyyy-MM-dd HH:mm')}"> th:with="_cover = ${#strings.isEmpty(post.spec.cover) ? cover_img : thumbnail.gen(post.spec.cover, 's')}"
<th:block th:replace="~{modules/widgets/time :: time(${post.spec.publishTime})}"></th:block> th:data-src="${_cover}" alt="">
</time> </a>
</div> </div>
<th:block th:if="${not #strings.isEmpty(post.status.excerpt)}"> <div class="card_right">
<p th:with="post_word_max = ${#conversions.convert(theme.config.post.post_word_max, 'java.lang.Integer') >= 0 ? theme.config.post.post_word_max : 100 }, <div class="entry-content">
excerpt = ${#strings.length(post.status.excerpt) > #conversions.convert(post_word_max, 'java.lang.Integer') ? post_word_max : #strings.length(post.status.excerpt)}"> <h2 class="entry-title"><a th:href="@{${post.status.permalink}}" rel="bookmark">[[${post.spec.title}]]</a></h2>
[[${#strings.substring(post.status.excerpt,0,excerpt)+'...'}]] <div class="entry_meta">
</p> <span class="nickname">@[[${post.owner.displayName}]]</span> - <time itemprop="datePublished"
</th:block> th:datetime="${#dates.format(post.spec.publishTime, 'yyyy-MM-dd HH:mm')}">
<div class="feature round12"> <th:block th:replace="~{modules/widgets/time :: time(${post.spec.publishTime})}"></th:block></time>
<a th:href="@{${post.status.permalink}}" </div>
th:with="cover_img=${#strings.contains(theme.config.post.post_banner,'http') ? ((#strings.contains(theme.config.post.post_banner,'?') ? theme.config.post.post_banner : theme.config.post.post_banner+'?')+','+post.spec.title): theme.config.post.post_banner}"> <th:block th:if="${not #strings.isEmpty(post.status.excerpt)}">
<img class="round12 shadow lazy" <p th:with="post_word_max = ${#conversions.convert(theme.config.post.post_word_max, 'java.lang.Integer') >= 0 ? theme.config.post.post_word_max : 100 },
th:with="_cover = ${#strings.isEmpty(post.spec.cover) ? cover_img : thumbnail.gen(post.spec.cover, 'm')}" excerpt = ${#strings.length(post.status.excerpt) > #conversions.convert(post_word_max, 'java.lang.Integer') ? post_word_max : #strings.length(post.status.excerpt)}">
th:src="@{/assets/img/lazyload.png}" [[${#strings.substring(post.status.excerpt,0,excerpt)+'...'}]]
th:data-src="${_cover}" alt=""> </p>
</a> </th:block>
</div>
</div><!-- .entry-content --> </div>
<div class="entry-footer">
<div class="post_footer_meta"> <div class="entry-footer">
<div class="left"> <div class="post_footer_meta">
<span class="post_views"><i class="ri-eye-line"></i> <div class="left">
[[${post.stats.visit}]] <span class="post_views"><i class="ri-eye-line"></i>[[${post.stats.visit}]]</span>
</span> <span class="comnum"><i class="ri-chat-4-line"></i><small>[[${post.stats.comment}]]</small></span>
<span class="comnum"><i class="ri-chat-4-line"></i> <a class="up_like " data-action="up" data-key="post" th:data-id="${post.metadata.name}">
<small> [[${post.stats.comment}]] </small> <i class="ri-heart-2-line"></i>
</span> <span>[[${post.stats.upvote}]]</span>
<a class="up_like " data-key="post" data-action="up" th:data-id="${post.metadata.name}"> </a>
<i class="ri-heart-2-line"></i> </div>
<span>[[${post.stats.upvote}]]</span> <div class="right">
</a> <span th:if="${post.spec.pinned}" class="sticky_icon"><i class="ri-fire-line"></i> TOP</span>
</div> <th:block th:if="${not #lists.isEmpty(post.categories)}">
<div class="right"> <div class="normal_cat"># [[${post.categories[0].spec.displayName}]]</div>
<span th:if="${post.spec.pinned}" class="sticky_icon"><i class="ri-fire-line"></i> TOP</span> </th:block>
<th:block th:if="${not #lists.isEmpty(post.categories)}"> </div>
<div class="normal_cat"># [[${post.categories[0].spec.displayName}]]</div> </div>
</th:block> </div>
</div> </div>
</div>
</div>
</div><!-- .entry-footer --> </div>
</div>
</div>
</div> </div>
+42 -42
View File
@@ -1,43 +1,43 @@
<div th:id="${'post-'+post.metadata.name}" <div th:id="${'post-'+post.metadata.name}"
class="loop_content p_item uk-animation-slide-bottom-small" th:classappend="${post.spec.pinned ? 'sticky' :''}"> class="loop_content p_item uk-animation-slide-bottom-small" th:classappend="${post.spec.pinned ? 'sticky' :''}">
<div class="normal_item_inner"> <div class="normal_item_inner">
<div class="normal_card_content"> <div class="normal_card_content">
<div class="card_right"> <div class="card_right">
<div class="entry-content"> <div class="entry-content">
<h2 class="entry-title"><a th:href="@{${post.status.permalink}}" rel="bookmark">[[${post.spec.title}]]</a></h2> <h2 class="entry-title"><a th:href="@{${post.status.permalink}}" rel="bookmark">[[${post.spec.title}]]</a></h2>
<div class="entry_meta"> <div class="entry_meta">
<span class="nickname">@[[${post.owner.displayName}]]</span> - <time itemprop="datePublished" <span class="nickname">@[[${post.owner.displayName}]]</span> - <time itemprop="datePublished"
th:datetime="${#dates.format(post.spec.publishTime, 'yyyy-MM-dd HH:mm')}"> th:datetime="${#dates.format(post.spec.publishTime, 'yyyy-MM-dd HH:mm')}">
<th:block th:replace="~{modules/widgets/time :: time(${post.spec.publishTime})}"></th:block></time> <th:block th:replace="~{modules/widgets/time :: time(${post.spec.publishTime})}"></th:block></time>
</div> </div>
<th:block th:if="${not #strings.isEmpty(post.status.excerpt)}"> <th:block th:if="${not #strings.isEmpty(post.status.excerpt)}">
<p style="display: block" th:with="post_word_max = ${#conversions.convert(theme.config.post.post_word_max, 'java.lang.Integer') >= 0 ? theme.config.post.post_word_max : 100 }, <p style="display: block" th:with="post_word_max = ${#conversions.convert(theme.config.post.post_word_max, 'java.lang.Integer') >= 0 ? theme.config.post.post_word_max : 100 },
excerpt = ${#strings.length(post.status.excerpt) > #conversions.convert(post_word_max, 'java.lang.Integer') ? post_word_max : #strings.length(post.status.excerpt)}"> excerpt = ${#strings.length(post.status.excerpt) > #conversions.convert(post_word_max, 'java.lang.Integer') ? post_word_max : #strings.length(post.status.excerpt)}">
[[${#strings.substring(post.status.excerpt,0,excerpt)+'...'}]] [[${#strings.substring(post.status.excerpt,0,excerpt)+'...'}]]
</p> </p>
</th:block> </th:block>
</div> </div>
<div class="entry-footer"> <div class="entry-footer">
<div class="post_footer_meta"> <div class="post_footer_meta">
<div class="left"> <div class="left">
<span class="post_views"><i class="ri-eye-line"></i>[[${post.stats.visit}]]</span> <span class="post_views"><i class="ri-eye-line"></i>[[${post.stats.visit}]]</span>
<span class="comnum"><i class="ri-chat-4-line"></i><small>[[${post.stats.comment}]]</small></span> <span class="comnum"><i class="ri-chat-4-line"></i><small>[[${post.stats.comment}]]</small></span>
<a class="up_like " data-action="up" data-key="post" th:data-id="${post.metadata.name}"> <a class="up_like " data-action="up" data-key="post" th:data-id="${post.metadata.name}">
<i class="ri-heart-2-line"></i> <i class="ri-heart-2-line"></i>
<span>[[${post.stats.upvote}]]</span> <span>[[${post.stats.upvote}]]</span>
</a> </a>
</div> </div>
<div class="right"> <div class="right">
<span th:if="${post.spec.pinned}" class="sticky_icon"><i class="ri-fire-line"></i> TOP</span> <span th:if="${post.spec.pinned}" class="sticky_icon"><i class="ri-fire-line"></i> TOP</span>
<th:block th:if="${not #lists.isEmpty(post.categories)}"> <th:block th:if="${not #lists.isEmpty(post.categories)}">
<div class="normal_cat"># [[${post.categories[0].spec.displayName}]]</div> <div class="normal_cat"># [[${post.categories[0].spec.displayName}]]</div>
</th:block> </th:block>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
+17 -23
View File
@@ -1,24 +1,18 @@
<article th:id="${'post-'+singlePage.metadata.name}" th:fragment="content-page"> <article th:id="${'post-'+singlePage.metadata.name}" th:fragment="content-page">
<header class="entry-header"> <header class="entry-header">
<div class="left"> <div class="left">
<i class="ri-focus-2-line"></i> <i class="ri-focus-2-line"></i>
<h1 class="entry-title" th:text="${singlePage.spec.title}"></h1> <h1 class="entry-title" th:text="${singlePage.spec.title}"></h1>
</div> </div>
<div class="right"> </header>
<time itemprop="datePublished"
th:datetime="${#dates.format(singlePage.spec.publishTime, 'yyyy-MM-dd HH:mm')}"> <div class="entry-content single-content">
[[${#dates.format(singlePage.spec.publishTime, 'yyyy年MM月dd日')}]] <th:block th:utext="${singlePage.content.content}"></th:block>
</time> </div>
</div>
</header> <div id="comments" th:if="${(not theme.config.com.com_close) && haloCommentEnabled}" class="comments comments-area" data="normal">
<div class="halo-comment" >
<div class="entry-content single-content"> <th:block th:replace="~{modules/widgets/halo-comment :: halo-comment(name=${singlePage.metadata.name},kind='SinglePage',group='content.halo.run')}"></th:block>
<th:block th:utext="${singlePage.content.content}"></th:block> </div>
</div> </div>
<div id="comments" th:if="${(not theme.config.com.com_close) && haloCommentEnabled}" class="comments comments-area" data="normal">
<div class="halo-comment" >
<th:block th:replace="~{modules/widgets/halo-comment :: halo-comment(name=${singlePage.metadata.name},kind='SinglePage',group='content.halo.run')}"></th:block>
</div>
</div>
</article> </article>
+146 -146
View File
@@ -1,147 +1,147 @@
<th:block th:fragment="content-single" th:with="mu_on = ${#annotations.get(post, 'mu_on')},plays_id = ${#annotations.get(post, 'plays_id')}, <th:block th:fragment="content-single" th:with="mu_on = ${#annotations.get(post, 'mu_on')},plays_id = ${#annotations.get(post, 'plays_id')},
cover_img=${#strings.contains(theme.config.post.post_banner,'http') ? ((#strings.contains(theme.config.post.post_banner,'?') ? theme.config.post.post_banner : theme.config.post.post_banner+'?')+','+post.spec.title): theme.config.post.post_banner} cover_img=${#strings.contains(theme.config.post.post_banner,'http') ? ((#strings.contains(theme.config.post.post_banner,'?') ? theme.config.post.post_banner : theme.config.post.post_banner+'?')+','+post.spec.title): theme.config.post.post_banner}
,_cover = ${not #strings.isEmpty(post.spec.cover) ? post.spec.cover : cover_img}"> ,_cover = ${not #strings.isEmpty(post.spec.cover) ? post.spec.cover : cover_img}">
<div class="blog_header"> <div class="blog_header">
<div th:if="${mu_on == 'true' && not #strings.isEmpty(plays_id)}" class="single_music_header"> <div th:if="${mu_on == 'true' && not #strings.isEmpty(plays_id)}" class="single_music_header">
<div class="mu_img"><img class="round12 shadow lazy" src="/themes/theme-pix/assets/img/lazyload.png" th:data-src="${_cover}" ></div> <div class="mu_img"><img class="round12 shadow lazy" src="/themes/theme-pix/assets/img/logo.webp" th:data-src="${_cover}" ></div>
<div class="right_info"> <div class="right_info">
<h2 class="entry-title" th:text="${post.spec.title}"></h2> <h2 class="entry-title" th:text="${post.spec.title}"></h2>
<div class="header_meta"> <div class="header_meta">
<span class="single_time"> <span class="single_time">
<i class="ri-time-line"></i> <i class="ri-time-line"></i>
<time itemprop="datePublished" th:datetime="${#dates.format(post.spec.publishTime, 'yyyy-MM-dd HH:mm')}"> <time itemprop="datePublished" th:datetime="${#dates.format(post.spec.publishTime, 'yyyy-MM-dd HH:mm')}">
<th:block th:replace="~{modules/widgets/time :: time(${post.spec.publishTime})}"></th:block> <th:block th:replace="~{modules/widgets/time :: time(${post.spec.publishTime})}"></th:block>
</time> </time>
</span> </span>
<span class="post_views"><i class="ri-eye-line"></i>[[${post.stats.visit}]]</span> <span class="post_views"><i class="ri-eye-line"></i>[[${post.stats.visit}]]</span>
<span class="edit_post" th:if="${roleName == 'super-role'}"> <span class="edit_post" th:if="${roleName == 'super-role'}">
<a th:href="${'/console/posts/editor?name='+post.metadata.name+'&returnToView=true'}" <a th:href="${'/console/posts/editor?name='+post.metadata.name+'&returnToView=true'}"
target="_self"> target="_self">
<i class="ri-edit-line"></i>编辑</a> <i class="ri-edit-line"></i>编辑</a>
</span> </span>
</div> </div>
<div class="single-content_header"> <div class="single-content_header">
<span class="single_cat"> <span class="single_cat">
<ul class="post-categories"> <ul class="post-categories">
<li th:each="category : ${post.categories}"> <li th:each="category : ${post.categories}">
<a th:href="@{${category.status.permalink}}" th:text="${category.spec.displayName}" <a th:href="@{${category.status.permalink}}" th:text="${category.spec.displayName}"
th:title="${category.spec.displayName}" rel="category tag"></a> th:title="${category.spec.displayName}" rel="category tag"></a>
</li> </li>
</ul> </ul>
</span> </span>
</div> </div>
<div class="mu_des"><th:block th:utext="${#annotations.get(post, 'mu_des')}"></th:block></div> <div class="mu_des"><th:block th:utext="${#annotations.get(post, 'mu_des')}"></th:block></div>
</div> </div>
</div> </div>
<div th:unless="${mu_on == 'true' && not #strings.isEmpty(plays_id)}" class="post_header"> <div th:unless="${mu_on == 'true' && not #strings.isEmpty(plays_id)}" class="post_header">
<h2 class="entry-title" th:text="${post.spec.title}"></h2> <h2 class="entry-title" th:text="${post.spec.title}"></h2>
<ul> <ul>
<div class="header_meta"> <div class="header_meta">
<span class="single_time"> <span class="single_time">
<i class="ri-time-line"></i> <i class="ri-time-line"></i>
<time itemprop="datePublished" <time itemprop="datePublished"
th:datetime="${#dates.format(post.spec.publishTime, 'yyyy-MM-dd HH:mm')}"> th:datetime="${#dates.format(post.spec.publishTime, 'yyyy-MM-dd HH:mm')}">
<th:block th:replace="~{modules/widgets/time :: time(${post.spec.publishTime})}"></th:block> <th:block th:replace="~{modules/widgets/time :: time(${post.spec.publishTime})}"></th:block>
</time> </time>
</span> </span>
<span class="post_views"><i class="ri-eye-line"></i>[[${post.stats.visit}]]</span> <span class="post_views"><i class="ri-eye-line"></i>[[${post.stats.visit}]]</span>
<span class="edit_post" th:if="${roleName == 'super-role'}"> <span class="edit_post" th:if="${roleName == 'super-role'}">
<a th:href="${'/console/posts/editor?name='+post.metadata.name+'&returnToView=true'}" <a th:href="${'/console/posts/editor?name='+post.metadata.name+'&returnToView=true'}"
target="_self"> target="_self">
<i class="ri-edit-line"></i>编辑</a> <i class="ri-edit-line"></i>编辑</a>
</span> </span>
</div> </div>
<div class="single-content_header"> <div class="single-content_header">
<span class="single_cat"> <span class="single_cat">
<ul class="post-categories"> <ul class="post-categories">
<li th:each="category : ${post.categories}"> <li th:each="category : ${post.categories}">
<a th:href="@{${category.status.permalink}}" th:text="${category.spec.displayName}" <a th:href="@{${category.status.permalink}}" th:text="${category.spec.displayName}"
th:title="${category.spec.displayName}" rel="category tag"></a> th:title="${category.spec.displayName}" rel="category tag"></a>
</li> </li>
</ul> </ul>
</span> </span>
</div> </div>
</ul> </ul>
</div> </div>
</div> </div>
<div class="single-inner"> <div class="single-inner">
<th:block th:if="${mu_on == 'true' && not #strings.isEmpty(plays_id)}"> <th:block th:if="${mu_on == 'true' && not #strings.isEmpty(plays_id)}">
<div th:if="${mu_on == 'true' && not #strings.isEmpty(plays_id)}" class="mu_l_title"><i class="ri-play-list-line"></i>歌曲列表</div> <div th:if="${mu_on == 'true' && not #strings.isEmpty(plays_id)}" class="mu_l_title"><i class="ri-play-list-line"></i>歌曲列表</div>
<div class="posts_mu_list" th:pid="${post.metadata.name}" th:plays_id="${plays_id}" <div class="posts_mu_list" th:pid="${post.metadata.name}" th:plays_id="${plays_id}"
th:mus_source="${#annotations.get(post, 'mus_source')}" th:mus_source="${#annotations.get(post, 'mus_source')}"
th:mus_type="${#annotations.getOrDefault(post, 'mus_type', 'playlist')}" th:mus_type="${#annotations.getOrDefault(post, 'mus_type', 'playlist')}"
th:mu_on="${mu_on}"></div> th:mu_on="${mu_on}"></div>
</th:block> </th:block>
<div class="single-content" th:utext="${post.content.content}"> <div class="single-content" th:utext="${post.content.content}">
</div> </div>
<div class="single-footer"> <div class="single-footer">
<div class="single_footer_box"> <div class="single_footer_box">
<a class="up_like " data-action="up" data-key="post" th:data-id="${post.metadata.name}"> <a class="up_like " data-action="up" data-key="post" th:data-id="${post.metadata.name}">
<i class="ri-heart-2-line"></i> <i class="ri-heart-2-line"></i>
<span>[[${post.stats.upvote}]]</span> <span>[[${post.stats.upvote}]]</span>
</a> </a>
<div class="pix_share_btn"> <div class="pix_share_btn">
<a class="pix_icon share_btn_icon cr_poster" <a class="pix_icon share_btn_icon cr_poster"
th:poster-data="${post.metadata.name}" th:poster-data="${post.metadata.name}"
th:banner = "${_cover}" th:banner = "${_cover}"
th:uk-toggle="${'target: #share_modal_'+post.metadata.name}" th:uk-toggle="${'target: #share_modal_'+post.metadata.name}"
uk-tooltip="title: 文章分享; pos: top;"> uk-tooltip="title: 文章分享; pos: top;">
<i class="ri-share-line"></i> <i class="ri-share-line"></i>
</a> </a>
<div th:id="${'share_modal_'+post.metadata.name}" class="uk-flex-top poster_modal" <div th:id="${'share_modal_'+post.metadata.name}" class="uk-flex-top poster_modal"
uk-modal> uk-modal>
<div class="uk-modal-dialog uk-modal-body uk-margin-auto-vertical"> <div class="uk-modal-dialog uk-modal-body uk-margin-auto-vertical">
<button class="uk-modal-close-outside" type="button" uk-close></button> <button class="uk-modal-close-outside" type="button" uk-close></button>
<div class="poster_box_ap"></div> <div class="poster_box_ap"></div>
<div class="post_share_box hide"> <div class="post_share_box hide">
<a <a
th:onclick="handleShare('wb','[(${_cover})]','[(${post.spec.title})]','[(${post.status.permalink})]')" th:onclick="handleShare('wb','[(${_cover})]','[(${post.spec.title})]','[(${post.status.permalink})]')"
uk-tooltip="title: 微博分享; pos: top;" target="_blank"><i uk-tooltip="title: 微博分享; pos: top;" target="_blank"><i
class="ri-weibo-line"></i> class="ri-weibo-line"></i>
</a> </a>
<a th:onclick="handleShare('qzone','[(${_cover})]','[(${post.spec.title})]','[(${post.status.permalink})]')" <a th:onclick="handleShare('qzone','[(${_cover})]','[(${post.spec.title})]','[(${post.status.permalink})]')"
uk-tooltip="title: QQ分享; pos: top;" target="_blank"> uk-tooltip="title: QQ分享; pos: top;" target="_blank">
<i class="ri-chrome-line"></i> <i class="ri-chrome-line"></i>
</a> </a>
<a <a
th:onclick="handleShare('qq','[(${_cover})]','[(${post.spec.title})]','[(${post.status.permalink})]')" th:onclick="handleShare('qq','[(${_cover})]','[(${post.spec.title})]','[(${post.status.permalink})]')"
uk-tooltip="title: QQ好友分享; pos: top;" target="_blank"> uk-tooltip="title: QQ好友分享; pos: top;" target="_blank">
<i class="ri-qq-line"></i> <i class="ri-qq-line"></i>
</a> </a>
<a th:onclick="handleShare('copy','','','[(${post.status.permalink})]')" <a th:onclick="handleShare('copy','','','[(${post.status.permalink})]')"
uk-tooltip="title: 复制链接; pos: top;"> uk-tooltip="title: 复制链接; pos: top;">
<i class="ri-file-copy-line"></i> <i class="ri-file-copy-line"></i>
</a> </a>
<a class="poster_download" uk-tooltip="title: 下载海报; pos: top;"> <a class="poster_download" uk-tooltip="title: 下载海报; pos: top;">
<i class="ri-download-line"></i> <i class="ri-download-line"></i>
</a> </a>
</div> </div>
<img id="twoCode" style="display: none;" /> <img id="twoCode" style="display: none;" />
</div> </div>
</div> </div>
</div> </div>
<div class="pix_donate_btn" th:if="${theme.config.post.donate_on}"> <div class="pix_donate_btn" th:if="${theme.config.post.donate_on}">
<a class="pix_icon donate_btn_icon" uk-toggle="target: #donate_modal" uk-tooltip="title: 打赏作者; pos: top;"> <a class="pix_icon donate_btn_icon" uk-toggle="target: #donate_modal" uk-tooltip="title: 打赏作者; pos: top;">
<i class="ri-gift-2-line"></i> <i class="ri-gift-2-line"></i>
</a> </a>
<div id="donate_modal" class="uk-flex-top donate_modal" uk-modal> <div id="donate_modal" class="uk-flex-top donate_modal" uk-modal>
<div class="uk-modal-dialog uk-modal-body uk-margin-auto-vertical"> <div class="uk-modal-dialog uk-modal-body uk-margin-auto-vertical">
<button class="uk-modal-close-outside" type="button" uk-close></button> <button class="uk-modal-close-outside" type="button" uk-close></button>
<div class="donate_des" th:text="${theme.config.post.donate_des}"></div> <div class="donate_des" th:text="${theme.config.post.donate_des}"></div>
<div class="donate_pic"><img th:src="${theme.config.post.donate_pic}"></div> <div class="donate_pic"><img th:src="${theme.config.post.donate_pic}"></div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</th:block> </th:block>
+98 -88
View File
@@ -1,89 +1,99 @@
<th:block th:if="${not #lists.isEmpty(content.medium)}"> <th:block th:if="${not #lists.isEmpty(content.medium)}">
<div class="img_list" th:if="${#strings.contains(content.medium,'PHOTO')}">
<div class="list_inner" th:num="${content.medium.size}"> <div class="img_list">
<span th:each="momentItem : ${content.medium}" th:if="${momentItem.type.name == 'PHOTO'}" <div class="list_inner" th:num="${content.medium.size}">
class="fancybox mo_img" th:href="${momentItem.url}" <th:block th:each="momentItem : ${content.medium}">
th:data-fancybox="${'post-images-'+moment.metadata.name}">
<img class="lazy" th:src="@{/assets/img/lazyload.png}" th:data-src="${momentItem.url}"> <th:block th:with="isStrictVideo=${momentItem.type.name == 'VIDEO'},
</span> isVideoExt=${#strings.endsWith(#strings.toLowerCase(momentItem.url), '.mp4')
</div> || #strings.endsWith(#strings.toLowerCase(momentItem.url), '.webm')
</div> || #strings.endsWith(#strings.toLowerCase(momentItem.url), '.ogv')}">
<div class="video_list" th:if="${#strings.contains(content.medium,'VIDEO')}">
<div class="list_inner"> <div class="pix_bili_player" th:if="${isStrictVideo && momentItem.originType == 'video/bili'}">
<th:block th:each="momentItem : ${content.medium}"> <iframe th:src="|//player.bilibili.com/player.html?bvid=${momentItem.url}&autoplay=0&page=1|"
<div class="pix_local_player" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"></iframe>
th:if="${momentItem.type.name == 'VIDEO' && momentItem.originType != 'video/bili'}"> </div>
<div class="video_play_btn">
<a><i class="ri-play-mini-line"></i></a> <!-- 非 Bilibili 视频:使用 Fancybox 灯箱 -->
</div> <span th:if="${(isStrictVideo || isVideoExt) && momentItem.originType != 'video/bili'}"
<video th:src="${momentItem.url}" id="pix_video_player" objectfit="cover" x5-video-player-type="h5" class="fancybox mo_img" th:href="${momentItem.url}"
onplay="stopOtherMedia(this)"></video> th:data-fancybox="${'post-images-' + moment.metadata.name}" th:data-type="video">
</div> <video th:src="${momentItem.url}" class="pix_video"
<div class="pix_bili_player" style="width:100%; border-radius:8px;"></video>
th:if="${momentItem.type.name == 'VIDEO' && momentItem.originType == 'video/bili'}"> <div
<iframe th:src="|//player.bilibili.com/player.html?bvid=${momentItem.url}&autoplay=0&page=1|" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); pointer-events: none;">
scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"></iframe> <i class="ri-play-circle-line"
</div> style="color: #41b87e; font-size: 40px; background-color: rgba(255, 255, 255, 0.8); border-radius: 50%;"></i>
</th:block> </div>
</span>
</div>
</div> <span th:if="${momentItem.type.name == 'PHOTO' && !isVideoExt}" class="fancybox mo_img"
<div class="audio_list" th:if="${#strings.contains(content.medium,'AUDIO')}"> th:href="${momentItem.url}" th:data-fancybox="${'post-images-' + moment.metadata.name}">
<div class="list_inner"> <img class="lazy" th:src="@{/assets/img/logo.webp}" th:data-src="${momentItem.url}">
<th:block th:each="momentItem : ${content.medium}"> </span>
<div class="pix_player loacl_audio" th:if="${momentItem.type.name == 'AUDIO' &&
(momentItem.originType != 'audio/netease' && momentItem.originType != 'audio/tencent' && </th:block>
momentItem.originType != 'audio/kugou' && momentItem.originType != 'audio/kuwo')}">
<audio th:src="${momentItem.url}" controls></audio> </th:block>
</div> </div>
<div class="pix_player qt" th:if="${momentItem.type.name == 'AUDIO' && </div>
(momentItem.originType == 'audio/netease' || momentItem.originType == 'audio/tencent' ||
momentItem.originType == 'audio/kugou' || momentItem.originType == 'audio/kuwo')}" <div class="audio_list" th:if="${#strings.contains(content.medium,'AUDIO')}">
th:id="${momentItem.url}" <div class="list_inner">
th:type="${momentItem.originType}"> <th:block th:each="momentItem : ${content.medium}">
</div> <div class="pix_player loacl_audio" th:if="${momentItem.type.name == 'AUDIO' &&
</th:block> (momentItem.originType != 'audio/netease' && momentItem.originType != 'audio/tencent' &&
</div> momentItem.originType != 'audio/kugou' && momentItem.originType != 'audio/kuwo')}">
<audio th:src="${momentItem.url}" controls></audio>
</div> </div>
<div class="card_list" th:if="${#strings.contains(content.medium,'POST')}"> <div class="pix_player qt" th:if="${momentItem.type.name == 'AUDIO' &&
<div class="list_inner"> (momentItem.originType == 'audio/netease' || momentItem.originType == 'audio/tencent' ||
<th:block th:each="momentItem : ${content.medium}"> momentItem.originType == 'audio/kugou' || momentItem.originType == 'audio/kuwo')}"
<th:block th:if="${momentItem.type.name == 'POST'}"> th:id="${momentItem.url}" th:type="${momentItem.originType}">
<div class="moment_card_item loop_card_item" th:pid="${momentItem.url}" </div>
th:type="${momentItem.originType}"> </th:block>
<th:block th:if="${momentItem.originType == 'post'}"> </div>
<a th:with="post = ${postFinder.getByName(momentItem.url)}"
th:href="@{${post.status.permalink}}" target="_blank"> </div>
<div class="left"><img th:src="${post.spec.cover}"></div> <div class="card_list" th:if="${#strings.contains(content.medium,'POST')}">
<div class="right"> <div class="list_inner">
<h4 th:text="${post.spec.title}"></h4> <th:block th:each="momentItem : ${content.medium}">
<div class="content"> <th:block th:if="${momentItem.type.name == 'POST'}">
<th:block th:if="${!post.spec.excerpt.autoGenerate}" <div class="moment_card_item loop_card_item" th:pid="${momentItem.url}"
th:text="${post.spec.excerpt.raw}" /> th:type="${momentItem.originType}">
</div> <th:block th:if="${momentItem.originType == 'post'}">
</div> <a th:with="post = ${postFinder.getByName(momentItem.url)}"
</a> th:href="@{${post.status.permalink}}">
</th:block> <div class="left"><img th:src="${post.spec.cover}"></div>
<th:block th:if="${momentItem.originType == 'page'}"> <div class="right">
<a th:with="page = ${singlePageFinder.getByName(momentItem.url)}" <h4 th:text="${post.spec.title}"></h4>
th:href="@{${page.status.permalink}}" target="_blank"> <div class="content">
<div class="left"><img th:src="${page.spec.cover}"></div> <th:block th:if="${!post.spec.excerpt.autoGenerate}"
<div class="right"> th:text="${post.spec.excerpt.raw}" />
<h4 th:text="${page.spec.title}"></h4> </div>
<div class="content"> </div>
<th:block th:if="${!page.spec.excerpt.autoGenerate}" </a>
th:text="${page.spec.excerpt.raw}" /> </th:block>
</div> <th:block th:if="${momentItem.originType == 'page'}">
</div> <a th:with="page = ${singlePageFinder.getByName(momentItem.url)}"
</a> th:href="@{${page.status.permalink}}" target="_blank">
</th:block> <div class="left"><img th:src="${page.spec.cover}"></div>
<div class="right">
</div> <h4 th:text="${page.spec.title}"></h4>
</th:block> <div class="content">
<th:block th:if="${!page.spec.excerpt.autoGenerate}"
</th:block> th:text="${page.spec.excerpt.raw}" />
</div>
</div> </div>
</div> </a>
</th:block>
</div>
</th:block>
</th:block>
</div>
</div>
</th:block> </th:block>
+132 -28
View File
@@ -1,29 +1,133 @@
<th:block th:fragment="footer"> <th:block th:fragment="footer">
<script th:if="${pluginFinder.available('plugin-douban')}" th:src="@{/plugins/plugin-douban/assets/static/db.min.js?v='1.1.6'}" class="pjax"></script> <th:block th:with="customJsVersion='1.2.5-9'">
<script th:src="@{/assets/libs/axios/axios.min.js}" id="axios.min-js"></script>
<script th:src="@{/assets/libs/jsencrypt/jsencrypt.min.js}" id="jsencrypt.min-js"></script> <!-- 奇遇容器 -->
<script th:src="@{/assets/libs/jquery/jquery.min.js}" id="jquery.min-js"></script> <div class="center-img-container" id="centerImgContainer">
<script th:src="@{/assets/libs/qrious/qrious.min.js}"></script> <img class="center-img" id="targetImg">
<script th:src="@{/assets/libs/uikit/uikit.min.js}" id="uikit.js-js"></script> </div>
<script th:src="@{/assets/libs/jquery/jquery.cookie.js}" id="jquery.cookie-js"></script>
<script th:src="@{/assets/libs/fancybox/jquery.fancybox.min.js}" id="fancybox.js-js"></script> <!-- 登录容器 -->
<script th:src="@{/assets/libs/jquery/jquery.form.js}" id="jquery.form-js"></script> <div class="custom-login-modal" id="customLoginModal">
<script th:src="@{/assets/libs/jquery/jquery.validate.js}" id="jquery.validate-js"></script> <div class="login-modal-content login-modern-card">
<script th:src="@{/assets/libs/message/coco-message.js}" id="mesage-js"></script> <button class="close-modal-btn" id="closeLoginModal">&times;</button>
<script th:src="@{/assets/libs/lazyload/lazyload.min.js}" id="lazyload-js"></script> <form class="login-modern-form">
<script th:src="@{/assets/js/moment-push.js?v={version}(version=${theme.spec.version})}" id="moment-push-js"></script> <p class="login-modern-title">日记簿署名</p>
<script th:src="@{/assets/libs/nprogress/nprogress.js}" id="nprogress-js"></script> <div class="login-modern-input-wrapper">
<script th:src="@{/assets/js/poster.js?v={version}(version=${theme.spec.version})}" id="poster-js"></script> <i class="ri-user-line login-modern-icon"></i>
<script th:src="@{/assets/libs/pjax/pjax.min.js}" id="pjax-js"></script> <input type="text" id="customUsername" class="login-modern-input" autocomplete="username"
<script th:src="@{/assets/js/pixplayer.js?v={version}(version=${theme.spec.version})}" id="pixplayer.js-js"></script> placeholder="执笔人">
<script th:src="@{/assets/js/pix.js?v={version}(version=${theme.spec.version})}" id="pix-js"></script> </div>
<script th:src="@{/assets/js/app.js?v={version}(version=${theme.spec.version})}" id="app-js"></script> <div class="login-modern-input-wrapper">
<!-- 自定义js --> <i class="ri-lock-line login-modern-icon"></i>
<script type="text/javascript" th:if="${not #strings.isEmpty(theme.config.custom.code_js)}" <input type="password" id="customPassword" class="login-modern-input"
th:utext="${theme.config.custom.code_js}"> autocomplete="current-password" placeholder="心上锁">
</div>
</script> <button type="button" class="login-modern-submit" id="customLoginBtn">
落 款
<th:block th:if="${not #strings.isEmpty(theme.config.custom.footer_html)}" </button>
th:utext="${theme.config.custom.footer_html}"> </th:block> </form>
</div>
</div>
<!-- 退出登录 -->
<div id="pixarNoticeWrapper" class="pixar-notice-wrapper">
<div class="pixar-notice-card" role="article" aria-labelledby="pixarNoticeUsername">
<div class="pixar-notice-header">
<div class="pixar-notice-avatar"></div>
<p class="pixar-notice-username" id="pixarNoticeUsername">阿年的日记簿</p>
</div>
<div class="pixar-notice-image-area">
<p class="pixar-notice-caption">
您确定要退出登录吗?<br>
请注意:<br>
未保存的内容将会被清空!
</p>
</div>
<div class="pixar-notice-actions">
<!-- 关闭按钮 -->
<button class="pixar-notice-btn pixar-notice-btn-close" aria-label="Close"
onclick="hidePixarCard()">
<i class="ri-close-line"></i>
</button>
<!-- 确认按钮 -->
<button class="pixar-notice-btn pixar-notice-btn-confirm" aria-label="Confirm"
onclick="confirmPixarAction()">
<i class="ri-check-line"></i>
</button>
</div>
</div>
</div>
<!-- 删除确认弹窗 -->
<div class="moments-modal-overlay" id="momentsDeleteModal" style="display: none;">
<div class="moments-card-widget">
<div class="moments-card-header">
<!-- 新增这一层包裹 -->
<div class="moments-header-row">
<div class="moments-icon-wrapper">
<svg aria-hidden="true" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"
fill="none">
<path
d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z"
stroke-linejoin="round" stroke-linecap="round"></path>
</svg>
</div>
<div class="moments-text-content">
<span class="moments-title-text"></span>
<p class="moments-message-text"></p>
</div>
</div>
<div class="moments-button-group">
<button class="moments-btn-cancel" type="button" id="momentsCancelBtn">取消</button>
<button class="moments-btn-confirm" type="button" id="momentsConfirmBtn">确认</button>
</div>
</div>
</div>
</div>
<!-- <script th:if="${pluginFinder.available('plugin-douban')}"
th:src="@{/plugins/plugin-douban/assets/static/db.min.js(v=${customJsVersion})}" class="pjax"></script> -->
<script th:src="@{/assets/libs/axios/axios.min.js(v=${customJsVersion})}" id="axios.min-js"></script>
<script th:src="@{/assets/libs/jsencrypt/jsencrypt.min.js(v=${customJsVersion})}"
id="jsencrypt.min-js"></script>
<script th:src="@{/assets/libs/jquery/jquery.min.js(v=${customJsVersion})}" id="jquery.min-js"></script>
<script th:src="@{/assets/libs/qrious/qrious.min.js(v=${customJsVersion})}"></script>
<script th:src="@{/assets/libs/uikit/uikit.min.js(v=${customJsVersion})}" id="uikit.js-js"></script>
<script th:src="@{/assets/libs/jquery/jquery.cookie.js(v=${customJsVersion})}" id="jquery.cookie-js"></script>
<script th:src="@{/assets/libs/fancybox/jquery.fancybox.min.js(v=${customJsVersion})}"
id="fancybox.js-js"></script>
<script th:src="@{/assets/libs/jquery/jquery.form.js(v=${customJsVersion})}" id="jquery.form-js"></script>
<script th:src="@{/assets/libs/jquery/jquery.validate.js(v=${customJsVersion})}"
id="jquery.validate-js"></script>
<script th:src="@{/assets/libs/message/coco-message.js(v=${customJsVersion})}" id="mesage-js"></script>
<script th:src="@{/assets/libs/lazyload/lazyload.min.js(v=${customJsVersion})}" id="lazyload-js"></script>
<script th:src="@{/assets/libs/nprogress/nprogress.js(v=${customJsVersion})}" id="nprogress-js"></script>
<script th:src="@{/assets/libs/pjax/pjax.min.js(v=${customJsVersion})}" id="pjax-js"></script>
<script th:src="@{/assets/js/moment-push.js(v=${customJsVersion})}" id="moment-push-js"></script>
<script th:src="@{/assets/js/poster.js(v=${customJsVersion})}" id="poster-js"></script>
<script th:src="@{/assets/js/pixplayer.js(v=${customJsVersion})}" id="pixplayer.js-js"></script>
<script th:src="@{/assets/js/pix.js(v=${customJsVersion})}" id="pix-js"></script>
<script th:src="@{/assets/js/app.js(v=${customJsVersion})}" id="app-js"></script>
<script th:src="@{/assets/js/anian.js(v=${customJsVersion})}" id="anian-js"></script>
<link rel="prefetch" href="/upload/ma-shan-zheng.woff2" crossorigin="anonymous">
<link rel="prefetch" href="/upload/mu-yao-sui-xin-shou-xie-ti.woff2" crossorigin="anonymous">
<link rel="prefetch" href="/themes/theme-pix/assets/img/loading.webp">
</th:block>
<!-- 自定义js -->
<script type="text/javascript" th:if="${not #strings.isEmpty(theme.config.custom.code_js)}"
th:utext="${theme.config.custom.code_js}">
</script>
<th:block th:if="${not #strings.isEmpty(theme.config.custom.footer_html)}"
th:utext="${theme.config.custom.footer_html}"> </th:block>
</th:block> </th:block>
+49 -108
View File
@@ -1,109 +1,50 @@
<th:block th:fragment="head"> <th:block th:fragment="head" th:with="customJsVersion=${'1.2.5-9'}">
<th:block th:replace="~{'modules/variables/msg'}" />
<th:block th:replace="~{'modules/variables/msg'}"/> <meta charset="UTF-8">
<meta charset="UTF-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no">
<meta name="viewport" <title th:text="${site_title}"></title>
content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no"> <link rel="shortcut icon" th:href="@{${theme.config.base_set.favicon}}" title="Favicon">
<title th:text="${site_title}"></title> <meta name="robots" content="max-image-preview:large">
<link rel="shortcut icon" th:href="@{${theme.config.base_set.favicon}}" title="Favicon">
<th:block th:replace="~{modules/variables/cst-script-parameter}"/> <link rel="stylesheet" id="wp-block-library-css" th:href="@{/assets/css/style.min.css(v=${customJsVersion})}" media="all">
<meta name="robots" content="max-image-preview:large"> <link rel="stylesheet" id="pix-style-css" th:href="@{/assets/css/style.css(v=${customJsVersion})}" media="all">
<link rel="stylesheet" id="fancybox.css-css" th:href="@{/assets/libs/fancybox/jquery.fancybox.min.css(v=${customJsVersion})}" media="all">
<th:block th:if="${not #strings.isEmpty(theme.config.custom.head_html)}" <link rel="stylesheet" id="nprogress.css-css" th:href="@{/assets/libs/nprogress/nprogress.css(v=${customJsVersion})}" media="all">
th:utext="${theme.config.custom.head_html}"> </th:block> <link rel="stylesheet" id="iconfont-css" th:href="@{/assets/libs/fonts/remixicon.css(v=${customJsVersion})}" media="all">
<link rel="stylesheet" id="uikit.css-css" th:href="@{/assets/libs/uikit/uikit.min.css(v=${customJsVersion})}" media="all">
<meta property="og:title" th:content="${site_title}"> <link rel="stylesheet" id="main.css-css" th:href="@{/assets/css/main.css(v=${customJsVersion})}" media="all">
<meta property="og:site_name" th:content="${site.title}"> <link rel="stylesheet" id="dark-css" th:href="@{/assets/css/dark.css(v=${customJsVersion})}" media="all">
<meta property="og:type" content="website"> <link rel="stylesheet" id="mobile-css" th:href="@{/assets/css/mobile.css(v=${customJsVersion})}" media="all">
<link rel="stylesheet" id="wp-block-library-css" th:href="@{/assets/css/style.min.css?v={version}(version=${theme.spec.version})}" media="all"> <link rel="stylesheet" id="album-css" th:href="@{/assets/css/album.css(v=${customJsVersion})}" media="all">
<style id="classic-theme-styles-inline-css"> <link rel="stylesheet" id="anian-css" th:href="@{/assets/css/anian.css(v=${customJsVersion})}" media="all">
/*! This file is auto-generated */ <link th:if="${theme.config.style.theme_set != 'green-normal' && theme.config.style.theme_set != 'dark-theme'}" rel="stylesheet" id="theme.color-css" th:href="@{/assets/css/{theme_set}.css(theme_set=${theme.config.style.theme_set},v=${customJsVersion})}" media="all">
.wp-block-button__link { <!-- <link th:if="${pluginFinder.available('plugin-douban')}" rel="stylesheet" id="wpd-css-css" th:href="@{/plugins/plugin-douban/assets/static/db.min.css(v=${customJsVersion})}" type="text/css" media="screen"> -->
color: #fff;
background-color: #32373c; <style th:if="${not #strings.isEmpty(theme.config.custom.code_css)}" th:utext="${theme.config.custom.code_css}"></style>
border-radius: 9999px; <style>:root { --toc-color: [(${theme.config.post.tocColor})]; } .wp-block-button__link { color: #fff; background-color: #32373c; border-radius: 9999px; text-decoration: none; padding: .6em 1.3em; } .wp-block-file__button { background: #32373c; color: #fff; text-decoration: none; }</style>
box-shadow: none; <style th:if="${!theme.config.post.toc.mobile_open}">@media screen and (max-width: 768px) { .listree-box, .toc_nav { display: none !important; } }</style>
text-decoration: none; <style th:if="${not #strings.isEmpty(theme.config.base_set.custom_fonts)}">@font-face { font-family: "HarmonyOS_M"; src: url("[(${theme.config.base_set.custom_fonts})]") format("truetype"); font-display: swap; }</style>
padding: calc(.667em + 2px) calc(1.333em + 2px);
font-size: 1.125em <th:block th:replace="~{modules/variables/cst-script-parameter}" />
} <th:block th:if="${not #strings.isEmpty(theme.config.custom.head_html)}" th:utext="${theme.config.custom.head_html}"></th:block>
<meta property="og:title" th:content="${site_title}"><meta property="og:site_name" th:content="${site.title}"><meta property="og:type" content="website">
.wp-block-file__button {
background: #32373c; <script th:src="@{/assets/libs/imgstatus/imgStatus.min.js(v=${customJsVersion})}" id="imgstatus-js"></script>
color: #fff; <script th:src="@{/assets/libs/waterfall/waterfall.min.js(v=${customJsVersion})}" id="waterfall-js"></script>
text-decoration: none <script th:if="${pluginFinder.available('PluginPrismJS')}" th:src="@{/plugins/PluginPrismJS/assets/static/highlight.js/highlight.min.js(v=${customJsVersion})}"></script>
} <th:block th:replace="~{'modules/variables/moments-img-style'}"></th:block>
</style>
<link rel="stylesheet" id="pix-style-css" th:href="@{/assets/css/style.css?v={version}(version=${theme.spec.version})}" media="all"> <script>
<link rel="stylesheet" id="fancybox.css-css" th:href="@{/assets/libs/fancybox/jquery.fancybox.min.css}" media="all"> (win => {
<link rel="stylesheet" id="nprogress.css-css" th:href="@{/assets/libs/nprogress/nprogress.css}" media="all"> win.getCookie = (name) => {
<link rel="stylesheet" id="iconfont-css" th:href="@{/assets/libs/fonts/remixicon.css}" media="all"> let pair = document.cookie.split('; ').find(row => row.startsWith(name + '='));
<link rel="stylesheet" id="uikit.css-css" th:href="@{/assets/libs/uikit/uikit.min.css}" media="all"> return pair ? decodeURIComponent(pair.split('=')[1]) : "";
<link rel="stylesheet" id="main.css-css" th:href="@{/assets/css/main.css?v={version}(version=${theme.spec.version})}" media="all"> };
<link th:if="${theme.config.style.theme_set != 'green-normal' && theme.config.style.theme_set != 'dark-theme'}" win.darkmode = () => {
rel="stylesheet" id="theme.color-css" th:href="@{/assets/css/{theme_set}.css?v={version}(version=${theme.spec.version},theme_set=${theme.config.style.theme_set})}" media="all"> const dark = getCookie("dark");
<link rel="stylesheet" id="dark-css" th:href="@{/assets/css/dark.css?v={version}(version=${theme.spec.version})}" media="all"> return dark ? (dark === 'dark') : (Theme.theme_set === 'dark-theme');
<link rel="stylesheet" id="mobile-css" th:href="@{/assets/css/mobile.css?v={version}(version=${theme.spec.version})}" media="all"> };
<link rel="stylesheet" id="album-css" th:href="@{/assets/css/album.css?v={version}(version=${theme.spec.version})}" media="all"> if (darkmode()) document.documentElement.classList.add('dark');
<script th:src="@{/assets/libs/imgstatus/imgStatus.min.js}" id="imgstatus-js"></script> })(window)
<script th:src="@{/assets/libs/waterfall/waterfall.min.js}" id="waterfall-js"></script> </script>
<script th:if="${pluginFinder.available('PluginPrismJS')}" src="/plugins/PluginPrismJS/assets/static/highlight.js/highlight.min.js"></script>
<link th:if="${pluginFinder.available('plugin-douban')}" rel="stylesheet" id="wpd-css-css" th:href="@{/plugins/plugin-douban/assets/static/db.min.css}" type="text/css"
media="screen">
<style type="text/css"
th:if="${not #strings.isEmpty(theme.config.custom.code_css)}"
th:utext="${theme.config.custom.code_css}" ></style>
<style type="text/css">
:root {
--toc-color: [(${theme.config.post.tocColor})];
}
</style>
<style type="text/css" th:if="${!theme.config.post.toc.mobile_open}">
@media screen and (max-width: 768px) {
.listree-box {
display: none !important;
}
}
</style>
<!-- 自定义css -->
<style type="text/css" th:if="${not #strings.isEmpty(theme.config.base_set.custom_fonts)}">
@font-face {
font-style: normal;
font-family: "HarmonyOS_M";
src: url("[(${theme.config.base_set.custom_fonts})]") format("truetype");
font-display: swap;
}
</style>
<th:block th:replace="~{'modules/variables/moments-img-style'}"></th:block>
<script>
(win => {
win.getCookie = function(name) {
var cookieArr = document.cookie.split(';');
for (var i = 0; i < cookieArr.length; i++) {
var cookiePair = cookieArr[i].split('=');
var cookieName = cookiePair[0].trim();
if (cookieName === name) {
return decodeURIComponent(cookiePair[1]);
}
}
return "";
}
win.darkmode = function () {
$darkmode = false;
const dark = getCookie("dark")
if (dark) {
$darkmode = (dark == 'dark') ? true : false;
} else if (Theme.theme_set == 'dark-theme') {
$darkmode = true;
}
return $darkmode;
}
if (darkmode()) {
document.documentElement.classList.add('dark')
}
})(window)
</script>
</th:block> </th:block>

Some files were not shown because too many files have changed in this diff Show More