|
|
| document.addEventListener('DOMContentLoaded', function() {
|
|
|
| setTimeout(() => {
|
|
|
| initializeSearch();
|
|
|
| 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';
|
| }
|
| });
|
| }
|
| }
|
|
|
|
|
| function initializeMarkdownRendering() {
|
| if (typeof marked === 'undefined') {
|
| console.error('Marked library not loaded');
|
| return;
|
| }
|
|
|
|
|
| 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';
|
|
|
|
|
| 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>
|
| `;
|
| } |