Skip to content

Commit 85620d5

Browse files
author
Taois
committed
feat: 完成书画柜逻辑
1 parent 16a89af commit 85620d5

File tree

2 files changed

+577
-101
lines changed

2 files changed

+577
-101
lines changed
Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
/**
2+
* 智能断章工具
3+
* 用于将txt内容解析为章节列表
4+
*/
5+
6+
/**
7+
* 章节标题的正则表达式模式
8+
*/
9+
const CHAPTER_PATTERNS = [
10+
// 中文章节模式
11+
/^[\d]+[]/,
12+
/^[\d]+[][\s\S]*$/,
13+
/^[]?[\d]+[\s]*[]/,
14+
15+
// 数字章节模式
16+
/^\d+/,
17+
/^\d+/,
18+
/^\d+/,
19+
/^\d+/,
20+
/^\d+[\.\s]*[]/,
21+
/^Chapter\s*\d+/i,
22+
/^Ch\.\s*\d+/i,
23+
24+
// 特殊格式
25+
/^/,
26+
/^/,
27+
/^/,
28+
/^/,
29+
/^/,
30+
/^/,
31+
/^/,
32+
/^/,
33+
/^/,
34+
/^/,
35+
36+
// 英文章节模式
37+
/^Chapter\s+[IVX]+/i,
38+
/^Part\s+\d+/i,
39+
/^Section\s+\d+/i,
40+
41+
// 其他常见模式
42+
/^.*$/,
43+
/^.*$/,
44+
/^.*$/,
45+
/^.*$/,
46+
/^.*$/
47+
]
48+
49+
/**
50+
* 检查一行文本是否可能是章节标题
51+
* @param {string} line - 要检查的文本行
52+
* @returns {boolean} 是否是章节标题
53+
*/
54+
function isChapterTitle(line) {
55+
const trimmedLine = line.trim()
56+
57+
// 空行不是章节标题
58+
if (!trimmedLine) return false
59+
60+
// 太长的行通常不是章节标题(超过50个字符)
61+
if (trimmedLine.length > 50) return false
62+
63+
// 检查是否匹配章节模式
64+
for (const pattern of CHAPTER_PATTERNS) {
65+
if (pattern.test(trimmedLine)) {
66+
return true
67+
}
68+
}
69+
70+
// 检查是否是纯数字标题
71+
if (/^\d+$/.test(trimmedLine) && trimmedLine.length <= 3) {
72+
return true
73+
}
74+
75+
// 检查是否是短标题(可能是章节名)
76+
if (trimmedLine.length <= 20 && !trimmedLine.includes('。') && !trimmedLine.includes(',')) {
77+
// 如果包含常见的章节关键词
78+
const chapterKeywords = ['章', '回', '节', '部', '篇', 'Chapter', 'Part']
79+
for (const keyword of chapterKeywords) {
80+
if (trimmedLine.includes(keyword)) {
81+
return true
82+
}
83+
}
84+
}
85+
86+
return false
87+
}
88+
89+
/**
90+
* 解析txt内容为章节列表
91+
* @param {string} content - txt文件内容
92+
* @param {Object} options - 解析选项
93+
* @returns {Array} 章节列表
94+
*/
95+
export function parseChapters(content, options = {}) {
96+
const {
97+
minChapterLength = 500, // 最小章节长度
98+
maxChapters = 1000, // 最大章节数
99+
autoDetect = true // 是否自动检测章节
100+
} = options
101+
102+
if (!content || typeof content !== 'string') {
103+
return []
104+
}
105+
106+
const lines = content.split(/\r?\n/)
107+
const chapters = []
108+
let currentChapter = null
109+
let chapterIndex = 0
110+
111+
// 如果没有检测到章节标题,创建一个默认章节
112+
let hasChapterTitles = false
113+
114+
for (let i = 0; i < lines.length; i++) {
115+
const line = lines[i].trim()
116+
117+
if (autoDetect && isChapterTitle(line)) {
118+
hasChapterTitles = true
119+
120+
// 保存上一章节
121+
if (currentChapter && currentChapter.content.trim().length >= minChapterLength) {
122+
chapters.push(currentChapter)
123+
chapterIndex++
124+
}
125+
126+
// 创建新章节
127+
currentChapter = {
128+
id: chapterIndex,
129+
title: line || `第${chapterIndex + 1}章`,
130+
content: '',
131+
startLine: i,
132+
endLine: i
133+
}
134+
} else if (line) {
135+
// 添加内容到当前章节
136+
if (!currentChapter) {
137+
// 如果还没有章节,创建第一个章节
138+
currentChapter = {
139+
id: chapterIndex,
140+
title: `第${chapterIndex + 1}章`,
141+
content: '',
142+
startLine: i,
143+
endLine: i
144+
}
145+
}
146+
147+
if (currentChapter.content) {
148+
currentChapter.content += '\n'
149+
}
150+
currentChapter.content += line
151+
currentChapter.endLine = i
152+
}
153+
154+
// 限制章节数量
155+
if (chapters.length >= maxChapters) {
156+
break
157+
}
158+
}
159+
160+
// 添加最后一个章节
161+
if (currentChapter && currentChapter.content.trim().length >= minChapterLength) {
162+
chapters.push(currentChapter)
163+
}
164+
165+
// 如果没有检测到章节标题,按长度自动分章
166+
if (!hasChapterTitles && content.length > minChapterLength) {
167+
return autoSplitChapters(content, options)
168+
}
169+
170+
// 如果章节太少,尝试更宽松的检测
171+
if (chapters.length < 2 && content.length > minChapterLength * 2) {
172+
return autoSplitChapters(content, options)
173+
}
174+
175+
return chapters
176+
}
177+
178+
/**
179+
* 自动按长度分章
180+
* @param {string} content - 文本内容
181+
* @param {Object} options - 选项
182+
* @returns {Array} 章节列表
183+
*/
184+
function autoSplitChapters(content, options = {}) {
185+
const {
186+
chapterLength = 3000, // 每章大约长度
187+
minChapterLength = 500 // 最小章节长度
188+
} = options
189+
190+
const chapters = []
191+
const paragraphs = content.split(/\n\s*\n/).filter(p => p.trim())
192+
193+
let currentChapter = {
194+
id: 0,
195+
title: '第1章',
196+
content: '',
197+
startLine: 0,
198+
endLine: 0
199+
}
200+
201+
let chapterIndex = 0
202+
203+
for (let i = 0; i < paragraphs.length; i++) {
204+
const paragraph = paragraphs[i].trim()
205+
206+
if (currentChapter.content.length + paragraph.length > chapterLength &&
207+
currentChapter.content.length >= minChapterLength) {
208+
// 当前章节已经足够长,开始新章节
209+
chapters.push(currentChapter)
210+
chapterIndex++
211+
212+
currentChapter = {
213+
id: chapterIndex,
214+
title: `第${chapterIndex + 1}章`,
215+
content: paragraph,
216+
startLine: i,
217+
endLine: i
218+
}
219+
} else {
220+
// 添加到当前章节
221+
if (currentChapter.content) {
222+
currentChapter.content += '\n\n'
223+
}
224+
currentChapter.content += paragraph
225+
currentChapter.endLine = i
226+
}
227+
}
228+
229+
// 添加最后一个章节
230+
if (currentChapter.content.trim()) {
231+
chapters.push(currentChapter)
232+
}
233+
234+
return chapters
235+
}
236+
237+
/**
238+
* 获取章节摘要
239+
* @param {string} content - 章节内容
240+
* @param {number} maxLength - 最大长度
241+
* @returns {string} 摘要
242+
*/
243+
export function getChapterSummary(content, maxLength = 100) {
244+
if (!content) return ''
245+
246+
const cleanContent = content.replace(/\s+/g, ' ').trim()
247+
if (cleanContent.length <= maxLength) {
248+
return cleanContent
249+
}
250+
251+
return cleanContent.substring(0, maxLength) + '...'
252+
}
253+
254+
/**
255+
* 验证章节数据
256+
* @param {Array} chapters - 章节列表
257+
* @returns {boolean} 是否有效
258+
*/
259+
export function validateChapters(chapters) {
260+
if (!Array.isArray(chapters) || chapters.length === 0) {
261+
return false
262+
}
263+
264+
for (const chapter of chapters) {
265+
if (!chapter.title || !chapter.content || typeof chapter.id !== 'number') {
266+
return false
267+
}
268+
}
269+
270+
return true
271+
}
272+
273+
export default {
274+
parseChapters,
275+
getChapterSummary,
276+
validateChapters,
277+
isChapterTitle
278+
}

0 commit comments

Comments
 (0)