first initial commit
This commit is contained in:
299
assets/css/global.css
Normal file
299
assets/css/global.css
Normal file
@@ -0,0 +1,299 @@
|
||||
:root {
|
||||
/* 定义一个衬线字体栈 */
|
||||
--font-serif-zh: "SimSun","Noto Serif SC", "Georgia", "STSong", "Songti SC", serif;
|
||||
}
|
||||
|
||||
body {
|
||||
/* 全站应用 */
|
||||
font-family: var(--font-serif-zh);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
color: #262626; /* 衬线体配合深灰色阅读感更好 */
|
||||
}
|
||||
|
||||
/* ========== 强制隐藏模板容器 ========== */
|
||||
#mobile-drawer-template {
|
||||
display: none !important;
|
||||
visibility: hidden !important;
|
||||
position: absolute !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
/* ========== 桌面端导航样式 ========== */
|
||||
@media (min-width: 768px) {
|
||||
/* 强制隐藏移动端元素 */
|
||||
#mobile-menu-trigger,
|
||||
#mobile-sidebar-overlay,
|
||||
#mobile-sidebar-overlay-active {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* 桌面端导航居中 */
|
||||
nav .hidden.md\\:flex.items-center.justify-center nav,
|
||||
nav .hidden.md\\:flex.items-center.justify-center .nav {
|
||||
display: flex !important;
|
||||
justify-content: center !important;
|
||||
align-items: center !important;
|
||||
gap: 2rem !important;
|
||||
}
|
||||
|
||||
nav .hidden.md\\:flex.items-center.justify-center ul {
|
||||
display: flex !important;
|
||||
flex-direction: row !important;
|
||||
gap: 2rem !important;
|
||||
list-style: none !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
nav .hidden.md\\:flex.items-center.justify-center li {
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
nav .hidden.md\\:flex.items-center.justify-center a {
|
||||
text-decoration: none !important;
|
||||
color: #374151 !important;
|
||||
font-weight: 500 !important;
|
||||
font-size: 0.875rem !important;
|
||||
transition: color 0.2s !important;
|
||||
}
|
||||
|
||||
nav .hidden.md\\:flex.items-center.justify-center a:hover {
|
||||
color: #2563eb !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* ========== 移动端抽屉基础样式 ========== */
|
||||
#mobile-sidebar-overlay-active {
|
||||
display: none; /* 默认隐藏,通过 JS 控制显示 */
|
||||
}
|
||||
|
||||
/* ========== 抽屉导航网格布局 ========== */
|
||||
.mobile-nav-grid {
|
||||
display: grid !important;
|
||||
grid-template-columns: repeat(2, 1fr) !important;
|
||||
gap: 0.75rem !important;
|
||||
}
|
||||
|
||||
.mobile-nav-grid nav,
|
||||
.mobile-nav-grid .nav {
|
||||
display: contents !important;
|
||||
}
|
||||
|
||||
.mobile-nav-grid ul {
|
||||
display: contents !important;
|
||||
list-style: none !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.mobile-nav-grid li {
|
||||
display: block !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.mobile-nav-grid a {
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
padding: 0.75rem !important;
|
||||
border-radius: 0.5rem !important;
|
||||
background-color: #f9fafb !important;
|
||||
color: #4b5563 !important;
|
||||
font-weight: 500 !important;
|
||||
text-decoration: none !important;
|
||||
border: 1px solid #e5e7eb !important;
|
||||
transition: all 0.2s !important;
|
||||
font-size: 0.875rem !important;
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.mobile-nav-grid a:hover,
|
||||
.mobile-nav-grid a:active {
|
||||
background-color: #eff6ff !important;
|
||||
color: #2563eb !important;
|
||||
border-color: #3b82f6 !important;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
/* ========== 动画和过渡 ========== */
|
||||
.drawer-backdrop {
|
||||
transition: opacity 300ms ease-out;
|
||||
}
|
||||
|
||||
.drawer-panel {
|
||||
transition: transform 300ms ease-out;
|
||||
}
|
||||
|
||||
/* ========== 通用优化 ========== */
|
||||
#mobile-menu-trigger {
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* 平滑滚动 */
|
||||
.drawer-panel {
|
||||
overscroll-behavior: contain;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
/* 防止内容抖动 */
|
||||
body.drawer-open {
|
||||
overflow: hidden !important;
|
||||
position: fixed !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/* 确保抽屉在最顶层 */
|
||||
#mobile-sidebar-overlay-active {
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
|
||||
#welcome-ip-location-info b {
|
||||
color: #2563eb; /* 对应 Tailwind 的 blue-600 */
|
||||
font-weight: 700;
|
||||
}
|
||||
/* 默认模糊状态 */
|
||||
#welcome-ip-location-info .ip-mask {
|
||||
filter: blur(5px);
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-block;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
padding: 0 4px;
|
||||
border-radius: 4px;
|
||||
user-select: none; /* 防止未解锁前被选中复制 */
|
||||
}
|
||||
|
||||
/* 鼠标悬停时轻微减少模糊(给予视觉反馈) */
|
||||
#welcome-ip-location-info .ip-mask:hover {
|
||||
filter: blur(3px);
|
||||
background-color: rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
/* 点击或被激活时完全显示 */
|
||||
#welcome-ip-location-info .ip-mask.unmask,
|
||||
#welcome-ip-location-info .ip-mask:active {
|
||||
filter: blur(0);
|
||||
background-color: transparent;
|
||||
user-select: auto;
|
||||
}
|
||||
|
||||
.site-footer {
|
||||
padding: 60px 20px;
|
||||
background: #f9f9f9; /* 浅灰背景,简洁干净 */
|
||||
color: #555;
|
||||
font-size: 0.9rem;
|
||||
border-top: 1px solid #eee;
|
||||
margin-top: 80px;
|
||||
}
|
||||
|
||||
.footer-content {
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr 1fr; /* 三栏布局 */
|
||||
gap: 40px;
|
||||
}
|
||||
|
||||
.footer-column h3, .footer-column h4 {
|
||||
color: #111;
|
||||
margin-bottom: 15px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.footer-column p { line-height: 1.6; }
|
||||
|
||||
/* 微信公众号悬浮交互 */
|
||||
.wechat-trigger {
|
||||
color: #1a73e8;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
border-bottom: 1px dashed #1a73e8;
|
||||
}
|
||||
|
||||
/* 弹出层容器 */
|
||||
.wechat-qr {
|
||||
display: none;
|
||||
position: absolute;
|
||||
bottom: 35px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%); /* 配合 left: 50% 实现相对于文字水平居中 */
|
||||
background: white;
|
||||
padding: 12px;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
|
||||
border-radius: 8px;
|
||||
z-index: 100;
|
||||
|
||||
width: 140px;
|
||||
text-align: center !important;
|
||||
box-sizing: border-box; /* 确保 padding 不撑破宽度 */
|
||||
}
|
||||
|
||||
/* 二维码图片 */
|
||||
.wechat-qr img {
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
display: block;
|
||||
margin: 0 auto 10px; /* 这里的 auto 确保图片在容器内水平居中 */
|
||||
}
|
||||
|
||||
/* 下方的文字 */
|
||||
.wechat-qr p {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: center; /* 确保文字在 p 标签内居中 */
|
||||
line-height: 1.2;
|
||||
white-space: nowrap; /* 避免文字太长换行导致视觉偏移 */
|
||||
}
|
||||
.wechat-trigger:hover .wechat-qr { display: block; }
|
||||
|
||||
/* 次级导航样式 */
|
||||
.footer-nav ul { list-style: none; padding: 0; }
|
||||
.footer-nav ul li { margin-bottom: 8px; }
|
||||
.footer-nav a { color: #555; text-decoration: none; }
|
||||
.footer-nav a:hover { color: #1a73e8; }
|
||||
|
||||
/* 移动端适配 */
|
||||
@media (max-width: 768px) {
|
||||
.footer-content {
|
||||
grid-template-columns: 1fr;
|
||||
text-align: center;
|
||||
}
|
||||
.wechat-qr { left: 50%; }
|
||||
}
|
||||
|
||||
/* 确保整个头部在滚动时固定 */
|
||||
.site-header-container {
|
||||
position: sticky !important;
|
||||
top: 0 !important;
|
||||
z-index: 1000 !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 公告栏样式优化 */
|
||||
.gh-announcement {
|
||||
background-color: var(--ghost-accent-color, #3eb0ef);
|
||||
color: #fff;
|
||||
padding: 10px 24px;
|
||||
text-align: center;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.gh-announcement a {
|
||||
color: #fff;
|
||||
text-decoration: underline;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* 修复移动端抽屉层级,确保在 header 之上 */
|
||||
#mobile-sidebar-overlay {
|
||||
z-index: 10001 !important;
|
||||
}
|
||||
4337
assets/css/index.css
Normal file
4337
assets/css/index.css
Normal file
File diff suppressed because it is too large
Load Diff
1
assets/css/input.css
Normal file
1
assets/css/input.css
Normal file
@@ -0,0 +1 @@
|
||||
@import "tailwindcss";
|
||||
153
assets/css/post.css
Normal file
153
assets/css/post.css
Normal file
@@ -0,0 +1,153 @@
|
||||
/* 1. 标题和元数据美化 */
|
||||
.gh-article-title {
|
||||
margin-bottom: 1.5rem;
|
||||
line-height: 1.1;
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.gh-article-meta-modern {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 3rem;
|
||||
padding-bottom: 2rem;
|
||||
border-bottom: 1px solid rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.gh-author-avatar img {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #fff;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.gh-author-avatar:hover img {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.gh-article-meta-text {
|
||||
line-height: 1.4;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.gh-author-name {
|
||||
font-weight: 700;
|
||||
color: var(--ghost-accent-color);
|
||||
}
|
||||
|
||||
.gh-article-meta-sub {
|
||||
opacity: 0.6;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* 2. 封面图圆角与阴影 */
|
||||
.gh-feature-image-wrapper img {
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 20px 40px rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
/* 3. 正文排版优化 */
|
||||
.gh-content {
|
||||
font-size: 1.15rem;
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
.gh-content p {
|
||||
margin-bottom: 0.8em;
|
||||
text-indent: 2em;
|
||||
}
|
||||
|
||||
.gh-content figure p,
|
||||
.gh-content blockquote p {
|
||||
text-indent: 0;
|
||||
}
|
||||
|
||||
/* 4. 底部标签样式 */
|
||||
.gh-post-tags a {
|
||||
display: inline-block;
|
||||
padding: 4px 12px;
|
||||
margin: 4px;
|
||||
background: #f0f0f0;
|
||||
border-radius: 20px;
|
||||
font-size: 0.8rem;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.gh-post-tags a:hover {
|
||||
background: var(--ghost-accent-color);
|
||||
color: #fff !important;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* 5. 阅读更多部分的卡片提升 */
|
||||
.gh-feed {
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.post-card {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.post-card:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
/* 3.5 分割线美化 */
|
||||
.gh-content hr {
|
||||
margin: 3rem 0; /* 上下留出一定间距,让页面有呼吸感 */
|
||||
border: 0; /* 去除默认的凹凸边框 */
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1); /* 使用带透明度的黑色,形成优雅的灰色 */
|
||||
/* 或者使用具体的十六进制色值: border-top: 1px solid #e0e0e0; */
|
||||
}
|
||||
|
||||
/* 标题样式美化 */
|
||||
.gh-content h1,
|
||||
.gh-content h2,
|
||||
.gh-content h3 {
|
||||
color: #1a1a1a;
|
||||
line-height: 1.3;
|
||||
margin: 2.5rem 0 1rem; /* 增加上方间距,减少下方间距 */
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.gh-content h1 { font-size: 2rem; }
|
||||
.gh-content h2 { font-size: 1.65rem; }
|
||||
.gh-content h3 { font-size: 1.35rem; }
|
||||
|
||||
/* 重点:标题不需要首行缩进 */
|
||||
.gh-content h1, .gh-content h2, .gh-content h3 {
|
||||
text-indent: 0 !important;
|
||||
}
|
||||
|
||||
/* 引文样式优化 */
|
||||
.gh-content blockquote {
|
||||
margin: 2rem 0;
|
||||
padding: 0.5rem 1.5rem;
|
||||
border-left: 4px solid var(--ghost-accent-color, #15171a); /* 使用主题色或深色作为边框 */
|
||||
background: rgba(0, 0, 0, 0.02); /* 极淡的背景色 */
|
||||
font-style: italic;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
/* 引文内的段落不需要缩进 */
|
||||
.gh-content blockquote p {
|
||||
text-indent: 0 !important;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
|
||||
/* 宽屏图片样式示例 */
|
||||
.kg-width-wide .kg-image {
|
||||
max-width: 1200px;
|
||||
margin: 2em auto;
|
||||
}
|
||||
|
||||
/* 全屏图片样式示例 */
|
||||
.kg-width-full .kg-image {
|
||||
max-width: 100vw;
|
||||
margin: 2em auto;
|
||||
}
|
||||
13
assets/css/screen.css
Normal file
13
assets/css/screen.css
Normal file
@@ -0,0 +1,13 @@
|
||||
@keyframes marquee-custom {
|
||||
0% { transform: translateX(0); }
|
||||
100% { transform: translateX(-100%); }
|
||||
}
|
||||
.animate-marquee-custom { animation: marquee-custom 20s linear infinite; }
|
||||
@keyframes slide-down {
|
||||
from { opacity: 0; transform: translateY(-10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.animate-slide-down {
|
||||
animation: slide-down 0.2s ease-out forwards;
|
||||
}
|
||||
79
assets/js/carousel.js
Normal file
79
assets/js/carousel.js
Normal file
@@ -0,0 +1,79 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const carousel = document.querySelector('.js-carousel');
|
||||
if (!carousel) return;
|
||||
|
||||
const track = carousel.querySelector('.js-carousel-track');
|
||||
const slides = track.children;
|
||||
const dots = carousel.querySelectorAll('.js-dot');
|
||||
const prevBtn = carousel.querySelector('.js-carousel-prev');
|
||||
const nextBtn = carousel.querySelector('.js-carousel-next');
|
||||
|
||||
let current = 0;
|
||||
const total = slides.length;
|
||||
let touchStartX = 0;
|
||||
let touchEndX = 0;
|
||||
|
||||
function updateDisplay() {
|
||||
// 移动轨道
|
||||
track.style.transform = `translateX(-${current * 100}%)`;
|
||||
// 更新指示器
|
||||
dots.forEach((dot, i) => {
|
||||
if (i === current) {
|
||||
dot.classList.add('bg-white', 'w-6');
|
||||
dot.classList.remove('bg-white/50');
|
||||
} else {
|
||||
dot.classList.remove('bg-white', 'w-6');
|
||||
dot.classList.add('bg-white/50');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function next() {
|
||||
current = (current === total - 1) ? 0 : current + 1;
|
||||
updateDisplay();
|
||||
}
|
||||
|
||||
function prev() {
|
||||
current = (current === 0) ? total - 1 : current - 1;
|
||||
updateDisplay();
|
||||
}
|
||||
|
||||
// 自动轮播
|
||||
let autoPlay = setInterval(next, 5000);
|
||||
|
||||
function resetTimer() {
|
||||
clearInterval(autoPlay);
|
||||
autoPlay = setInterval(next, 5000);
|
||||
}
|
||||
|
||||
// 事件监听
|
||||
if (nextBtn) nextBtn.addEventListener('click', () => { next(); resetTimer(); });
|
||||
if (prevBtn) prevBtn.addEventListener('click', () => { prev(); resetTimer(); });
|
||||
|
||||
dots.forEach(dot => {
|
||||
dot.addEventListener('click', () => {
|
||||
current = parseInt(dot.getAttribute('data-index'));
|
||||
updateDisplay();
|
||||
resetTimer();
|
||||
});
|
||||
});
|
||||
|
||||
// --- 触摸滑动逻辑 ---
|
||||
carousel.addEventListener('touchstart', (e) => {
|
||||
touchStartX = e.changedTouches[0].screenX;
|
||||
}, { passive: true });
|
||||
|
||||
carousel.addEventListener('touchend', (e) => {
|
||||
touchEndX = e.changedTouches[0].screenX;
|
||||
const distance = touchStartX - touchEndX;
|
||||
const minSwipeDistance = 50;
|
||||
|
||||
if (Math.abs(distance) > minSwipeDistance) {
|
||||
if (distance > 0) next(); else prev();
|
||||
resetTimer();
|
||||
}
|
||||
}, { passive: true });
|
||||
|
||||
// 初始化
|
||||
updateDisplay();
|
||||
});
|
||||
150
assets/js/mobile-menu.js
Normal file
150
assets/js/mobile-menu.js
Normal file
@@ -0,0 +1,150 @@
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
let drawerElement = null;
|
||||
let isDrawerOpen = false;
|
||||
|
||||
// 创建抽屉 (从隐藏的 div 克隆)
|
||||
function createDrawer() {
|
||||
const template = document.getElementById('mobile-drawer-template');
|
||||
if (!template) {
|
||||
console.error('Drawer template not found');
|
||||
return null;
|
||||
}
|
||||
|
||||
const overlay = template.querySelector('#mobile-sidebar-overlay');
|
||||
if (!overlay) {
|
||||
console.error('Overlay element not found in template');
|
||||
return null;
|
||||
}
|
||||
|
||||
const clone = overlay.cloneNode(true);
|
||||
clone.id = 'mobile-sidebar-overlay-active';
|
||||
|
||||
// --- [新增逻辑] 自动搬运侧边栏挂件 ---
|
||||
const sidebarWidgets = document.getElementById('sidebar-widgets');
|
||||
const drawerContent = clone.querySelector('.p-5.space-y-6'); // 抽屉的内容容器
|
||||
|
||||
if (sidebarWidgets && drawerContent) {
|
||||
// 创建一个专门存放侧边栏内容的容器
|
||||
const sidebarMobileContainer = document.createElement('section');
|
||||
sidebarMobileContainer.className = 'mobile-sidebar-content space-y-6 pt-4 border-t border-gray-100';
|
||||
sidebarMobileContainer.innerHTML = '<h3 class="text-xs font-bold text-gray-400 uppercase tracking-wider mb-4">实时资讯</h3>';
|
||||
|
||||
// 克隆侧边栏所有的子元素(欢迎卡片、计时器等)
|
||||
const widgetsClone = sidebarWidgets.cloneNode(true);
|
||||
// 移除原本的 ID 避免冲突
|
||||
widgetsClone.removeAttribute('id');
|
||||
// 将侧边栏内容追加到抽屉中
|
||||
sidebarMobileContainer.appendChild(widgetsClone);
|
||||
drawerContent.appendChild(sidebarMobileContainer);
|
||||
}
|
||||
// ------------------------------------
|
||||
|
||||
document.body.appendChild(clone);
|
||||
return clone;
|
||||
}
|
||||
|
||||
function openDrawer() {
|
||||
if (isDrawerOpen) return;
|
||||
|
||||
if (!drawerElement) {
|
||||
drawerElement = createDrawer();
|
||||
if (!drawerElement) return;
|
||||
bindDrawerEvents();
|
||||
}
|
||||
|
||||
// 锁定背景滚动
|
||||
const scrollY = window.scrollY;
|
||||
document.body.style.overflow = 'hidden';
|
||||
document.body.style.position = 'fixed';
|
||||
document.body.style.top = `-${scrollY}px`;
|
||||
document.body.style.width = '100%';
|
||||
|
||||
drawerElement.style.display = 'block';
|
||||
isDrawerOpen = true;
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
const backdrop = drawerElement.querySelector('.drawer-backdrop');
|
||||
const panel = drawerElement.querySelector('.drawer-panel');
|
||||
if (backdrop) backdrop.style.opacity = '1';
|
||||
if (panel) panel.style.transform = 'translateX(0)';
|
||||
});
|
||||
});
|
||||
|
||||
// 如果克隆的内容里包含计时器,需要重新初始化计时器逻辑
|
||||
if (window.initTimers) window.initTimers(drawerElement);
|
||||
}
|
||||
|
||||
function closeDrawer() {
|
||||
if (!isDrawerOpen || !drawerElement) return;
|
||||
|
||||
const backdrop = drawerElement.querySelector('.drawer-backdrop');
|
||||
const panel = drawerElement.querySelector('.drawer-panel');
|
||||
|
||||
if (backdrop) backdrop.style.opacity = '0';
|
||||
if (panel) panel.style.transform = 'translateX(100%)';
|
||||
|
||||
setTimeout(() => {
|
||||
if (drawerElement) {
|
||||
drawerElement.style.display = 'none';
|
||||
}
|
||||
|
||||
// 恢复滚动
|
||||
const scrollY = document.body.style.top;
|
||||
document.body.style.overflow = '';
|
||||
document.body.style.position = '';
|
||||
document.body.style.top = '';
|
||||
document.body.style.width = '';
|
||||
window.scrollTo(0, parseInt(scrollY || '0') * -1);
|
||||
|
||||
isDrawerOpen = false;
|
||||
}, 300);
|
||||
}
|
||||
|
||||
function bindDrawerEvents() {
|
||||
if (!drawerElement) return;
|
||||
|
||||
const closeBtn = drawerElement.querySelector('#drawer-close-btn');
|
||||
if (closeBtn) {
|
||||
closeBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
closeDrawer();
|
||||
});
|
||||
}
|
||||
|
||||
const backdrop = drawerElement.querySelector('.drawer-backdrop');
|
||||
if (backdrop) {
|
||||
backdrop.addEventListener('click', closeDrawer);
|
||||
}
|
||||
|
||||
const links = drawerElement.querySelectorAll('.drawer-link, .mobile-nav-grid a, a');
|
||||
links.forEach(link => {
|
||||
link.addEventListener('click', () => {
|
||||
setTimeout(closeDrawer, 100);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const trigger = document.getElementById('mobile-menu-trigger');
|
||||
if (trigger) {
|
||||
trigger.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
openDrawer();
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && isDrawerOpen) closeDrawer();
|
||||
});
|
||||
});
|
||||
|
||||
window.mobileMenu = {
|
||||
open: openDrawer,
|
||||
close: closeDrawer,
|
||||
isOpen: () => isDrawerOpen
|
||||
};
|
||||
})();
|
||||
87
assets/js/share.js
Normal file
87
assets/js/share.js
Normal file
@@ -0,0 +1,87 @@
|
||||
(function() {
|
||||
const initShare = () => {
|
||||
const shareBtn = document.getElementById('share-btn');
|
||||
const sharePanel = document.getElementById('share-panel');
|
||||
const copyBtn = document.getElementById('copy-link');
|
||||
const shareContainer = document.getElementById('share-component');
|
||||
const closeBtn = document.getElementById('close-share');
|
||||
|
||||
if (!shareBtn || !sharePanel) return;
|
||||
|
||||
// 1. 处理分享主按钮
|
||||
shareBtn.addEventListener('click', async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const shareData = {
|
||||
title: document.title,
|
||||
url: window.location.href
|
||||
};
|
||||
|
||||
// 原生分享触发条件:浏览器支持 + 必须是 HTTPS 环境
|
||||
if (navigator.share && window.isSecureContext) {
|
||||
try {
|
||||
await navigator.share(shareData);
|
||||
return; // 成功唤起系统分享,不再向下执行
|
||||
} catch (err) {
|
||||
// 如果不是用户主动取消(AbortError),则打印错误并在下方降级处理
|
||||
if (err.name !== 'AbortError') console.error('Share failed:', err);
|
||||
}
|
||||
}
|
||||
|
||||
// 降级方案:显示自定义 HTML 分享面板
|
||||
sharePanel.classList.toggle('hidden');
|
||||
});
|
||||
|
||||
// 2. 处理链接复制
|
||||
copyBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const currentUrl = window.location.href;
|
||||
|
||||
navigator.clipboard.writeText(currentUrl).then(() => {
|
||||
const iconBg = document.getElementById('copy-icon-bg');
|
||||
const copySvg = document.getElementById('copy-svg');
|
||||
const checkSvg = document.getElementById('check-svg');
|
||||
const copyText = document.getElementById('copy-text');
|
||||
|
||||
// 成功反馈状态
|
||||
iconBg.classList.replace('bg-slate-100', 'bg-green-500');
|
||||
iconBg.classList.replace('text-slate-600', 'text-white');
|
||||
copySvg.classList.add('hidden');
|
||||
checkSvg.classList.remove('hidden');
|
||||
copyText.innerText = '已复制';
|
||||
|
||||
setTimeout(() => {
|
||||
iconBg.classList.replace('bg-green-500', 'bg-slate-100');
|
||||
iconBg.classList.replace('text-white', 'text-slate-600');
|
||||
copySvg.classList.remove('hidden');
|
||||
checkSvg.classList.add('hidden');
|
||||
copyText.innerText = '复制';
|
||||
}, 2000);
|
||||
}).catch(() => {
|
||||
alert('复制失败,请手动复制地址栏链接');
|
||||
});
|
||||
});
|
||||
|
||||
// 3. 面板交互控制
|
||||
const closePanel = () => sharePanel.classList.add('hidden');
|
||||
|
||||
// 点击关闭按钮隐藏
|
||||
if (closeBtn) closeBtn.addEventListener('click', closePanel);
|
||||
|
||||
// 点击页面其他地方隐藏
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!shareContainer.contains(e.target)) closePanel();
|
||||
});
|
||||
|
||||
// 阻止面板内部点击触发隐藏
|
||||
sharePanel.addEventListener('click', (e) => e.stopPropagation());
|
||||
};
|
||||
|
||||
// 确保 DOM 加载后运行
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initShare);
|
||||
} else {
|
||||
initShare();
|
||||
}
|
||||
})();
|
||||
57
assets/js/timer.js
Normal file
57
assets/js/timer.js
Normal file
@@ -0,0 +1,57 @@
|
||||
function initClassTimers() {
|
||||
const timers = document.querySelectorAll('.js-event-timer');
|
||||
|
||||
timers.forEach(timer => {
|
||||
const targetTimeStr = timer.getAttribute('data-target');
|
||||
const labelEl = timer.querySelector('.js-timer-label');
|
||||
const daysEl = timer.querySelector('.js-days');
|
||||
const hoursEl = timer.querySelector('.js-hours');
|
||||
const minutesEl = timer.querySelector('.js-minutes');
|
||||
const secondsEl = timer.querySelector('.js-seconds');
|
||||
|
||||
// 清除可能存在的旧定时器(防止PJAX导致的叠加)
|
||||
if (timer.timerId) clearInterval(timer.timerId);
|
||||
|
||||
function updateTimer() {
|
||||
const targetDate = new Date(targetTimeStr).getTime();
|
||||
// 简单格式校验,防止 Excerpt 填错导致报错
|
||||
if (isNaN(targetDate)) {
|
||||
if (labelEl) labelEl.innerText = "日期错误";
|
||||
return;
|
||||
}
|
||||
|
||||
const now = new Date().getTime();
|
||||
const difference = targetDate - now;
|
||||
const isPast = difference < 0;
|
||||
const absDiff = Math.abs(difference);
|
||||
|
||||
// 更新状态标签
|
||||
if (labelEl) {
|
||||
labelEl.innerText = isPast ? "已经过去" : "倒计时中";
|
||||
}
|
||||
|
||||
// 计算时间
|
||||
const d = Math.floor(absDiff / (1000 * 60 * 60 * 24));
|
||||
const h = Math.floor((absDiff / (1000 * 60 * 60)) % 24);
|
||||
const m = Math.floor((absDiff / 1000 / 60) % 60);
|
||||
const s = Math.floor((absDiff / 1000) % 60);
|
||||
|
||||
// 更新数字显示 (加了动画平滑过渡感)
|
||||
if (daysEl) daysEl.innerText = d.toString().padStart(2, '0');
|
||||
if (hoursEl) hoursEl.innerText = h.toString().padStart(2, '0');
|
||||
if (minutesEl) minutesEl.innerText = m.toString().padStart(2, '0');
|
||||
if (secondsEl) secondsEl.innerText = s.toString().padStart(2, '0');
|
||||
}
|
||||
|
||||
updateTimer();
|
||||
timer.timerId = setInterval(updateTimer, 1000);
|
||||
});
|
||||
}
|
||||
|
||||
// 首次加载执行
|
||||
document.addEventListener('DOMContentLoaded', initClassTimers);
|
||||
|
||||
// PJAX 兼容执行 (适配 Ghost 常用的主题加载方式)
|
||||
if (typeof pjax !== 'undefined' || document.querySelector('[data-pjax]')) {
|
||||
document.addEventListener('pjax:complete', initClassTimers);
|
||||
}
|
||||
Reference in New Issue
Block a user