Better way to display chapter

This commit is contained in:
2026-02-20 13:34:05 +08:00
parent 514f9aaff1
commit 69d8e8ec9f
2 changed files with 66 additions and 18 deletions

View File

@@ -4,12 +4,12 @@ import { loadChapterListsIfNeeded, useBookContext, useBookStatus } from "./BookS
import { useEffect, useRef, useState } from "react";
import { useDb } from "../dbProvider";
import type { QdChapterInfo } from "../../types";
import MonacoEditor, { MonacoEditorHandle } from 'react-monaco-editor';
import type { Volume } from "../../qdtypes";
import { useBookInfo } from "./BookInfoProvider";
import { get_new_volumes } from "../../utils/qd";
import VolumesList from "./VolumesList";
import styles from './BookChapter.module.css';
import ChapterEditor from "./ChapterEditor";
export default function BookChapter() {
const setItems = useBookContext();
@@ -20,23 +20,34 @@ export default function BookChapter() {
const [err, setErr] = useState<string | null>(null);
const [listErr, setListErr] = useState<string | null>(null);
const [chapter, setChapter] = useState<QdChapterInfo | null>(null);
const [chapterContent, setChapterContent] = useState<string>('');
const bookInfo = useBookInfo();
const editorRef = useRef<MonacoEditorHandle>(null);
async function load() {
const editorRef = useRef<ChapterEditor>(null);
async function load(controller?: AbortController) {
const primaryKey = bookStatus.chapterLists?.find(chapter => chapter.id === id)?.primaryKey;
const data = await (primaryKey ? db.getQdChapter(primaryKey) : db.getLatestQdChapter(id));
if (controller?.signal.aborted) {
return;
}
if (data) {
if (data.id !== id) return;
setChapter(data);
setChapterContent(data.contents ? data.contents.join('\n') : data.chapterInfo.content);
setErr(null);
} else {
setErr("章节不存在");
setChapter(null);
}
}
function handle_load() {
load().catch(e => {
const controller = new AbortController();
load(controller).catch(e => {
if (controller.signal.aborted) {
return;
}
setErr(e instanceof Error ? e.message : String(e));
});
return () => {
controller.abort();
}
}
function handle_list_load() {
if (listErr) {
@@ -52,10 +63,10 @@ export default function BookChapter() {
return;
}
if (chapter) setChapter(null);
if (chapterContent) setChapterContent('');
if (err) setErr(null);
handle_load();
const abort = handle_load();
handle_list_load();
return abort;
}, [id]);
setItems([{
title: chapter ? `章节详情:${chapter.chapterInfo.chapterName}` : '章节详情'
@@ -75,7 +86,7 @@ export default function BookChapter() {
return (<>
<Splitter onResize={() => {
setTimeout(() => {
editorRef.current?.editor.layout();
editorRef.current?.layout();
}, 1);
}}>
<Splitter.Panel min='20%' max='40%' defaultSize='30%' collapsible className={styles.chs}>
@@ -89,15 +100,7 @@ export default function BookChapter() {
title="数据加载失败"
subTitle={err}
extra={<Button type="primary" onClick={() => { setErr(null); handle_load(); }}></Button>} />}
{chapter && <MonacoEditor
ref={editorRef}
value={chapterContent}
language="plaintext"
onChange={setChapterContent}
options={{
wordWrap: 'on',
}}
/>}
{chapter && <ChapterEditor ref={editorRef} chapter={chapter} />}
{!chapter && !err && <Skeleton active />}
</Splitter.Panel>
</Splitter>

View File

@@ -0,0 +1,45 @@
import { Component, createRef } from "react";
import { QdChapterInfo } from "../../types";
import MonacoEditor, { MonacoEditorHandle } from 'react-monaco-editor';
export interface ChapterEditorProps {
chapter: QdChapterInfo;
}
export interface ChapterEditorState {
content: string;
}
export default class ChapterEditor extends Component<ChapterEditorProps, ChapterEditorState> {
ref;
constructor(props: ChapterEditorProps) {
super(props);
this.ref = createRef<MonacoEditorHandle>();
this.state = {
content: props.chapter.contents ? props.chapter.contents.join('\n') : props.chapter.chapterInfo.content,
};
}
componentDidUpdate(prevProps: Readonly<ChapterEditorProps>, _prevState: Readonly<ChapterEditorState>, _snapshot?: unknown): void {
if (prevProps.chapter.id !== this.props.chapter.id) {
this.setState({
content: this.props.chapter.contents ? this.props.chapter.contents.join('\n') : this.props.chapter.chapterInfo.content,
});
}
}
layout() {
this.ref.current?.editor.layout();
}
render() {
return (<>
<MonacoEditor
ref={this.ref}
value={this.state.content}
language="plaintext"
onChange={(newValue) => this.setState({ content: newValue })}
options={{
wordWrap: 'on',
}}
/>
</>);
}
}