From 1659d9fc8dfafb45433729373cee1f7604e9c87a Mon Sep 17 00:00:00 2001
From: zhuba-Ahhh <3477826311@qq.com>
Date: Sun, 22 Sep 2024 05:24:04 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20:sparkles:=20=E9=93=BE=E8=B7=AF?=
=?UTF-8?q?=E8=B5=B0=E9=80=9A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
package.json | 1 +
src/App.tsx | 13 +-----
src/components/Navbar.tsx | 4 +-
src/router/index.tsx | 14 +++++-
src/utils/index.ts | 1 +
src/utils/math.ts | 7 +++
src/views/BookChapter.tsx | 90 ++++++++++++++++++++++++++++++++++++++
src/views/BookListView.tsx | 37 +++++-----------
src/views/BookView.tsx | 78 ++++++++++++++++++++++++++++-----
9 files changed, 192 insertions(+), 53 deletions(-)
create mode 100644 src/utils/math.ts
create mode 100644 src/views/BookChapter.tsx
diff --git a/package.json b/package.json
index 4b17b72..d6bb29c 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
"axios": "^1.6.8",
"daisyui": "^4.12.10",
"lodash-es": "^4.17.21",
+ "rc-virtual-list": "^3.14.7",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.23.0",
diff --git a/src/App.tsx b/src/App.tsx
index c6fe37f..ecc340d 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -10,18 +10,7 @@ function App() {
}, 1000);
}, []);
- return (
-
- {isLoading ? (
-
- ) : (
- <>
-
-
- >
- )}
-
- );
+ return {isLoading ? : };
}
export default App;
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx
index 3bd92f6..efb7e32 100644
--- a/src/components/Navbar.tsx
+++ b/src/components/Navbar.tsx
@@ -2,7 +2,7 @@ import { Link } from 'react-router-dom';
const Navbar = () => {
return (
-
+
-
剑来小说
@@ -13,7 +13,7 @@ const Navbar = () => {
首页
-
- 章节列表
+ 小说列表
-
搜索
diff --git a/src/router/index.tsx b/src/router/index.tsx
index d82b2eb..7d19ba3 100644
--- a/src/router/index.tsx
+++ b/src/router/index.tsx
@@ -3,7 +3,8 @@ import App from '../App';
import BookList from '../views/BookListView';
import SearchPage from '../views/SearchView';
import BookView from '../views/BookView';
-import Navbar from 'components/Navbar';
+import BookChapter from '../views/BookChapter';
+import Navbar from '../components/Navbar';
const router = createBrowserRouter([
{
@@ -34,7 +35,7 @@ const router = createBrowserRouter([
),
},
{
- path: '/book/:url',
+ path: '/book/:name',
element: (
<>
@@ -42,6 +43,15 @@ const router = createBrowserRouter([
>
),
},
+ {
+ path: '/chapter/:url',
+ element: (
+ <>
+
+
+ >
+ ),
+ },
]);
export default router;
diff --git a/src/utils/index.ts b/src/utils/index.ts
index 56e4b05..d84e9ab 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -1 +1,2 @@
export * from './request';
+export * from './math';
diff --git a/src/utils/math.ts b/src/utils/math.ts
new file mode 100644
index 0000000..db24651
--- /dev/null
+++ b/src/utils/math.ts
@@ -0,0 +1,7 @@
+export const getLastNumbersFromUrl = (url: string) => {
+ // 使用正则表达式匹配URL路径中的所有数字
+ const regex = /\d+/g;
+ const matches = url.match(regex);
+ // 如果匹配成功,则将匹配的数字字符串转换为整数数组,否则返回空数组
+ return matches ? matches.map(Number) : [];
+};
diff --git a/src/views/BookChapter.tsx b/src/views/BookChapter.tsx
new file mode 100644
index 0000000..2b0dd76
--- /dev/null
+++ b/src/views/BookChapter.tsx
@@ -0,0 +1,90 @@
+import Loading from 'components/Loading';
+import { useCallback, useEffect, useState } from 'react';
+import { useNavigate, useParams } from 'react-router-dom';
+import { getLastNumbersFromUrl, http } from 'utils';
+
+type ChapterDetails = {
+ text: string;
+ name: string;
+ next?: string;
+ up?: string;
+};
+
+const BookChapter = () => {
+ const navigate = useNavigate();
+ const { url } = useParams<{ url: string }>();
+ const [chapter, setChapter] = useState(null);
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ const fetchBookDetails = async () => {
+ try {
+ if (url) {
+ const urls = url?.split('-');
+ const response = await http.get(
+ `book/read/bqs/xiaoshuo/${urls[0]}/${urls[1]}.html`
+ );
+ setChapter(response);
+ }
+ } catch (error) {
+ console.error('获取书籍详情失败:', error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchBookDetails();
+ }, [url]);
+
+ const JumpToBookChapter = useCallback(
+ (url: string) => {
+ const urls = getLastNumbersFromUrl(url);
+ if (urls.length > 1) {
+ navigate(`/chapter/${urls.join('-')}`);
+ }
+ },
+ [navigate]
+ );
+
+ if (loading) {
+ return ;
+ }
+
+ if (!chapter) {
+ return (
+
+ );
+ }
+
+ const StoryText = (content: string) => {
+ // 将HTML字符串分解为HTML对象
+ const htmlContent = {
+ __html: content,
+ };
+
+ return ;
+ };
+
+ return (
+
+
+ {chapter?.up && (
+
+ )}
+ {chapter?.next && (
+
+ )}
+
+
{chapter?.name}
+ {StoryText(chapter?.text || '')}
+
+ );
+};
+
+export default BookChapter;
diff --git a/src/views/BookListView.tsx b/src/views/BookListView.tsx
index 748f5de..6cd9815 100644
--- a/src/views/BookListView.tsx
+++ b/src/views/BookListView.tsx
@@ -16,13 +16,6 @@ interface Book {
author: string;
}
-type BookDetail = {
- name: string;
- url: string;
- new: string;
- newurl: string;
-};
-
interface BookListProps {
initialCategory?: '全部类型' | '都市' | '玄幻' | '奇幻' | '历史' | '科幻' | '军事' | '游戏';
}
@@ -54,20 +47,19 @@ const BookList = ({ initialCategory = '全部类型' }: BookListProps) => {
}, [category]);
const JumpToTableOfContents = useCallback(
- async (name: string) => {
- const response = await http.get(`/book?name=${name}`);
- if (response?.length > 0) {
- navigate(`/book/${getLastNumberFromUrl(response[0]?.url)}`);
+ (name: string) => {
+ if (name) {
+ navigate(`/book/${name}`);
}
},
[navigate]
);
return (
-
-
+
({
key,
label: key,
@@ -85,7 +77,7 @@ const BookList = ({ initialCategory = '全部类型' }: BookListProps) => {
JumpToTableOfContents(book.name)}
- className="card card-compact hover:shadow-lg hover:rounded-lg transition-shadow duration-300" // 增加圆角样式
+ className="card card-compact hover:shadow-lg hover:rounded-lg transition-shadow duration-300 cursor-pointer" // 增加圆角样式
>
{
/>
{book.name}
-
{book.desc}
+
{book.desc}
{
+ className="badge badge-outline text-sm px-2 py-1 bg-secondary-100 text-secondary-700 rounded-full cursor-pointer"
+ onClick={(e) => {
if (category !== book.type) {
setCategory(book.type as BookListProps['initialCategory']);
}
+ e.stopPropagation();
}}
>
{book.type}
@@ -128,11 +121,3 @@ const BookList = ({ initialCategory = '全部类型' }: BookListProps) => {
};
export default BookList;
-
-const getLastNumberFromUrl = (url: string) => {
- // 使用正则表达式匹配URL路径中的数字
- const regex = /\d+(?=\/?$)/;
- const match = url.match(regex);
- // 如果匹配成功,则返回匹配的数字,否则返回null
- return match ? parseInt(match[0], 10) : null;
-};
diff --git a/src/views/BookView.tsx b/src/views/BookView.tsx
index 147ab67..129e7c2 100644
--- a/src/views/BookView.tsx
+++ b/src/views/BookView.tsx
@@ -1,7 +1,15 @@
-import { useState, useEffect } from 'react';
-import { useParams } from 'react-router-dom';
+import { useState, useEffect, useCallback } from 'react';
+import { useNavigate, useParams } from 'react-router-dom';
import { http } from '../utils/request';
import Loading from '../components/Loading';
+import { getLastNumbersFromUrl } from 'utils';
+
+type BookDetail = {
+ name: string;
+ url: string;
+ new: string;
+ newurl: string;
+};
interface BookDetails {
img: string;
@@ -13,15 +21,32 @@ interface BookDetails {
}
const BookView = () => {
- const { url } = useParams<{ url: string }>();
+ const navigate = useNavigate();
+ const { name } = useParams<{ name: string }>();
const [book, setBook] = useState(null);
const [loading, setLoading] = useState(true);
+ const [options, setOptions] = useState<{ label: string; value: string }[]>([]);
+
+ const getBookDetails = useCallback(async (url: string) => {
+ try {
+ const response = await http.get(url);
+ setBook(response);
+ } catch (error) {
+ console.error('获取书籍详情失败:', error);
+ } finally {
+ setLoading(false);
+ }
+ }, []);
useEffect(() => {
const fetchBookDetails = async () => {
try {
- const response = await http.get(`/book/bqs/xiaoshuo/${url}`);
- setBook(response);
+ setLoading(true);
+ const origins = await http.get(`/book?name=${name}`);
+ setOptions(
+ origins.map((origin) => ({ value: origin?.url, label: `${origin?.name}-${origin?.new}` }))
+ );
+ getBookDetails(origins[0].url);
} catch (error) {
console.error('获取书籍详情失败:', error);
} finally {
@@ -30,7 +55,16 @@ const BookView = () => {
};
fetchBookDetails();
- }, [url]);
+ }, [getBookDetails, name]);
+
+ const JumpToBookChapter = useCallback(
+ (url: string) => {
+ if (url) {
+ navigate(`/chapter/${url}`);
+ }
+ },
+ [navigate]
+ );
if (loading) {
return ;
@@ -46,13 +80,12 @@ const BookView = () => {
return (
-
-
-
章节列表
+
+
章节列表
+
+ 源:
+
+
+