Update render Book in database

This commit is contained in:
2026-02-17 14:59:53 +08:00
parent 6366e0385b
commit e160b1da53
10 changed files with 115 additions and 15 deletions

View File

@@ -4,12 +4,14 @@ import { useDb } from "../dbProvider";
import type { QdBookInfo } from "../../types";
import { useEffect, useState } from "react";
import { BookInfoContext } from "./BookInfoProvider";
import { BookStatusContext, createBookStatus } from "./BookStatusProvider";
export default function Book() {
const db = useDb();
const { id } = useParams();
const [book, setBook] = useState<QdBookInfo | null>(null);
const [err, setErr] = useState<string | null>(null);
const [bookStatus, setBookStatus] = useState(createBookStatus());
async function load() {
const data = await db.getQdBook(Number(id));
if (data) {
@@ -46,7 +48,9 @@ export default function Book() {
subTitle={err}
extra={<Button type="primary" onClick={() => { setErr(null); handle(); }}></Button>} />}
{book && (<BookInfoContext.Provider value={book}>
<Outlet />
<BookStatusContext.Provider value={[bookStatus, setBookStatus]}>
<Outlet />
</BookStatusContext.Provider>
</BookInfoContext.Provider>)}
</>
);

View File

@@ -1,10 +1,11 @@
.c .img {
width: 180px;
height: auto;
width: 140px;
height: fit-content;
}
.c .info {
min-width: 300px;
max-width: min(calc(100% - 160px), 800px);
}
.c .name {
@@ -20,3 +21,20 @@
.c .author > span {
font-weight: bold;
}
.c .tags {
margin: 0 8px;
}
.c .sign {
margin: 0 8px;
}
.c .intro {
margin: 0 8px;
white-space: pre-wrap;
}
.affix {
background-color: white;
}

View File

@@ -1,20 +1,47 @@
import { Flex, Space, Typography } from "antd";
import { Affix, Flex, Space, Tag, Typography, Switch } from "antd";
import { useBookInfo } from "./BookInfoProvider";
import styles from './BookIndex.module.css';
import { useBookStatus } from "./BookStatusProvider";
import VolumesList from "./VolumesList";
const { Paragraph } = Typography;
const { Paragraph, Link } = Typography;
const QD_BOOK_TAG_COLOR = ['blue', 'cyan', 'orange'];
export default function BookIndex() {
const bookInfo = useBookInfo();
const [bookStatus, setBookStatus] = useBookStatus();
function setShowSavedOnly(showSavedOnly: boolean) {
setBookStatus({ ...bookStatus, showSavedOnly });
}
return (
<div className={styles.c}>
<Flex justify="center">
<Flex justify="center" align="center">
<img className={styles.img} src={bookInfo.bookInfo.imgUrl} />
<Space orientation="vertical" className={styles.info}>
<h2 className={styles.name}>{bookInfo.bookName}</h2>
<Paragraph className={styles.author}><span>{bookInfo.bookInfo.pageJson.authorInfo.authorName}</span> </Paragraph>
<Flex wrap justify="flex-end" gap={8} className={styles.tags}>
{bookInfo.tags.map((tag) => (
<Tag color={QD_BOOK_TAG_COLOR[tag.type]} key={tag.name} variant="outlined">{
tag.url ? <Link href={tag.url} target="_blank">{tag.name}</Link> : tag.name
}</Tag>
))}
</Flex>
<Flex justify="flex-end">
<Tag className={styles.sign} color="green" variant="outlined">{bookInfo.bookInfo.pageJson.signStatus}</Tag>
</Flex>
<Paragraph className={styles.intro}>{bookInfo.intro.split('\n').map((line, index) => (
<>{line}<br /></>
))}</Paragraph>
</Space>
</Flex>
<Affix offsetTop={10}>
<Flex justify="flex-end" className={styles.affix}>
<Switch checked={bookStatus.showSavedOnly} onChange={setShowSavedOnly} checkedChildren={"仅显示已保存章节"} unCheckedChildren={"显示所有章节"} />
</Flex>
</Affix>
{!bookStatus.showSavedOnly && <VolumesList bookId={bookInfo.id} volumes={bookInfo.volumes} />}
</div>
);
}

View File

@@ -0,0 +1,18 @@
import { createContext, useContext, Dispatch } from "react";
export type BookStatus = {
showSavedOnly: boolean;
}
export function createBookStatus(): BookStatus {
return {
showSavedOnly: false,
}
}
export const BookStatusContext = createContext<[BookStatus, Dispatch<BookStatus>]>(null as any);
export function useBookStatus() {
return useContext(BookStatusContext);
}

View File

@@ -18,3 +18,7 @@
.books-card .intro {
margin-left: 8px;
}
.affix {
background-color: white;
}

View File

@@ -10,7 +10,6 @@ const { Paragraph } = Typography;
export default function Books() {
const db = useDb();
const [searchParams, setSearchParams] = useSearchParams();
const [container, setContainer] = useState<HTMLDivElement | null>(null);
const [page, setPage] = useState(Number(searchParams.get('page') || '1'));
const [pageSize, setPageSize] = useState(Number(searchParams.get('pageSize') || '10'));
const [totalCount, setTotalCount] = useState<number | null>(null);
@@ -43,9 +42,10 @@ export default function Books() {
}]
} />
<Divider />
<div ref={setContainer}>
{totalCount !== null && (<Affix target={() => container}>
<div>
{totalCount !== null && (<Affix offsetTop={10}>
<Pagination
className={styles.affix}
total={totalCount}
pageSize={pageSize}
pageSizeOptions={[10, 20, 50, 100]}

View File

@@ -0,0 +1,4 @@
.ch {
margin: 4px 8px;
width: min(calc((100% - 40px) / 4), 300px);
}

View File

@@ -0,0 +1,27 @@
import { Collapse, Flex, Typography } from "antd";
import type { Volume } from "../../qdtypes";
import styles from './VolumesList.module.css';
const { Text } = Typography;
export type VolumesListProps = {
volumes: Volume[];
bookId: number;
}
export default function VolumesList({ volumes, bookId }: VolumesListProps) {
return (<Collapse
items={volumes.map(v => {
return {
key: v.id,
label: v.name,
extra: v.isVip ? <span style={{ color: 'red' }}>VIP卷</span> : null,
children: <Flex wrap>
{v.chapters.map(chapter => (
<Text className={styles.ch} key={chapter.id}>{chapter.name}</Text>
))}
</Flex>
}
})}
/>);
}

View File

@@ -85,7 +85,6 @@ function PocketBaseSettings({config}: { config: PocketBaseConfig }) {
}
export default function DbSettings() {
const [container, setContainer] = useState<HTMLElement | null>(null);
const [config] = useState(new DbConfig());
const [alert, setAlert] = useState<{ title?: string; content: string } | null>(null);
const [dbType, setDbType] = useState<DbType>(DbType.IndexedDb);
@@ -114,8 +113,8 @@ export default function DbSettings() {
handleConfig();
}
return (
<div ref={setContainer}>
<Affix target={() => container}>
<div>
<Affix offsetTop={10}>
<Button type="primary" icon={<SaveOutlined />} onClick={saveSettings}></Button>
<Button onClick={resetSettings} style={{ marginLeft: 8 }} icon={<SyncOutlined />}></Button>
</Affix>

View File

@@ -6,7 +6,6 @@ import SwitchLabel from "../components/SwitchLabel";
import AlertWarn from "../components/AlertWarn";
export default function QdSettings() {
const [container, setContainer] = useState<HTMLElement | null>(null);
const [config] = useState(new QdConfig());
const [autoSaveChapter, setAutoSaveChapter] = useState(false);
const [alert, setAlert] = useState<{ title?: string; content: string } | null>(null);
@@ -33,8 +32,8 @@ export default function QdSettings() {
handleConfig();
}
return (
<div ref={setContainer}>
<Affix target={() => container}>
<div>
<Affix offsetTop={10}>
<Button type="primary" icon={<SaveOutlined />} onClick={saveSettings}></Button>
<Button onClick={resetSettings} style={{ marginLeft: 8 }} icon={<SyncOutlined />}></Button>
</Affix>