重构章节系统:自动从JSON文件生成目录,移除chapters.json依赖

This commit is contained in:
李策 2026-03-26 00:52:50 +08:00
parent 526c805679
commit 4607b94187
4 changed files with 231 additions and 258 deletions

View File

@ -119,28 +119,18 @@
</div>
</footer>
<script src="js/app.js?v=3"></script>
<script src="js/app.js?v=4"></script>
<script>
// 动态加载章节数据(禁用缓存)
async function loadChaptersData() {
try {
const response = await fetch('data/chapters.json?t=' + Date.now());
const data = await response.json();
window.chaptersData = data.chapters;
document.getElementById('totalChapters').textContent = data.total;
renderChaptersList();
setupFilters();
setupSearch();
updateReadingProgress();
} catch (error) {
console.error('加载章节数据失败:', error);
// 降级使用硬编码数据
renderChaptersList();
setupFilters();
setupSearch();
updateReadingProgress();
// 页面加载完成后更新总章节数显示
document.addEventListener('DOMContentLoaded', async function() {
// 等待章节数据加载完成
await loadChaptersData();
// 更新总章节数显示
const totalEl = document.getElementById('totalChapters');
if (totalEl && window.chaptersData) {
totalEl.textContent = window.chaptersData.length;
}
}
});
// 章节页面特定逻辑
document.addEventListener('DOMContentLoaded', function() {

View File

@ -225,6 +225,6 @@
</div>
</footer>
<script src="js/app.js?v=2"></script>
<script src="js/app.js?v=4"></script>
</body>
</html>

View File

@ -1,100 +1,75 @@
// 章节数据
const chaptersData = [
{ id: 1, title: "第一章:洛兰", subtitle: "初入艾尔文防线", desc: "林克初到艾尔文防线,在洛兰森林遭遇哥布林,被阿甘左救下。", status: "已完结", date: "2026-03-20" },
{ id: 2, title: "第二章:洛兰深处", subtitle: "哥布林头目", desc: "林克在阿甘左指导下修炼剑术,深入洛兰深处击败哥布林头目。", status: "已完结", date: "2026-03-20" },
{ id: 3, title: "第三章:幽暗密林", subtitle: "猫妖的伏击", desc: "林克进入格兰之森深处的幽暗密林,遭遇猫妖伏击,历经苦战突破重围。", status: "已完结", date: "2026-03-23" },
{ id: 4, title: "第四章:幽暗密林深处", subtitle: "猫妖王", desc: "林克再次进入幽暗密林深处,遭遇猫妖王,揭开卡赞诅咒的部分真相。", status: "已完结", date: "2026-03-23" },
{ id: 5, title: "第五章:雷鸣废墟", subtitle: "闪电哥布林", desc: "林克挑战雷鸣废墟,遭遇闪电哥布林,经历雷电危机。", status: "已完结", date: "2026-03-23" },
{ id: 6, title: "第六章:格拉卡", subtitle: "牛头王萨乌塔", desc: "林克进入格拉卡,面对强大的牛头王萨乌塔,激战突破自身极限。", status: "已完结", date: "2026-03-23" },
{ id: 7, title: "第七章:烈焰格拉卡", subtitle: "火女彼诺修", desc: "林克挑战火女彼诺修,她的血之诅咒让战斗更加凶险。", status: "已完结", date: "2026-03-23" },
{ id: 8, title: "第八章:冰霜幽暗密林", subtitle: "冰女克拉赫", desc: "为寻找赛丽亚身世线索,林克前往冰霜幽暗密林挑战冰女克拉赫。", status: "已完结", date: "2026-03-23" },
{ id: 9, title: "第九章:转职之路", subtitle: "选择剑魂", desc: "林克前往赫顿玛尔寻找GSD了解四大转职方向坚定选择剑魂之道。", status: "已完结", date: "2026-03-24" },
{ id: 10, title: "第十章:暗黑雷鸣废墟", subtitle: "转职试炼", desc: "林克在暗黑雷鸣废墟完成转职试炼,在死亡气息中验证自己的剑道。", status: "已完结", date: "2026-03-24" },
{ id: 11, title: "第十一章:剑魂转职仪式", subtitle: "武器大师", desc: "GSD为林克举行正式转职仪式详细讲解剑魂武器大师特性。", status: "已完结", date: "2026-03-24" },
{ id: 12, title: "第十二章:西海岸", subtitle: "天空之城入口", desc: "林克前往西海岸了解天空之城背景GSD决定同行。", status: "已完结", date: "2026-03-24" },
{ id: 13, title: "第十三章:龙人之塔", subtitle: "GSD带队", desc: "GSD亲自带队刷龙人之塔展现'开挂瞎子'的恐怖实力。", status: "已完结", date: "2026-03-24" },
{ id: 14, title: "第十四章:人偶玄关", subtitle: "觉醒技能", desc: "GSD带队刷人偶玄关展现阿修罗觉醒技能'暗天波动眼'。", status: "已完结", date: "2026-03-24" },
{ id: 15, title: "第十五章:石巨人塔", subtitle: "独立挑战", desc: "林克和赛丽亚独立挑战石巨人塔,面对黄金巨人普拉塔尼。", status: "已完结", date: "2026-03-24" },
{ id: 16, title: "第十六章:黑暗玄廊", subtitle: "光明与黑暗", desc: "回赫顿玛尔交任务,探索黑暗玄廊,击败天之驱逐者。", status: "已完结", date: "2026-03-24" },
{ id: 17, title: "第十七章:城主宫殿", subtitle: "光之城主", desc: "面对光之城主赛格哈特,净化光之核心,揭晓晨曦的来历。", status: "已完结", date: "2026-03-24" },
{ id: 18, title: "第十八章:番外·悬空城", subtitle: "光之骑士", desc: "探索天空之城最深处,恢复晨曦的光芒,获得光之骑士称号。", status: "已完结", date: "2026-03-24" },
{ id: 19, title: "第十九章:天帷巨兽·神殿外围", subtitle: "使徒的呼唤", desc: "前往天帷巨兽调查GBL教异变遭遇第二使徒罗特斯的精神控制。", status: "已完结", date: "2026-03-24" },
{ id: 20, title: "第二十章:树精丛林", subtitle: "禁忌实验", desc: "探索GBL教的秘密实验室发现使徒融合实验击败树精王。", status: "已完结", date: "2026-03-24" },
{ id: 21, title: "第二十一章:炼狱", subtitle: "火焰试炼", desc: "穿越夜叉栖息的炼狱,击败夜叉王,晨曦恢复七成。", status: "已完结", date: "2026-03-24" },
{ id: 22, title: "第二十二章:西海岸的闲暇", subtitle: "支线与成长", desc: "返回西海岸休整,完成支线任务,与卡坤互动,学会拔刀斩。", status: "已完结", date: "2026-03-24" },
{ id: 23, title: "第二十三章:极昼", subtitle: "天空之巅", desc: "赛丽亚因精神污染离队林克独自挑战极昼击败EX多尼尔。", status: "已完结", date: "2026-03-24" },
{ id: 24, title: "第二十四章:第一脊椎", subtitle: "圣光降临", desc: "圣骑士艾伦加入队伍,联手击败巨型黑章鱼,晨曦恢复九成。", status: "已完结", date: "2026-03-24" },
{ id: 25, title: "第二十五章:赫顿玛尔的准备", subtitle: "天界科技", desc: "凯丽制造抗精神干扰手环,为最终决战做准备。", status: "已完结", date: "2026-03-24" },
{ id: 26, title: "第二十六章:第二脊椎", subtitle: "使徒决战", desc: "最终BOSS罗特斯晨曦完全觉醒天帷巨兽篇完结。", status: "已完结", date: "2026-03-24" },
{ id: 27, title: "第二十七章:重逢的温柔", subtitle: "赛丽亚线", desc: "林克与赛丽亚久别重逢,两人感情突破,正式确立关系。", status: "已完结", date: "2026-03-24" },
{ id: 28, title: "第二十八章:暗精灵的委托", subtitle: "后宫建立", desc: "莎兰暧昧互动,奥菲利亚表白加入后宫,前往暗黑城新任务。", status: "已完结", date: "2026-03-24" },
{ id: 29, title: "第二十九章:阿法利亚营地", subtitle: "暗黑城篇开启", desc: "抵达阿法利亚营地,接受克伦特委托,后宫修罗场帐篷同眠。", status: "已完结", date: "2026-03-24" },
{ id: 30, title: "第三十章:浅栖之地", subtitle: "寻找摩根", desc: "探索浅栖之地,击败怨恨之摩根,发现夏普伦长老的阴谋。", status: "已完结", date: "2026-03-24" },
{ id: 31, title: "第三十一章:蜘蛛洞穴", subtitle: "猛龙断空斩", desc: "首次实战使用猛龙断空斩击败BOSS艾克洛索突破Lv.50。", status: "已完结", date: "2026-03-24" },
{ id: 32, title: "第三十二章:克伦特的委托", subtitle: "四把钥匙", desc: "返回营地汇报,接受克伦特委托,准备前往暗精灵墓地收集四把钥匙。", status: "已完结", date: "2026-03-24" },
{ id: 33, title: "第三十三章:暗精灵墓地·左翼守卫", subtitle: "奥菲利亚线", desc: "击败火焰骷髅将军获得第一把钥匙,奥菲利亚献身,后宫正式确立。", status: "已完结", date: "2026-03-24" },
{ id: 34, title: "第三十四章:暗精灵墓地·剩余三将军", subtitle: "三战连捷", desc: "连续击败冰冻、速度、剧毒三位骷髅将军,集齐四把钥匙。", status: "已完结", date: "2026-03-25" },
{ id: 35, title: "第三十五章:邪龙斯皮兹", subtitle: "远古邪龙", desc: "用四把钥匙解开封印击败暗精灵墓地最终BOSS邪龙斯皮兹彻底消灭远古邪恶。", status: "已完结", date: "2026-03-25" },
{ id: 36, title: "第三十六章:莎兰的探望", subtitle: "暧昧之夜", desc: "击败邪龙后回到西海岸,莎兰深夜单独约见林克,两人关系更进一步。", status: "已完结", date: "2026-03-25" },
{ id: 37, title: "第三十七章:暗影迷宫·影子剑士", subtitle: "剑士对决", desc: "进入暗影迷宫挑战影子剑士刹影,林克以光之剑对决暗影之剑,最终净化刹影。", status: "已完结", date: "2026-03-25" },
{ id: 38, title: "第三十八章:熔岩穴·泰坦之怒", subtitle: "熔岩巨人", desc: "挑战熔岩穴深处的熔岩巨人泰坦,林克找到核心弱点,以龙之力彻底消灭怪物。", status: "已完结", date: "2026-03-25" },
{ id: 39, title: "第三十九章:暗黑城入口·无头骑士", subtitle: "幻影剑舞", desc: "林克向GSD学习45级技能幻影剑舞挑战无头骑士以新剑技击败强敌。", status: "已完结", date: "2026-03-25" },
{ id: 40, title: "第四十章:万年雪山·冰心少年", subtitle: "冰心查理", desc: "抵达班图族营地,购买新武器细雪之舞,遭遇被冰龙侵蚀的查理,团队配合制服暴走。", status: "已完结", date: "2026-03-25" },
{ id: 41, title: "第四十一章:敏泰的加入", subtitle: "队伍调整", desc: "艾伦退场返回赫顿玛尔,敏泰加入队伍,奥菲利亚转为后勤,新队伍前往利库天井。", status: "已完结", date: "2026-03-25" },
{ id: 42, title: "第四十二章:山脊·野兽师", subtitle: "后宫互动", desc: "挑战山脊BOSS野兽师鲁乌格赛丽亚受伤深夜教导敏泰如何成为林克的女人。", status: "已完结", date: "2026-03-25" },
{ id: 43, title: "第四十三章:番外·雪山温泉", subtitle: "敏泰献身", desc: "班图族温泉中,赛丽亚引导敏泰向林克献身,三人共度温馨之夜。", status: "已完结", date: "2026-03-25" },
{ id: 44, title: "第四十四章:白色废墟", subtitle: "雪魈王", desc: "探索白色废墟,遭遇雪魈、冰精灵、被控制的班图族战士,配合击败雪魈王。", status: "已完结", date: "2026-03-25" },
{ id: 45, title: "第四十五章:冰雪宫殿", subtitle: "冰龙侍从", desc: "深入冰雪宫殿,遭遇冰蜘蛛、冰霜巨人,最终击败冰龙侍从,获得进入巢穴的资格。", status: "已完结", date: "2026-03-25" },
{ id: 46, title: "第四十六章:布万加修炼场", subtitle: "突破", desc: "在布万加修炼场接受训练,林克领悟山岳剑势,赛丽亚点燃心火,敏泰获得母亲骨杖。", status: "已完结", date: "2026-03-25" },
{ id: 47, title: "第四十七章:斯卡萨之巢·龙威", subtitle: "初战冰龙", desc: "首次挑战冰龙斯卡萨,巴卡尔三龙崽背景揭露,不敌撤退,奥尔卡、奥菲利亚、查理赶来支援。", status: "已完结", date: "2026-03-25" },
{ id: 48, title: "第四十八章:斯卡萨之巢·龙陨", subtitle: "雪山篇完结", desc: "全员集结决战冰龙斯卡萨,击破逆鳞,班图族解放,雪山篇落幕。", status: "已完结", date: "2026-03-25" },
{ id: 49, title: "第四十九章:重返赫顿玛尔", subtitle: "休整", desc: "返回赫顿玛尔休整,与艾伦重逢喝酒,莎兰献身,接受诺斯玛尔异变任务。", status: "已完结", date: "2026-03-25" },
{ id: 50, title: "第五十章:诺斯玛尔·盗贼团", subtitle: "帕丽丝加入", desc: "到达诺斯玛尔,遭遇被瘟疫控制的盗贼团,救下街霸帕丽丝,奥菲利亚退场回西海岸。", status: "已完结", date: "2026-03-25" },
{ id: 51, title: "第五十一章:哈穆林·鼠患", subtitle: "鼠王", desc: "深入哈穆林下水道,遭遇变异巨鼠群,配合击败鼠王,获得瘟疫核心晶石。", status: "已完结", date: "2026-03-25" },
{ id: 52, title: "第五十二章:月光酒馆", subtitle: "长三郎", desc: "探索月光酒馆遭遇被控制的冒险者击败BOSS长三郎获得痛苦之村地图。", status: "已完结", date: "2026-03-25" },
{ id: 53, title: "第五十三章:番外·帕丽丝的誓言", subtitle: "帕丽丝献身", desc: "废弃别墅中,帕丽丝向林克表白献身,两人成为真正的恋人,赛丽亚大度接纳。", status: "已完结", date: "2026-03-25" },
{ id: 54, title: "第五十四章:觉醒之路·启程", subtitle: "GSD", desc: "返回赫顿玛尔找GSD接受觉醒试炼准备前往比尔马克帝国试验场挑战机械牛。", status: "已完结", date: "2026-03-25" },
{ id: 55, title: "第五十五章:比尔马克帝国试验场", subtitle: "机械牛", desc: "挑战比尔马克帝国试验场,遭遇各种改造怪物,击败牛头械王获得牛妖之血。", status: "已完结", date: "2026-03-25" },
{ id: 56, title: "第五十六章:王的遗迹·远古五骑士", subtitle: "五骑士", desc: "挑战王的遗迹,依次击败风、守护、冰、火、光五位远古骑士,获得五大印记。", status: "已完结", date: "2026-03-25" },
{ id: 57, title: "第五十七章:心灵试炼·剑圣觉醒", subtitle: "觉醒", desc: "完成心灵试炼,战胜心魔,成功觉醒成为剑圣,获得极·鬼剑术。", status: "已完结", date: "2026-03-25" },
{ id: 58, title: "第五十八章:番外·后宫的温馨", subtitle: "后宫群像", desc: "后宫大聚会,林克与五位少女在雪山温泉共度温馨时光。", status: "已完结", date: "2026-03-25" },
{ id: 59, title: "第五十九章:敏泰的思念", subtitle: " intimate ", desc: "深夜私会,敏泰带林克去私人温泉,倾诉思念后深情缠绵。", status: "已完结", date: "2026-03-25" },
{ id: 60, title: "第六十章:敏泰加入·前往诺伊佩拉", subtitle: "假野猪", desc: "敏泰正式加入队伍,四人小队前往诺伊佩拉,击败狄瑞吉幻影(假野猪)。", status: "已完结", date: "2026-03-25" },
{ id: 61, title: "第六十一章:悲鸣洞穴·阿甘左与卢克西", subtitle: "悲鸣洞穴", desc: "探索悲鸣洞穴,阿甘左与恋人卢克西重逢,击败虫王戮蛊,卢克西安息。", status: "已完结", date: "2026-03-25" },
{ id: 62, title: "第六十二章:战前准备·痛苦之村的情报", subtitle: "过渡", desc: "休整收集情报GSD教导骗属性机制和风向机制准备前往痛苦之村列瑟芬。", status: "已完结", date: "2026-03-25" },
{ id: 63, title: "第六十三章:痛苦之村列瑟芬·狄瑞吉的真身", subtitle: "真野猪", desc: "决战痛苦之村列瑟芬,击败第六使徒狄瑞吉真身,诺斯玛尔篇正式完结。", status: "已完结", date: "2026-03-25" },
{ id: 64, title: "第六十四章:胜利归来·赫顿玛尔的庆祝", subtitle: "过渡", desc: "击败狄瑞吉后返回赫顿玛尔,举行盛大庆祝,后宫五人团聚,入住新豪宅。", status: "已完结", date: "2026-03-25" },
{ id: 65, title: "第六十五章:正宫的专属时光·赛丽亚的深情", subtitle: " intimate ", desc: "赫顿玛尔新豪宅中,林克与正宫赛丽亚的独处亲密时光,深情缠绵。", status: "已完结", date: "2026-03-25" },
{ id: 66, title: "第六十六章:番外·六人同床·后宫大被同眠", subtitle: "后宫群像", desc: "赫顿玛尔新豪宅温泉中,林克与五位后宫的亲密之夜,五人共同服侍。", status: "已完结", date: "2026-03-25" },
{ id: 67, title: "热血八番街·火箭侠的阴谋", subtitle: "海岸三番街", desc: "凯丽委托调查卡勒特组织,林克六人前往热血八番街,击败守备队长火箭侠。", status: "已完结", date: "2026-03-25" },
{ id: 68, title: "绿都格罗兹尼·魔雷者莫纳亨", subtitle: "诺斯玛尔", desc: "前往绿都格罗兹尼解救念动力者,击败虫之狄桑,救下莫纳亨,新后宫预定。", status: "已完结", date: "2026-03-25" },
{ id: 69, title: "魔雷者的苏醒·莫纳亨的抉择", subtitle: "后宫新成员", desc: "莫纳亨在凯丽工坊苏醒,被林克的真诚打动,正式加入后宫成为第六位成员。", status: "已完结", date: "2026-03-25" },
{ id: 70, title: "天界之门·根特外围的战火", subtitle: "启程天界", desc: "林克与后宫六女告别,通过天空之城传送阵抵达天界,加入皇都军对抗卡勒特。", status: "已完结", date: "2026-03-25" },
{ id: 71, title: "根特北门·泽丁的誓言", subtitle: " intimate ", desc: "根特北门之战,泽丁与林克并肩作战,战后泽丁向林克表白,成为第七位后宫成员。", status: "已完结", date: "2026-03-25" },
{ id: 72, title: "根特南门·马琳的效忠", subtitle: " intimate ", desc: "根特南门之战马琳与林克共同对抗GT-9600战后马琳向林克效忠成为第八位后宫成员。", status: "已完结", date: "2026-03-25" },
{ id: 73, title: "根特防御战·钢铁与血肉的碰撞", subtitle: "大规模战役", desc: "皇都军与卡勒特展开最大规模决战,林克发挥关键作用,最终击退敌军。", status: "已完结", date: "2026-03-25" },
{ id: 74, title: "夜间袭击战·银勺杂技团的覆灭", subtitle: "夜袭行动", desc: "林克与马琳夜袭卡勒特前哨基地,击败帕菲斯兄弟,获取作战地图。", status: "已完结", date: "2026-03-25" },
{ id: 75, title: "补给线阻断战·UM-0终结者", subtitle: "切断生命线", desc: "林克、泽丁、马琳潜入卡勒特补给基地摧毁UM-0终结者切断敌军补给线。", status: "已完结", date: "2026-03-25" },
{ id: 76, title: "追击歼灭战·机械吉赛尔的末路", subtitle: "决战科学家", desc: "皇都军发起追击歼灭战,林克击败机械吉赛尔,生擒吉赛尔博士,获取皇女囚禁地点。", status: "已完结", date: "2026-03-25" },
{ id: 77, title: "卡勒特指挥部·皇女艾丽婕", subtitle: "后宫新成员", desc: "林克潜入卡勒特指挥部,击败兰蒂卢斯,救出皇女艾丽婕,皇女以身相许成为第九位后宫成员。", status: "已完结", date: "2026-03-25" },
{ id: 78, title: "皇女的告白·天界的新篇章", subtitle: " intimate ", desc: "皇女艾丽婕正式向林克告白,两人在皇宫中确定关系,天界战争正式结束。", status: "已完结", date: "2026-03-25" },
{ id: 79, title: "海上列车·鲁夫特悬空海港", subtitle: "新的征程", desc: "林克、泽丁、马琳前往海上列车,遇到小灯笼和贝伦,开始清理铁鳞海贼团。", status: "已完结", date: "2026-03-25" },
{ id: 80, title: "列车上的海贼·铁鳞团的覆灭", subtitle: "海上激战", desc: "林克击败铁鳞海贼团团长黑鳞莫贝尼和副船长蓝影马萨乔,夺回海上列车控制权。", status: "已完结", date: "2026-03-25" },
{ id: 81, title: "夺回西部线·卡勒特的余孽", subtitle: "追踪残党", desc: "林克用计反包围卡勒特残党,击败副指挥官眼镜,但西部线被毁,改道幽灵列车。", status: "已完结", date: "2026-03-25" },
{ id: 82, title: "幽灵列车·亡者的低语", subtitle: "神秘列车", desc: "众人乘坐幽灵列车,林克净化被诅咒的列车长灵魂,打通通往伊顿工业区的道路。", status: "已完结", date: "2026-03-25" },
{ id: 83, title: "伊顿工业区·克雷发电站", subtitle: "火焰之菲茨", desc: "众人来到伊顿工业区遇到米娅·里克特击败克雷发电站BOSS火焰之菲茨救出梅尔文。", status: "已完结", date: "2026-03-26" },
{ id: 84, title: "普鲁兹发电站·闪电之帕特里斯", subtitle: "米娅的心意", desc: "米娅向林克告白并加入后宫众人击败普鲁兹发电站BOSS闪电之帕特里斯。", status: "已完结", date: "2026-03-26" },
{ id: 85, title: "特伦斯发电站·熔岩之萨姆", subtitle: "半机械半熔岩的怪物", desc: "众人击败特伦斯发电站BOSS熔岩之萨姆米娅与林克感情进一步加深。", status: "已完结", date: "2026-03-26" },
{ id: 86, title: "格兰迪发电站·虚空之弗曼", subtitle: "安徒恩最强的守护者", desc: "众人击败格兰迪发电站BOSS虚空之弗曼切断安徒恩的能量供应。", status: "已完结", date: "2026-03-26" },
{ id: 87, title: "安图恩攻坚战·黑雾之源", subtitle: "第一阶段开启", desc: "安图恩攻坚战开始,林克带领突击队摧毁第一个黑雾之源,击败吞噬魔和内尔贝。", status: "已完结", date: "2026-03-26" },
{ id: 88, title: "安图恩攻坚战·震颤的大地", subtitle: "切断安徒恩的触手", desc: "林克切断安徒恩的前肢,击败无数岩石生物,第一阶段完成一半。", status: "已完结", date: "2026-03-26" },
{ id: 89, title: "安图恩攻坚战·擎天之柱", subtitle: "切断安徒恩的后腿", desc: "林克切断安徒恩的后腿,第一阶段即将完成,准备进入第二阶段。", status: "已完结", date: "2026-03-26" },
{ id: 90, title: "安图恩攻坚战·黑色火山", subtitle: "第二阶段开启", desc: "众人进入安徒恩体内,与安徒恩意识化身激战,破坏能量管道。", status: "已完结", date: "2026-03-26" },
{ id: 91, title: "安图恩攻坚战·使徒陨落", subtitle: "最终的胜利", desc: "米娅操控卫星武器,林克全力攻击,最终击败安徒恩,天界恢复和平。", status: "已完结", date: "2026-03-26" }
];
// ==================== 章节数据管理 ====================
// 自动从 data/chapter-*.json 文件加载章节数据
let chaptersData = [];
let isDataLoaded = false;
// 加载所有章节数据
async function loadChaptersData() {
if (isDataLoaded) return chaptersData;
const chapters = [];
let chapterNum = 1;
// 循环加载章节,直到找不到文件
while (true) {
try {
// 格式化章节号(带前导零)
const chapterId = chapterNum.toString().padStart(2, '0');
const response = await fetch(`data/chapter-${chapterId}.json`);
if (!response.ok) {
// 尝试不带前导零的格式
const response2 = await fetch(`data/chapter-${chapterNum}.json`);
if (!response2.ok) break;
const data = await response2.json();
chapters.push(normalizeChapterData(data, chapterNum));
} else {
const data = await response.json();
chapters.push(normalizeChapterData(data, chapterNum));
}
chapterNum++;
} catch (error) {
// 没有更多章节了
break;
}
}
chaptersData = chapters;
isDataLoaded = true;
// 将数据挂载到 window 对象,供其他函数使用
window.chaptersData = chaptersData;
return chaptersData;
}
// 标准化章节数据
function normalizeChapterData(data, defaultId) {
// 从内容中提取描述前100个字符
const extractDesc = (content) => {
if (!content) return '';
// 去除换行和多余空格取前100字符
const cleanContent = content.replace(/\n/g, ' ').replace(/\s+/g, ' ').trim();
return cleanContent.substring(0, 100) + (cleanContent.length > 100 ? '...' : '');
};
return {
id: data.id || defaultId,
title: data.title || `${defaultId}`,
subtitle: data.subtitle || '',
desc: data.desc || extractDesc(data.content),
content: data.content || '',
status: data.status || '已完结',
date: data.date || '2026-03-26'
};
}
// ==================== DOM加载完成后初始化 ====================
document.addEventListener('DOMContentLoaded', async function() {
// 先加载章节数据
await loadChaptersData();
// DOM加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
initTheme();
initNavigation();
initParticles();
@ -148,7 +123,9 @@ function updateThemeIcon(theme) {
if (!themeToggle) return;
const icon = themeToggle.querySelector('.theme-icon');
icon.textContent = theme === 'dark' ? '🌙' : '☀️';
if (icon) {
icon.textContent = theme === 'dark' ? '🌙' : '☀️';
}
}
// ==================== 导航栏 ====================
@ -167,17 +144,19 @@ function initNavigation() {
let lastScroll = 0;
const navbar = document.querySelector('.navbar');
window.addEventListener('scroll', () => {
const currentScroll = window.pageYOffset;
if (navbar) {
window.addEventListener('scroll', () => {
const currentScroll = window.pageYOffset;
if (currentScroll > 100) {
navbar.style.background = 'rgba(15, 15, 26, 0.95)';
} else {
navbar.style.background = 'rgba(15, 15, 26, 0.8)';
}
if (currentScroll > 100) {
navbar.style.background = 'rgba(15, 15, 26, 0.95)';
} else {
navbar.style.background = 'rgba(15, 15, 26, 0.8)';
}
lastScroll = currentScroll;
});
lastScroll = currentScroll;
});
}
}
// ==================== 粒子效果 ====================
@ -232,7 +211,7 @@ function renderChaptersList() {
const container = document.getElementById('chaptersList');
if (!container) return;
// 优先使用从JSON动态加载的数据
// 使用加载的章节数据
const data = window.chaptersData || chaptersData;
container.innerHTML = data.map(chapter => `
@ -326,13 +305,18 @@ function updateReadingProgress() {
}
// ==================== 阅读器 ====================
function initReader() {
async function initReader() {
// 确保数据已加载
if (!isDataLoaded) {
await loadChaptersData();
}
// 获取章节参数
const urlParams = new URLSearchParams(window.location.search);
const chapterId = parseInt(urlParams.get('chapter')) || 1;
const chapterId = parseInt(urlParams.get('id')) || 1;
// 加载章节内容
loadChapter(chapterId);
await loadChapter(chapterId);
// 初始化阅读器设置
initReaderSettings();
@ -350,118 +334,88 @@ function initReader() {
initAutoHide();
}
function loadChapter(chapterId) {
async function loadChapter(chapterId) {
const chapter = chaptersData.find(c => c.id === chapterId);
if (!chapter) return;
if (!chapter) {
// 尝试从JSON文件直接加载
try {
const chapterIdStr = chapterId.toString().padStart(2, '0');
const response = await fetch(`data/chapter-${chapterIdStr}.json`);
if (response.ok) {
const data = await response.json();
const normalizedData = normalizeChapterData(data, chapterId);
renderChapterContent(normalizedData);
return;
}
} catch (error) {
console.error('加载章节失败:', error);
}
return;
}
renderChapterContent(chapter);
}
function renderChapterContent(chapter) {
// 更新标题
document.getElementById('chapterTitle').textContent = chapter.title;
const chapterTitleEl = document.getElementById('chapterTitle');
if (chapterTitleEl) {
chapterTitleEl.textContent = chapter.title;
}
document.title = `${chapter.title} - 阿拉德:剑之回响`;
// 加载内容(模拟)
// 加载内容
const contentEl = document.getElementById('chapterContent');
contentEl.innerHTML = `
<div class="chapter-header">
<h1>${chapter.title}</h1>
<p class="chapter-subtitle">${chapter.subtitle}</p>
</div>
<div class="chapter-body">
<p class="placeholder-text">正在从飞书文档加载内容...</p>
<p class="placeholder-text">章节链接<a href="${getChapterUrl(chapterId)}" target="_blank">在飞书文档中查看</a></p>
</div>
<div class="chapter-footer">
<p>本章完</p>
</div>
`;
}
if (contentEl) {
// 将内容中的换行转换为段落
const paragraphs = chapter.content.split('\n\n').filter(p => p.trim());
const contentHtml = paragraphs.map(p => {
// 处理分隔线
if (p.trim() === '---') {
return '<hr class="chapter-divider">';
}
// 处理普通段落
return `<p>${p.replace(/\n/g, '<br>')}</p>`;
}).join('');
function getChapterUrl(chapterId) {
const urls = {
1: 'https://www.feishu.cn/docx/KBdwdTd8foLLfSxCOmDcNr1LnKe',
2: 'https://www.feishu.cn/docx/Jt0wdpCwDoq7zKxT2TUc54Hlnmd',
3: 'https://www.feishu.cn/docx/G0jXdqGzPoVLe9x9S9qcPzTJnsc',
4: 'https://www.feishu.cn/docx/IckMdiy7cor3fCxap2ic1D40n4d',
5: 'https://www.feishu.cn/docx/PQBMdL9DQosxe1xsq34czRnFnPc',
6: 'https://www.feishu.cn/docx/XflsdWsoRoyS5Xxg91VcmkysnlK',
7: 'https://www.feishu.cn/docx/XL9YdobRMoaSwXxTe23cpuwynkh',
8: 'https://www.feishu.cn/docx/FJ80dI1A1oCRgIxR6i2cIXESnnd',
9: 'https://www.feishu.cn/docx/L2tydWs0Bo84h4xE3hbcfl8MnPg',
10: 'https://www.feishu.cn/docx/Iu2LdfRg2oEP2zxh3kRcqBJ9n1f',
11: 'https://www.feishu.cn/docx/NEiJdna0Jo1OHEx1s84cbNgBn1e',
12: 'https://www.feishu.cn/docx/L2j3dt7PQodXFKxSuoScT3BZnkc',
13: 'https://www.feishu.cn/docx/WHgFdA6NXoyynwxdLmMcjZaPnfd',
14: 'https://www.feishu.cn/docx/FhoNdTZq8oPs91xrb2Gcg2udnAh'
};
return urls[chapterId] || '#';
}
function initReaderSettings() {
const settingsToggle = document.getElementById('settingsToggle');
const readerSettings = document.getElementById('readerSettings');
const overlay = document.getElementById('overlay');
if (settingsToggle) {
settingsToggle.addEventListener('click', () => {
readerSettings.classList.toggle('active');
overlay.classList.toggle('active');
});
contentEl.innerHTML = `
<div class="chapter-header">
<h1>${chapter.title}</h1>
<p class="chapter-subtitle">${chapter.subtitle}</p>
</div>
<div class="chapter-body">
${contentHtml}
</div>
<div class="chapter-footer">
<p>本章完</p>
</div>
`;
}
if (overlay) {
overlay.addEventListener('click', () => {
readerSettings.classList.remove('active');
overlay.classList.remove('active');
});
}
// 字体大小
const sizeBtns = document.querySelectorAll('.size-btn');
sizeBtns.forEach(btn => {
btn.addEventListener('click', () => {
sizeBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
const size = btn.dataset.size;
const sizes = { small: '16px', normal: '18px', large: '20px' };
document.getElementById('chapterContent').style.fontSize = sizes[size];
});
});
// 主题
const themeOptions = document.querySelectorAll('.theme-option');
themeOptions.forEach(option => {
option.addEventListener('click', () => {
themeOptions.forEach(o => o.classList.remove('active'));
option.classList.add('active');
const theme = option.dataset.theme;
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
});
});
}
function initReaderNav(currentId) {
const prevBtn = document.getElementById('prevChapter');
const nextBtn = document.getElementById('nextChapter');
const data = window.chaptersData || chaptersData;
if (prevBtn) {
if (currentId > 1) {
prevBtn.disabled = false;
prevBtn.addEventListener('click', () => {
window.location.href = `reader.html?chapter=${currentId - 1}`;
});
prevBtn.onclick = () => {
window.location.href = `reader.html?id=${currentId - 1}`;
};
} else {
prevBtn.disabled = true;
}
}
if (nextBtn) {
if (currentId < chaptersData.length) {
if (currentId < data.length) {
nextBtn.disabled = false;
nextBtn.addEventListener('click', () => {
window.location.href = `reader.html?chapter=${currentId + 1}`;
});
nextBtn.onclick = () => {
window.location.href = `reader.html?id=${currentId + 1}`;
};
} else {
nextBtn.disabled = true;
}
@ -477,32 +431,42 @@ function initSidebar() {
// 生成侧边栏章节列表
const sidebarChapters = document.getElementById('sidebarChapters');
if (sidebarChapters) {
sidebarChapters.innerHTML = chaptersData.map(chapter => `
<a href="reader.html?chapter=${chapter.id}" class="sidebar-chapter ${chapter.id === getCurrentChapterId() ? 'active' : ''}">
const data = window.chaptersData || chaptersData;
const currentId = getCurrentChapterId();
sidebarChapters.innerHTML = data.map(chapter => `
<a href="reader.html?id=${chapter.id}" class="sidebar-chapter ${chapter.id === currentId ? 'active' : ''}">
<span class="chapter-num">${chapter.id}</span>
<span class="chapter-name">${chapter.title}</span>
</a>
`).join('');
}
if (openSidebar) {
if (openSidebar && readerSidebar && overlay) {
openSidebar.addEventListener('click', () => {
readerSidebar.classList.add('active');
overlay.classList.add('active');
});
}
if (sidebarClose) {
if (sidebarClose && readerSidebar && overlay) {
sidebarClose.addEventListener('click', () => {
readerSidebar.classList.remove('active');
overlay.classList.remove('active');
});
}
if (overlay && readerSidebar) {
overlay.addEventListener('click', () => {
readerSidebar.classList.remove('active');
overlay.classList.remove('active');
});
}
}
function getCurrentChapterId() {
const urlParams = new URLSearchParams(window.location.search);
return parseInt(urlParams.get('chapter')) || 1;
return parseInt(urlParams.get('id')) || 1;
}
function recordReadingProgress(chapterId) {
@ -600,22 +564,45 @@ function initScrollTop() {
}
// ==================== 独立章节页面初始化 ====================
function initChapterReader() {
async function initChapterReader() {
// 确保数据已加载
if (!isDataLoaded) {
await loadChaptersData();
}
const openSidebar = document.getElementById('openSidebar');
const sidebarClose = document.getElementById('sidebarClose');
const readerSidebar = document.getElementById('readerSidebar');
const overlay = document.getElementById('overlay');
const progressFill = document.getElementById('progressFill');
const data = window.chaptersData || chaptersData;
// 动态计算进度条(基于总章节数)
if (progressFill) {
const totalChapters = chaptersData.length; // 从 chaptersData 获取总章节数
const totalChapters = data.length;
const currentChapterMatch = window.location.pathname.match(/chapter-(\d+)\.html/);
const currentChapter = currentChapterMatch ? parseInt(currentChapterMatch[1]) : 1;
const progress = Math.round((currentChapter / totalChapters) * 100);
progressFill.style.width = progress + '%';
}
// 生成侧边栏章节列表
if (readerSidebar) {
const sidebarChapters = document.getElementById('sidebarChapters');
if (sidebarChapters) {
const currentChapterMatch = window.location.pathname.match(/chapter-(\d+)\.html/);
const currentChapter = currentChapterMatch ? parseInt(currentChapterMatch[1]) : 1;
sidebarChapters.innerHTML = data.map(chapter => `
<a href="chapter-${chapter.id.toString().padStart(2, '0')}.html" class="sidebar-chapter ${chapter.id === currentChapter ? 'active' : ''}">
<span class="chapter-num">${chapter.id}</span>
<span class="chapter-name">${chapter.title}</span>
</a>
`).join('');
}
}
if (openSidebar && readerSidebar && overlay) {
openSidebar.addEventListener('click', () => {
readerSidebar.classList.add('active');
@ -701,7 +688,7 @@ function initFontSize() {
const decreaseBtn = document.getElementById('fontDecrease');
const increaseBtn = document.getElementById('fontIncrease');
const fontValue = document.getElementById('fontValue');
const content = document.querySelector('.chapter-article');
const content = document.querySelector('.chapter-article') || document.getElementById('chapterContent');
if (!content) return;
@ -710,14 +697,14 @@ function initFontSize() {
// 应用保存的字体大小
content.style.fontSize = currentSize;
fontValue.textContent = currentSize;
if (fontValue) fontValue.textContent = currentSize;
decreaseBtn?.addEventListener('click', () => {
const index = sizes.indexOf(currentSize);
if (index > 0) {
currentSize = sizes[index - 1];
content.style.fontSize = currentSize;
fontValue.textContent = currentSize;
if (fontValue) fontValue.textContent = currentSize;
localStorage.setItem('readerFontSize', currentSize);
}
});
@ -727,7 +714,7 @@ function initFontSize() {
if (index < sizes.length - 1) {
currentSize = sizes[index + 1];
content.style.fontSize = currentSize;
fontValue.textContent = currentSize;
if (fontValue) fontValue.textContent = currentSize;
localStorage.setItem('readerFontSize', currentSize);
}
});
@ -736,7 +723,7 @@ function initFontSize() {
// 行间距设置
function initLineHeight() {
const lineHeightBtns = document.querySelectorAll('.line-height-btn');
const content = document.querySelector('.chapter-article');
const content = document.querySelector('.chapter-article') || document.getElementById('chapterContent');
if (!content) return;

View File

@ -310,36 +310,22 @@
<div class="sidebar-content" id="sidebarContent"></div>
</aside>
<script src="js/app.js?v=4"></script>
<script>
// 获取URL参数中的章节ID
const urlParams = new URLSearchParams(window.location.search);
let currentChapter = parseInt(urlParams.get('id')) || 1;
let chaptersData = [];
// 获取带缓存禁用的时间戳
function getCacheBuster() {
return `?t=${Date.now()}`;
}
// 加载章节列表
async function loadChaptersIndex() {
try {
const response = await fetch('data/chapters.json' + getCacheBuster());
const data = await response.json();
chaptersData = data.chapters;
renderSidebar();
loadChapter(currentChapter);
} catch (error) {
console.error('Failed to load chapters index:', error);
document.getElementById('chapterContent').innerHTML =
'<div class="error">加载失败,请刷新页面重试</div>';
}
}
// 加载具体章节内容
async function loadChapter(id) {
if (id < 1 || id > chaptersData.length) {
async function loadChapterContent(id) {
const data = window.chaptersData || chaptersData;
if (id < 1 || id > data.length) {
document.getElementById('chapterContent').innerHTML =
'<div class="error">章节不存在</div>';
return;
@ -389,10 +375,12 @@
}
// 渲染侧边栏
function renderSidebar() {
function renderReaderSidebar() {
const data = window.chaptersData || chaptersData;
const sidebarContent = document.getElementById('sidebarContent');
let html = '';
chaptersData.forEach(ch => {
data.forEach(ch => {
html += `<a href="?id=${ch.id}" class="sidebar-chapter" data-id="${ch.id}">第${ch.id}章:${ch.title}</a>`;
});
sidebarContent.innerHTML = html;
@ -419,6 +407,7 @@
// 更新导航按钮状态
function updateNavButtons() {
const data = window.chaptersData || chaptersData;
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
@ -430,7 +419,7 @@
prevBtn.href = `?id=${currentChapter - 1}`;
}
if (currentChapter >= chaptersData.length) {
if (currentChapter >= data.length) {
nextBtn.classList.add('disabled');
nextBtn.href = '#';
} else {
@ -462,8 +451,15 @@
window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
});
// 初始化
loadChaptersIndex();
// 初始化 - 等待app.js加载章节数据
document.addEventListener('DOMContentLoaded', async function() {
// 等待章节数据加载完成
await loadChaptersData();
// 渲染侧边栏
renderReaderSidebar();
// 加载当前章节
loadChapterContent(currentChapter);
});
</script>
</body>
</html>