mirror of
https://github.com/lifegpc/bookdownload.git
synced 2026-06-14 01:14:15 +08:00
Update render Book in database
This commit is contained in:
@@ -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>)}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
18
src/manage/qd/BookStatusProvider.ts
Normal file
18
src/manage/qd/BookStatusProvider.ts
Normal 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);
|
||||
}
|
||||
|
||||
@@ -18,3 +18,7 @@
|
||||
.books-card .intro {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.affix {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
@@ -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]}
|
||||
|
||||
4
src/manage/qd/VolumesList.module.css
Normal file
4
src/manage/qd/VolumesList.module.css
Normal file
@@ -0,0 +1,4 @@
|
||||
.ch {
|
||||
margin: 4px 8px;
|
||||
width: min(calc((100% - 40px) / 4), 300px);
|
||||
}
|
||||
27
src/manage/qd/VolumesList.tsx
Normal file
27
src/manage/qd/VolumesList.tsx
Normal 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>
|
||||
}
|
||||
})}
|
||||
/>);
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user