File size: 7,144 Bytes
d82405d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
// 全局工具函数和初始化
document.addEventListener('DOMContentLoaded', function() {
// 等待 marked 库加载完成
setTimeout(() => {
// 初始化搜索功能
initializeSearch();
// 初始化Markdown渲染
initializeMarkdownRendering();
// 初始化返回顶部按钮
initializeScrollToTop();
// 初始化移动端导航菜单
initializeMobileNavigation();
}, 100);
});
// 搜索功能实现
function initializeSearch() {
const searchInput = document.querySelector('.search-input');
const searchResults = document.querySelector('.search-results');
if (searchInput) {
searchInput.addEventListener('input', debounce(async (event) => {
const searchTerm = event.target.value.trim().toLowerCase();
if (searchTerm.length < 2) {
searchResults.style.display = 'none';
return;
}
try {
const response = await fetch(`/api/search?q=${encodeURIComponent(searchTerm)}`);
const data = await response.json();
if (data.articles.length > 0) {
displaySearchResults(data.articles, searchResults);
} else {
searchResults.innerHTML = '<div class="no-results">没有找到相关文章</div>';
}
searchResults.style.display = 'block';
} catch (error) {
console.error('搜索出错:', error);
searchResults.innerHTML = '<div class="error">搜索服务暂时不可用</div>';
}
}, 300));
// 点击其他区域关闭搜索结果
document.addEventListener('click', (event) => {
if (!event.target.closest('.search-container')) {
searchResults.style.display = 'none';
}
});
}
}
// Markdown渲染初始化
function initializeMarkdownRendering() {
if (typeof marked === 'undefined') {
console.error('Marked library not loaded');
return;
}
// 配置 marked
marked.use({
breaks: true,
gfm: true
});
const markdownElements = document.querySelectorAll('.markdown-body');
markdownElements.forEach(element => {
// 检查元素是否已经被渲染过
if (element.dataset.rendered) {
return;
}
try {
// 标记为已渲染
element.dataset.rendered = 'true';
// 如果内容是通过 Flask 的 markdown 过滤器渲染的,就不需要再次渲染
if (!element.classList.contains('server-rendered')) {
const content = element.textContent;
element.innerHTML = marked(content);
}
// 处理代码块
element.querySelectorAll('pre code').forEach(block => {
if (typeof hljs !== 'undefined') {
hljs.highlightElement(block);
}
});
// 为图片添加点击放大功能
element.querySelectorAll('img').forEach(img => {
img.addEventListener('click', () => {
openImageViewer(img.src);
});
});
} catch (error) {
console.error('Markdown渲染错误:', error);
}
});
}
// 返回顶部按钮实现
function initializeScrollToTop() {
const scrollTopButton = document.createElement('button');
scrollTopButton.className = 'scroll-top-button';
scrollTopButton.innerHTML = '↑';
document.body.appendChild(scrollTopButton);
window.addEventListener('scroll', debounce(() => {
if (window.scrollY > 500) {
scrollTopButton.classList.add('visible');
} else {
scrollTopButton.classList.remove('visible');
}
}, 100));
scrollTopButton.addEventListener('click', () => {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
}
// 移动端导航菜单实现
function initializeMobileNavigation() {
const menuButton = document.querySelector('.menu-button');
const navLinks = document.querySelector('.nav-links');
if (menuButton && navLinks) {
menuButton.addEventListener('click', () => {
navLinks.classList.toggle('active');
menuButton.classList.toggle('active');
});
// 点击导航链接后关闭菜单
navLinks.querySelectorAll('a').forEach(link => {
link.addEventListener('click', () => {
navLinks.classList.remove('active');
menuButton.classList.remove('active');
});
});
}
}
// 图片查看器实现
function openImageViewer(imageSrc) {
const viewer = document.createElement('div');
viewer.className = 'image-viewer';
viewer.innerHTML = `
<div class="image-viewer-content">
<img src="${imageSrc}" alt="预览图片">
<button class="close-button">×</button>
</div>
`;
viewer.addEventListener('click', (event) => {
if (event.target === viewer || event.target.className === 'close-button') {
viewer.remove();
}
});
document.body.appendChild(viewer);
}
// 工具函数: 防抖
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// 工具函数: 搜索结果显示
function displaySearchResults(articles, container) {
container.innerHTML = articles.map(article => `
<a href="/article/${article.slug}" class="search-result-item">
<h3>${highlightSearchTerm(article.title)}</h3>
${article.summary ? `<p>${highlightSearchTerm(article.summary)}</p>` : ''}
<span class="article-date">${formatDate(article.created_at)}</span>
</a>
`).join('');
}
// 工具函数: 高亮搜索词
function highlightSearchTerm(text, searchTerm) {
if (!searchTerm) return text;
const regex = new RegExp(searchTerm, 'gi');
return text.replace(regex, match => `<mark>${match}</mark>`);
}
// 工具函数: 日期格式化
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit'
});
}
// 错误处理函数
function handleError(error, container) {
console.error('发生错误:', error);
container.innerHTML = `
<div class="error-message">
<p>抱歉,发生了一些错误</p>
<button onclick="window.location.reload()">刷新页面</button>
</div>
`;
} |