/**
* 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);
// 搜索输入 - 实时搜索建议
let searchSuggestionTimer = null;
$('#search-input').on('input', function() {
const keyword = $(this).val().trim();
if (keyword) {
$('#search-clear').removeClass('hidden');
// 显示搜索建议
clearTimeout(searchSuggestionTimer);
searchSuggestionTimer = setTimeout(() => {
loadSearchSuggestions(keyword);
}, 300);
} else {
$('#search-clear').addClass('hidden');
$('#search-results').html('
输入关键词搜索小说
');
$('#search-suggestions').html('').addClass('hidden');
// 清空时重新显示搜索历史和标签
renderSearchTags();
}
});
// 搜索输入 - 回车确认搜索
$('#search-input').on('keydown', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
const keyword = $(this).val().trim();
if (keyword) {
$('#search-suggestions').html('').addClass('hidden');
doSearch(keyword);
}
} else if (e.key === 'Escape') {
$('#search-suggestions').html('').addClass('hidden');
}
});
// 搜索输入 - 回车确认搜索
$('#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') ? '展开' : '收起');
});
// 点击搜索建议项
$(document).on('click', '.suggestion-item', function(e) {
e.preventDefault();
e.stopPropagation();
const novelId = $(this).data('novel-id');
const author = $(this).data('author');
if (novelId) {
// 跳转到小说详情
hideSearch();
setTimeout(() => {
navigate('#/novel/' + novelId);
}, 100);
} else if (author) {
// 按作者搜索
$('#search-input').val(author);
$('#search-suggestions').html('').addClass('hidden');
doSearch(author);
}
});
// 加载更多按钮(使用事件委托,在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('输入关键词搜索小说
');
$('#search-suggestions').html('').addClass('hidden');
// 渲染搜索历史和热门标签
renderSearchTags();
}
// 加载搜索建议
async function loadSearchSuggestions(keyword) {
if (!keyword || keyword.length < 1) {
$('#search-suggestions').html('').addClass('hidden');
return;
}
try {
console.log('正在加载搜索建议,关键词:', keyword);
const suggestions = await API.getSearchSuggestions(keyword, 8);
console.log('搜索建议结果:', suggestions);
if (!suggestions || suggestions.length === 0) {
$('#search-suggestions').html('').addClass('hidden');
return;
}
let html = '';
suggestions.forEach(suggestion => {
if (!suggestion || !suggestion.text) return;
const highlightText = highlightKeyword(suggestion.text, keyword);
if (suggestion.novelId) {
html += `
${suggestion.type || '书名'}
${highlightText}
`;
} else {
html += `
${suggestion.type || '作者'}
${highlightText}
`;
}
});
html += '
';
$('#search-suggestions').html(html).removeClass('hidden');
console.log('搜索建议已显示,HTML长度:', html.length);
} catch (error) {
console.error('加载搜索建议失败:', error);
$('#search-suggestions').html('').addClass('hidden');
}
}
// 高亮关键词
function highlightKeyword(text, keyword) {
if (!text || !keyword) return text;
const regex = new RegExp(`(${keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
return text.replace(regex, '$1');
}
async function renderSearchTags() {
let html = '';
// 搜索历史
const history = Store.getSearchHistory();
if (history.length > 0) {
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();
});