Skip to content

Commit

Permalink
feat: ✨ 链路走通
Browse files Browse the repository at this point in the history
  • Loading branch information
zhuba-Ahhh committed Sep 21, 2024
1 parent 90f7085 commit 1659d9f
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 53 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
13 changes: 1 addition & 12 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,7 @@ function App() {
}, 1000);
}, []);

return (
<main className="container mx-auto">
{isLoading ? (
<Loading />
) : (
<>
<div className="h-16" />
<DictumList />
</>
)}
</main>
);
return <main className="container mx-auto">{isLoading ? <Loading /> : <DictumList />}</main>;
}

export default App;
4 changes: 2 additions & 2 deletions src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Link } from 'react-router-dom';

const Navbar = () => {
return (
<ul className="navbar bg-base-100">
<ul className="navbar bg-base-100 text-base-content sticky top-0 z-30 flex h-16 w-full justify-center bg-opacity-90 backdrop-blur transition-shadow duration-100 [transform:translate3d(0,0,0)] shadow-sm">
<li className="navbar-start">
<Link to="/" className="text-2xl font-bold">
剑来小说
Expand All @@ -13,7 +13,7 @@ const Navbar = () => {
<Link to="/">首页</Link>
</li>
<li className="hover:text-gray-400">
<Link to="/list">章节列表</Link>
<Link to="/list">小说列表</Link>
</li>
<li className="hover:text-gray-400">
<Link to="/search">搜索</Link>
Expand Down
14 changes: 12 additions & 2 deletions src/router/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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([
{
Expand Down Expand Up @@ -34,14 +35,23 @@ const router = createBrowserRouter([
),
},
{
path: '/book/:url',
path: '/book/:name',
element: (
<>
<Navbar />
<BookView />
</>
),
},
{
path: '/chapter/:url',
element: (
<>
<Navbar />
<BookChapter />
</>
),
},
]);

export default router;
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './request';
export * from './math';
7 changes: 7 additions & 0 deletions src/utils/math.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const getLastNumbersFromUrl = (url: string) => {
// 使用正则表达式匹配URL路径中的所有数字
const regex = /\d+/g;
const matches = url.match(regex);
// 如果匹配成功,则将匹配的数字字符串转换为整数数组,否则返回空数组
return matches ? matches.map(Number) : [];
};
90 changes: 90 additions & 0 deletions src/views/BookChapter.tsx
Original file line number Diff line number Diff line change
@@ -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<ChapterDetails | null>(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
const fetchBookDetails = async () => {
try {
if (url) {
const urls = url?.split('-');
const response = await http.get<ChapterDetails>(
`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 <Loading />;
}

if (!chapter) {
return (
<div className="mx-auto">
<p className="text-center">未找到章节信息</p>
</div>
);
}

const StoryText = (content: string) => {
// 将HTML字符串分解为HTML对象
const htmlContent = {
__html: content,
};

return <div dangerouslySetInnerHTML={htmlContent} />;
};

return (
<div>
<div className="flex justify-between mt-4">
{chapter?.up && (
<button onClick={() => JumpToBookChapter(chapter.up || '')} className="btn">
上一章
</button>
)}
{chapter?.next && (
<button onClick={() => JumpToBookChapter(chapter.next || '')} className="btn">
下一章
</button>
)}
</div>
<h1 className="text-center font-bold mb-4">{chapter?.name}</h1>
{StoryText(chapter?.text || '')}
</div>
);
};

export default BookChapter;
37 changes: 11 additions & 26 deletions src/views/BookListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,6 @@ interface Book {
author: string;
}

type BookDetail = {
name: string;
url: string;
new: string;
newurl: string;
};

interface BookListProps {
initialCategory?: '全部类型' | '都市' | '玄幻' | '奇幻' | '历史' | '科幻' | '军事' | '游戏';
}
Expand Down Expand Up @@ -54,20 +47,19 @@ const BookList = ({ initialCategory = '全部类型' }: BookListProps) => {
}, [category]);

const JumpToTableOfContents = useCallback(
async (name: string) => {
const response = await http.get<BookDetail[]>(`/book?name=${name}`);
if (response?.length > 0) {
navigate(`/book/${getLastNumberFromUrl(response[0]?.url)}`);
(name: string) => {
if (name) {
navigate(`/book/${name}`);
}
},
[navigate]
);

return (
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="h-16" />
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mt-2 sticky">
<Tabs
initialActiveKey="全部类型"
key={category}
initialActiveKey={category}
tabs={categories.map((key) => ({
key,
label: key,
Expand All @@ -85,7 +77,7 @@ const BookList = ({ initialCategory = '全部类型' }: BookListProps) => {
<div
key={book.name}
onClick={() => 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" // 增加圆角样式
>
<img
src={book.img}
Expand All @@ -94,16 +86,17 @@ const BookList = ({ initialCategory = '全部类型' }: BookListProps) => {
/>
<div className="card-body flex-col items-start p-4">
<h2 className="text-xl font-semibold mb-2">{book.name}</h2>
<p className="text-sm text-gray-600 line-clamp-2 mb-3">{book.desc}</p>
<p className="text-sm text-gray-600 line-clamp-4 mb-3">{book.desc}</p>
<div className="flex justify-between items-center w-full">
<span
role="button"
tabIndex={0}
className="text-sm px-2 py-1 bg-secondary-100 text-secondary-700 rounded-full cursor-pointer"
onClick={() => {
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}
Expand All @@ -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;
};
78 changes: 67 additions & 11 deletions src/views/BookView.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<BookDetails | null>(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<BookDetails>(url);
setBook(response);
} catch (error) {
console.error('获取书籍详情失败:', error);
} finally {
setLoading(false);
}
}, []);

useEffect(() => {
const fetchBookDetails = async () => {
try {
const response = await http.get<BookDetails>(`/book/bqs/xiaoshuo/${url}`);
setBook(response);
setLoading(true);
const origins = await http.get<BookDetail[]>(`/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 {
Expand All @@ -30,7 +55,16 @@ const BookView = () => {
};

fetchBookDetails();
}, [url]);
}, [getBookDetails, name]);

const JumpToBookChapter = useCallback(
(url: string) => {
if (url) {
navigate(`/chapter/${url}`);
}
},
[navigate]
);

if (loading) {
return <Loading />;
Expand All @@ -46,13 +80,12 @@ const BookView = () => {

return (
<div className="max-w-4xl mx-auto px-4 py-8">
<div className="h-16" />
<div className="card card-side bg-base-100 shadow-xl transition-transform transform hover:scale-105">
<figure className="ml-6">
<figure className="ml-6 w-48 h-64 mb-4 rounded-lg shadow-md">
<img
src={`https://api.book.bbdaxia.com${book.img}`}
alt={book.name}
className="w-48 h-auto mb-4 md:mb-0 md:mr-6 object-contain rounded-lg shadow-md"
className="object-contain"
/>
</figure>
<div className="card-body">
Expand All @@ -62,10 +95,33 @@ const BookView = () => {
<p className="mb-4 text-gray-700">{book.desc}</p>
</div>
</div>
<h2 className="text-2xl font-semibold mt-4 mb-4 text-gray-800">章节列表</h2>
<div className="flex items-center justify-between">
<h2 className="text-2xl font-semibold mt-4 mb-4 text-gray-800">章节列表</h2>
<div className="flex items-center">
<span>源:</span>
<select
className="select select-bordered select-sm w-full max-w-xs ml-4"
style={{ outlineOffset: 0 }}
onChange={(e) => getBookDetails(e.target.value)}
>
{options.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
</div>
</div>
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
{book.list.map((chapter, index) => (
<a className="link link-hover truncate hover:underline" key={index} href={chapter.url}>
// eslint-disable-next-line jsx-a11y/anchor-is-valid
<a
className="link link-hover truncate hover:underline"
key={index}
onClick={() => {
JumpToBookChapter(getLastNumbersFromUrl(chapter.url).join('-'));
}}
>
{chapter.name}
</a>
))}
Expand Down

0 comments on commit 1659d9f

Please sign in to comment.