119 lines
4.3 KiB
JavaScript
119 lines
4.3 KiB
JavaScript
|
|
const fs = require('fs');
|
|||
|
|
const path = require('path');
|
|||
|
|
|
|||
|
|
// 读取模板
|
|||
|
|
const templatePath = path.join(__dirname, 'template.html');
|
|||
|
|
const template = fs.readFileSync(templatePath, 'utf-8');
|
|||
|
|
|
|||
|
|
// 读取章节索引
|
|||
|
|
const indexPath = path.join(__dirname, 'data/chapters-index.json');
|
|||
|
|
const indexData = JSON.parse(fs.readFileSync(indexPath, 'utf-8'));
|
|||
|
|
const chaptersIndex = indexData.chapters;
|
|||
|
|
|
|||
|
|
// 生成侧边栏章节列表
|
|||
|
|
function generateSidebarChapters(currentId, chapters) {
|
|||
|
|
const start = Math.max(1, currentId - 30);
|
|||
|
|
const end = Math.min(chapters.length, currentId + 30);
|
|||
|
|
let html = '';
|
|||
|
|
|
|||
|
|
for (let i = start; i <= end; i++) {
|
|||
|
|
const chapter = chapters.find(c => c.id === i);
|
|||
|
|
if (chapter) {
|
|||
|
|
const isCurrent = i === currentId ? 'current' : '';
|
|||
|
|
html += `<a href="chapter-${i}.html" class="sidebar-chapter ${isCurrent}">第${i}章:${chapter.title}</a>\n`;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return html;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理章节内容(转换JSON格式为HTML段落)
|
|||
|
|
function processContent(content) {
|
|||
|
|
// 分割成段落
|
|||
|
|
const paragraphs = content.split('\n').filter(p => p.trim());
|
|||
|
|
let html = '';
|
|||
|
|
|
|||
|
|
for (const para of paragraphs) {
|
|||
|
|
const trimmed = para.trim();
|
|||
|
|
if (trimmed.startsWith('---')) {
|
|||
|
|
// 分隔线转为特殊标记
|
|||
|
|
html += `<p style="text-align: center; color: var(--text-secondary); text-indent: 0;">···</p>\n`;
|
|||
|
|
} else if (trimmed.startsWith('(') && trimmed.endsWith(')')) {
|
|||
|
|
// 结尾标记
|
|||
|
|
html += `<p style="text-align: center; color: var(--text-secondary); text-indent: 0; margin-top: 2em;">${trimmed}</p>\n`;
|
|||
|
|
} else {
|
|||
|
|
html += `<p>${trimmed}</p>\n`;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return html;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 生成HTML页面
|
|||
|
|
function generateChapterHtml(chapter, chapters) {
|
|||
|
|
const prevChapter = chapter.id > 1 ? `chapter-${chapter.id - 1}.html` : '#';
|
|||
|
|
const nextChapter = chapter.id < chapters.length ? `chapter-${chapter.id + 1}.html` : '#';
|
|||
|
|
const prevDisabled = chapter.id <= 1 ? 'disabled' : '';
|
|||
|
|
const nextDisabled = chapter.id >= chapters.length ? 'disabled' : '';
|
|||
|
|
|
|||
|
|
let html = template;
|
|||
|
|
html = html.replace('{{CHAPTER_ID}}', chapter.id);
|
|||
|
|
html = html.replace('{{CHAPTER_NUMBER}}', `Chapter ${chapter.id}`);
|
|||
|
|
html = html.replace('{{CHAPTER_TITLE}}', chapter.title);
|
|||
|
|
html = html.replace('{{CHAPTER_CONTENT}}', processContent(chapter.content));
|
|||
|
|
html = html.replace('{{PREV_CHAPTER}}', prevChapter);
|
|||
|
|
html = html.replace('{{NEXT_CHAPTER}}', nextChapter);
|
|||
|
|
html = html.replace('{{PREV_DISABLED}}', prevDisabled);
|
|||
|
|
html = html.replace('{{NEXT_DISABLED}}', nextDisabled);
|
|||
|
|
html = html.replace('{{SIDEBAR_CHAPTERS}}', generateSidebarChapters(chapter.id, chapters));
|
|||
|
|
|
|||
|
|
return html;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 主函数
|
|||
|
|
function main() {
|
|||
|
|
const chaptersDir = path.join(__dirname, 'chapters');
|
|||
|
|
const dataDir = path.join(__dirname, 'data');
|
|||
|
|
|
|||
|
|
// 确保目录存在
|
|||
|
|
if (!fs.existsSync(chaptersDir)) {
|
|||
|
|
fs.mkdirSync(chaptersDir, { recursive: true });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
let generated = 0;
|
|||
|
|
let skipped = 0;
|
|||
|
|
|
|||
|
|
for (const chapterMeta of chaptersIndex) {
|
|||
|
|
const outputPath = path.join(chaptersDir, `chapter-${chapterMeta.id}.html`);
|
|||
|
|
const jsonPath = path.join(dataDir, `chapter-${chapterMeta.id}.json`);
|
|||
|
|
|
|||
|
|
// 检查JSON文件是否存在
|
|||
|
|
if (!fs.existsSync(jsonPath)) {
|
|||
|
|
console.log(`⏭️ 跳过: chapter-${chapterMeta.id}.html (JSON不存在)`);
|
|||
|
|
skipped++;
|
|||
|
|
continue;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
// 读取完整章节内容
|
|||
|
|
const jsonData = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'));
|
|||
|
|
const chapter = {
|
|||
|
|
id: chapterMeta.id,
|
|||
|
|
title: jsonData.title || chapterMeta.title,
|
|||
|
|
content: jsonData.content || ''
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const html = generateChapterHtml(chapter, chaptersIndex);
|
|||
|
|
fs.writeFileSync(outputPath, html, 'utf-8');
|
|||
|
|
generated++;
|
|||
|
|
console.log(`✅ 生成: chapter-${chapter.id}.html (${chapter.title})`);
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error(`❌ 错误: chapter-${chapterMeta.id}.html - ${err.message}`);
|
|||
|
|
skipped++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log(`\n📊 统计: 生成 ${generated} 个文件, 跳过 ${skipped} 个`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
main();
|