3 Commits

Author SHA1 Message Date
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
66 changed files with 33332 additions and 25664 deletions
Executable
+21
View File
@@ -0,0 +1,21 @@
# halo-theme-pix
> 基于原作者v1.2.5代码修改
#### v1.2.5-3
1. 朋友圈支持根据作者筛选内容(需同步更新插件)
2. 支持前台弹窗登入登出
2. 附录页面功能补全
3. 对线路切换、加载异常等情况进行了深度优化
#### 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/8aea7800gy1i2vfbxoqhdj24w07c0b2p.jpg">
</div>
<div class="info">
<p class="name">黄诗扶</p>
<p class="bio">
又甜又脆的大冬枣!好听!好看!好吃!黄黄的伟大无需多言!
</p>
</div>
</div>
<div class="person">
<div class="avatar">
<img src="/upload/ec39c62fly1i3rextah4gj22dc35s1ky.jpg">&nbsp;
</div>
<div class="info">
<p class="name">双笙</p>
<p class="bio">
什么?你问我为什么喜欢包包?可能因为是“青春”吧?
</p>
</div>
</div>
<div class="person">
<div class="avatar">
<img src="/upload/005Jyswjgy1hywx7z7sb7j30zk1betbd.jpg">
</div>
<div class="info">
<p class="name">浅影阿</p>
<p class="bio">
谁能拒绝唱歌这么好看的老大呢?反正我是不行!下一位!
</p>
</div>
</div>
<div class="person">
<div class="avatar">
<img src="/upload/ab0efe4ely1hnpfzqqw1oj23ls5eoqvg.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="/upload/logo.png">
</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.png">
</div>
<div class="yg-2025-hero">
<img src="/upload/2025-0-2.png">
</div>
</div>
<!-- ========= 12 个月:4 行 3 列 ========= -->
<div class="yg-grid-2025-3col">
<div class="yg-card">
<img src="/upload/2025-1-1.png">
<img src="/upload/2025-1-2.png">
</div>
<div class="yg-card">
<img src="/upload/2025-2-1.png">
<img src="/upload/2025-2-2.png">
</div>
<div class="yg-card">
<img src="/upload/2025-3-1.png">
<img src="/upload/2025-3-2.png">
</div>
<div class="yg-card">
<img src="/upload/2025-4-1.png">
<img src="/upload/2025-4-2.png">
</div>
<div class="yg-card">
<img src="/upload/2025-5-1.png">
<img src="/upload/2025-5-2.png">
</div>
<div class="yg-card">
<img src="/upload/2025-6-1.png">
<img src="/upload/2025-6-2.png">
</div>
<div class="yg-card">
<img src="/upload/2025-7-1.png">
<img src="/upload/2025-7-2.png">
</div>
<div class="yg-card">
<img src="/upload/2025-8-1.png">
<img src="/upload/2025-8-2.png">
</div>
<div class="yg-card">
<img src="/upload/2025-9-1.png">
<img src="/upload/2025-9-2.png">
</div>
<div class="yg-card">
<img src="/upload/2025-10-1.png">
<img src="/upload/2025-10-2.png">
</div>
<div class="yg-card">
<img src="/upload/2025-11-1.png">
<img src="/upload/2025-11-2.png">
</div>
<div class="yg-card">
<img src="/upload/2025-12-1.png">
<img src="/upload/2025-12-2.png">
</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.png">
</div>
</div>
<div class="yg-grid-2026">
<div class="yg-2026-month"><img src="/upload/2026-1.png"></div>
<div class="yg-2026-month"><img src="/upload/2026-2.png"></div>
<div class="yg-2026-month"><img src="/upload/2026-3.png"></div>
<div class="yg-2026-month"><img src="/upload/2026-4.png"></div>
<div class="yg-2026-month"><img src="/upload/2026-5.png"></div>
<div class="yg-2026-month"><img src="/upload/2026-6.png"></div>
<div class="yg-2026-month"><img src="/upload/2026-7.png"></div>
<div class="yg-2026-month"><img src="/upload/2026-8.png"></div>
<div class="yg-2026-month"><img src="/upload/2026-9.png"></div>
<div class="yg-2026-month"><img src="/upload/2026-10.png"></div>
<div class="yg-2026-month"><img src="/upload/2026-11.png"></div>
<div class="yg-2026-month"><img src="/upload/2026-12.png"></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>
+249
View File
@@ -0,0 +1,249 @@
<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="/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.cc/explore/repos" 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="#" 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>
<a href="#" 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-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>
</div>
<style>
@media (max-width: 767px) {
#my-links-widget { margin-top: 40px !important; }
}
@media (min-width: 768px) {
.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();
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.cc', 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.png">
<img id="calendar-2025-intro-img2" src="/upload/2025-0-2.png">
</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.png`;
monthImg2.src = `/upload/2025-${month}-2.png`;
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.png">
</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}.png`;
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>
+275
View File
@@ -0,0 +1,275 @@
<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.cc/anian/halo-plugin-photos/releases.rss">
<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.cc/anian/halo-plugin-friends/releases.rss">
<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.cc/anian/halo-theme-pix/releases.rss">
<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: 1;
min-width: 320px;
/* 优化最小宽度 */
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-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: 380px;
/* 优化高度 */
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 {
min-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;">请求失败: ${error.message}</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>
+315
View File
@@ -0,0 +1,315 @@
<style>
.store * {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Inter", "Microsoft YaHei", Arial;
}
.store {
--primary: #50C878;
--primary-light: #90EE90;
--primary-dark: #3A9156;
--danger: #E74C3C;
--bg: #F9FFFB;
--shadow: 0 12px 30px rgba(80, 200, 120, .12);
--radius: 18px;
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: 10px;
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: 12px;
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: 9px;
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;
color: #7f8c8d;
font-size: 13px;
padding: 8px 0;
display: none;
}
/* 输入框 */
.store .input-group {
display: flex;
gap: 10px;
margin: 16px 0;
}
#file-path {
flex: 1;
padding: 8px 14px;
border-radius: 12px;
border: 2px solid #e8f7ec;
font-size: 14px;
outline: none;
}
#submit-btn {
padding: 10px;
border-radius: 12px;
border: none;
background: linear-gradient(135deg, #50c878, #3a9156);
color: #fff;
font-size: 14px;
cursor: pointer;
}
/* 底部提示(改成浅黄色系) */
.store .tips {
font-size: 12px;
text-align: center;
color: #a97400;
padding: 8px;
border-radius: 8px;
background: rgba(255, 215, 0, .12);
border: 1px solid rgba(243, 156, 18, .25);
}
/* 手机端 */
@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
}
#submit-btn {
width: 100%
}
.store .tag-list {
max-height: 120px
}
}
/* Toast */
.custom-toast {
position: fixed;
top: 26px;
left: 50%;
transform: translateX(-50%) translateY(-80px);
background: #2ECC71;
color: #fff;
padding: 12px 20px;
border-radius: 10px;
transition: .4s;
opacity: 0;
z-index: 9999;
}
.toast-show {
transform: translateX(-50%) translateY(0);
opacity: 1
}
.toast-error {
background: #E74C3C
}
.toast-warning {
background: #F39C12
}
</style>
<div class="store">
<div class="container">
<div class="top-bar"></div>
<h1 class="title">Anian的储物柜</h1>
<div class="tag-container">
<div class="tag-search-wrapper">
<span class="search-icon">🔍</span>
<input id="tag-search" placeholder="搜索标签">
</div>
<div class="tag-list">
<div class="tag-item" data-value="test.zip">测试压缩包</div>
<div class="tag-item" data-value="docs/report.pdf">示例报告</div>
<div class="tag-item" data-value="images/banner.jpg">横幅图片</div>
<div class="tag-item" data-value="software/setup.exe">安装程序</div>
<div class="no-result" id="noResult">没有匹配的结果</div>
</div>
</div>
<div class="input-group">
<input id="file-path" placeholder="请输入路径" autocomplete="off">
<button id="submit-btn">访问 / 下载</button>
</div>
<p class="tips">⚠️ 错误过多将可能被封禁</p>
</div>
<div class="custom-toast" id="customToast"></div>
</div>
<script>
function initializeStoreEvents() {
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 toast = document.getElementById('customToast');
const tagItems = document.querySelectorAll('.tag-item');
const tagSearch = document.getElementById('tag-search');
const noResult = document.getElementById('noResult');
function showToast(msg, type = 'warning') {
toast.textContent = msg;
toast.className = 'custom-toast toast-' + type + ' toast-show';
setTimeout(() => toast.classList.remove('toast-show'), 2600);
}
tagItems.forEach(t => {
t.onclick = () => {
input.value = t.dataset.value;
showToast('已填入 ' + t.dataset.value, '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 ? 'block' : 'none';
};
function check(url) {
return new Promise(r => {
const xhr = new XMLHttpRequest();
xhr.open('HEAD', url, true);
xhr.timeout = 5000;
xhr.onload = () => r(xhr.status === 200);
xhr.onerror = () => r(false);
xhr.ontimeout = () => r(false);
xhr.send();
});
}
async function go() {
const v = input.value.trim();
if (!v) return showToast('请输入路径');
const url = '/' + v;
btn.textContent = '检测中...';
btn.disabled = true;
const ok = await check(url);
if (ok) {
showToast('资源存在,跳转中', 'success');
setTimeout(() => location.href = url, 700);
} else showToast('资源不存在', 'error');
setTimeout(() => { btn.textContent = '访问 / 下载'; btn.disabled = false }, 700);
}
btn.onclick = go;
input.onkeydown = e => { if (e.key === 'Enter') go(); };
}
document.addEventListener("DOMContentLoaded", initializeStoreEvents);
document.addEventListener("pjax:complete", initializeStoreEvents);
</script>
+18
View File
@@ -0,0 +1,18 @@
<div class="nav4lite-section">
<div class="nav4lite-grid">
<a href="/" class="nav4lite-item">
<span class="nav4lite-label">课外书</span>
</a>
<a href="/friends" class="nav4lite-item">
<span class="nav4lite-label">朋友圈</span>
</a>
<a href="/docs" class="nav4lite-item" target="_blank">
<span class="nav4lite-label">笔记本</span>
</a>
<a href="/address" class="nav4lite-item">
<span class="nav4lite-label">通讯录</span>
</a>
<a href="/navigator" class="nav4lite-center-dot" aria-label="查看详情"><i
class="sidebar-more ri-arrow-right-s-line"></i></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.cc" class="feedback-email">support@anian.cc</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" data-anianx-href="https://anian.cc">主线路</button>
<button class="anianx-btn" data-anianx-type="test">测速</button>
<button class="anianx-btn anianx-right" data-anianx-type="sub" data-anianx-href="https://www.anian.cc">副线路</button>
<div class="anianx-bubble" id="anianxBubble"><span id="anianxBubbleText" class="anianx-loading">当前线路检测中</span></div>
</div>
</div>
+21 -21
View File
@@ -1,22 +1,22 @@
<!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 = ${#strings.isEmpty(param.page) ? 1 : param.page}">
<th:block th:replace="~{macro/home-blog :: home-blog(${postFinder.list(page,site.post.postPageSize)},'/archives')}"></th:block> <th:block th:replace="~{macro/home-blog :: home-blog(${postFinder.list(page,site.post.postPageSize)},'/archives')}"></th:block>
</div> </div>
<th:block th:replace="~{modules/footer-main}"/> <th:block th:replace="~{modules/footer-main}"/>
</div> </div>
</th:block> </th:block>
</html> </html>
+326
View File
@@ -0,0 +1,326 @@
/* 瞬间标签与评论间距 */
.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);
}
}
@media (min-width: 1360px) {
.listree-box {
margin-left: calc((100vw - 1400px) / 2 + 291px);
}
}
/* 登录容器 */
.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 {
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;
}
+6875 -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

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 229 KiB

+261
View File
@@ -0,0 +1,261 @@
// 接管手机端底部音乐按钮
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);
});
}
// 视频默认正方形,播放恢复比例(接入灯箱后已无用)
// function modify_moment_video_size() {
// const videos = document.querySelectorAll('.pix_video');
// videos.forEach(video => {
// if (!video.hasAttribute('data-video-events-bound')) {
// video.addEventListener('play', () => {
// video.classList.add('is-playing');
// });
// video.addEventListener('ended', () => {
// video.classList.remove('is-playing');
// });
// video.setAttribute('data-video-events-bound', 'true');
// }
// });
// }
// 登录逻辑
let csrfToken = '';
let publicKey = '';
let loginTimer = null;
let countdownSeconds = 3;
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 = '加载中...';
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) {
return 'error';
}
}
// 打开弹窗逻辑
async function open_login_box() {
if (loginTimer) {
clearInterval(loginTimer);
}
countdownSeconds = 3;
const success = await fetchCSRFToken();
if (success === 'logged') {
cocoMessage.success('您已经署名过了,请刷新');
login_modal.classList.remove('show');
} else if (success === 'ok') {
loginTimer = setInterval(() => {
if (countdownSeconds > 0) {
login_loginBtn.textContent = '请稍等...' + countdownSeconds;
} else {
clearInterval(loginTimer);
login_loginBtn.textContent = '署 名';
login_loginBtn.disabled = false;
}
countdownSeconds--;
}, 1000);
} else {
cocoMessage.error('服务器错误,请刷新');
login_modal.classList.remove('show');
}
}
// 关闭弹窗
login_closeBtn.addEventListener('click', () => {
login_modal.classList.remove('show');
});
// 加密逻辑
function encryptPassword(password) {
try {
const encrypt = new JSEncrypt();
encrypt.setPublicKey(publicKey);
return encrypt.encrypt(password);
} catch (e) {
return null;
}
}
// 提交登录
login_loginBtn.addEventListener('click', async () => {
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_loginBtn.textContent = '验证中...';
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')) {
cocoMessage.success('署名成功,正在跳转...');
login_loginBtn.textContent = '跳转中...';
setTimeout(() => {
window.location.reload();
}, 800);
} else if (finalUrl.includes('error=invalid-credential')) {
cocoMessage.warning('署名人与心上锁不匹配');
fetchCSRFToken();
} else {
cocoMessage.error('速度过快,请稍后重试');
}
} catch (err) {
cocoMessage.error('服务器连接失败');
} finally {
loginTimer = setInterval(() => {
if (countdownSeconds > 0) {
login_loginBtn.textContent = '请稍等...' + countdownSeconds;
} else {
clearInterval(loginTimer);
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() {
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 令牌');
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) {
cocoMessage.success('您已成功退出登录');
setTimeout(() => {
window.location.reload();
}, 800);
} else {
throw new Error('退出请求被拒绝');
}
} catch (err) {
console.error('Logout Error:', err);
cocoMessage.error('退出失败,请手动刷新页面');
}
}
function showPixarCard() {
const wrapper = document.getElementById("pixarNoticeWrapper");
wrapper.style.display = "flex";
}
function hidePixarCard() {
const wrapper = document.getElementById("pixarNoticeWrapper");
wrapper.style.display = "none";
}
function confirmPixarAction() {
customLogout();
}
// = = = = = = = = = = = = = = = = 触发器 = = = = = = = = = = = = = = = =
// 集合:PJAX(初始化执行+PJAX执行)
function init_anian_pjax() {
// modify_moment_video_size();
}
// 集合:初始化(仅初始化执行一次)
function init_anian_inport() {
init_anian_pjax();
modify_m_bottom_music_button_link();
}
// PJAX监听
document.addEventListener('pjax:complete', function () {
init_anian_pjax();
})
// 入口
init_anian_inport();
+2947 -1594
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+216 -216
View File
@@ -1,217 +1,217 @@
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="/upload/nodata.png"></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="/upload/nodata.png"></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 () { topCategoriesBarScroll: function () {
if (document.getElementById("cat_nav_items")) { if (document.getElementById("cat_nav_items")) {
let xscroll = document.getElementById("cat_nav_items"); let xscroll = document.getElementById("cat_nav_items");
xscroll.addEventListener("mousewheel", function (e) { xscroll.addEventListener("mousewheel", function (e) {
//计算鼠标滚轮滚动的距离 //计算鼠标滚轮滚动的距离
let v = -e.wheelDelta / 2; let v = -e.wheelDelta / 2;
xscroll.scrollLeft += v; xscroll.scrollLeft += v;
//阻止浏览器默认方法 //阻止浏览器默认方法
e.preventDefault(); e.preventDefault();
}, false); }, false);
} }
}, },
topTableBarScroll: function () { topTableBarScroll: function () {
var tables = document.querySelectorAll('.single-content table') var tables = document.querySelectorAll('.single-content table')
if (tables.length>0) { if (tables.length>0) {
tables.forEach(function (xscroll) { tables.forEach(function (xscroll) {
xscroll.addEventListener("mousewheel", function (e) { xscroll.addEventListener("mousewheel", function (e) {
//计算鼠标滚轮滚动的距离 //计算鼠标滚轮滚动的距离
let v = -e.wheelDelta / 2; let v = -e.wheelDelta / 2;
xscroll.scrollLeft += v; xscroll.scrollLeft += v;
//阻止浏览器默认方法 //阻止浏览器默认方法
e.preventDefault(); e.preventDefault();
}, false); }, false);
}) })
} }
}, },
//处理瞬间权限 //处理瞬间权限
roleMoments: async function () { roleMoments: async function () {
const response = await fetch(`/apis/api.console.halo.run/v1alpha1/users/-`); const response = await fetch(`/apis/api.console.halo.run/v1alpha1/users/-`);
const data = await response.json(); const data = await response.json();
var roles = data.roles; var roles = data.roles;
if(roles.length>0) { if(roles.length>0) {
var name = roles[0].metadata.name var name = roles[0].metadata.name
if(name != 'super-role') { if(name != 'super-role') {
let dependencies = roles[0].metadata.annotations["rbac.authorization.halo.run/dependencies"] let dependencies = roles[0].metadata.annotations["rbac.authorization.halo.run/dependencies"]
if(dependencies != undefined) { if(dependencies != undefined) {
var targetRoles = ["role-template-uc-moments-approved","role-template-uc-moments-publish"] var targetRoles = ["role-template-uc-moments-approved","role-template-uc-moments-publish"]
const result = targetRoles.some(r => dependencies.includes(r)); const result = targetRoles.some(r => dependencies.includes(r));
if(!result) { if(!result) {
$('.normal_edit').remove(); $('.normal_edit').remove();
} }
}else { }else {
$('.normal_edit').remove(); $('.normal_edit').remove();
} }
} }
} }
}, },
} }
File diff suppressed because it is too large Load Diff
+520 -418
View File
@@ -1,418 +1,520 @@
// 部分代码来自ZIBILL主题作者 // 部分代码来自ZIBILL主题作者
// canvas生成海报 // canvas生成海报
const poster = (function() { const poster = (function() {
const DEBUG = false const DEBUG = false
const WIDTH = 700 const WIDTH = 700
const HEIGHT = 1160 const HEIGHT = 1160
function init(config) { /**
const $container = document.querySelector(config.selector) * 将图片URL加载为Image对象并返回Promise
$container.style.border = '1px solid #f0f0f0' * @param {string} url - 图片URL
const $wrapper = createDom('div', 'id', 'wrapper') * @returns {Promise<Image>} - 加载完成的Image对象
const $canvas = createDom('canvas', 'id', 'canvas', 'block') */
const $day = createDom('canvas', 'id', 'day') function loadImage(url) {
const $date = createDom('canvas', 'id', 'date') return new Promise((resolve, reject) => {
const $title = createDom('canvas', 'id', 'title') const img = new Image();
const $content = createDom('canvas', 'id', 'content') img.crossOrigin = "Anonymous";
const $logo = createDom('canvas', 'id', 'logo') img.onload = () => resolve(img);
const $description = createDom('canvas', 'id', 'description') img.onerror = () => reject(new Error('Failed to load image: ' + url));
img.src = url;
appendChilds($wrapper, $canvas, $day, $date, $title, $content, $logo, $description) });
$container.appendChild($wrapper) }
const date = new Date() /**
* 核心初始化函数
// day * @param {object} config - 配置对象
const dayStyle = { */
font: 'bold 80px Helvetica', function init(config) {
color: 'rgba(255, 255, 255, 1)', const $container = document.querySelector(config.selector)
position: 'right' // ------------------ 【修复点 2:清理容器,避免PJAX残余】 ------------------
} // 在重新绘制前,确保清空容器,移除旧的 img 和 wrapper
drawOneline($day, dayStyle, date.getDate()); $container.innerHTML = '';
// -----------------------------------------------------------------------
// date
const dateStyle = { $container.style.border = '1px solid #f0f0f0'
font: '23px Helvetica', const $wrapper = createDom('div', 'id', 'wrapper')
color: 'rgba(255, 255, 255, 1)', const $canvas = createDom('canvas', 'id', 'canvas', 'block')
position: 'right' const $day = createDom('canvas', 'id', 'day')
} const $date = createDom('canvas', 'id', 'date')
drawOneline($date, dateStyle, date.getFullYear() + '/' + (date.getMonth() + 1)) const $title = createDom('canvas', 'id', 'title')
const $content = createDom('canvas', 'id', 'content')
// title canvas const $logo = createDom('canvas', 'id', 'logo')
const titleStyle = { const $description = createDom('canvas', 'id', 'description')
font: '30px Helvetica',
lineHeight: 1.5, appendChilds($wrapper, $canvas, $day, $date, $title, $content, $logo, $description)
color: 'rgba(255, 255, 255, 1)', $container.appendChild($wrapper)
length: 2,
position: 'left' const date = new Date()
}
titleStyle.font = (config.titleStyle && config.titleStyle.font) || titleStyle.font // day canvas
titleStyle.color = (config.titleStyle && config.titleStyle.color) || titleStyle.color const dayStyle = {
titleStyle.position = (config.titleStyle && config.titleStyle.position) || titleStyle.position font: 'bold 80px Helvetica',
drawMoreLines($title, titleStyle, config.title) color: 'rgba(255, 255, 255, 1)',
position: 'right'
// content canvas }
const contentStyle = { drawOneline($day, dayStyle, date.getDate());
font: '22px Helvetica',
lineHeight: 1.5, // date canvas
position: 'left', const dateStyle = {
color: 'rgba(236, 241, 255, 1)' font: '23px Helvetica',
} color: 'rgba(255, 255, 255, 1)',
contentStyle.font = (config.contentStyle && config.contentStyle.font) || contentStyle.font position: 'right'
contentStyle.color = (config.contentStyle && config.contentStyle.color) || contentStyle.color }
contentStyle.lineHeight = (config.contentStyle && config.contentStyle.lineHeight) || contentStyle.lineHeight drawOneline($date, dateStyle, date.getFullYear() + '/' + (date.getMonth() + 1))
contentStyle.position = (config.contentStyle && config.contentStyle.position) || contentStyle.position
drawMoreLines($content, contentStyle, config.content); // title canvas
const titleStyle = {
// description font: '30px Helvetica',
const descriptionStyle = { lineHeight: 1.5,
font: '24px Helvetica', color: 'rgba(255, 255, 255, 1)',
color: 'rgba(180, 180, 180, 1)', length: 2,
lineHeight: 1.2, position: 'left'
position: 'left' }
} titleStyle.font = (config.titleStyle && config.titleStyle.font) || titleStyle.font
drawMoreLines($description, descriptionStyle, config.description) titleStyle.color = (config.titleStyle && config.titleStyle.color) || titleStyle.color
titleStyle.position = (config.titleStyle && config.titleStyle.position) || titleStyle.position
drawMoreLines($title, titleStyle, config.title)
// background image
const image = new Image(); // content canvas
image.crossOrigin = "Anonymous"; const contentStyle = {
font: '22px Helvetica',
//logo lineHeight: 1.5,
const logo = new Image(); position: 'left',
logo.crossOrigin = "Anonymous"; color: 'rgba(236, 241, 255, 1)'
logo.src = config.logo; }
contentStyle.font = (config.contentStyle && config.contentStyle.font) || contentStyle.font
//qrcode contentStyle.color = (config.contentStyle && config.contentStyle.color) || contentStyle.color
const qrcode = new Image(); contentStyle.lineHeight = (config.contentStyle && config.contentStyle.lineHeight) || contentStyle.lineHeight
qrcode.src = config.qrcode; contentStyle.position = (config.contentStyle && config.contentStyle.position) || contentStyle.position
drawMoreLines($content, contentStyle, config.content);
const onload = function() { // description canvas
$canvas.width = WIDTH; const descriptionStyle = {
$canvas.height = HEIGHT; font: '24px Helvetica',
image.src = config.banner; color: 'rgba(180, 180, 180, 1)',
image.onload = function() { lineHeight: 1.2,
const ctx = $canvas.getContext('2d') position: 'left'
ctx.fillStyle = 'rgba(255, 255, 255, 1)'; }
ctx.fillRect(0, 0, $canvas.width, $canvas.height); drawMoreLines($description, descriptionStyle, config.description)
// banner
imgRect = coverImg($canvas.width - 40, $canvas.height / 1.2 - 40, image.width, image.height); // ------------------ 【修复点 1:使用 Promise.all 等待所有图片加载】 ------------------
ctx.drawImage(image, imgRect.sx, imgRect.sy, imgRect.sWidth, imgRect.sHeight, 20, 20, $canvas.width - 40, $canvas.height / 1.2 - 40); Promise.all([
loadImage(config.banner),
//覆盖层 loadImage(config.logo),
ctx.fillStyle="rgba(0, 0, 0, 0.3)"; loadImage(config.qrcode)
ctx.fillRect(20,20,$canvas.width - 40,$canvas.height / 1.2 - 40); ]).then(([image, logo, qrcode]) => {
// 时间 $canvas.width = WIDTH;
ctx.drawImage($day, -20, 50) $canvas.height = HEIGHT;
ctx.drawImage($date, -21, 125) const ctx = $canvas.getContext('2d')
//logo // 1. 绘制背景
var logowidth = logo.width; ctx.fillStyle = 'rgba(255, 255, 255, 1)';
var logoheight = logo.height; ctx.fillRect(0, 0, $canvas.width, $canvas.height);
var scale = logowidth / logoheight;
var logoh = 60; // 2. 绘制 Banner
var cwidth = logoh * scale; const imgRect = coverImg($canvas.width - 40, $canvas.height / 1.2 - 40, image.width, image.height);
ctx.drawImage(logo, 60, $canvas.height / 1.2 + 30, cwidth, logoh); ctx.drawImage(image, imgRect.sx, imgRect.sy, imgRect.sWidth, imgRect.sHeight, 20, 20, $canvas.width - 40, $canvas.height / 1.2 - 40);
ctx.drawImage(qrcode, $canvas.width - 160, $canvas.height / 1.2 + 20, 120, 120); // 3. 绘制覆盖层
ctx.fillStyle="rgba(0, 0, 0, 0.3)";
//标题文字 ctx.fillRect(20, 20, $canvas.width - 40, $canvas.height / 1.2 - 40);        
ctx.drawImage($title, 20, $canvas.height / 2 + 20)
ctx.drawImage($content, 20, $canvas.height / 2 + 120) // 4. 绘制时间 (使用预先绘制好的 Canvas)
ctx.drawImage($description, 20, $canvas.height / 1.2 + 110) ctx.drawImage($day, -20, 50)
ctx.strokeStyle = 'rgba(122, 122, 122, 0.5)'; ctx.drawImage($date, -21, 125)
const img = new Image(); // 5. 绘制 Logo
img.crossOrigin = "Anonymous"; var logoh = 60;
img.src = $canvas.toDataURL('image/png') var cwidth = logoh * (logo.width / logo.height);
const radio = config.radio || 0.7 ctx.drawImage(logo, 60, $canvas.height / 1.2 + 30, cwidth, logoh);
img.width = WIDTH * radio
img.height = HEIGHT * radio // 6. 绘制二维码
img.className = 'poster_load'; ctx.drawImage(qrcode, $canvas.width - 160, $canvas.height / 1.2 + 20, 120, 120);
ctx.clearRect(0, 0, $canvas.width, $canvas.height)
$canvas.style.display = 'none' // 7. 绘制文字 (使用预先绘制好的 Canvas)
ctx.drawImage($title, 20, $canvas.height / 2 + 20)
if ($container.querySelector('.poster_load')) { ctx.drawImage($content, 20, $canvas.height / 2 + 120)
$container.querySelector('.poster_load').src = img.src; ctx.drawImage($description, 20, $canvas.height / 1.2 + 110)
} else { ctx.strokeStyle = 'rgba(122, 122, 122, 0.5)';
$container.appendChild(img);
} // 8. 转换并显示最终图片
const img = new Image();
$container.appendChild(img); img.crossOrigin = "Anonymous";
$container.removeChild($wrapper) img.src = $canvas.toDataURL('image/png')
if (config.callback) { const radio = config.radio || 0.7
config.callback($container) img.width = WIDTH * radio
} img.height = HEIGHT * radio
} img.className = 'poster_load';
}
// 清理 Canvas 避免在屏幕上停留
onload() ctx.clearRect(0, 0, $canvas.width, $canvas.height)
} $canvas.style.display = 'none'
//裁切 // 移除旧的 wrapper 并添加最终海报图
function containImg(sx, sy, box_w, box_h, source_w, source_h) { $container.appendChild(img);
var dx = sx, // 必须先移除再调用 callback,以确保 DOM 结构干净
dy = sy, $container.removeChild($wrapper)
dWidth = box_w,
dHeight = box_h; if (config.callback) {
if (source_w > source_h || (source_w == source_h && box_w < box_h)) { config.callback($container)
dHeight = source_h * dWidth / source_w; }
dy = sy + (box_h - dHeight) / 2; }).catch(error => {
console.error('海报图片加载失败:', error);
} else if (source_w < source_h || (source_w == source_h && box_w > box_h)) { // 可在此处添加错误处理,例如显示一个错误信息
dWidth = source_w * dHeight / source_h; $container.removeChild($wrapper);
dx = sx + (box_w - dWidth) / 2; if (config.callback) {
} // 即使失败也调用回调,传入 null 或错误信息,以便上层逻辑处理
return { config.callback(null);
dx, }
dy, });
dWidth, // -----------------------------------------------------------------------
dHeight }
}
} // 裁剪函数(不变)
function containImg(sx, sy, box_w, box_h, source_w, source_h) {
function coverImg(box_w, box_h, source_w, source_h) { var dx = sx,
var sx = 0, dy = sy,
sy = 0, dWidth = box_w,
sWidth = source_w, dHeight = box_h;
sHeight = source_h; if (source_w > source_h || (source_w == source_h && box_w < box_h)) {
if (source_w > source_h || (source_w == source_h && box_w < box_h)) { dHeight = source_h * dWidth / source_w;
sWidth = box_w * sHeight / box_h; dy = sy + (box_h - dHeight) / 2;
sx = (source_w - sWidth) / 2;
} else if (source_w < source_h || (source_w == source_h && box_w > box_h)) { } else if (source_w < source_h || (source_w == source_h && box_w > box_h)) {
sHeight = box_h * sWidth / box_w; dWidth = source_w * dHeight / source_h;
sy = (source_h - sHeight) / 2; dx = sx + (box_w - dWidth) / 2;
} }
return { return {
sx, dx,
sy, dy,
sWidth, dWidth,
sHeight dHeight
} }
} }
function createDom(name, key, value, display = 'none') { // 覆盖函数(不变)
const $dom = document.createElement(name) function coverImg(box_w, box_h, source_w, source_h) {
$dom.setAttribute(key, value) var sx = 0,
$dom.style.display = display sy = 0,
$dom.width = WIDTH sWidth = source_w,
return $dom sHeight = source_h;
} if (source_w > source_h || (source_w == source_h && box_w < box_h)) {
sWidth = box_w * sHeight / box_h;
function appendChilds(parent, ...doms) { sx = (source_w - sWidth) / 2;
doms.forEach(dom => { } else if (source_w < source_h || (source_w == source_h && box_w > box_h)) {
parent.appendChild(dom) sHeight = box_h * sWidth / box_w;
}) sY = (source_h - sHeight) / 2;
} }
return {
function drawOneline(canvas, style, content) { sx,
const ctx = canvas.getContext('2d') sy,
canvas.height = parseInt(style.font.match(/\d+/), 10) + 20 sWidth,
ctx.font = style.font sHeight
ctx.fillStyle = style.color }
ctx.textBaseline = 'top' }
let lineWidth = 0 // 创建 DOM 元素(不变)
let idx = 0 function createDom(name, key, value, display = 'none') {
let truncated = false const $dom = document.createElement(name)
for (let i = 0; i < content.length; i++) { $dom.setAttribute(key, value)
lineWidth += ctx.measureText(content[i]).width; $dom.style.display = display
if (lineWidth > canvas.width - 60) { $dom.width = WIDTH
truncated = true $dom.height = HEIGHT // 新增:确保 canvas 都有默认高度,尽管后续会重设
idx = i return $dom
break }
}
} // 追加子元素(不变)
function appendChilds(parent, ...doms) {
let padding = 30 doms.forEach(dom => {
parent.appendChild(dom)
if (truncated) { })
content = content.substring(0, idx) }
padding = canvas.width / 2 - lineWidth / 2
} // 绘制单行文本(不变)
function drawOneline(canvas, style, content) {
if (DEBUG) { const ctx = canvas.getContext('2d')
ctx.strokeStyle = "#6fda92"; canvas.height = parseInt(style.font.match(/\d+/), 10) + 20
ctx.strokeRect(0, 0, canvas.width, canvas.height); ctx.font = style.font
} ctx.fillStyle = style.color
ctx.textBaseline = 'top'
if (style.position === 'center') {
ctx.textAlign = 'center'; let lineWidth = 0
ctx.fillText(content, canvas.width / 2, 0) let idx = 0
} else if (style.position === 'left') { let truncated = false
ctx.fillText(content, padding, 0) for (let i = 0; i < content.length; i++) {
} else { lineWidth += ctx.measureText(content[i]).width;
ctx.textAlign = 'right' if (lineWidth > canvas.width - 60) {
ctx.fillText(content, canvas.width - padding, 0) truncated = true
} idx = i
} break
}
function drawMoreLines(canvas, style, content) { }
const ctx = canvas.getContext('2d')
const fontHeight = parseInt(style.font.match(/\d+/), 10) let padding = 30
if (DEBUG) { if (truncated) {
ctx.strokeStyle = "#6fda92"; content = content.substring(0, idx)
ctx.strokeRect(0, 0, canvas.width, canvas.height); // 修正:如果内容被截断,应该重新计算截断后的文本宽度,再计算 padding
} lineWidth = ctx.measureText(content).width;
if (style.position === 'center') {
ctx.font = style.font padding = canvas.width / 2 - lineWidth / 2;
ctx.fillStyle = style.color } else if (style.position === 'left') {
ctx.textBaseline = 'top' padding = 30; // 保持左侧间距
ctx.textAlign = 'center' } else {
padding = canvas.width - 30; // 保持右侧间距
let alignX = 0 }
}
if (style.position === 'center') {
alignX = canvas.width / 2; if (DEBUG) {
} else if (style.position === 'left') { ctx.strokeStyle = "#6fda92";
ctx.textAlign = 'left' ctx.strokeRect(0, 0, canvas.width, canvas.height);
alignX = 40 }
} else {
ctx.textAlign = 'right' if (style.position === 'center') {
alignX = canvas.width - 40 ctx.textAlign = 'center';
} ctx.fillText(content, canvas.width / 2, 0)
} else if (style.position === 'left') {
let lineWidth = 0 ctx.textAlign = 'left'
let lastSubStrIndex = 0 ctx.fillText(content, padding, 0)
let offsetY = 0 } else {
for (let i = 0; i < content.length; i++) { ctx.textAlign = 'right'
lineWidth += ctx.measureText(content[i]).width; ctx.fillText(content, canvas.width - padding, 0)
if (lineWidth > canvas.width - 120) { }
ctx.fillText(content.substring(lastSubStrIndex, i), alignX, offsetY); }
offsetY += fontHeight * style.lineHeight
lineWidth = 0 // 绘制多行文本(不变)
lastSubStrIndex = i function drawMoreLines(canvas, style, content) {
} const ctx = canvas.getContext('2d')
if (i === content.length - 1) { const fontHeight = parseInt(style.font.match(/\d+/), 10)
ctx.fillText(content.substring(lastSubStrIndex, i + 1), alignX, offsetY); let totalLines = 0;
}
} // 预计算行高并设置 Canvas 高度
} let lineWidth = 0
let currentLine = 0
for (let i = 0; i < content.length; i++) {
return { lineWidth += ctx.measureText(content[i]).width;
init if (lineWidth > canvas.width - 120) {
} currentLine++;
})() lineWidth = 0
}
if (i === content.length - 1) {
currentLine++;
}
//ajax生成文章海报 }
$('body').on('click', '.cr_poster', function () { totalLines = currentLine;
var post_id = $(this).attr('poster-data'); canvas.height = (fontHeight * style.lineHeight) * totalLines + 20; // 留出一些底部边距
var banner = $(this).attr('banner');
var single_content = $('.single-content') if (DEBUG) {
var t_content = $('#post-'+post_id+' .t_content') ctx.strokeStyle = "#6fda92";
var content = single_content.length>0 ? single_content[0].innerText : t_content[0].innerText ctx.strokeRect(0, 0, canvas.width, canvas.height);
var content_length = content.length }
var permalink = t_content.length>0 ? window.location.origin + '/moments/'+post_id : window.location.origin + window.location.pathname;
var title = t_content.length>0 ? Theme.site_title+'-瞬间' : document.title; ctx.font = style.font
new QRious({ element: document.getElementById("twoCode"), value: permalink, size: 260, }); ctx.fillStyle = style.color
$('.poster_box').remove(); ctx.textBaseline = 'top'
$('#share_modal_' + post_id + ' .poster_box_ap').append('<div class="poster_box"></div>'); ctx.textAlign = 'center'
if (content_length > 120) { let alignX = 0
content = content.substring(0, 80) + '...'
} if (style.position === 'center') {
alignX = canvas.width / 2;
function Posterdown(e) { } else if (style.position === 'left') {
if (e == null) { ctx.textAlign = 'left'
return; alignX = 40
} } else {
ctx.textAlign = 'right'
var modal = '#share_modal_' + post_id; alignX = canvas.width - 40
}
var url = $('' + modal + ' .poster_box img').attr('src');
$('' + modal + ' .post_share_box').removeClass('hide'); lineWidth = 0
$('' + modal + ' .poster_download').attr('href', url).attr('download', 'poster_' + post_id + '.png'); let lastSubStrIndex = 0
} let offsetY = 0
for (let i = 0; i < content.length; i++) {
$('#share_modal_' + post_id + ' .poster_box').append('<div class="loading_box"><div uk-spinner></div></div>'); lineWidth += ctx.measureText(content[i]).width;
$('.loading_box').remove(); if (lineWidth > canvas.width - 120) {
poster.init({ ctx.fillText(content.substring(lastSubStrIndex, i), alignX, offsetY);
banner: banner, offsetY += fontHeight * style.lineHeight
selector: '.poster_box', lineWidth = 0
title: title, lastSubStrIndex = i
content: content, }
logo: Theme.site_logo, if (i === content.length - 1) {
qrcode: $('#twoCode').attr('src'), ctx.fillText(content.substring(lastSubStrIndex, i + 1), alignX, offsetY);
description: Theme.admin_des, }
callback: Posterdown }
}); }
return {
}); init
}
})()
function convertImgToBase64(url, callback) {
var canvas = document.createElement('canvas');
var img = new Image(); // ajax生成文章海报
img.crossOrigin = 'Anonymous'; // 此处调用部分需要确保在 PJAX 环境下,该事件监听器能够重新绑定(如果使用传统的 document.ready,在 PJAX 中需要改为监听 pjax:success 等事件)
img.onload = function () { $('body').on('click', '.cr_poster', function () {
canvas.width = img.width; var post_id = $(this).attr('poster-data');
canvas.height = img.height; var banner = $(this).attr('banner');
var ctx = canvas.getContext('2d'); var single_content = $('.single-content');
ctx.drawImage(img, 0, 0); var t_content = $('#post-'+post_id+' .t_content');
var ext = img.src.substring(img.src.lastIndexOf('.') + 1).toLowerCase();
var dataURL = canvas.toDataURL('image/' + ext); // 确保 content 区域存在
callback(dataURL); var content_element = single_content.length > 0 ? single_content[0] : (t_content.length > 0 ? t_content[0] : null);
canvas = null;
}; if (!content_element) {
img.src = url; // 如果没有内容元素,则不执行后续操作或给出提示
} console.error('无法获取文章内容元素。');
return;
}
function handleShare(e, pic, title,pathname) {
var permalink = window.location.origin + pathname; var content = content_element.innerText;
switch (e) { var content_length = content.length;
case 'wb': var permalink = t_content.length > 0 ? window.location.origin + '/moments/' + post_id : window.location.origin + window.location.pathname;
var url = `https://service.weibo.com/share/share.php?url=${permalink}&type=button&language=zh_cn&pic=${pic}&title=${title}` var title = t_content.length > 0 ? Theme.site_title + '-瞬间' : document.title;
window.open(url);
break // 确保 QRious 库可用
case 'qzone': if (typeof QRious === 'undefined') {
var url = `https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=${permalink}&title=${title}&pics=${pic}` console.error('QRious 库未加载。');
window.open(url); return;
break }
case "qq":
var url = `http://connect.qq.com/widget/shareqq/index.html?url=${permalink}&title=${title}&pics=${pic}` // 检查 #twoCode 是否存在,如果不存在需要动态创建,否则 QRious 会失败
window.open(url); let twoCodeCanvas = document.getElementById("twoCode");
break; if (!twoCodeCanvas) {
case "copy": twoCodeCanvas = document.createElement('canvas');
if (navigator.clipboard) { twoCodeCanvas.id = 'twoCode';
navigator.clipboard.writeText(permalink).then(function() { twoCodeCanvas.style.display = 'none';
cocoMessage.success("链接已复制到剪贴板!"); document.body.appendChild(twoCodeCanvas); // 插入到 DOM 中
}, function(err) { }
cocoMessage.error("复制失败,请手动复制链接");
}); new QRious({ element: twoCodeCanvas, value: permalink, size: 260, });
} else {
// 兼容旧浏览器 var poster_box_selector = '#share_modal_' + post_id + ' .poster_box';
var tempInput = document.createElement('input');
tempInput.value = permalink; // ------------------ 【修复点 2:清理容器】 ------------------
document.body.appendChild(tempInput); // 先移除旧的 poster_box
tempInput.select(); $('.poster_box').remove();
try { // 重新创建并插入新的 poster_box
document.execCommand('copy'); $('#share_modal_' + post_id + ' .poster_box_ap').append('<div class="poster_box"></div>');
cocoMessage.success("链接已复制到剪贴板!"); // -----------------------------------------------------------------------
} catch (err) {
cocoMessage.error("复制失败,请手动复制链接"); if (content_length > 120) {
} content = content.substring(0, 80) + '...'
document.body.removeChild(tempInput); }
}
break; // Posterdown 函数中不再需要移除 loading_box,因为 init 会清空容器
default: function Posterdown(e) {
break var modal = '#share_modal_' + post_id;
}
// 检查海报是否成功生成
} if (e && $(modal + ' .poster_box img').length > 0) {
var url = $(modal + ' .poster_box img').attr('src');
$(modal + ' .post_share_box').removeClass('hide');
$(modal + ' .poster_download').attr('href', url).attr('download', 'poster_' + post_id + '.png');
} else {
// 错误处理,如果海报生成失败
cocoMessage.error("海报生成失败,请检查图片链接!");
}
}
// 移除 loading box 的时机提前,或者在 init 开始前完成
// $('.loading_box').remove(); // 原始代码中这行在创建 loading_box 之后,但其作用是移除**所有** .loading_box,可能存在问题
// 重新添加 loading 状态,并在 init 中等待
$(poster_box_selector).append('<div class="loading_box"><div uk-spinner></div></div>');
poster.init({
banner: banner,
selector: poster_box_selector, // 传入完整的选择器
title: title,
content: content,
logo: Theme.site_logo,
qrcode: $('#twoCode').attr('src'),
description: Theme.admin_des,
callback: Posterdown
});
// 移除 loading 动画
$(poster_box_selector + ' .loading_box').remove();
});
function convertImgToBase64(url, callback) {
var canvas = document.createElement('canvas');
var img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = function () {
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
var ext = img.src.substring(img.src.lastIndexOf('.') + 1).toLowerCase();
// 修正:确保 toDataURL 使用正确的 MIME type,如果是 jpg/jpeg 应使用 image/jpeg
var mimeType = (ext === 'jpg' || ext === 'jpeg') ? 'image/jpeg' : 'image/png';
var dataURL = canvas.toDataURL(mimeType);
callback(dataURL);
canvas = null;
};
img.onerror = function() {
console.error('convertImgToBase64: 无法加载图片 ' + url);
callback(null);
}
img.src = url;
}
function handleShare(e, pic, title, pathname) {
var permalink = window.location.origin + pathname;
// 确保 cocoMessage 库可用
if (typeof cocoMessage === 'undefined') {
console.error('cocoMessage 库未加载。');
// 如果 cocoMessage 不可用,可以替换为原生的 alert
if (e === 'copy') {
alert("链接已复制到剪贴板!");
}
}
switch (e) {
case 'wb':
var url = `https://service.weibo.com/share/share.php?url=${encodeURIComponent(permalink)}&type=button&language=zh_cn&pic=${encodeURIComponent(pic)}&title=${encodeURIComponent(title)}`
window.open(url);
break
case 'qzone':
var url = `https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=${encodeURIComponent(permalink)}&title=${encodeURIComponent(title)}&pics=${encodeURIComponent(pic)}`
window.open(url);
break
case "qq":
var url = `http://connect.qq.com/widget/shareqq/index.html?url=${encodeURIComponent(permalink)}&title=${encodeURIComponent(title)}&pics=${encodeURIComponent(pic)}`
window.open(url);
break;
case "copy":
if (navigator.clipboard) {
navigator.clipboard.writeText(permalink).then(function() {
cocoMessage.success("链接已复制到剪贴板!");
}, function(err) {
cocoMessage.error("复制失败,请手动复制链接");
});
} else {
// 兼容旧浏览器
var tempInput = document.createElement('input');
tempInput.value = permalink;
document.body.appendChild(tempInput);
tempInput.select();
try {
document.execCommand('copy');
cocoMessage.success("链接已复制到剪贴板!");
} catch (err) {
cocoMessage.error("复制失败,请手动复制链接");
}
document.body.removeChild(tempInput);
}
break;
default:
break
}
}
+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("/upload/jiazai.gif");
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>
+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="/upload/404.png">
</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>
+112 -75
View File
@@ -1,76 +1,113 @@
<!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">
<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 id="post_item" class="moment_list"> <!-- 作者姓名导航栏(参照图库风格新增) -->
<div class="friends_cat_nav">
<div th:each="postInfo, iterstat: ${friends.items}" th:id="${'post-'+postInfo.metadata.name}" <ul id="cat_nav_items">
class="loop_content p_item moment_item uk-animation-slide-bottom-small" <li>
th:with="spec = ${postInfo.spec}"> <!-- 全部按钮 -->
<div class="p_item_inner"> <a th:attr="data-href=@{/friends}"
<div class="list_user_meta"> th:classappend="${#lists.isEmpty(param.author) ? 'active' : ''}" class="cat-link">
<div class="avatar"> 全部<span></span>
<img th:src="@{${spec.logo}}"> </a>
</div> </li>
<div class="name"> <!-- 遍历所有作者 -->
<div class="avatar_title"> <th:block th:each="author : ${authors}">
<a target="_blank" th:href="${spec.authorUrl}">[[${spec.author}]]</a> <li>
</div> <a th:attr="data-href=@{|/friends?author=${author}|}"
th:classappend="${#strings.equals(param.author, author) ? 'active' : ''}"
<time itemprop="datePublished" th:text="${author}" class="cat-link">
th:datetime="${#dates.format(spec.pubDate, 'yyyy-MM-dd HH:mm')}"> <span></span>
<th:block th:replace="~{modules/widgets/time :: time(${spec.pubDate})}"></th:block> </a>
</time> </li>
</div> </th:block>
</div> </ul>
</div>
<div class="blog_content">
<div class="entry-content"> <div id="post_item" class="moment_list">
<div class="p_title">
<a target="_blank" th:href="${spec.postLink}"><i class="ri-at-line"></i>[[${spec.title}]]</a> <div th:each="postInfo, iterstat: ${friends.items}"
</div> th:id="${'post-'+postInfo.metadata.name}"
<th:block th:with="excerpt = ${#strings.length(spec.description) > 100 ? 100 : #strings.length(spec.description)}"> class="loop_content p_item moment_item uk-animation-slide-bottom-small"
<div class="t_content" th:text="${#strings.substring(spec.description,0,excerpt)+'...'}"> th:with="spec = ${postInfo.spec}">
</div>
</th:block> <div class="p_item_inner">
</div>
</div> <div class="list_user_meta">
</div> <div class="avatar">
</div> <img th:src="@{${spec.logo}}">
</div>
<th:block th:if="${#lists.isEmpty(friends.items)}">
<th:block th:replace="~{macro/tpl/content-none}"/> <div class="name">
</th:block> <div class="avatar_title">
</div> <a target="_blank" th:href="${spec.authorUrl}">
<th:block th:if="${friends.totalPages>1}"> [[${spec.author}]]
<div id="t_pagination"> </a>
<div class="post-paging" th:if="${friends.hasNext()}" </div>
th:with="_page = ${friends.page+1}">
<a th:data="${'/friends/page/'+_page+'?preview-theme=theme-pix'}" th:text="${theme.config.base_set.site_page}"></a> <time itemprop="datePublished"
</div> th:datetime="${#dates.format(spec.pubDate, 'yyyy-MM-dd HH:mm')}">
</div> <th:block th:replace="~{modules/widgets/time :: time(${spec.pubDate})}">
</th:block> </th:block>
</time>
<div id="comment_form_reset"></div> </div>
</div> </div>
</div>
<div class="blog_content">
</div> <div class="entry-content">
<div class="p_title moment-collapse-container">
<th:block th:replace="~{modules/footer-main}"/> <a target="_blank" th:href="${spec.postLink}">
<div th:utext="${spec.description}"></div>
</div> </a>
</th:block> </div>
</div>
</div>
</div>
</div>
<!-- 空数据 -->
<th:block th:if="${#lists.isEmpty(friends.items)}">
<th:block th:replace="~{macro/tpl/content-none}" />
</th:block>
</div>
<!-- 分页:必须保持在 blog_list_inner 里面 -->
<th:block th:if="${friends.totalPages>1}">
<div id="t_pagination">
<div class="post-paging"
th:if="${friends.hasNext()}">
<!-- 直接使用后端生成的完整 nextUrl -->
<a th:data="${friends.nextUrl}"
th:text="${theme.config.base_set.site_page}">
</a>
</div>
</div>
</th:block>
<div id="comment_form_reset"></div>
</div>
</div>
</div>
<th:block th:replace="~{modules/footer-main}" />
</div>
</th:block>
</html> </html>
+4 -4
View File
@@ -5,17 +5,17 @@
<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}" class="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:data="${categoryItem.status.permalink}"
class="">[[${categoryItem.spec.displayName}]]<span></span></a> class="">[[${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}"
th:data="|${categoryItem.status.permalink}?preview-theme=theme-pix|" th:data="${categoryItem.status.permalink}"
class="">[[${categoryItem.spec.displayName}]]<span></span> class="">[[${categoryItem.spec.displayName}]]<span></span>
</a> </a>
</th:block> </th:block>
@@ -31,7 +31,7 @@
<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:if="${_posts.hasNext()}"
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 == '/archives' ? path+'?page='+_page : path +'page/'+_page }">
<a th:data="${_path}" th:text="${theme.config.base_set.site_page}"></a> <a th:data="${_path}" th:text="${theme.config.base_set.site_page}"></a>
</div> </div>
</div> </div>
+38 -38
View File
@@ -1,39 +1,39 @@
<th:block th:fragment="home-moment(_moments,tags)"> <th:block th:fragment="home-moment(_moments,tags)">
<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 data="/moments" 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="|/moments?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:if="${_moments.hasNext()}"
th:with="tag = ${not #lists.isEmpty(param.tag) ? '?tag='+param.tag : ''},_page = ${_moments.page+1}"> th:with="tag = ${not #lists.isEmpty(param.tag) ? '?tag='+param.tag : ''},_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:data="${'/moments/page/'+_page+tag}" th:text="${theme.config.base_set.site_page}"></a>
</div> </div>
</div> </div>
</th:block> </th:block>
<div id="comment_form_reset"></div> <div id="comment_form_reset"></div>
</div> </div>
</div> </div>
</th:block> </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.jpg'}">
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="/upload/nodata.png">
</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/lazyload.png" 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/lazyload.png}" 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>
+104 -28
View File
@@ -1,29 +1,105 @@
<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-3'">
<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>
退出后您仍能以访客身份翻阅本日记簿...
</p>
</div>
<div class="pixar-notice-actions">
<!-- 确认按钮 -->
<button class="pixar-notice-btn pixar-notice-btn-confirm" aria-label="Confirm"
onclick="confirmPixarAction()">
<i class="ri-check-line"></i>
</button>
<!-- 关闭按钮 -->
<button class="pixar-notice-btn pixar-notice-btn-close" aria-label="Close"
onclick="hidePixarCard()">
<i class="ri-close-line"></i>
</button>
</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="/upload/jiazai.gif">
</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>
+104 -108
View File
@@ -1,109 +1,105 @@
<th:block th:fragment="head"> <th:block th:fragment="head" th:with="customJsVersion=${'1.2.5-3'}">
<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}"/> <style>
<meta name="robots" content="max-image-preview:large"> .dl-warning-card { overflow: hidden; position: fixed; bottom: 1rem; left: 0; right: 0; margin: 0 auto; background-color: #ffffff; border-radius: 0.5rem; max-width: 720px; width: calc(100% - 2rem); box-shadow: 0 4px 12px rgba(0,0,0,0.1); z-index: 9999; display: none; opacity: 0; transform: translateY(20px); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); box-sizing: border-box; border-left: 4px solid #f59e0b; }
.dl-warning-header { display: flex; align-items: center; padding: 1rem; gap: 1rem; }
<th:block th:if="${not #strings.isEmpty(theme.config.custom.head_html)}" .dl-warning-image { flex-shrink: 0; background-color: #fef3c7; justify-content: center; align-items: center; width: 2.5rem; height: 2.5rem; border-radius: 9999px; display: flex; }
th:utext="${theme.config.custom.head_html}"> </th:block> .dl-warning-image svg { color: #f59e0b; width: 1.25rem; height: 1.25rem; }
.dl-warning-content { flex: 1; text-align: left; word-wrap: break-word; word-break: break-all; min-width: 0; }
<meta property="og:title" th:content="${site_title}"> .dl-warning-title { color: #F59E0B; font-size: 0.95rem; font-weight: 600; line-height: 1.3rem; display: block; margin-bottom: 0.25rem; }
<meta property="og:site_name" th:content="${site.title}"> .dl-warning-message { color: #6b7280; font-size: 0.875rem; line-height: 1.25rem; margin: 0; }
<meta property="og:type" content="website"> .dl-warning-actions { display: flex; flex-direction: row; gap: 0.75rem; align-items: center; flex-shrink: 0; }
<link rel="stylesheet" id="wp-block-library-css" th:href="@{/assets/css/style.min.css?v={version}(version=${theme.spec.version})}" media="all"> .dl-warning-desactivate { padding: 0.375rem 0.75rem; background-color: #f59e0b; color: #ffffff; font-size: 0.875rem; line-height: 1.25rem; font-weight: 500; justify-content: center; align-items: center; display: inline-flex; border-radius: 0.375rem; border: none; box-shadow: 0 1px 2px 0 rgba(0,0,0,0.05); cursor: pointer; white-space: nowrap; }
<style id="classic-theme-styles-inline-css"> .dl-warning-desactivate.loading { background-color: #d97706; cursor: not-allowed; opacity: 0.8; pointer-events: none; }
/*! This file is auto-generated */ .dl-warning-desactivate.loading::after { content: ""; width: 0.75rem; height: 0.75rem; border: 2px solid rgba(255,255,255,0.5); border-top-color: #ffffff; border-radius: 50%; margin-left: 0.5rem; margin-right: 0.25rem; animation: spin 1s linear infinite; flex-shrink: 0; }
.wp-block-button__link { .dl-warning-desactivate:not(.loading):hover { background-color: #fbbf24; box-shadow: 0 2px 3px 0 rgba(0,0,0,0.06); transition: all 0.2s ease-in-out; }
color: #fff; .dl-warning-cancel { padding: 0.375rem 0.75rem; background-color: #ffffff; color: #374151; font-size: 0.875rem; line-height: 1.25rem; font-weight: 500; justify-content: center; align-items: center; display: inline-flex; border-radius: 0.375rem; border: 1px solid #d1d5db; box-shadow: 0 1px 2px 0 rgba(0,0,0,0.05); cursor: pointer; white-space: nowrap; }
background-color: #32373c; .dl-warning-cancel:hover { background-color: #ededed; border-color: #e5e7eb; box-shadow: 0 2px 3px 0 rgba(0,0,0,0.06); transition: all 0.2s ease-in-out; }
border-radius: 9999px; @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
box-shadow: none; @media (max-width: 480px) {
text-decoration: none; .dl-warning-card { bottom: 1rem; width: calc(100% - 1rem); max-width: 100%; }
padding: calc(.667em + 2px) calc(1.333em + 2px); .dl-warning-header { padding: 0.75rem; gap: 0.75rem; align-items: center; }
font-size: 1.125em .dl-warning-image { width: 2rem; height: 2rem; }
} .dl-warning-image svg { width: 1rem; height: 1rem; }
.dl-warning-title { font-size: 0.9rem; }
.wp-block-file__button { .dl-warning-message { font-size: 0.85rem; }
background: #32373c; .dl-warning-actions { flex-direction: column; gap: 0.375rem; align-items: stretch; flex-shrink: 0; width: auto; margin-top: 0; }
color: #fff; .dl-warning-desactivate, .dl-warning-cancel { font-size: 0.75rem; padding: 0.25rem 0.5rem; width: 100%; }
text-decoration: none .dl-warning-content { min-width: 0; word-wrap: break-word; word-break: break-all; }
} }
</style> </style>
<link rel="stylesheet" id="pix-style-css" th:href="@{/assets/css/style.css?v={version}(version=${theme.spec.version})}" media="all">
<link rel="stylesheet" id="fancybox.css-css" th:href="@{/assets/libs/fancybox/jquery.fancybox.min.css}" media="all"> <div class="dl-warning-card" id="lineCard">
<link rel="stylesheet" id="nprogress.css-css" th:href="@{/assets/libs/nprogress/nprogress.css}" media="all"> <div class="dl-warning-header">
<link rel="stylesheet" id="iconfont-css" th:href="@{/assets/libs/fonts/remixicon.css}" media="all"> <div class="dl-warning-image"><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>
<link rel="stylesheet" id="uikit.css-css" th:href="@{/assets/libs/uikit/uikit.min.css}" media="all"> <div class="dl-warning-content"><span class="dl-warning-title" id="cardHeading"></span><p class="dl-warning-message" id="cardPara"></p></div>
<link rel="stylesheet" id="main.css-css" th:href="@{/assets/css/main.css?v={version}(version=${theme.spec.version})}" media="all"> <div class="dl-warning-actions"><button class="dl-warning-desactivate" id="switchBtn"></button><button class="dl-warning-cancel" id="cancelBtn">取消</button></div>
<link th:if="${theme.config.style.theme_set != 'green-normal' && theme.config.style.theme_set != 'dark-theme'}" </div>
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"> </div>
<link rel="stylesheet" id="dark-css" th:href="@{/assets/css/dark.css?v={version}(version=${theme.spec.version})}" media="all">
<link rel="stylesheet" id="mobile-css" th:href="@{/assets/css/mobile.css?v={version}(version=${theme.spec.version})}" media="all"> <script>
<link rel="stylesheet" id="album-css" th:href="@{/assets/css/album.css?v={version}(version=${theme.spec.version})}" media="all"> (function () {
<script th:src="@{/assets/libs/imgstatus/imgStatus.min.js}" id="imgstatus-js"></script> const lineCardEl = document.getElementById('lineCard'), cardHeadingEl = document.getElementById('cardHeading'), cardParaEl = document.getElementById('cardPara'), switchLineBtn = document.getElementById('switchBtn'), cancelLineBtn = document.getElementById('cancelBtn');
<script th:src="@{/assets/libs/waterfall/waterfall.min.js}" id="waterfall-js"></script> const getCookie = (name) => document.cookie.split('; ').find(row => row.startsWith(`${name}=`))?.split('=')[1];
<script th:if="${pluginFinder.available('PluginPrismJS')}" src="/plugins/PluginPrismJS/assets/static/highlight.js/highlight.min.js"></script> const setSessionCookie = (name, value, domain = 'anian.cc') => document.cookie = `${name}=${value}; path=/; domain=${domain}; SameSite=Lax`;
<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" const showLineCard = () => { lineCardEl.style.display = 'block'; setTimeout(() => { lineCardEl.style.opacity = '1'; lineCardEl.style.transform = 'translateY(0)'; }, 10) };
media="screen"> const hideLineCard = (setNoPrompt = false, setNoSpeedTest = false) => { lineCardEl.style.opacity = '0'; lineCardEl.style.transform = 'translateY(20px)'; setTimeout(() => { lineCardEl.style.display = 'none'; if (setNoPrompt) setSessionCookie('noPrompt_anian_cc', 'true'); if (setNoSpeedTest) setSessionCookie('noSpeedTest_anian_cc', 'true'); }, 300) };
<style type="text/css" const handleLineSwitch = (baseUrl) => { switchLineBtn.classList.add('loading'); switchLineBtn.textContent = '切换中'; switchLineBtn.disabled = true; const currentScrollTop = window.scrollY || document.documentElement.scrollTop; document.cookie = `scrollParam=${currentScrollTop}; path=/; domain=anian.cc`; let finalUrl = baseUrl + location.pathname + location.search + location.hash; setTimeout(() => location.href = finalUrl, 500) };
th:if="${not #strings.isEmpty(theme.config.custom.code_css)}" let isResourceLoaded = false, pageLoadTimeoutTimer = null, isLoadTimeoutAborted = false;
th:utext="${theme.config.custom.code_css}" ></style> const currentHost = location.hostname, isMainDomain = currentHost === 'anian.cc', isSubDomain = currentHost === 'www.anian.cc', isTestDomain = !isMainDomain && !isSubDomain;
<style type="text/css"> const scrollParam = getCookie('scrollParam'), noPromptCookie = getCookie('noPrompt_anian_cc'), noSpeedTestCookie = getCookie('noSpeedTest_anian_cc');
:root { cancelLineBtn.addEventListener('click', () => { hideLineCard(!isMainDomain, isMainDomain); });
--toc-color: [(${theme.config.post.tocColor})]; if (isSubDomain) { if (!scrollParam && !noPromptCookie) { cardHeadingEl.textContent = '温馨提示'; cardParaEl.textContent = '当前为备用线路,若主线路正常,请优先选择主线路'; switchLineBtn.textContent = '切换主线路'; switchLineBtn.onclick = () => handleLineSwitch('https://anian.cc'); showLineCard(); } }
} if (isMainDomain) { if (!noSpeedTestCookie) { pageLoadTimeoutTimer = setTimeout(() => { if (!isResourceLoaded) { isLoadTimeoutAborted = true; cardHeadingEl.textContent = '线路异常'; cardParaEl.textContent = '当前线路拥塞,若本地网络正常,可尝试切换至备用线路'; switchLineBtn.textContent = '切换副线路'; switchLineBtn.onclick = () => handleLineSwitch('https://www.anian.cc'); showLineCard(); } }, 15000); window.onload = () => { isResourceLoaded = true; clearTimeout(pageLoadTimeoutTimer); if (isLoadTimeoutAborted) return; const xhr = new XMLHttpRequest(); xhr.open('GET', `/upload/%E4%B8%8B%E8%BD%BD%20(9).mp4?_t=${Date.now()}`, true); let t = setTimeout(() => { xhr.abort(); cardHeadingEl.textContent = '线路异常'; cardParaEl.textContent = '当前线路拥塞,若本地网络正常,可尝试切换至备用线路'; switchLineBtn.textContent = '切换副线路'; switchLineBtn.onclick = () => handleLineSwitch('https://www.anian.cc'); showLineCard() }, 7500); xhr.onload = xhr.onerror = xhr.onabort = () => clearTimeout(t); xhr.send(); } } }
</style> if (isTestDomain) { if (!noPromptCookie) { cardHeadingEl.textContent = '线路警告'; cardParaEl.textContent = '当前为测试线路,若非开发所需,请优先选择主线路'; switchLineBtn.textContent = '切换主线路'; switchLineBtn.onclick = () => handleLineSwitch('https://anian.cc'); showLineCard(); } }
<style type="text/css" th:if="${!theme.config.post.toc.mobile_open}"> })();
@media screen and (max-width: 768px) { </script>
.listree-box {
display: none !important; <link rel="stylesheet" id="wp-block-library-css" th:href="@{/assets/css/style.min.css(v=${customJsVersion})}" media="all">
} <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">
</style> <link rel="stylesheet" id="nprogress.css-css" th:href="@{/assets/libs/nprogress/nprogress.css(v=${customJsVersion})}" media="all">
<!-- 自定义css --> <link rel="stylesheet" id="iconfont-css" th:href="@{/assets/libs/fonts/remixicon.css(v=${customJsVersion})}" media="all">
<style type="text/css" th:if="${not #strings.isEmpty(theme.config.base_set.custom_fonts)}"> <link rel="stylesheet" id="uikit.css-css" th:href="@{/assets/libs/uikit/uikit.min.css(v=${customJsVersion})}" media="all">
@font-face { <link rel="stylesheet" id="main.css-css" th:href="@{/assets/css/main.css(v=${customJsVersion})}" media="all">
font-style: normal; <link rel="stylesheet" id="dark-css" th:href="@{/assets/css/dark.css(v=${customJsVersion})}" media="all">
font-family: "HarmonyOS_M"; <link rel="stylesheet" id="mobile-css" th:href="@{/assets/css/mobile.css(v=${customJsVersion})}" media="all">
src: url("[(${theme.config.base_set.custom_fonts})]") format("truetype"); <link rel="stylesheet" id="album-css" th:href="@{/assets/css/album.css(v=${customJsVersion})}" media="all">
font-display: swap; <link rel="stylesheet" id="anian-css" th:href="@{/assets/css/anian.css(v=${customJsVersion})}" media="all">
} <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">
</style> <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">
<th:block th:replace="~{'modules/variables/moments-img-style'}"></th:block> <style th:if="${not #strings.isEmpty(theme.config.custom.code_css)}" th:utext="${theme.config.custom.code_css}"></style>
<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>
<script> <style th:if="${!theme.config.post.toc.mobile_open}">@media screen and (max-width: 768px) { .listree-box { display: none !important; } }</style>
(win => { <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>
win.getCookie = function(name) {
var cookieArr = document.cookie.split(';'); <th:block th:replace="~{modules/variables/cst-script-parameter}" />
for (var i = 0; i < cookieArr.length; i++) { <th:block th:if="${not #strings.isEmpty(theme.config.custom.head_html)}" th:utext="${theme.config.custom.head_html}"></th:block>
var cookiePair = cookieArr[i].split('='); <meta property="og:title" th:content="${site_title}"><meta property="og:site_name" th:content="${site.title}"><meta property="og:type" content="website">
var cookieName = cookiePair[0].trim();
if (cookieName === name) { <script th:src="@{/assets/libs/imgstatus/imgStatus.min.js(v=${customJsVersion})}" id="imgstatus-js"></script>
return decodeURIComponent(cookiePair[1]); <script th:src="@{/assets/libs/waterfall/waterfall.min.js(v=${customJsVersion})}" id="waterfall-js"></script>
} <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>
return "";
} <script>
win.darkmode = function () { (win => {
$darkmode = false; win.getCookie = (name) => {
const dark = getCookie("dark") let pair = document.cookie.split('; ').find(row => row.startsWith(name + '='));
if (dark) { return pair ? decodeURIComponent(pair.split('=')[1]) : "";
$darkmode = (dark == 'dark') ? true : false; };
} else if (Theme.theme_set == 'dark-theme') { win.darkmode = () => {
$darkmode = true; const dark = getCookie("dark");
} return dark ? (dark === 'dark') : (Theme.theme_set === 'dark-theme');
return $darkmode; };
} if (darkmode()) document.documentElement.classList.add('dark');
})(window)
if (darkmode()) { </script>
document.documentElement.classList.add('dark')
}
})(window)
</script>
</th:block> </th:block>
+142 -34
View File
@@ -1,34 +1,142 @@
<header id="masthead" class="site-header"> <header id="masthead" class="site-header">
<div class="top_bar uk-sticky"> <div class="top_bar uk-sticky">
<div class="mobile_logo"> <div class="mobile_logo">
<a href="/"><img th:src="${theme.config.base_top_set.mobile_logo}"></a> <a href="/"><img th:src="${theme.config.base_top_set.mobile_logo}"></a>
</div> </div>
<th:block th:replace="~{'modules/header/header-search'}"></th:block> <th:block th:replace="~{'modules/header/header-search'}"></th:block>
<th:block th:if="${not #strings.contains('mod_third,mod_third_s',theme.config.style.layout_set)}"> <th:block th:if="${not #strings.contains('mod_third,mod_third_s',theme.config.style.layout_set)}">
<th:block th:replace="~{modules/header/header-tool :: header-tool}"></th:block> <th:block th:replace="~{modules/header/header-tool :: header-tool}"></th:block>
</th:block> </th:block>
<th:block th:unless="${not #strings.contains('mod_third,mod_third_s',theme.config.style.layout_set)}"> <th:block th:unless="${not #strings.contains('mod_third,mod_third_s',theme.config.style.layout_set)}">
<th:block th:replace="~{modules/header/header-tool :: header-tool-third}"></th:block> <th:block th:replace="~{modules/header/header-tool :: header-tool-third}"></th:block>
</th:block> </th:block>
</div> </div>
<div class="uk-sticky-placeholder" hidden="" style="height: 72px; width: 590px; margin: 0px;"></div> <div class="uk-sticky-placeholder" hidden="" style="height: 72px; width: 590px; margin: 0px;"></div>
</header><!-- #masthead --> </header><!-- #masthead -->
<div class="index_banner" <!-- <div class="index_banner">
th:data-src="${theme.config.style.topbg_banner}" uk-img="loading: eager"> <div class="bg bg1"></div>
<div class="user_info"> <div class="bg bg2"></div>
<div class="top"> <div class="user_info">
<div class="left"> <div class="top">
<div class="name">[[${theme.config.base_set.nice_name}]]</div> <div class="left">
<div class="des">[[${theme.config.base_set.admin_des}]]</div> <div class="name">[[${theme.config.base_set.nice_name}]]</div>
</div> <div class="des">[[${theme.config.base_set.admin_des}]]</div>
<div class="avatar"><img th:src="${theme.config.base_set.default_avatar}"></div> </div>
</div> <div class="avatar"><img th:src="${theme.config.base_set.default_avatar}"></div>
</div> </div>
</div> </div>
</div> -->
<div class="index_banner" id="banner" uk-img="loading: eager">
<div class="user_info">
<div class="top">
<div class="left">
<div class="name">[[${theme.config.base_set.nice_name}]]</div>
<div class="des">[[${theme.config.base_set.admin_des}]]</div>
</div>
<div class="avatar"><img th:src="${theme.config.base_set.default_avatar}"></div>
</div>
</div>
</div>
<script>
// --------------------------
// 配置区域(请替换为实际图片地址)
// --------------------------
const BANNER_CONFIG = {
seasonal: {
'春季': '/upload/dingbu-chun.png', // 春季 Banner
'夏季': '/upload/dingbu-xia.png', // 夏季 Banner
'秋季': '/upload/dingbu-qiu.png', // 秋季 Banner
'冬季': '/upload/dingbu-dong.png' // 冬季 Banner
},
fallback: '/upload/dingbu-siji.png' // 兜底图
};
// --------------------------
// 原有 Cookie 工具函数(保持不变)
// --------------------------
function anian_setDailyCookie(name, value) {
const now = new Date();
const china = new Date(now.getTime() + 8 * 3600 * 1000);
const expires = new Date(Date.UTC(china.getUTCFullYear(), china.getUTCMonth(), china.getUTCDate(), 15, 59, 59));
document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}; expires=${expires.toUTCString()}; path=/; domain=anian.cc`;
}
function anian_getCookie(name) {
const nameEQ = encodeURIComponent(name) + '=';
const ca = document.cookie.split(';');
for (let i = 0; i < ca.length; i++) {
let c = ca[i];
while (c.charAt(0) === ' ') c = c.substring(1);
if (c.indexOf(nameEQ) === 0) return decodeURIComponent(c.substring(nameEQ.length));
}
return null;
}
// --------------------------
// 新增:Banner 图片切换逻辑
// --------------------------
function setBanner(lunarInfo) {
const banner = document.getElementById('banner');
if (!banner) return;
// 根据季节选图,无匹配则用兜底
const season = lunarInfo?.jijie;
const imgUrl = BANNER_CONFIG.seasonal[season] || BANNER_CONFIG.fallback;
// 可以根据日期、节日覆盖imgUrl
// 设置图片并触发 UIkit 加载
banner.setAttribute('data-src', imgUrl);
window.UIkit && UIkit.update(banner);
}
// --------------------------
// 初始化逻辑(优化版)
// --------------------------
(function() {
const apiUrl = 'https://cn.apihz.cn/api/time/getday.php?id=10011724&key=0bdd2d8b3d97c0d476c98a41f108a96f';
const cookieName = 'anian_lunar_info';
// 1. 优先尝试从 Cookie 读取
const cached = anian_getCookie(cookieName);
if (cached) {
try {
setBanner(JSON.parse(cached));
return; // 有缓存直接结束
} catch (e) {}
}
// 2. 无缓存则请求 API
fetch(apiUrl)
.then(res => res.json())
.then(data => {
if (data.code !== 200) throw new Error('API Error');
// 组装农历信息(保留原有逻辑)
const lunarInfo = {
ganzhiYear: data.ganzhinian || '',
lunarMonth: data.nyue || '',
lunarDay: data.nri || '',
jieqi: (data.JIEQICN || '').includes('第1天') ? data.jieqi : '',
jijie: data.jijie || '',
traditionalFestival: data.YIFESTIVAL || ''
};
// 存 Cookie 并设置 Banner
anian_setDailyCookie(cookieName, JSON.stringify(lunarInfo));
setBanner(lunarInfo);
})
.catch(() => {
// 3. 失败兜底:显示默认图
setBanner(null);
});
})();
</script>
+10 -6
View File
@@ -5,9 +5,8 @@
<div class="top_menu_box" <div class="top_menu_box"
uk-dropdown="mode: click;flip: false;offset: 12;toggle:.top_nav_btn;animation:uk-animation-slide-top-small;animate-out:uk-animation-slide-top-small;delay-hide:5000;boundary: .top_bar;stretch: x;"> uk-dropdown="mode: click;flip: false;offset: 12;toggle:.top_nav_btn;animation:uk-animation-slide-top-small;animate-out:uk-animation-slide-top-small;delay-hide:5000;boundary: .top_bar;stretch: x;">
<div class="inner" <div class="inner"
th:with="topMenu = ${#strings.isEmpty(theme.config.base_top_set.top_nav) ? menuFinder.getPrimary() : menuFinder.getByName(theme.config.base_top_set.top_nav)}"> th:with="topMenu = ${#strings.isEmpty(theme.config.base_top_set.top_nav) ? menuFinder.getPrimary() : menuFinder.getByName(theme.config.base_top_set.top_nav)}">
<div class="menu-top-container" <div class="menu-top-container" th:if="${not #lists.isEmpty(topMenu)}">
th:if="${not #lists.isEmpty(topMenu)}">
<ul id="top_menu" class="menu"> <ul id="top_menu" class="menu">
<li class="menu-item menu-item-type-post_type menu-item-object-page" <li class="menu-item menu-item-type-post_type menu-item-object-page"
th:each="menuItem : ${topMenu.menuItems}"> th:each="menuItem : ${topMenu.menuItems}">
@@ -23,7 +22,7 @@
</div> </div>
</div> </div>
<div class="m_top_nav top_tool is_mobile"> <!-- <div class="m_top_nav top_tool is_mobile">
<a class="top_nav_btn" uk-toggle="target: #m_offcanvas"><i class="ri-menu-2-line"></i></a> <a class="top_nav_btn" uk-toggle="target: #m_offcanvas"><i class="ri-menu-2-line"></i></a>
<div id="m_offcanvas" class="m_offcanvas uk-animation-fast" <div id="m_offcanvas" class="m_offcanvas uk-animation-fast"
uk-offcanvas="overlay: true;mode: push;container:.main_wrap"> uk-offcanvas="overlay: true;mode: push;container:.main_wrap">
@@ -85,8 +84,7 @@
<div th:unless="${not #lists.isEmpty(topMenu)}" class="nav_tips">请前往后台设置左侧菜单/移动端菜单</div> <div th:unless="${not #lists.isEmpty(topMenu)}" class="nav_tips">请前往后台设置左侧菜单/移动端菜单</div>
</div> </div>
</div> </div>
</div> </div> -->
<!-- 搜索区域 --> <!-- 搜索区域 -->
<div class="search_mod"> <div class="search_mod">
@@ -94,4 +92,10 @@
<a onclick="SearchWidget.open()"><i class="ri-search-line s_toogle_btn"></i></a> <a onclick="SearchWidget.open()"><i class="ri-search-line s_toogle_btn"></i></a>
</div> </div>
</div> </div>
<!-- 回到顶部 -->
<div class="is_mobile">
<a class="tool_top" href="#page" uk-scroll="" data-pjax-state="">
<i class="ri-skip-up-line"></i></a>
</div>
</div> </div>
+60 -60
View File
@@ -1,61 +1,61 @@
<div class="top_right" th:fragment="header-tool"> <div class="top_right" th:fragment="header-tool">
<th:block sec:authorize="isAuthenticated()"> <th:block sec:authorize="isAuthenticated()">
<div class="top_tool"> <div class="top_tool">
<a class="com_msg_btn" href="#msg_modal" check="0" uk-toggle> <a class="com_msg_btn" href="#msg_modal" check="0" uk-toggle>
<i class="ri-notification-2-line"></i> <i class="ri-notification-2-line"></i>
</a> </a>
</div> </div>
<div class="t_login top_tool icon_color" th:if="${pluginFinder.available('PluginMoments')}"> <div class="t_login top_tool icon_color" th:if="${pluginFinder.available('PluginMoments')}">
<a class="normal_edit" uk-toggle="target: #create_post_box"><i class="ri-edit-box-line"></i></a> <a class="normal_edit" uk-toggle="target: #create_post_box"><i class="ri-edit-box-line"></i></a>
</div> </div>
<div class="admin_ava"> <div class="admin_ava">
<a class="mobile_edit" uk-toggle="target: #create_post_box"><i class="ri-add-line"></i></a> <a class="mobile_edit" uk-toggle="target: #create_post_box"><i class="ri-add-line"></i></a>
<div id="top_ava" class="top_ava"><img th:src="${user.avatar}"></div> <div id="top_ava" class="top_ava"><img th:src="${user.avatar}"></div>
<div class="user_pannel round12" uk-dropdown="mode: click;toggle:#top_ava;pos:bottom-right;animation:uk-animation-slide-top-small"> <div class="user_pannel round12" uk-dropdown="mode: click;toggle:#top_ava;pos:bottom-right;animation:uk-animation-slide-top-small">
<div class="inner"> <div class="inner">
<a href="/console" target="_blank" pjax="exclude"><i class="ri-function-line"></i>控制台</a> <a href="/console" target="_blank" pjax="exclude"><i class="ri-function-line"></i>管理面板</a>
<a class="logout" href="/logout"><i class="ri-logout-circle-r-line"></i>登出</a> <a class="logout" href="javascript:void(0);" onclick="showPixarCard()"><i class="ri-logout-circle-r-line"></i>退出登录</a>
</div> </div>
</div> </div>
</div> </div>
</th:block> </th:block>
<div sec:authorize="isAnonymous()" class="top_tool"> <div sec:authorize="isAnonymous()" class="top_tool">
<a th:if="${theme.config.base_set.login_button}" onclick="loginUrl()"><i class="ri-user-4-fill"></i></a> <a th:if="${theme.config.base_set.login_button}" onclick="open_login_box()"><i class="ri-user-3-line"></i></a>
</div> </div>
</div> </div>
<div class="top_right" id="top_third" th:fragment="header-tool-third"> <div class="top_right" id="top_third" th:fragment="header-tool-third">
<th:block sec:authorize="isAuthenticated()"> <th:block sec:authorize="isAuthenticated()">
<div class="top_tool"> <div class="top_tool">
<a class="com_msg_btn" href="#msg_modal" check="0" uk-toggle> <a class="com_msg_btn" href="#msg_modal" check="0" uk-toggle>
<i class="ri-notification-2-line"></i> <i class="ri-notification-2-line"></i>
</a> </a>
</div> </div>
<div class="t_login top_tool icon_color" th:if="${pluginFinder.available('PluginMoments')}"> <div class="t_login top_tool icon_color" th:if="${pluginFinder.available('PluginMoments')}">
<a class="normal_edit" uk-toggle="target: #create_post_box"><i class="ri-edit-box-line"></i></a> <a class="normal_edit" uk-toggle="target: #create_post_box"><i class="ri-edit-box-line"></i></a>
</div> </div>
<div class="admin_ava"> <div class="admin_ava">
<a class="mobile_edit" uk-toggle="target: #create_post_box"><i class="ri-add-line"></i></a> <a class="mobile_edit" uk-toggle="target: #create_post_box"><i class="ri-add-line"></i></a>
<div id="top_ava2" class="top_ava"><img th:src="${user.avatar}"></div> <div id="top_ava2" class="top_ava"><img th:src="${user.avatar}"></div>
<div class="user_pannel round12" uk-dropdown="mode: click;toggle:#top_ava2;pos:bottom-right;animation:uk-animation-slide-top-small"> <div class="user_pannel round12" uk-dropdown="mode: click;toggle:#top_ava2;pos:bottom-right;animation:uk-animation-slide-top-small">
<div class="inner"> <div class="inner">
<a href="/console" target="_blank" pjax="exclude"><i class="ri-function-line"></i>控制台</a> <a href="/console" target="_blank" pjax="exclude"><i class="ri-function-line"></i>管理面板</a>
<a class="logout" href="/logout"><i class="ri-logout-circle-r-line"></i>登出</a> <a class="logout" href="javascript:void(0);" onclick="showPixarCard()"><i class="ri-logout-circle-r-line"></i>退出登录</a>
</div> </div>
</div> </div>
</div> </div>
</th:block> </th:block>
<div sec:authorize="isAnonymous()" class="top_tool"> <div sec:authorize="isAnonymous()" class="top_tool">
<a th:if="${theme.config.base_set.login_button}" onclick="loginUrl()"><i class="ri-user-4-fill"></i></a> <a th:if="${theme.config.base_set.login_button}" onclick="open_login_box()"><i class="ri-user-3-line"></i></a>
</div> </div>
</div> </div>
+70 -70
View File
@@ -1,71 +1,71 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="zh-CN" th:classappend="(${theme.config.style.theme_set=='dark-theme'} ? 'dark' : '')" <html lang="zh-CN" th:classappend="(${theme.config.style.theme_set=='dark-theme'} ? 'dark' : '')"
th:fragment="layout(_head, _content, _title)" xmlns:th="http://www.thymeleaf.org" th:fragment="layout(_head, _content, _title)" xmlns:th="http://www.thymeleaf.org"
th:with="site_title = ${not #strings.isEmpty(_title) ? _title : #strings.isEmpty(site.subtitle) ? site.title : site.title +' - ' +site.subtitle }, th:with="site_title = ${not #strings.isEmpty(_title) ? _title : #strings.isEmpty(site.subtitle) ? site.title : site.title +' - ' +site.subtitle },
user = ${contributorFinder.getContributor(#authentication.name)}, user = ${contributorFinder.getContributor(#authentication.name)},
roleNames = ${#annotations.get(user, 'rbac.authorization.halo.run/role-names')}, roleNames = ${#annotations.get(user, 'rbac.authorization.halo.run/role-names')},
roleName= ${roleNames == '[]' ? 'super-' : #strings.substring(roleNames,2, #strings.length(roleNames) - 2) }"> roleName= ${roleNames == '[]' ? 'super-' : #strings.substring(roleNames,2, #strings.length(roleNames) - 2) }">
<head> <head>
<th:block th:replace="~{modules/head :: head}"></th:block> <th:block th:replace="~{modules/head :: head}"></th:block>
</head> </head>
<body class="home blog normal" th:classappend="${theme.config.style.layout_set}"> <body class="home blog normal" th:classappend="${theme.config.style.layout_set}">
<style> <style>
.color-scheme-dark,.dark,[data-color-scheme=dark] { .color-scheme-dark,.dark,[data-color-scheme=dark] {
--halo-search-widget-divider-color: rgb(17, 19, 36); --halo-search-widget-divider-color: rgb(17, 19, 36);
--halo-search-widget-base-bg-color: rgb(17, 19, 36); --halo-search-widget-base-bg-color: rgb(17, 19, 36);
--halo-search-widget-modal-bg-color: #161829; --halo-search-widget-modal-bg-color: #161829;
--halo-search-widget-hit-bg-color: #292c45; --halo-search-widget-hit-bg-color: #292c45;
} }
</style> </style>
<div class="player_box"></div> <div class="player_box"></div>
<div id="page" class="site main_wrap"> <div id="page" class="site main_wrap">
<div class="main_body uk-flex uk-flex-center uk-grid-collapse" uk-grid> <div class="main_body uk-flex uk-flex-center uk-grid-collapse" uk-grid>
<div th:replace="~{modules/aside/aside-left}"/> <div th:replace="~{modules/aside/aside-left}"/>
<th:block th:replace="${_content}"></th:block> <th:block th:replace="${_content}"></th:block>
<div th:replace="~{modules/aside/aside-right}"/> <div th:replace="~{modules/aside/aside-right}"/>
</div> </div>
<div class="listree-box" style="display: none;"> <div class="listree-box" style="display: none;">
<h3 class="listree-titles"></h3> <h3 class="listree-titles"></h3>
<ul id="listree-ol" style="display:none;"> <ul id="listree-ol" style="display:none;">
</ul> </ul>
</div> </div>
<div class="go_top_box"> <div class="go_top_box">
<div class="footer_tool"> <div class="footer_tool">
<div th:if="${theme.config.music.bgm_open}" class="t_music top_tool icon_color"> <div th:if="${theme.config.music.bgm_open}" class="t_music top_tool icon_color">
<a class="bg_music"><i class="ri-disc-line"></i></a> <a class="bg_music"><i class="ri-disc-line"></i></a>
</div> </div>
<div th:if="${theme.config.music.lrc_open}" class="t_lrc top_tool icon_color" style="margin-bottom: 8px;"> <div th:if="${theme.config.music.lrc_open}" class="t_lrc top_tool icon_color" style="margin-bottom: 8px;">
<a class="bg_lrc"> <a class="bg_lrc">
<i class="ri-play-list-line"></i> <i class="ri-play-list-line"></i>
</a> </a>
</div> </div>
<div th:if="${theme.config.style.theme_set != 'dark-theme'}" class="t_dark top_tool icon_color"><a><i class="ri-contrast-2-line"></i></a></div> <div th:if="${theme.config.style.theme_set != 'dark-theme'}" class="t_dark top_tool icon_color"><a><i class="ri-contrast-2-line"></i></a></div>
</div> </div>
<a class="go_top" href="#page" uk-scroll=""><i class="ri-arrow-up-s-line"></i></a> <a class="go_top" href="#page" uk-scroll=""><i class="ri-arrow-up-s-line"></i></a>
</div> </div>
<!-- 底部 --> <!-- 底部 -->
<dev th:replace="~{modules/footer-nav :: footer-nav}"></dev> <dev th:replace="~{modules/footer-nav :: footer-nav}"></dev>
<dev th:replace="~{modules/msg-modal}"/> <dev th:replace="~{modules/msg-modal}"/>
<dev th:replace="~{modules/topic-push :: topic-push}"></dev> <dev th:replace="~{modules/topic-push :: topic-push}"></dev>
</div> </div>
<th:block th:replace="~{modules/footer}"></th:block> <th:block th:replace="~{modules/footer}"></th:block>
</body> </body>
</html> </html>
+34 -34
View File
@@ -1,35 +1,35 @@
<div id="msg_modal" uk-modal="" class="uk-modal"> <div id="msg_modal" uk-modal="" class="uk-modal">
<div class="uk-modal-dialog uk-overflow-auto" uk-overflow-auto=""> <div class="uk-modal-dialog uk-overflow-auto" uk-overflow-auto="">
<div class="msg_title"><i class="ri-mail-unread-line"></i>消息盒子</div> <div class="msg_title"><i class="ri-mail-unread-line"></i>消息盒子</div>
<button class="uk-modal-close-default uk-icon uk-close" type="button" uk-close=""></button> <button class="uk-modal-close-default uk-icon uk-close" type="button" uk-close=""></button>
<div class="msg_modal_inner"> <div class="msg_modal_inner">
<p class="no_posts"><small># 暂无消息 #</small> <p class="no_posts"><small># 暂无消息 #</small>
<img class="s_nodata" rc="/themes/theme-pix/assets/img/nodata.png"> <img class="s_nodata" rc="/upload/nodata.png">
</p> </p>
<p class="msg_limit">只显示最新10条未读和已读信息</p> <p class="msg_limit">只显示最新10条未读和已读信息</p>
</div> </div>
</div> </div>
<style> <style>
.msg_modal_inner .unread_box .box .mark_read{ .msg_modal_inner .unread_box .box .mark_read{
display: none; display: none;
position: absolute; position: absolute;
right: 0; right: 0;
top: 0; top: 0;
z-index: 1000; z-index: 1000;
cursor: pointer; cursor: pointer;
} }
.msg_modal_inner .unread_box .box:hover .mark_read{ .msg_modal_inner .unread_box .box:hover .mark_read{
display: block; display: block;
} }
.msg_modal_inner .unread_box .box .mark_read:hover{ .msg_modal_inner .unread_box .box .mark_read:hover{
color: red; color: red;
} }
.msg_modal_inner .box{ .msg_modal_inner .box{
position: relative; position: relative;
} }
.msg_modal_inner .notification-content .head { .msg_modal_inner .notification-content .head {
display: none; display: none;
} }
</style> </style>
</div> </div>
+140 -140
View File
@@ -1,141 +1,141 @@
<th:block th:fragment="topic-push"> <th:block th:fragment="topic-push">
<div id="create_post_box" class="uk-flex-top" uk-modal="bg-close:false" <div id="create_post_box" class="uk-flex-top" uk-modal="bg-close:false"
th:if="${pluginFinder.available('PluginMoments')}"> th:if="${pluginFinder.available('PluginMoments')}">
<div class="modal_inner uk-modal-dialog uk-modal-body round8 uk-margin-auto-vertical"> <div class="modal_inner uk-modal-dialog uk-modal-body round8 uk-margin-auto-vertical">
<div class="t_form"> <div class="t_form">
<div class="edit_text"> <div class="edit_text">
<div class="cat_header"> <div class="cat_header">
<div class="de_cat"> <div class="de_cat">
<span class="t_cat_toogle" tabindex="0" aria-haspopup="true" aria-expanded="false"> <span class="t_cat_toogle" tabindex="0" aria-haspopup="true" aria-expanded="false">
<i class="ri-hashtag"></i><span>[[${theme.config.moments.topics_de_cat}]]</span> <i class="ri-hashtag"></i><span>[[${theme.config.moments.topics_de_cat}]]</span>
</span> </span>
<div class="t_cat_box round8 shadow" uk-dropdown="mode: click;toggle:.t_cat_toogle;boundary: .tool_box; stretch: x;pos: top-center"> <div class="t_cat_box round8 shadow" uk-dropdown="mode: click;toggle:.t_cat_toogle;boundary: .tool_box; stretch: x;pos: top-center">
<div class="set_cat"> <div class="set_cat">
<input type="text" placeholder="创建话题" name="add_cat" id="add_cat"> <input type="text" placeholder="创建话题" name="add_cat" id="add_cat">
<a class="up_cat_btn">创建</a> <a class="up_cat_btn">创建</a>
</div> </div>
<ul style="overscroll-behavior: contain;" th:with="tags = ${momentFinder.listAllTags()}"> <ul style="overscroll-behavior: contain;" th:with="tags = ${momentFinder.listAllTags()}">
<li class="topic_default_cat" th:each="tag : ${tags}"> <li class="topic_default_cat" th:each="tag : ${tags}">
<span class="c_name"> <span class="c_name">
<i class="ri-hashtag"></i> <i class="ri-hashtag"></i>
<span th:text="${tag.name}"></span> <span th:text="${tag.name}"></span>
</span> </span>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
<textarea id="topic_content" name="topic_content" placeholder="今日份分享 ! " maxlength="800"></textarea> <textarea id="topic_content" name="topic_content" placeholder="可否归来?在水一方" maxlength="800"></textarea>
</div> </div>
</div> </div>
<div class="topic_tool"> <div class="topic_tool">
<div class="tool_box"> <div class="tool_box">
<div class="left"> <div class="left">
<div class="moment_image_type moment_type_btn" uk-tooltip="title:图文; pos:top; offset:2;"> <div class="moment_image_type moment_type_btn" uk-tooltip="title:图文; pos:top; offset:2;">
<a><i class="ri-image-line"></i></a> <a><i class="ri-image-line"></i></a>
</div> </div>
<div class="moment_video_type moment_type_btn" uk-tooltip="title:视频; pos:top; offset:2;"> <div class="moment_video_type moment_type_btn" uk-tooltip="title:视频; pos:top; offset:2;">
<a><i class="ri-movie-line"></i></a> <a><i class="ri-movie-line"></i></a>
</div> </div>
<div class="moment_audio_type moment_type_btn" uk-tooltip="title:音乐; pos: top; offset:2;"> <div class="moment_audio_type moment_type_btn" uk-tooltip="title:音乐; pos: top; offset:2;">
<a><i class="ri-disc-line"></i></a> <a><i class="ri-disc-line"></i></a>
</div> </div>
<div class="moment_card_type moment_type_btn" uk-tooltip="title:卡片; pos: top; offset:2;"> <div class="moment_card_type moment_type_btn" uk-tooltip="title:卡片; pos: top; offset:2;">
<a><i class="ri-pages-line"></i></a> <a><i class="ri-pages-line"></i></a>
</div> </div>
<span class="moment_btn_line"> | </span> <span class="moment_btn_line"> | </span>
<div class="smile_box" uk-tooltip="title:表情; pos: top; offset:2;"> <div class="smile_box" uk-tooltip="title:表情; pos: top; offset:2;">
<a><i class="ri-emotion-line"></i></a> <a><i class="ri-emotion-line"></i></a>
</div> </div>
<div class="simi" uk-tooltip="title:私密; pos: top; offset:2;"> <div class="simi" uk-tooltip="title:私密; pos: top; offset:2;">
<a visible="PUBLIC"><i class="ri-lock-unlock-line"></i></a> <a visible="PUBLIC"><i class="ri-lock-unlock-line"></i></a>
</div> </div>
</div> </div>
<div class="right"> <div class="right">
<div class="loca"> <div class="loca">
<a class="loca_text" state="0" uk-tooltip="title:自定义; pos: top; offset:2;"></a> <a class="loca_text" state="0" uk-tooltip="title:自定义; pos: top; offset:2;"></a>
<a class="laqu"><i class="ri-map-pin-2-line"></i></a> <a class="laqu"><i class="ri-map-pin-2-line"></i></a>
<div class="local_box round8 shadow" uk-dropdown="mode: click;toggle:.loca_text;boundary: .tool_box; stretch: x;pos: top-center"> <div class="local_box round8 shadow" uk-dropdown="mode: click;toggle:.loca_text;boundary: .tool_box; stretch: x;pos: top-center">
<div class="inner"> <div class="inner">
<div class="set_local_box"><input type="text" placeholder="输入自定义位置" name="set_local" id="set_local"><a class="set_local_btn">确定</a></div> <div class="set_local_box"><input type="text" placeholder="输入自定义位置" name="set_local" id="set_local"><a class="set_local_btn">确定</a></div>
<a class="close_local">位置已开启</a> <a class="close_local">位置已开启,点击关闭</a>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="smile_show"><div class="smile_show_inner"><a class="smile_btn">😃</a><a class="smile_btn">😊</a><a class="smile_btn">😉</a><a class="smile_btn">😒</a><a class="smile_btn">😘</a><a class="smile_btn">👌</a><a class="smile_btn">👍</a><a class="smile_btn">💖</a><a class="smile_btn">💋</a><a class="smile_btn">👏</a><a class="smile_btn">😂</a><a class="smile_btn">😜</a><a class="smile_btn"></a><a class="smile_btn">🎁</a><a class="smile_btn"></a><a class="smile_btn">🌹</a><a class="smile_btn">🤦</a><a class="smile_btn">👀</a><a class="smile_btn">😆</a><a class="smile_btn">🤢</a><a class="smile_btn">😈</a><a class="smile_btn">👻</a><a class="smile_btn">🙊</a><a class="smile_btn">😥</a><a class="smile_btn">😶</a><a class="smile_btn">😐</a><a class="smile_btn">😤</a><a class="smile_btn">😱</a><a class="smile_btn">🙏</a><a class="smile_btn">💪</a></div></div> <div class="smile_show"><div class="smile_show_inner"><a class="smile_btn">😃</a><a class="smile_btn">😊</a><a class="smile_btn">😉</a><a class="smile_btn">😒</a><a class="smile_btn">😘</a><a class="smile_btn">👌</a><a class="smile_btn">👍</a><a class="smile_btn">💖</a><a class="smile_btn">💋</a><a class="smile_btn">👏</a><a class="smile_btn">😂</a><a class="smile_btn">😜</a><a class="smile_btn"></a><a class="smile_btn">🎁</a><a class="smile_btn"></a><a class="smile_btn">🌹</a><a class="smile_btn">🤦</a><a class="smile_btn">👀</a><a class="smile_btn">😆</a><a class="smile_btn">🤢</a><a class="smile_btn">😈</a><a class="smile_btn">👻</a><a class="smile_btn">🙊</a><a class="smile_btn">😥</a><a class="smile_btn">😶</a><a class="smile_btn">😐</a><a class="smile_btn">😤</a><a class="smile_btn">😱</a><a class="smile_btn">🙏</a><a class="smile_btn">💪</a></div></div>
<div class="moment_type_main"> <div class="moment_type_main">
<div class="add_img_box"> <div class="add_img_box">
<div class="img_show" uk-sortable="handle: .t_media_item;cls-no-drag:up_img_btn;"> <div class="img_show" uk-sortable="handle: .t_media_item;cls-no-drag:up_img_btn;">
<a class="up_img_btn"> <a class="up_img_btn">
<i class="ri-add-line"></i> <i class="ri-add-line"></i>
<input type="file" name="topic_img_up" id="topic_img_up" accept="image/jpg,image/jpeg,image/png,image/gif,image/webp" multiple="multiple" title="上传图片"> <input type="file" name="topic_img_up" id="topic_img_up" accept="image/*,video/*" multiple="multiple" title="上传图片/视频">
</a> </a>
</div> </div>
<div class="up_img_type"> <div class="up_img_type">
<div class="up_from_media"> <div class="up_from_media">
<a><i class="ri-gallery-line"></i>从媒体库选择</a> <a><i class="ri-gallery-line"></i>从媒体库选择</a>
</div> </div>
<div class="up_from_cdn"> <div class="up_from_cdn">
<a><i class="ri-image-add-line"></i>插入图床图片</a> <a><i class="ri-image-add-line"></i>从链接中插入</a>
</div> </div>
</div> </div>
<div class="show_media_box" style="display:none"> <div class="show_media_box" style="display:none">
<div class="wp_get_media_list"></div> <div class="wp_get_media_list"></div>
<div class="nav_tool"> <div class="nav_tool">
<div class="attch_nav" paged=""> <div class="attch_nav" paged="">
<a class="pre"><i class="ri-arrow-left-s-line"></i></a> <a class="pre"><i class="ri-arrow-left-s-line"></i></a>
<a class="nex"><i class="ri-arrow-right-s-line"></i></a> <a class="nex"><i class="ri-arrow-right-s-line"></i></a>
</div> </div>
<a class="souqi"><i class="ri-upload-line"></i> 收起</a> <a class="souqi"><i class="ri-upload-line"></i> 收起</a>
</div> </div>
</div> </div>
<div class="show_cdn_media" style="display:none"> <div class="show_cdn_media" style="display:none">
<div class="inner"> <div class="inner">
<input type="text" placeholder="外部图片链接" name="img_link_up" id="img_link_up"> <input type="text" placeholder="外部图片链接" name="img_link_up" id="img_link_up">
<a class="img_link_btn">插入</a><a class="img_link_cancel">取消</a> <a class="img_link_btn">插入</a><a class="img_link_cancel">取消</a>
</div> </div>
<span>支持: jpg | png | gif | webp | jpeg</span> <span>支持图片与视频类型</span>
</div> </div>
</div> </div>
</div> </div>
<div class="form_footer"> <div class="form_footer">
<div class="admin_tool"> <div class="admin_tool">
<a href="/console" target="_blank"><i class="ri-function-line"></i> 控制台</a> <a href="/console" target="_blank"><i class="ri-function-line"></i> 管理</a>
<a class="logout" href="/logout"><i class="ri-logout-circle-r-line"></i> 登出</a> <a class="logout" href="javascript:void(0);" onclick="showPixarCard()"><i class="ri-logout-circle-r-line"></i> 登出</a>
</div> </div>
<div class="moment_sure"> <div class="moment_sure">
<button class="uk-modal-close push_close">取消</button> <button class="uk-modal-close push_close">取消</button>
<button class="push_item" type="image" action="push" pid="0"><i class="ri-send-plane-2-line"></i>发布</button> <button class="push_item" type="image" action="push" pid="0"><i class="ri-send-plane-2-line"></i>发布</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="image_edit_temp" style="display:none"></div> <div class="image_edit_temp" style="display:none"></div>
</th:block> </th:block>
+253 -212
View File
@@ -1,213 +1,254 @@
<style th:if="${theme.config.moments.img_style == 'one'}"> <style th:if="${theme.config.moments.img_style == 'one'}">
.img_list .list_inner { .img_list .list_inner {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
flex-direction: row; flex-direction: row;
align-content: center; align-content: center;
} }
.img_list span { .img_list span {
padding: 3px; padding: 3px;
position: relative; position: relative;
width: 25%; width: 25%;
margin: 3px; margin: 3px;
} }
.img_list span:nth-child(1):nth-last-child(4), .img_list span:nth-child(1):nth-last-child(4),
.img_list span:nth-child(3):nth-last-child(2), .img_list span:nth-child(3):nth-last-child(2),
.img_list span:nth-child(4):nth-last-child(1) { .img_list span:nth-child(4):nth-last-child(1) {
width: 25%; width: 25%;
} }
.img_list span:nth-child(2):nth-last-child(3) { .img_list span:nth-child(2):nth-last-child(3) {
margin-right: 30%; margin-right: 30%;
} }
.img_list span img { .img_list span img {
border-radius: 5px; border-radius: 5px;
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
bottom: 0; bottom: 0;
right: 0; right: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
object-fit: cover; object-fit: cover;
} }
.img_list .list_inner span:before { .img_list .list_inner span:before {
content: ""; content: "";
display: block; display: block;
padding-top: 100%; padding-top: 100%;
} }
@media only screen and (max-width:540px) { @media only screen and (max-width:540px) {
.img_list span { .img_list span {
width: 30%; width: 30%;
margin: 2px; margin: 2px;
} }
.img_list span:nth-child(1):nth-last-child(4), .img_list span:nth-child(1):nth-last-child(4),
.img_list span:nth-child(3):nth-last-child(2), .img_list span:nth-child(3):nth-last-child(2),
.img_list span:nth-child(4):nth-last-child(1) { .img_list span:nth-child(4):nth-last-child(1) {
width: 30%; width: 30%;
} }
} }
</style> </style>
<style th:if="${theme.config.moments.img_style == 'two'}"> <style th:if="${theme.config.moments.img_style == 'two'}">
.img_list span:nth-last-child(1):first-child { .img_list span:nth-last-child(1):first-child {
width: 85%; width: 85%;
} }
.img_list .list_inner { .img_list .list_inner {
display: grid; display: grid;
max-width: 25rem; max-width: 25rem;
flex-wrap: wrap; flex-wrap: wrap;
flex-direction: row; flex-direction: row;
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
align-content: center; align-content: center;
} }
.img_list span { .img_list span {
position: relative; position: relative;
margin: 3px; margin: 3px;
} }
.img_list .list_inner[num='1'] { .img_list .list_inner[num='1'] {
grid-template-columns: repeat(1, 1fr); grid-template-columns: repeat(1, 1fr);
} }
.img_list .list_inner[num='1'] img { .img_list .list_inner[num='1'] img {
aspect-ratio: unset; aspect-ratio: unset;
max-height: 30rem; max-height: 30rem;
max-width: 25rem; max-width: 25rem;
} }
.img_list .list_inner[num='2'], .img_list .list_inner[num='2'],
.img_list .list_inner[num='4'] { .img_list .list_inner[num='4'] {
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
} }
.img_list .list_inner[num='3'], .img_list .list_inner[num='3'],
.img_list .list_inner[num='6'], .img_list .list_inner[num='6'],
.img_list .list_inner[num='8'] { .img_list .list_inner[num='8'] {
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
} }
.img_list .list_inner[num='5'] { .img_list .list_inner[num='5'] {
grid-template-columns: repeat(6, 1fr); grid-template-columns: repeat(6, 1fr);
} }
.img_list .list_inner[num='7'] { .img_list .list_inner[num='7'] {
grid-template-columns: repeat(4, 1fr); grid-template-columns: repeat(4, 1fr);
} }
.img_list .list_inner[num='3'] span:nth-child(1) { .img_list .list_inner[num='3'] span:nth-child(1) {
grid-area: 1 / 1 / 3 / 3; grid-area: 1 / 1 / 3 / 3;
} }
.img_list .list_inner[num='3'] span:nth-child(2) { .img_list .list_inner[num='3'] span:nth-child(2) {
grid-area: 1 / 3 / 2 / 4; grid-area: 1 / 3 / 2 / 4;
} }
.img_list .list_inner[num='3'] span:nth-child(3) { .img_list .list_inner[num='3'] span:nth-child(3) {
grid-area: 2 / 3 / 3 / 4; grid-area: 2 / 3 / 3 / 4;
} }
.img_list .list_inner[num='5'] span:nth-child(1) { .img_list .list_inner[num='5'] span:nth-child(1) {
grid-area: 1 / 1 / 5 / 5; grid-area: 1 / 1 / 5 / 5;
} }
.img_list .list_inner[num='5'] span:nth-child(2) { .img_list .list_inner[num='5'] span:nth-child(2) {
grid-area: 1 / 5 / 3 / 7; grid-area: 1 / 5 / 3 / 7;
} }
.img_list .list_inner[num='5'] span:nth-child(3) { .img_list .list_inner[num='5'] span:nth-child(3) {
grid-area: 3 / 5 / 5 / 7; grid-area: 3 / 5 / 5 / 7;
} }
.img_list .list_inner[num='5'] span:nth-child(4) { .img_list .list_inner[num='5'] span:nth-child(4) {
grid-area: 5 / 1 / 7 / 4; grid-area: 5 / 1 / 7 / 4;
} }
.img_list .list_inner[num='5'] span:nth-child(5) { .img_list .list_inner[num='5'] span:nth-child(5) {
grid-area: 5 / 4 / 7 / 7; grid-area: 5 / 4 / 7 / 7;
} }
.img_list .list_inner[num='5'] span:nth-child(4) img,.img_list .list_inner[num='5'] span:nth-child(5) img { .img_list .list_inner[num='5'] span:nth-child(4) img,.img_list .list_inner[num='5'] span:nth-child(5) img {
height: 127.35px; height: 127.35px;
} }
.img_list .list_inner[num='6'] span:nth-child(1) { .img_list .list_inner[num='6'] span:nth-child(1) {
grid-area: 1 / 1 / 3 / 3; grid-area: 1 / 1 / 3 / 3;
} }
.img_list .list_inner[num='6'] span:nth-child(2) { .img_list .list_inner[num='6'] span:nth-child(2) {
grid-area: 1 / 3 / 2 / 4; grid-area: 1 / 3 / 2 / 4;
} }
.img_list .list_inner[num='6'] span:nth-child(3) { .img_list .list_inner[num='6'] span:nth-child(3) {
grid-area: 2 / 3 / 3 / 4; grid-area: 2 / 3 / 3 / 4;
} }
.img_list .list_inner[num='6'] span:nth-child(4) { .img_list .list_inner[num='6'] span:nth-child(4) {
grid-area: 3 / 1 / 4 / 2; grid-area: 3 / 1 / 4 / 2;
} }
.img_list .list_inner[num='6'] span:nth-child(5) { .img_list .list_inner[num='6'] span:nth-child(5) {
grid-area: 3 / 2 / 4 / 3; grid-area: 3 / 2 / 4 / 3;
} }
.img_list .list_inner[num='6'] span:nth-child(6) { .img_list .list_inner[num='6'] span:nth-child(6) {
grid-area: 3 / 3 / 4 / 4; grid-area: 3 / 3 / 4 / 4;
} }
.img_list .list_inner[num='7'] span:nth-child(1) { .img_list .list_inner[num='7'] {
grid-area: 1 / 1 / 3 / 3; display: grid;
} /* 采用 6 列网格,支持 50% (span 3) 和 33.3% (span 2) 两种尺寸 */
grid-template-columns: repeat(6, 1fr);
.img_list .list_inner[num='7'] span:nth-child(2) {
grid-area: 1 / 3 / 3 / 5; /* 间距修正 */
} gap: 6px;
padding: 3px;
.img_list .list_inner[num='7'] span:nth-child(3) {
grid-area: 3 / 1 / 5 / 3; /* 清除干扰 */
} justify-content: unset;
align-content: unset;
.img_list .list_inner[num='7'] span:nth-child(4) { align-items: unset;
grid-area: 3 / 3 / 4 / 4; }
}
.img_list .list_inner[num='7'] span {
.img_list .list_inner[num='7'] span:nth-child(5) { /* 清除干扰,保持稳定性 */
grid-area: 3 / 4 / 4 / 5; margin: 0 !important;
} padding: 0 !important;
width: auto;
.img_list .list_inner[num='7'] span:nth-child(6) { height: auto;
grid-area: 4 / 3 / 5 / 4;
} /* 默认尺寸:中等 (33.3% 宽度, span 2/6) */
grid-column: span 2;
.img_list .list_inner[num='7'] span:nth-child(7) { }
grid-area: 4 / 4 / 5 / 5;
} /* 第一行 (Item 1, 2):大尺寸 (50% 宽度, span 3/6) */
.img_list .list_inner[num='7'] span:nth-child(1),
.img_list .list_inner[num='8'] span:nth-child(8) { .img_list .list_inner[num='7'] span:nth-child(2) {
grid-area: 3 / 2 / 4 / 4; grid-column: span 3;
} }
.img_list .list_inner[num='8'] span:nth-child(8) img { /* 第三行 (Item 6, 7):大尺寸 (50% 宽度, span 3/6) */
aspect-ratio: 3; .img_list .list_inner[num='7'] span:nth-child(6),
} .img_list .list_inner[num='7'] span:nth-child(7) {
grid-column: span 3;
}
.img_list span img {
aspect-ratio: 1; /* 容器设置:保持 6 列网格,用于计算 1/3 和 1/2 宽度 */
display: block; .img_list .list_inner[num='8'] {
border-radius: 5px; display: grid;
width: 100%; grid-template-columns: repeat(6, 1fr); /* 6 列 */
height: 100%;
object-fit: cover; max-height: 100vw;
}
gap: 6px;
padding: 3px;
justify-content: unset;
align-content: unset;
align-items: unset;
}
/* 默认规则:应用到所有子项,用于第 1 行和第 3 行 (1/3 宽度) */
.img_list .list_inner[num='8'] span {
margin: 0 !important;
padding: 0 !important;
width: auto;
height: auto;
/* 跨 2 列 = 1/3 宽度 (6列/2=3张/行) */
grid-column: span 2;
}
/* 特殊规则:仅应用于第 2 行的 2 张图片 (1/2 宽度) */
.img_list .list_inner[num='8'] span:nth-child(4),
.img_list .list_inner[num='8'] span:nth-child(5) {
/* 跨 3 列 = 1/2 宽度 (6列/3=2张/行) */
grid-column: span 3;
}
/* 确保第 1 行和第 3 行的宽度不被其他 CSS 规则干扰 */
.img_list .list_inner[num='8'] span:nth-child(-n+3), /* 第 1, 2, 3 个 */
.img_list .list_inner[num='8'] span:nth-child(n+6) { /* 第 6, 7, 8 个 */
grid-column: span 2;
}
.img_list span img {
aspect-ratio: 1;
display: block;
border-radius: 5px;
width: 100%;
height: 100%;
object-fit: cover;
}
</style> </style>
@@ -1,16 +1,16 @@
<section id="pix_widget_yiyan-2" class="widget pix_widget_yiyan"> <section id="pix_widget_yiyan-2" class="widget pix_widget_yiyan">
<div class="wid_title" th:text="${theme.config.sidebar.pix_widget_yiyan.title}">一言</div> <div class="wid_title" th:text="${theme.config.sidebar.pix_widget_yiyan.title}">一言</div>
<ul class="items wid_yiyan"> <ul class="items wid_yiyan">
<div class="yiyan_box"> <div class="yiyan_box">
<img class="lazy" <img class="lazy"
th:data-src="${theme.config.sidebar.pix_widget_yiyan.yiyan_bg}"> th:data-src="${theme.config.sidebar.pix_widget_yiyan.yiyan_bg}">
<div class="yiyan_info"> <div class="yiyan_info">
<div class="time"> <div class="time">
<div class="left"><span class="day">[[${#dates.format(#dates.createNow(),'dd')}]]</span><span class="ym">[[${#dates.format(#dates.createNow(),'yyyy/MM')}]]</span></div><a <div class="left"><span class="day">[[${#dates.format(#dates.createNow(),'dd')}]]</span><span class="ym">[[${#dates.format(#dates.createNow(),'yyyy/MM')}]]</span></div><a
class="change"><i class="ri-refresh-line"></i></a> class="change"><i class="ri-refresh-line"></i></a>
</div> </div>
<p uk-tooltip="东京喰种" aria-describedby="uk-tooltip-23" tabindex="0">错的不是我,而是这个世界!</p> <p uk-tooltip="阿年的日记簿" aria-describedby="uk-tooltip-23" tabindex="0">一日不见 如隔三秋</p>
</div> </div>
</div> </div>
</ul> </ul>
</section> </section>
+63 -21
View File
@@ -1,44 +1,81 @@
<!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">
<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="photos_cat_nav"> <div class="photos_cat_nav">
<ul id="cat_nav_items"> <ul id="cat_nav_items">
<li> <li>
<a href="/photos" <!-- 修改:href 改为 data-href,并添加一个通用 class 如 'cat-link' -->
th:classappend="${#lists.isEmpty(param.group) ? 'active' : ''}">全部<span></span></a> <a th:attr="data-href=@{/photos}"
th:classappend="${#lists.isEmpty(param.group) ? 'active' : ''}" class="cat-link">
全部<span></span>
</a>
</li> </li>
<th:block th:each="group : ${groups}"> <th:block th:each="group : ${groups}">
<li> <li>
<a th:href="@{|/photos?group=${group.metadata.name}|}" <!-- 修改:这里也改为 data-href -->
th:classappend="${#lists.contains(param.group, group.metadata.name) ? 'active' : ''}" <a th:attr="data-href=@{|/photos?group=${group.metadata.name}|}"
th:text="${group.spec.displayName}"><span></span> th:classappend="${#lists.contains(param.group, group.metadata.name) ? 'active' : ''}"
th:text="${group.spec.displayName}" class="cat-link">
<span></span>
</a> </a>
</li> </li>
</th:block> </th:block>
</ul> </ul>
</div> </div>
<!-- 相册列表 -->
<div id="photos_item" class="norpost_list"> <div id="photos_item" class="norpost_list">
<div th:unless="${#lists.isEmpty(photos.items)}" class="gallery-photos page" > <div th:unless="${#lists.isEmpty(photos.items)}" class="gallery-photos page">
<div th:each="photo : ${photos.items}" class="gallery-photo"> <div th:each="photo : ${photos.items}" class="gallery-photo">
<span th:with="img = ${ not #strings.isEmpty(photo.spec.cover) ? photo.spec.cover : photo.spec.url}"
th:href="${img}" data-fancybox="gallery" class="fancybox" <th:block th:with="
th:data-thumb="${img}"> img = ${!#strings.isEmpty(photo.spec.cover) ? photo.spec.cover : photo.spec.url},
<img class="photo-img" loading='lazy' th:src="${img}"> isVideo = ${#strings.endsWith(img, '.mp4') or #strings.endsWith(img, '.webm') or #strings.endsWith(img, '.ogg')}
</span> ">
<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>
</div> </div>
<!-- 空数据 -->
<th:block th:if="${#lists.isEmpty(photos.items)}"> <th:block th:if="${#lists.isEmpty(photos.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="${photos.hasPrevious()} or ${photos.hasNext()}"> <th:block th:if="${photos.hasPrevious()} or ${photos.hasNext()}">
<div id="p_pagination"> <div id="p_pagination">
<div class="post-paging" th:if="${photos.hasNext()}"> <div class="post-paging" th:if="${photos.hasNext()}">
@@ -46,19 +83,24 @@
</div> </div>
</div> </div>
</th:block> </th:block>
</div> </div>
</div> </div>
</div> </div>
<!-- 瀑布流自适应 -->
<script> <script>
window.onresize = () => { window.onresize = () => {
var galleryPhotos = document.querySelectorAll('.gallery-photos .gallery-photo'); const galleryPhotos = document.querySelectorAll('.gallery-photos .gallery-photo');
if(galleryPhotos.length>0) { if (galleryPhotos.length > 0) {
waterfall('.gallery-photos'); waterfall('.gallery-photos');
} }
}; };
</script> </script>
<th:block th:replace="~{modules/footer-main}"/>
<th:block th:replace="~{modules/footer-main}" />
</div> </div>
</th:block> </th:block>
</html> </html>
+1 -1
View File
@@ -33,7 +33,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>
+26 -26
View File
@@ -1,27 +1,27 @@
apiVersion: theme.halo.run/v1alpha1 apiVersion: theme.halo.run/v1alpha1
kind: Theme kind: Theme
metadata: metadata:
name: theme-pix name: theme-pix
annotations: annotations:
# Add supports for Halo App Store # Add supports for Halo App Store
# https://www.halo.run/store/apps/app-TUSTB # https://www.halo.run/store/apps/app-TUSTB
"store.halo.run/app-id": "app-TUSTB" "store.halo.run/app-id": "app-TUSTB"
spec: spec:
displayName: PIX displayName: PIX
author: author:
name: 困困鱼 & Roozen name: 困困鱼 & Roozen
website: "https://www.halo.run/store/apps/app-TUSTB" website: "https://www.halo.run/store/apps/app-TUSTB"
description: Halo 2.x Theme PIX description: Halo 2.x Theme PIX
logo: "https://www.halo.run/upload/store-resources/theme-pix-logo.png" logo: "https://www.halo.run/upload/store-resources/theme-pix-logo.png"
homepage: "https://www.halo.run/store/apps/app-TUSTB" homepage: "https://www.halo.run/store/apps/app-TUSTB"
issues: "https://github.com/chengzhongxue/halo-theme-pix/issues" issues: "https://github.com/chengzhongxue/halo-theme-pix/issues"
settingName: "theme-pix-setting" settingName: "theme-pix-setting"
configMapName: "theme-pix-configMap" configMapName: "theme-pix-configMap"
version: "1.2.5" version: "1.2.5-3"
require: ">=2.20.0" require: ">=2.20.0"
customTemplates: customTemplates:
page: page:
- name: 落地页模版 - name: 落地页模版
description: 落地页 description: 落地页
screenshot: screenshot:
file: landing.html file: landing.html