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);
|
||||
}
|
||||
83
custom-profile.hbs
Normal file
83
custom-profile.hbs
Normal file
@@ -0,0 +1,83 @@
|
||||
{{!< default}}
|
||||
|
||||
{{#post}}
|
||||
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12 animate-fade-in">
|
||||
|
||||
{{!-- 面包屑导航 --}}
|
||||
<nav class="mb-8 text-sm text-gray-500">
|
||||
<a href="/" class="hover:text-blue-600">首页</a>
|
||||
<span class="mx-2">/</span>
|
||||
{{!-- 使用 match,直接比对 primary_tag 的 slug --}}
|
||||
{{#match primary_tag.slug "=" "teacher-profile"}}
|
||||
<a href="/tag/teacher-profile/" class="hover:text-blue-600">教师档案</a>
|
||||
{{else}}
|
||||
<a href="/tag/student-profile/" class="hover:text-blue-600">学子档案</a>
|
||||
{{/match}}
|
||||
<span class="mx-2">/</span>
|
||||
<span class="text-gray-900 font-medium">{{title}}</span>
|
||||
</nav>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-12">
|
||||
|
||||
<aside class="lg:col-span-4">
|
||||
<div class="sticky top-24">
|
||||
<div class="aspect-[3/4] w-full overflow-hidden rounded-2xl bg-gray-100 mb-8 shadow-md border border-gray-100">
|
||||
{{#if feature_image}}
|
||||
<img src="{{img_url feature_image size="m"}}" alt="{{title}}" class="w-full h-full object-cover" />
|
||||
{{else}}
|
||||
<div class="w-full h-full flex flex-col items-center justify-center text-gray-300 bg-gray-50">
|
||||
<span class="text-6xl mb-2">
|
||||
{{#match primary_tag.slug "=" "teacher-profile"}}👨🏫{{else}}🎓{{/match}}
|
||||
</span>
|
||||
<span class="text-sm">尚未上传照片</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<h1 class="text-4xl font-bold text-gray-900 mb-3">{{title}}</h1>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-3 mb-8">
|
||||
{{#match primary_tag.slug "=" "teacher-profile"}}
|
||||
<span class="px-3 py-1 bg-blue-50 text-blue-700 rounded-full text-sm font-bold">教师</span>
|
||||
{{else}}
|
||||
<span class="px-3 py-1 bg-emerald-50 text-emerald-700 rounded-full text-sm font-bold">学生</span>
|
||||
{{/match}}
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl p-6 border border-gray-100 shadow-sm space-y-5">
|
||||
{{#if custom_excerpt}}
|
||||
<div>
|
||||
<span class="block text-gray-400 mb-1 text-xs uppercase tracking-widest font-semibold">Motto / Title</span>
|
||||
<span class="font-medium text-gray-900 leading-relaxed">{{custom_excerpt}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="pt-4 border-t border-gray-50 text-sm text-gray-500">
|
||||
更新:{{date format="YYYY-MM-DD"}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main class="lg:col-span-8">
|
||||
<div class="bg-white rounded-3xl p-8 md:p-12 shadow-sm border border-gray-100 min-h-[600px]">
|
||||
<div class="flex items-center justify-between mb-8 pb-4 border-b border-gray-50">
|
||||
<h2 class="text-2xl font-bold text-gray-900">
|
||||
{{#match primary_tag.slug "=" "teacher-profile"}}教师简介{{else}}个人简介{{/match}}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="gh-content prose prose-lg max-w-none">
|
||||
{{content}}
|
||||
</div>
|
||||
{{!-- Ghost 原生评论区 --}}
|
||||
{{#if comments}}
|
||||
<section class="article-comments mt-16 pt-10 border-t border-slate-100">
|
||||
<h3 class="text-2xl font-bold text-slate-900 mb-8">参与讨论</h3>
|
||||
{{comments}}
|
||||
</section>
|
||||
{{/if}}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</main>
|
||||
{{/post}}
|
||||
157
default.hbs
Normal file
157
default.hbs
Normal file
@@ -0,0 +1,157 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;700&display=swap" />
|
||||
<link rel="stylesheet" type="text/css" href="{{asset "css/global.css"}}" />
|
||||
<link rel="stylesheet" type="text/css" href="{{asset "css/index.css"}}" />
|
||||
<title>{{meta_title}}</title>
|
||||
{{ghost_head}}
|
||||
</head>
|
||||
<body class="{{body_class}}">
|
||||
|
||||
{{!-- 统一的固定头部容器 --}}
|
||||
<header class="site-header-container">
|
||||
{{!-- 1. 公告栏:仅在后台有内容时渲染 --}}
|
||||
{{#if @site.announcement}}
|
||||
<div class="gh-announcement">
|
||||
{{announcement}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{!-- 2. 导航栏 --}}
|
||||
<nav class="border-b border-gray-100 transition-all duration-300 bg-white/90 backdrop-blur-md shadow-sm">
|
||||
<div class="container mx-auto px-4 h-16">
|
||||
<div class="flex items-center justify-between h-full md:grid md:grid-cols-3 md:gap-4">
|
||||
|
||||
{{!-- Logo 区域 --}}
|
||||
<a href="{{@site.url}}" class="relative z-[41] flex-shrink-0">
|
||||
{{#if @site.logo}}
|
||||
<img src="{{@site.logo}}" alt="{{@site.title}}" class="h-8 w-auto">
|
||||
{{else}}
|
||||
<span class="text-xl font-bold text-blue-600">{{@site.title}}</span>
|
||||
{{/if}}
|
||||
</a>
|
||||
|
||||
{{!-- 桌面端菜单 --}}
|
||||
<div class="hidden md:flex items-center justify-center">
|
||||
{{navigation}}
|
||||
</div>
|
||||
|
||||
{{!-- 右侧功能区 --}}
|
||||
<div class="flex items-center justify-end gap-3">
|
||||
<button data-ghost-search class="p-2 text-gray-600 hover:text-blue-600 transition-colors focus:outline-none" aria-label="搜索">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div class="hidden md:flex items-center gap-3">
|
||||
{{#if @member}}
|
||||
<a href="#/portal/account" class="flex items-center gap-2 px-3 py-2 rounded-lg bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-200/60 hover:border-blue-300 transition-all">
|
||||
<div class="w-7 h-7 rounded-full bg-gradient-to-br from-blue-500 to-indigo-600 flex items-center justify-center">
|
||||
<svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<span class="text-sm font-semibold text-gray-800">我的档案</span>
|
||||
</a>
|
||||
{{else}}
|
||||
<a href="#/portal/signin/" class="text-sm font-medium text-gray-600 hover:text-blue-600 transition-colors">登录</a>
|
||||
<a href="#/portal/signup/" class="flex items-center gap-1.5 px-4 py-2 rounded-lg bg-gradient-to-r from-blue-500 to-indigo-600 hover:from-blue-600 text-white font-semibold shadow-md text-sm transition-all">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1"></path>
|
||||
</svg>
|
||||
注册
|
||||
</a>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<button id="mobile-menu-trigger" class="md:hidden relative z-[41] p-2 text-gray-600 hover:text-gray-900 focus:outline-none -mr-2" aria-label="打开菜单" type="button">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
{{!-- 页面主体内容:现在内容会自动排在 sticky header 下方,无需手动 pt-16 --}}
|
||||
<main class="site-main">
|
||||
{{{body}}}
|
||||
{{> "footer"}}
|
||||
</main>
|
||||
|
||||
{{!-- 移动端菜单模板 --}}
|
||||
<div id="mobile-drawer-template" style="display: none !important;">
|
||||
<div id="mobile-sidebar-overlay" class="fixed inset-0 md:hidden">
|
||||
<div class="drawer-backdrop absolute inset-0 bg-black/60 backdrop-blur-sm opacity-0 transition-opacity duration-300"></div>
|
||||
<div class="drawer-panel absolute top-0 right-0 h-full w-[85%] max-w-sm bg-white shadow-2xl translate-x-full transition-transform duration-300 ease-out overflow-y-auto">
|
||||
<div class="sticky top-0 bg-white z-20 px-5 h-16 border-b border-gray-100 flex justify-between items-center">
|
||||
<span class="font-bold text-lg text-gray-800">菜单导航</span>
|
||||
<button id="drawer-close-btn" class="p-2 -mr-2 text-gray-400 hover:text-gray-600 transition-colors" aria-label="关闭菜单">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="p-5 space-y-6 pb-24">
|
||||
<section>
|
||||
<h3 class="text-xs font-bold text-gray-400 uppercase tracking-wider mb-4">页面导航</h3>
|
||||
<div class="mobile-nav-grid grid grid-cols-2 gap-3">
|
||||
{{navigation}}
|
||||
</div>
|
||||
</section>
|
||||
<hr class="border-gray-100">
|
||||
<section>
|
||||
{{#if @member}}
|
||||
<div class="bg-gray-50 rounded-xl p-4 border border-gray-100">
|
||||
<div class="flex items-center gap-3 mb-3">
|
||||
<div class="w-10 h-10 rounded-full bg-gradient-to-br from-blue-500 to-indigo-600 flex items-center justify-center flex-shrink-0">
|
||||
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="overflow-hidden">
|
||||
<p class="font-bold text-gray-800 truncate">会员</p>
|
||||
<a href="#/portal/account" class="drawer-link text-xs text-blue-600 hover:underline">个人中心</a>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#/portal/signout" class="flex items-center justify-center gap-2 w-full py-2 text-sm text-red-600 bg-white border border-gray-200 rounded-lg shadow-sm hover:bg-red-50 transition-colors">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"></path>
|
||||
</svg>
|
||||
退出登录
|
||||
</a>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="space-y-3">
|
||||
<a href="#/portal/signin/" class="drawer-link flex items-center justify-center gap-2 w-full py-3 rounded-xl bg-white text-gray-800 font-semibold border-2 border-gray-200 hover:border-blue-500 hover:text-blue-600 transition-all mb-3">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 16l-4-4m0 0l4-4m-4 4h14m-5 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h7a3 3 0 013 3v1"></path>
|
||||
</svg>
|
||||
登录
|
||||
</a>
|
||||
<a href="#/portal/signup/" class="drawer-link flex items-center justify-center gap-2 w-full py-3 rounded-xl bg-gradient-to-r from-blue-600 to-indigo-600 text-white font-bold shadow-lg hover:from-blue-700 hover:shadow-xl transition-all">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z"></path>
|
||||
</svg>
|
||||
立即注册
|
||||
</a>
|
||||
</div>
|
||||
{{/if}}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{ghost_foot}}
|
||||
<script src="{{asset "js/timer.js"}}"></script>
|
||||
<script src="{{asset "js/carousel.js"}}"></script>
|
||||
<script src="{{asset "js/mobile-menu.js"}}"></script>
|
||||
</body>
|
||||
</html>
|
||||
219
index.hbs
Normal file
219
index.hbs
Normal file
@@ -0,0 +1,219 @@
|
||||
{{!< default}}
|
||||
|
||||
<main class="min-h-screen bg-white pb-20 relative">
|
||||
{{!-- 1. 轮播图 --}}
|
||||
{{#get "posts" filter="tag:hash-slider" limit="5" as |slides|}}
|
||||
<section id="home-carousel" class="relative w-full aspect-[16/9] md:aspect-[21/9] overflow-hidden group bg-gray-100 js-carousel">
|
||||
{{#if slides}}
|
||||
<div class="js-carousel-track flex transition-transform duration-500 ease-out h-full w-full">
|
||||
{{#foreach slides}}
|
||||
<div class="w-full flex-shrink-0 relative h-full">
|
||||
<a href="{{url}}" class="block w-full h-full relative cursor-pointer select-none">
|
||||
<img src="{{img_url feature_image size="xl"}}" alt="{{title}}" class="absolute inset-0 w-full h-full object-cover pointer-events-none" />
|
||||
<div class="absolute bottom-0 w-full bg-gradient-to-t from-black/70 to-transparent p-4 md:p-8">
|
||||
<h2 class="text-white text-xl md:text-3xl font-bold">{{title}}</h2>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
{{/foreach}}
|
||||
</div>
|
||||
<div class="js-carousel-dots absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-2 z-10">
|
||||
{{#foreach slides}}<div class="js-dot w-2 h-2 rounded-full transition-all cursor-pointer bg-white/50" data-index="{{@index}}"></div>{{/foreach}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</section>
|
||||
{{/get}}
|
||||
|
||||
<div class="container mx-auto px-4 sm:px-6 lg:px-8 mt-12 max-w-7xl">
|
||||
<div class="grid gap-8 lg:grid-cols-12">
|
||||
{{!-- ============ 左侧主内容区 ============ --}}
|
||||
<div class="lg:col-span-8 xl:col-span-9 space-y-20">
|
||||
|
||||
{{!-- 板块一:班级热点 🔥 --}}
|
||||
<section id="hot-topics" class="animate-fade-in">
|
||||
<div class="flex justify-center mb-8">
|
||||
<div class="bg-gradient-to-r from-red-500 to-rose-600 text-white px-10 py-3 rounded-full shadow-lg font-bold text-xl">🔥 班级热点</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-10">
|
||||
{{!-- 1.1 特别策划 (Red Theme) --}}
|
||||
{{#get "posts" filter="tag:special-event" limit="4" include="tags" order="featured desc, published_at desc"}}
|
||||
<div class="group">
|
||||
<div class="flex items-center justify-between mb-4 border-l-4 border-red-500 pl-4">
|
||||
<h3 class="text-xl font-bold text-gray-800">特别策划</h3>
|
||||
<a href="/tag/special-event/" class="text-sm text-gray-400 hover:text-red-500">更多 →</a>
|
||||
</div>
|
||||
<div class="grid md:grid-cols-2 gap-4">
|
||||
{{#foreach posts}}
|
||||
<a href="{{url}}" class="p-4 border rounded-xl hover:bg-red-50/30 transition-all {{#if featured}}bg-red-50/50 border-red-100 shadow-sm{{/if}}">
|
||||
<h4 class="font-bold text-gray-800 {{#if featured}}text-red-600{{/if}}">
|
||||
{{#if featured}}📌 {{/if}}{{title}}
|
||||
</h4>
|
||||
<p class="text-sm text-gray-500 mt-1 line-clamp-1">{{excerpt}}</p>
|
||||
</a>
|
||||
{{/foreach}}
|
||||
</div>
|
||||
</div>
|
||||
{{/get}}
|
||||
|
||||
{{!-- 1.2 班级活动 (Blue Theme) --}}
|
||||
{{#get "posts" filter="tag:event" limit="4" include="tags"}}
|
||||
<div class="group">
|
||||
<div class="flex items-center justify-between mb-4 border-l-4 border-blue-500 pl-4">
|
||||
<h3 class="text-xl font-bold text-gray-800">班级活动</h3>
|
||||
<a href="/tag/event/" class="text-sm text-gray-400 hover:text-blue-500">更多 →</a>
|
||||
</div>
|
||||
<div class="grid md:grid-cols-2 gap-4">
|
||||
{{#foreach posts}}
|
||||
<a href="{{url}}" class="p-4 border rounded-xl hover:bg-blue-50/30 transition-all">
|
||||
<h4 class="font-bold text-gray-800">{{title}}</h4>
|
||||
<p class="text-sm text-gray-500 mt-1 line-clamp-1">{{excerpt}}</p>
|
||||
</a>
|
||||
{{/foreach}}
|
||||
</div>
|
||||
</div>
|
||||
{{/get}}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{!-- 板块二:风采展示 🌟 --}}
|
||||
<section id="showcase" class="animate-fade-in">
|
||||
<div class="flex justify-center mb-8">
|
||||
<div class="bg-gradient-to-r from-blue-500 to-indigo-600 text-white px-10 py-3 rounded-full shadow-lg font-bold text-xl">🌟 风采展示</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-10">
|
||||
{{!-- 2.1 学生风采 (Green Theme) --}}
|
||||
{{#get "posts" filter="tag:student" limit="4"}}
|
||||
<div class="group">
|
||||
<div class="flex items-center justify-between mb-4 border-l-4 border-emerald-500 pl-4">
|
||||
<h3 class="text-xl font-bold text-gray-800">学生风采</h3>
|
||||
<a href="/tag/student/" class="text-sm text-gray-400 hover:text-emerald-500">更多 →</a>
|
||||
</div>
|
||||
<div class="grid md:grid-cols-2 gap-4">
|
||||
{{#foreach posts}}
|
||||
<a href="{{url}}" class="p-4 border rounded-xl hover:bg-emerald-50/30 transition-all">
|
||||
<h4 class="font-bold text-gray-800">{{title}}</h4>
|
||||
<p class="text-sm text-gray-500 mt-1 line-clamp-1">{{excerpt}}</p>
|
||||
</a>
|
||||
{{/foreach}}
|
||||
</div>
|
||||
</div>
|
||||
{{/get}}
|
||||
|
||||
{{!-- 2.2 教师风采 (Purple Theme) --}}
|
||||
{{#get "posts" filter="tag:teacher" limit="4"}}
|
||||
<div class="group">
|
||||
<div class="flex items-center justify-between mb-4 border-l-4 border-purple-500 pl-4">
|
||||
<h3 class="text-xl font-bold text-gray-800">教师风采</h3>
|
||||
<a href="/tag/teacher/" class="text-sm text-gray-400 hover:text-purple-500">更多 →</a>
|
||||
</div>
|
||||
<div class="grid md:grid-cols-2 gap-4">
|
||||
{{#foreach posts}}
|
||||
<a href="{{url}}" class="p-4 border rounded-xl hover:bg-purple-50/30 transition-all">
|
||||
<h4 class="font-bold text-gray-800">{{title}}</h4>
|
||||
<p class="text-sm text-gray-500 mt-1 line-clamp-1">{{excerpt}}</p>
|
||||
</a>
|
||||
{{/foreach}}
|
||||
</div>
|
||||
</div>
|
||||
{{/get}}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{{!-- 板块三:档案库 📂 --}}
|
||||
<section id="archive-vault" class="animate-fade-in">
|
||||
<div class="flex justify-center mb-10">
|
||||
<div class="bg-gradient-to-r from-emerald-500 to-teal-600 text-white px-10 py-3 rounded-full shadow-lg font-bold text-xl">📂 档案库</div>
|
||||
</div>
|
||||
|
||||
{{!-- 3.1 教师档案 --}}
|
||||
<div class="mb-16">
|
||||
<div class="flex items-center justify-between mb-6 border-l-4 border-blue-500 pl-4">
|
||||
<h3 class="text-xl font-bold text-gray-800">👨🏫 教师档案</h3>
|
||||
<a href="/tag/teacher-profile/" class="text-sm text-gray-400 hover:text-blue-500">查看全部 →</a>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
{{#get "posts" filter="tag:teacher-profile" limit="8" include="tags,authors"}}
|
||||
{{#foreach posts}}
|
||||
{{> "teacher-card"}}
|
||||
{{else}}
|
||||
<p class="text-gray-400 text-sm col-span-full italic">目前还没有教师档案入库...</p>
|
||||
{{/foreach}}
|
||||
{{/get}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{!-- 3.2 学生档案 (含权限判断) --}}
|
||||
<div class="mb-12">
|
||||
<div class="flex items-center justify-between mb-6 border-l-4 border-emerald-500 pl-4">
|
||||
<h3 class="text-xl font-bold text-gray-800">🎓 学生档案</h3>
|
||||
{{#if @member}}
|
||||
<a href="/tag/student-profile/" class="text-sm text-gray-400 hover:text-emerald-500">查看全部 →</a>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if @member}}
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||
{{#get "posts" filter="tag:student-profile" limit="8" include="tags,authors"}}
|
||||
{{#foreach posts}}
|
||||
{{> "teacher-card"}}
|
||||
{{else}}
|
||||
<p class="text-gray-400 text-sm col-span-full italic">目前还没有学生档案入库...</p>
|
||||
{{/foreach}}
|
||||
{{/get}}
|
||||
</div>
|
||||
{{else}}
|
||||
{{!-- 未登录占位卡片 --}}
|
||||
<div class="bg-gray-50 rounded-2xl p-12 text-center border-2 border-dashed border-gray-200">
|
||||
<div class="text-5xl mb-4">🔐</div>
|
||||
<h4 class="text-gray-800 font-bold mb-2">学生档案仅限内部成员访问</h4>
|
||||
<p class="text-gray-500 mb-6 text-sm">为了保护隐私,请先登录你的班级账号</p>
|
||||
<a href="#/portal/signin" data-portal="signin" class="inline-block bg-emerald-600 text-white px-8 py-2.5 rounded-full font-bold hover:bg-emerald-700 transition-all transform hover:scale-105 shadow-md">
|
||||
立即登录
|
||||
</a>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
{{!-- ============ 右侧边栏 ============ --}}
|
||||
<aside class="hidden lg:block lg:col-span-4 xl:col-span-3">
|
||||
<div class="sticky top-20 space-y-6" id="sidebar-widgets">
|
||||
{{!-- 欢迎卡片容器 --}}
|
||||
<div id="welcome-card" class="bg-gradient-to-br from-blue-50 via-white to-purple-50 rounded-xl p-6 border border-slate-200/60 shadow-sm animate-fade-in">
|
||||
<div id="welcome-ip-location-info" class="text-sm leading-relaxed text-slate-700">
|
||||
<div class="flex items-center justify-center py-2">
|
||||
<div class="w-1.5 h-1.5 bg-blue-400 rounded-full animate-bounce"></div>
|
||||
<div class="ml-2 text-xs text-slate-400">正在定位...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#get "posts" filter="tag:hash-timer" limit="3"}}
|
||||
{{#foreach posts}}
|
||||
<div class="js-event-timer rounded-xl p-6 border {{#if featured}}bg-gradient-to-br from-red-600 to-red-700 text-white shadow-lg{{else}}bg-white text-gray-800 shadow-sm{{/if}}" data-target="{{excerpt}}">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<h4 class="text-lg font-bold">{{title}}</h4>
|
||||
<span class="js-timer-label text-[10px] px-2 py-1 rounded-md {{#if featured}}bg-white/20 text-white{{else}}bg-blue-50 text-blue-600{{/if}} font-medium">
|
||||
计算中...
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-4 gap-2 text-center text-[10px]">
|
||||
<div class="p-2 {{#if featured}}bg-white/20{{else}}bg-gray-50{{/if}} rounded-lg"><span class="js-days block text-xl font-bold">00</span>DAYS</div>
|
||||
<div class="p-2 {{#if featured}}bg-white/20{{else}}bg-gray-50{{/if}} rounded-lg"><span class="js-hours block text-xl font-bold">00</span>HRS</div>
|
||||
<div class="p-2 {{#if featured}}bg-white/20{{else}}bg-gray-50{{/if}} rounded-lg"><span class="js-minutes block text-xl font-bold">00</span>MIN</div>
|
||||
<div class="p-2 {{#if featured}}bg-white/20{{else}}bg-gray-50{{/if}} rounded-lg"><span class="js-seconds block text-xl font-bold">00</span>SEC</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/foreach}}
|
||||
{{/get}}
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
<script src="https://cdn.jsdmirror.com/gh/bishshi/welcomemessage@1.6/txmap.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||
</main>
|
||||
12
package.json
Normal file
12
package.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "my-class-theme",
|
||||
"version": "3.1.1",
|
||||
"engines": {
|
||||
"ghost": ">=5.0.0"
|
||||
},
|
||||
"author": {
|
||||
"name": "BI",
|
||||
"email": "hello@ghost.org",
|
||||
"url": "https://ghost.org/"
|
||||
}
|
||||
}
|
||||
31
partials/footer.hbs
Normal file
31
partials/footer.hbs
Normal file
@@ -0,0 +1,31 @@
|
||||
<footer class="site-footer">
|
||||
<div class="footer-content">
|
||||
<div class="footer-column footer-about">
|
||||
<h3>{{@site.title}}</h3>
|
||||
<p>{{@site.description}}</p>
|
||||
<p class="copyright">© {{date format="YYYY"}} · <a href="/">All Rights Resevered</a></p>
|
||||
</div>
|
||||
|
||||
<div class="footer-column footer-contact">
|
||||
<h4>联系我们</h4>
|
||||
<div class="contact-item">
|
||||
<span class="label">Email:</span>
|
||||
<a href="mailto:feedback@biss.click">feedback</a>
|
||||
</div>
|
||||
<div class="contact-item">
|
||||
<span class="label">WeChat:</span>
|
||||
<span class="wechat-trigger">微信公众号
|
||||
<div class="wechat-qr">
|
||||
<img src="https://pic.biss.click/image/0f38ae9c-5cd8-4cd8-acb1-4aa39daccbef.webp" alt="微信公众号">
|
||||
<p>扫码关注公众号</p>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer-column footer-nav">
|
||||
<h4>探索</h4>
|
||||
{{navigation type="secondary"}}
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
20
partials/navigation.hbs
Normal file
20
partials/navigation.hbs
Normal file
@@ -0,0 +1,20 @@
|
||||
<ul class="flex flex-col md:flex-row md:items-center gap-1 md:gap-8">
|
||||
{{#foreach navigation}}
|
||||
<li class="group">
|
||||
<a href="{{url}}"
|
||||
class="flex items-center justify-between py-4 px-4 rounded-lg transition-all active:scale-[0.98]
|
||||
{{#if current}}
|
||||
text-blue-600 bg-blue-50/50
|
||||
{{else}}
|
||||
text-gray-700 hover:text-blue-600 active:bg-gray-100
|
||||
{{/if}}">
|
||||
|
||||
<span class="text-base md:text-sm font-bold tracking-wide">{{label}}</span>
|
||||
|
||||
<svg class="w-5 h-5 md:hidden opacity-50" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</a>
|
||||
</li>
|
||||
{{/foreach}}
|
||||
</ul>
|
||||
78
partials/teacher-card.hbs
Normal file
78
partials/teacher-card.hbs
Normal file
@@ -0,0 +1,78 @@
|
||||
{{!--
|
||||
通用档案卡片组件
|
||||
功能:根据 primary_tag.slug="teacher-profile" 自动切换 Faculty(蓝) 或 Student(绿) 样式
|
||||
--}}
|
||||
|
||||
<a href="{{url}}" class="group relative block bg-white rounded-2xl overflow-hidden border border-gray-100 shadow-sm hover:shadow-xl hover:-translate-y-1 transition-all duration-300 h-full flex flex-col">
|
||||
|
||||
{{!-- 1. 图片区域 --}}
|
||||
<div class="aspect-[3/4] relative overflow-hidden bg-gray-50 flex-shrink-0">
|
||||
{{#if feature_image}}
|
||||
<img
|
||||
src="{{img_url feature_image size="m"}}"
|
||||
alt="{{title}}"
|
||||
class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"
|
||||
loading="lazy"
|
||||
/>
|
||||
{{else}}
|
||||
{{!-- 无图时的占位图标 --}}
|
||||
<div class="w-full h-full flex items-center justify-center text-5xl opacity-20 bg-gray-200">
|
||||
{{#match primary_tag.slug "=" "teacher-profile"}}👨🏫{{else}}🎓{{/match}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{!-- 左上角悬浮身份标签 --}}
|
||||
<div class="absolute top-3 left-3 z-10">
|
||||
{{#match primary_tag.slug "=" "teacher-profile"}}
|
||||
<span class="px-2.5 py-1 bg-blue-600 text-white text-[10px] font-black rounded-lg shadow-md uppercase tracking-wider">Teacher</span>
|
||||
{{else}}
|
||||
<span class="px-2.5 py-1 bg-emerald-500 text-white text-[10px] font-black rounded-lg shadow-md uppercase tracking-wider">Student</span>
|
||||
{{/match}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{!-- 2. 信息内容区域 --}}
|
||||
<div class="p-5 flex-1 flex flex-col {{#match primary_tag.slug "=" "teacher-profile"}}bg-slate-50/50{{else}}bg-white{{/match}}">
|
||||
|
||||
{{!-- 姓名标题 --}}
|
||||
<div class="mb-2">
|
||||
<h4 class="text-xl font-bold text-gray-900 group-hover:text-blue-600 transition-colors truncate">
|
||||
{{title}}
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
{{!-- 摘要/头衔展示 --}}
|
||||
<div class="flex-1">
|
||||
{{#if custom_excerpt}}
|
||||
<p class="text-sm text-gray-500 line-clamp-2 italic leading-relaxed">
|
||||
{{#match primary_tag.slug "=" "teacher-profile"}}
|
||||
<span class="not-italic font-medium text-blue-600/70">Position:</span>
|
||||
{{else}}
|
||||
“
|
||||
{{/match}}
|
||||
{{custom_excerpt}}
|
||||
{{#match primary_tag.slug "!==" "teacher-profile"}}”{{/match}}
|
||||
</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{!-- 底部元数据栏 --}}
|
||||
<div class="flex items-center justify-between mt-5 pt-4 border-t border-gray-100/80">
|
||||
{{!-- 分类/学科标签 --}}
|
||||
<span class="text-[11px] font-bold text-gray-400 flex items-center gap-2 uppercase tracking-tight">
|
||||
{{#if primary_tag}}
|
||||
<span class="w-2 h-2 rounded-full {{#match primary_tag.slug "=" "teacher-profile"}}bg-blue-500 animate-pulse{{else}}bg-emerald-500{{/match}}"></span>
|
||||
{{primary_tag.name}}
|
||||
{{else}}
|
||||
<span class="w-2 h-2 rounded-full bg-gray-300"></span>
|
||||
档案库
|
||||
{{/if}}
|
||||
</span>
|
||||
|
||||
{{!-- 悬浮出现的动作提示 --}}
|
||||
<span class="text-[11px] text-blue-600 font-black opacity-0 group-hover:opacity-100 transition-all transform translate-x-2 group-hover:translate-x-0">
|
||||
PROFILE →
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
145
post.hbs
Normal file
145
post.hbs
Normal file
@@ -0,0 +1,145 @@
|
||||
{{!< default}}
|
||||
|
||||
{{#post}}
|
||||
<main class="min-h-screen bg-white pb-24 pt-24">
|
||||
|
||||
{{!-- 顶部导航 --}}
|
||||
<div class="container mx-auto max-w-7xl px-4 sm:px-6 mb-8">
|
||||
<a href="{{@site.url}}" class="group inline-flex items-center text-sm font-medium text-slate-500 hover:text-slate-900 transition-colors">
|
||||
<svg class="mr-1 h-4 w-4 transition-transform group-hover:-translate-x-1" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
||||
</svg>
|
||||
返回首页
|
||||
</a>
|
||||
</div>
|
||||
<link rel="stylesheet" type="text/css" href="{{asset "css/post.css"}}" />
|
||||
<article class="container mx-auto max-w-7xl px-4 sm:px-6 {{post_class}}">
|
||||
|
||||
{{!-- 1. 封面图 --}}
|
||||
{{#if feature_image}}
|
||||
<div class="mb-12 overflow-hidden rounded-2xl bg-slate-100">
|
||||
<img
|
||||
class="w-full h-auto object-cover max-h-[70vh]"
|
||||
srcset="{{img_url feature_image size="s"}} 300w,
|
||||
{{img_url feature_image size="m"}} 600w,
|
||||
{{img_url feature_image size="l"}} 1000w,
|
||||
{{img_url feature_image size="xl"}} 2000w"
|
||||
sizes="(min-width: 1200px) 1200px, 90vw"
|
||||
src="{{img_url feature_image size="xl"}}"
|
||||
alt="{{#if feature_image_alt}}{{feature_image_alt}}{{else}}{{title}}{{/if}}"
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{!-- 内容容器 --}}
|
||||
<div class="mx-auto max-w-5xl">
|
||||
|
||||
{{!-- 文章头部 --}}
|
||||
<header class="mb-10 border-b border-slate-100 pb-10">
|
||||
<div class="mb-6 flex flex-wrap items-center gap-3">
|
||||
{{#if primary_tag}}
|
||||
{{#primary_tag}}
|
||||
{{!-- 这里使用 tag-{{slug}} 类名,你可以在 CSS 中定义不同分类的颜色 --}}
|
||||
<a href="{{url}}" class="inline-flex items-center rounded-full px-3 py-1 text-xs font-semibold tag-{{slug}} bg-slate-100 text-slate-700">
|
||||
<svg class="mr-1.5 h-3 w-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
|
||||
</svg>
|
||||
{{name}}
|
||||
</a>
|
||||
{{/primary_tag}}
|
||||
{{/if}}
|
||||
|
||||
<span class="inline-flex items-center text-sm text-slate-500">
|
||||
<svg class="mr-1.5 h-3.5 w-3.5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
||||
</svg>
|
||||
<time datetime="{{date format="YYYY-MM-DD"}}">{{date format="YYYY年MM月DD日"}}</time>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h1 class="mb-6 text-3xl font-bold leading-tight text-slate-900 sm:text-4xl md:text-6xl">
|
||||
{{title}}
|
||||
</h1>
|
||||
|
||||
{{#if custom_excerpt}}
|
||||
<p class="text-xl leading-relaxed text-slate-600 md:text-2xl">
|
||||
{{custom_excerpt}}
|
||||
</p>
|
||||
{{/if}}
|
||||
</header>
|
||||
|
||||
{{!-- 正文区域 --}}
|
||||
<div class="gh-content gh-canvas max-w-none">
|
||||
{{content}}
|
||||
</div>
|
||||
|
||||
{{!-- 底部装饰 --}}
|
||||
<div class="mt-16 flex items-center justify-between border-t border-slate-100 pt-8">
|
||||
<div class="text-sm text-slate-400 flex items-center">
|
||||
<svg class="mr-1.5 h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
完 · 阅读时长 {{reading_time seconds="< 1 分钟" minutes="% 分钟"}}
|
||||
</div>
|
||||
|
||||
{{!-- 这里插入分享按钮 --}}
|
||||
<div class="relative inline-block" id="share-component">
|
||||
<button id="share-btn" class="group flex items-center gap-2 rounded-full bg-slate-50 px-5 py-2 text-sm font-semibold text-slate-600 hover:bg-blue-600 hover:text-white transition-all duration-300 active:scale-95">
|
||||
<svg class="h-4 w-4 transition-transform group-hover:rotate-12" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z" />
|
||||
</svg>
|
||||
分享
|
||||
</button>
|
||||
|
||||
{{!-- 下拉面板 --}}
|
||||
<div id="share-panel" class="hidden absolute right-0 bottom-full mb-3 w-64 origin-bottom-right rounded-2xl bg-white p-4 shadow-2xl border border-slate-100 z-50">
|
||||
<div class="flex items-center justify-between mb-4 px-1">
|
||||
<span class="text-sm font-bold text-slate-800">分享至</span>
|
||||
<button id="close-share" class="p-1 hover:bg-slate-100 rounded-full transition">
|
||||
<svg class="h-4 w-4 text-slate-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /></svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="grid grid-cols-4 gap-2">
|
||||
{{!-- 微博 --}}
|
||||
<a href="https://service.weibo.com/share/share.php?url={{url absolute="true"}}&title={{encode title}}" target="_blank" class="flex flex-col items-center gap-1.5 hover:opacity-80">
|
||||
<div class="w-11 h-11 rounded-2xl bg-red-50 text-red-500 flex items-center justify-center font-bold">微</div>
|
||||
<span class="text-[11px] font-medium text-slate-500">微博</span>
|
||||
</a>
|
||||
{{!-- QQ --}}
|
||||
<a href="https://connect.qq.com/widget/shareqq/index.html?url={{url absolute="true"}}&title={{encode title}}" target="_blank" class="flex flex-col items-center gap-1.5 hover:opacity-80">
|
||||
<div class="w-11 h-11 rounded-2xl bg-blue-50 text-blue-500 flex items-center justify-center font-bold">Q</div>
|
||||
<span class="text-[11px] font-medium text-slate-500">QQ</span>
|
||||
</a>
|
||||
{{!-- 微信二维码 --}}
|
||||
<a href="https://api.qrserver.com/v1/create-qr-code/?size=300x300&data={{url absolute="true"}}" target="_blank" class="flex flex-col items-center gap-1.5 hover:opacity-80">
|
||||
<div class="w-11 h-11 rounded-2xl bg-green-50 text-green-600 flex items-center justify-center">
|
||||
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v1m6 11h2m-6 0h-2v4m0-11v3m0 0h.01M12 12h4.01M16 20h4M4 12h4m12 0h.01M5 8h2a1 1 0 001-1V5a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1zm12 0h2a1 1 0 001-1V5a1 1 0 00-1-1h-2a1 1 0 00-1 1v2a1 1 0 001 1zM5 20h2a1 1 0 001-1v-2a1 1 0 00-1-1H5a1 1 0 00-1 1v2a1 1 0 001 1z" /></svg>
|
||||
</div>
|
||||
<span class="text-[11px] font-medium text-slate-500">微信</span>
|
||||
</a>
|
||||
{{!-- 复制 --}}
|
||||
<button id="copy-link" class="flex flex-col items-center gap-1.5 hover:opacity-80">
|
||||
<div id="copy-icon-bg" class="w-11 h-11 rounded-2xl bg-slate-100 text-slate-600 flex items-center justify-center transition-colors">
|
||||
<svg id="copy-svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2" /></svg>
|
||||
<svg id="check-svg" class="hidden h-5 w-5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /></svg>
|
||||
</div>
|
||||
<span id="copy-text" class="text-[11px] font-medium text-slate-500">复制</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{!-- Ghost 原生评论区 --}}
|
||||
{{#if comments}}
|
||||
<section class="article-comments mt-16 pt-10 border-t border-slate-100">
|
||||
<h3 class="text-2xl font-bold text-slate-900 mb-8">参与讨论</h3>
|
||||
{{comments}}
|
||||
</section>
|
||||
{{/if}}
|
||||
</div>
|
||||
</article>
|
||||
</main>
|
||||
{{/post}}
|
||||
|
||||
<script src="{{asset "js/share.js"}}"></script>
|
||||
114
tag.hbs
Normal file
114
tag.hbs
Normal file
@@ -0,0 +1,114 @@
|
||||
{{!< default}}
|
||||
{{!-- 上面的代码块表示该页面嵌套在 default.hbs 模板中 --}}
|
||||
|
||||
<div class="min-h-screen bg-white pb-20">
|
||||
{{#tag}}
|
||||
<header class="pt-20 pb-12 border-b border-slate-100">
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
{{!--
|
||||
Ghost 的 Tag 颜色通常通过 slug 匹配类名
|
||||
或者在后台 Tag 设置里的 "Accent Color" 获取
|
||||
--}}
|
||||
<span class="px-4 py-1.5 rounded-full text-sm font-semibold tracking-wide
|
||||
{{#match slug "teacher"}}bg-purple-100 text-purple-800{{/match}}
|
||||
{{#match slug "student"}}bg-blue-100 text-blue-800{{/match}}
|
||||
{{#match slug "event"}}bg-amber-100 text-amber-800{{/match}}
|
||||
{{#match slug "special-event"}}bg-red-100 text-red-800{{/match}}
|
||||
{{^match slug "teacher"}}{{^match slug "student"}}bg-slate-100 text-slate-800{{/match}}{{/match}}">
|
||||
{{name}}
|
||||
</span>
|
||||
</div>
|
||||
<h1 class="text-4xl md:text-5xl font-bold text-slate-900 tracking-tight mb-4">
|
||||
{{name}}专栏
|
||||
</h1>
|
||||
<p class="text-xl text-slate-500 max-w-3xl">
|
||||
{{#if description}}
|
||||
{{description}}
|
||||
{{else}}
|
||||
分享{{name}}的相关内容
|
||||
{{/if}}
|
||||
— 共 {{plural ../pagination.total empty='0 篇' singular='% 篇' plural='% 篇'}}
|
||||
</p>
|
||||
</div>
|
||||
</header>
|
||||
{{/tag}}
|
||||
|
||||
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mt-12">
|
||||
{{#if posts}}
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-8 gap-y-16">
|
||||
{{#foreach posts}}
|
||||
<article class="group cursor-pointer flex flex-col h-full {{post_class}}">
|
||||
<a href="{{url}}" class="block h-full">
|
||||
<div class="relative aspect-[16/10] w-full overflow-hidden bg-slate-100 rounded-lg mb-6">
|
||||
{{#if feature_image}}
|
||||
<img src="{{img_url feature_image size="m"}}" alt="{{title}}" class="w-full h-full object-cover transition-transform duration-700 group-hover:scale-105" />
|
||||
{{else}}
|
||||
<div class="w-full h-full bg-slate-200 flex items-center justify-center text-slate-400">No Image</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if featured}}
|
||||
<div class="absolute top-3 right-3 z-10">
|
||||
<span class="inline-flex items-center gap-1 px-3 py-1.5 rounded-full text-xs font-bold bg-red-500 text-white shadow-lg">
|
||||
📌 置顶
|
||||
</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="flex flex-col flex-1">
|
||||
<h2 class="text-2xl font-bold text-slate-900 mb-3 group-hover:text-blue-600 transition-colors leading-tight">
|
||||
{{title}}
|
||||
</h2>
|
||||
<p class="text-slate-500 line-clamp-3 leading-relaxed mb-4 flex-1">
|
||||
{{excerpt}}
|
||||
</p>
|
||||
<div class="text-blue-600 font-medium text-sm flex items-center gap-1 mt-auto">
|
||||
阅读全文 →
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</article>
|
||||
{{/foreach}}
|
||||
</div>
|
||||
|
||||
{{!-- 分页控制 --}}
|
||||
{{#if pagination.pages}}
|
||||
{{#if pagination.total}}
|
||||
<div class="mt-20 flex justify-center items-center gap-6 border-t border-slate-100 pt-10">
|
||||
{{#if pagination.prev}}
|
||||
<a href="{{page_url pagination.prev}}" class="px-6 py-2.5 rounded-full border border-slate-200 text-slate-600 hover:border-blue-600 hover:text-blue-600 transition-colors text-sm font-medium">
|
||||
← 上一页
|
||||
</a>
|
||||
{{else}}
|
||||
<span class="px-6 py-2.5 rounded-full border border-slate-100 text-slate-300 cursor-not-allowed text-sm font-medium">
|
||||
← 上一页
|
||||
</span>
|
||||
{{/if}}
|
||||
|
||||
<span class="text-slate-500 font-medium text-sm">
|
||||
第 <span className="text-slate-900">{{pagination.page}}</span> / {{pagination.pages}} 页
|
||||
</span>
|
||||
|
||||
{{#if pagination.next}}
|
||||
<a href="{{page_url pagination.next}}" class="px-6 py-2.5 rounded-full border border-slate-200 text-slate-600 hover:border-blue-600 hover:text-blue-600 transition-colors text-sm font-medium">
|
||||
下一页 →
|
||||
</a>
|
||||
{{else}}
|
||||
<span class="px-6 py-2.5 rounded-full border border-slate-100 text-slate-300 cursor-not-allowed text-sm font-medium">
|
||||
下一页 →
|
||||
</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{else}}
|
||||
<div class="py-20 text-center">
|
||||
<p class="text-slate-400 text-lg">该分类下暂无文章</p>
|
||||
<a href="{{@site.url}}" class="mt-4 inline-block text-slate-900 font-medium hover:underline">
|
||||
返回首页
|
||||
</a>
|
||||
</div>
|
||||
{{/if}}
|
||||
</main>
|
||||
</div>
|
||||
Reference in New Issue
Block a user