/** * App模块 - 主应用入口和路由 */ const App = (function() { // ========== 路由处理 ========== function initRouter() { // 监听hash变化 $(window).on('hashchange', handleRoute); // 初始路由 handleRoute(); } // 记录上一个路由,用于返回 let lastHash = '#/shelf'; let lastNonReadHash = '#/shelf'; function handleRoute() { const hash = window.location.hash || '#/shelf'; const prevHash = lastHash; lastHash = hash; if (!hash.includes('#/read/')) { lastNonReadHash = hash; } const [path, ...params] = hash.slice(2).split('/'); // 更新导航状态 updateNavState(path); // 路由匹配 switch (path) { case 'shelf': showPage('shelf'); Pages.renderShelf(); break; case 'explore': showPage('explore'); Pages.renderExplore(); break; case 'history': showPage('history'); Pages.renderHistory(); break; case 'bookmarks': showPage('bookmarks'); Pages.renderBookmarks(); break; case 'novel': if (params[0]) { showPage('detail'); Pages.renderDetail(params[0]); } break; case 'read': if (params[0]) { const chapterIndex = parseInt(params[1]) || 0; // 记录进入阅读页前的路由,用于返回时回到详情页或来源页 const backRoute = prevHash && !prevHash.includes('#/read/') ? prevHash : lastNonReadHash; Store.setState('prevRouteBeforeRead', backRoute || '#/shelf'); showPage('reader'); Reader.init(params[0], chapterIndex); } break; default: window.location.hash = '#/shelf'; } } function showPage(page) { // 重置UI状态 if (page === 'reader') { // 阅读器模式:隐藏所有导航 $('#app-header').addClass('header-hidden'); $('#app-nav').addClass('nav-hidden'); $('#app-content').addClass('reader-mode').removeClass('detail-mode'); $('#reader-toolbar').removeClass('hidden'); } else if (page === 'detail') { // 详情页:隐藏底部导航(有自己的操作栏) $('#app-header').removeClass('header-hidden'); $('#app-nav').addClass('nav-hidden'); $('#app-content').removeClass('reader-mode').addClass('detail-mode'); $('#reader-toolbar').addClass('hidden'); } else { // 普通页面:显示所有导航 $('#app-header').removeClass('header-hidden'); $('#app-nav').removeClass('nav-hidden'); $('#app-content').removeClass('reader-mode').removeClass('detail-mode'); $('#reader-toolbar').addClass('hidden'); } // 更新页面标题 const titles = { 'shelf': '墨香阁', 'explore': '发现', 'history': '阅读历史', 'bookmarks': '我的书签', 'detail': '小说详情', 'reader': '' }; $('#page-title').text(titles[page] || '墨香阁'); // 显示/隐藏返回按钮 if (['detail'].includes(page)) { $('#btn-back').removeClass('hidden'); $('#btn-search').addClass('hidden'); } else if (page === 'reader') { // 阅读器有自己的返回按钮 } else { $('#btn-back').addClass('hidden'); $('#btn-search').removeClass('hidden'); } // 滚动到顶部 if (page !== 'reader') { window.scrollTo(0, 0); } } function updateNavState(page) { $('.nav-item').removeClass('active'); $(`.nav-item[data-page="${page}"]`).addClass('active'); } function navigate(hash) { window.location.hash = hash; } // ========== 事件绑定 ========== function bindEvents() { // 导航点击 $('.nav-item').on('click', function(e) { e.preventDefault(); const page = $(this).data('page'); navigate('#/' + page); }); // 返回按钮 - 使用浏览器历史返回 $('#btn-back').on('click', function() { window.history.back(); }); // 搜索按钮 $('#btn-search').on('click', function() { showSearch(); }); // 菜单按钮(暂时不使用) $('#btn-menu').on('click', function() { Toast.show('更多功能开发中...'); }); // 搜索面板 $('#search-back').on('click', hideSearch); // 搜索输入 - 实时搜索 $('#search-input').on('input', debounce(function() { const keyword = $(this).val().trim(); if (keyword) { $('#search-clear').removeClass('hidden'); doSearch(keyword); } else { $('#search-clear').addClass('hidden'); $('#search-results').html('
输入关键词搜索小说
'); } }, 500)); // 搜索输入 - 回车确认搜索 $('#search-input').on('keypress', function(e) { if (e.which === 13) { const keyword = $(this).val().trim(); if (keyword) { doSearch(keyword); } } }); $('#search-clear').on('click', function() { $('#search-input').val('').focus(); $(this).addClass('hidden'); $('#search-results').html('
输入关键词搜索小说
'); renderSearchTags(); // 重新显示搜索历史和标签 }); // 搜索历史点击 $(document).on('click', '#search-tags .history-tag', function(e) { // 检查是否点击了删除按钮 if ($(e.target).hasClass('remove-history') || $(e.target).closest('.remove-history').length) { return; } const keyword = $(this).data('keyword'); $('#search-input').val(keyword); doSearch(keyword); }); // 删除单条搜索历史 $(document).on('click', '.remove-history', function(e) { e.stopPropagation(); const keyword = $(this).data('keyword'); Store.removeSearchHistory(keyword); renderSearchTags(); }); // 清空搜索历史 $(document).on('click', '#clear-search-history', function() { Store.clearSearchHistory(); renderSearchTags(); Toast.show('搜索历史已清空'); }); // 搜索页热门标签点击 $(document).on('click', '#search-tags .tag-btn:not(.history-tag)', function() { const tag = $(this).data('tag'); if (tag) { $('#search-input').val(''); Pages.renderSearchByTag(tag); } }); // 发现页标签点击 $(document).on('click', '#tags-cloud .tag-btn', function() { $('.tag-btn').removeClass('active'); $(this).addClass('active'); const tag = $(this).data('tag'); Pages.loadNovels(0, tag, null); // 清除作者筛选,只按标签筛选 }); // 加载更多(追加模式,保持当前筛选条件) $(document).on('click', '.load-more-btn', function() { if (Pages.hasMore()) { Pages.loadNovels(Pages.currentPage() + 1, null, true); // append=true 会使用已保存的标签 } }); // 搜索结果中的小说卡片点击(优先处理) $(document).on('click', '#search-results .novel-card', function(e) { e.preventDefault(); e.stopPropagation(); const novelId = $(this).data('novel-id'); if (novelId) { hideSearch(); // 使用setTimeout确保搜索面板完全隐藏后再跳转 setTimeout(() => { navigate('#/novel/' + novelId); }, 100); } }); // 点击作者名字,筛选该作者的作品 $(document).on('click', '.clickable-author', function(e) { e.preventDefault(); e.stopPropagation(); const author = $(this).data('author'); if (author) { Pages.searchByAuthor(author); } }); // 其他页面的小说卡片点击 $(document).on('click', '.novel-card, .novel-grid-card, .shelf-item', function(e) { // 如果是搜索结果中的,上面的事件已处理,这里跳过 if ($(this).closest('#search-results').length) return; // 如果点击的是作者名字,跳过(上面的事件已处理) if ($(e.target).hasClass('clickable-author') || $(e.target).closest('.clickable-author').length) return; e.preventDefault(); if ($(e.target).closest('button').length) return; const novelId = $(this).data('novel-id') || $(this).closest('[data-novel-id]').data('novel-id'); if (novelId) { navigate('#/novel/' + novelId); } }); // 章节点击 $(document).on('click', '.chapters-preview .chapter-item', function() { const novelId = $(this).data('novel-id'); const index = $(this).data('chapter-index'); navigate('#/read/' + novelId + '/' + index); }); // 查看全部章节 $(document).on('click', '#view-all-chapters', function() { // 显示目录面板 const novelId = $(this).data('novel-id'); showAllChapters(novelId); }); // 历史记录点击 $(document).on('click', '.history-item', function(e) { if ($(e.target).closest('button').length) return; const novelId = $(this).data('novel-id'); const index = $(this).data('chapter-index'); navigate('#/read/' + novelId + '/' + index); }); // 书签点击 $(document).on('click', '.bookmark-item', function(e) { if ($(e.target).closest('button').length) return; const novelId = $(this).data('novel-id'); const index = $(this).data('chapter-index'); navigate('#/read/' + novelId + '/' + index); }); // 删除历史 $(document).on('click', '.delete-history-btn', async function(e) { e.stopPropagation(); const novelId = $(this).data('novel-id'); try { await API.deleteHistory(novelId, Store.getUserId()); $(this).closest('.history-item').fadeOut(300, function() { $(this).remove(); }); Toast.show('已删除'); } catch (error) { Toast.show('删除失败'); } }); // 删除书签 $(document).on('click', '.delete-bookmark-btn', async function(e) { e.stopPropagation(); const bookmarkId = $(this).data('bookmark-id'); try { await API.deleteBookmark(bookmarkId, Store.getUserId()); $(this).closest('.bookmark-item').fadeOut(300, function() { $(this).remove(); }); Toast.show('已删除'); } catch (error) { Toast.show('删除失败'); } }); // 清空历史 $(document).on('click', '#clear-history', function() { if (confirm('确定要清空所有阅读历史吗?')) { // 这里需要后端支持批量删除,暂时提示 Toast.show('功能开发中...'); } }); // 加入/移出书架 $(document).on('click', '#btn-shelf', async function() { const $btn = $(this); const novelId = $btn.data('novel-id'); const inShelf = $btn.hasClass('in-shelf'); try { if (inShelf) { await API.removeFromShelf(novelId, Store.getUserId()); Store.removeFromShelfCache(novelId); $btn.removeClass('in-shelf'); $btn.find('svg path').attr('d', 'M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z'); $btn.contents().last()[0].textContent = '加入书架'; Toast.show('已从书架移除'); // 更新当前页面所有相关书籍卡片的书架标识 $(`.novel-card[data-novel-id="${novelId}"] .shelf-badge, .novel-grid-card[data-novel-id="${novelId}"] .shelf-badge`).remove(); } else { await API.addToShelf({ userId: Store.getUserId(), novelId }); Store.addToShelfCache(novelId); $btn.addClass('in-shelf'); $btn.find('svg path').attr('d', 'M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z'); $btn.contents().last()[0].textContent = '已在书架'; Toast.show('已加入书架'); // 更新当前页面所有相关书籍卡片的书架标识 // 列表视图显示文字,网格视图只显示图标 const badgeHtmlList = '
已收藏
'; const badgeHtmlGrid = '
'; $(`.novel-card[data-novel-id="${novelId}"] .novel-cover`).append(badgeHtmlList); $(`.novel-grid-card[data-novel-id="${novelId}"] .novel-cover`).append(badgeHtmlGrid); } } catch (error) { Toast.show('操作失败'); } }); // 开始/继续阅读 $(document).on('click', '#btn-read', function() { const novelId = $(this).data('novel-id'); const index = $(this).data('chapter-index') || 0; navigate('#/read/' + novelId + '/' + index); }); // 展开简介 $(document).on('click', '#expand-synopsis', function() { const $text = $('#synopsis-text'); $text.toggleClass('text-clamp-3'); $(this).text($text.hasClass('text-clamp-3') ? '展开' : '收起'); }); // 加载更多按钮(使用事件委托,在pages.js中也有绑定,这里作为备用) $(document).on('click', '.load-more-btn', function() { if (Pages.hasMore()) { Pages.loadNovels(Pages.currentPage() + 1); } }); } // ========== 搜索功能 ========== async function showSearch() { $('#search-panel').removeClass('hidden'); $('#search-input').val('').focus(); $('#search-results').html('
输入关键词搜索小说
'); // 渲染搜索历史和热门标签 renderSearchTags(); } async function renderSearchTags() { let html = ''; // 搜索历史 const history = Store.getSearchHistory(); if (history.length > 0) { html += '
'; html += '
搜索历史'; html += '
'; html += '
'; history.forEach(keyword => { html += ``; }); html += '
'; } // 热门标签 try { const tags = await API.getPopularTags(10); if (tags.length > 0) { html += '
'; html += '
热门标签
'; tags.forEach(tag => { html += ``; }); html += '
'; } } catch (error) { console.error('加载标签失败'); } $('#search-tags').html(html); } function doSearch(keyword) { if (!keyword || !keyword.trim()) return; keyword = keyword.trim(); // 保存到搜索历史 Store.addSearchHistory(keyword); // 执行搜索 Pages.renderSearchResults(keyword); } function hideSearch() { $('#search-panel').addClass('hidden'); } // ========== 全部章节面板 ========== async function showAllChapters(novelId) { $('#catalog-panel').removeClass('hidden'); const $list = $('#catalog-list'); $list.html('
'); try { let allChapters = []; let page = 0; let hasMore = true; while (hasMore) { const data = await API.getChapters(novelId, { page, size: 100 }); allChapters = allChapters.concat(data.content || []); hasMore = !data.last; page++; if (page > 50) break; } let html = ''; allChapters.forEach(chapter => { html += `
${chapter.index + 1} ${chapter.title}
`; }); $list.html(html); // 绑定点击事件 $list.find('.chapter-item').on('click', function() { const idx = $(this).data('chapter-index'); $('#catalog-panel').addClass('hidden'); navigate('#/read/' + novelId + '/' + idx); }); } catch (error) { $list.html('

加载失败

'); } // 关闭按钮 $('#close-catalog').off('click').on('click', () => $('#catalog-panel').addClass('hidden')); $('.catalog-overlay').off('click').on('click', () => $('#catalog-panel').addClass('hidden')); // 搜索过滤 $('#catalog-search-input').off('input').on('input', function() { const keyword = $(this).val().toLowerCase(); $list.find('.chapter-item').each(function() { const title = $(this).find('.chapter-title').text().toLowerCase(); $(this).toggle(title.includes(keyword)); }); }); } // ========== 工具方法 ========== function debounce(func, wait) { let timeout; return function(...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; } // ========== 初始化 ========== function init() { // 初始化主题 Store.initTheme(); // 绑定事件 bindEvents(); // 初始化路由 initRouter(); // 隐藏加载动画 setTimeout(() => { $('#app-loader').addClass('fade-out'); setTimeout(() => { $('#app-loader').remove(); }, 400); }, 500); // 注册Service Worker registerServiceWorker(); } function registerServiceWorker() { if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js') .then(reg => { console.log('Service Worker 注册成功'); }) .catch(err => { console.log('Service Worker 注册失败:', err); }); } } // 公开API return { init, navigate }; })(); /** * Toast提示组件 */ const Toast = (function() { let timer = null; function show(message, duration = 2000) { const $toast = $('#toast'); $toast.text(message).addClass('show'); clearTimeout(timer); timer = setTimeout(() => { $toast.removeClass('show'); }, duration); } return { show }; })(); // 页面加载完成后初始化 $(document).ready(function() { App.init(); });