first initial commit

This commit is contained in:
2026-02-07 22:27:31 +08:00
commit 07786f8eb0
18 changed files with 6035 additions and 0 deletions

299
assets/css/global.css Normal file
View 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

File diff suppressed because it is too large Load Diff

1
assets/css/input.css Normal file
View File

@@ -0,0 +1 @@
@import "tailwindcss";

153
assets/css/post.css Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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">更多 &rarr;</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">更多 &rarr;</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">更多 &rarr;</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">更多 &rarr;</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">查看全部 &rarr;</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">查看全部 &rarr;</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
View 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
View 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
View 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
View 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
View 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
View 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">
阅读全文 &rarr;
</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>