feat(book): 添加章节标题模糊搜索功能
1. 后端接口新增keyword参数支持章节标题模糊查询 2. 前端书籍详情页添加章节搜索输入框,调用更新后的接口 3. 调整章节头部布局适配搜索框
This commit is contained in:
parent
e6329919be
commit
dd7504aae7
|
|
@ -155,19 +155,22 @@ pub async fn book_detail(
|
||||||
pub async fn chapter_page(
|
pub async fn chapter_page(
|
||||||
pool: State<'_, SqlitePool>,
|
pool: State<'_, SqlitePool>,
|
||||||
book_id: i64,
|
book_id: i64,
|
||||||
|
keyword: String,
|
||||||
page: i32,
|
page: i32,
|
||||||
limit: i32,
|
limit: i32,
|
||||||
) -> Result<ApiResponse<PageResult<Chapters>>, String> {
|
) -> Result<ApiResponse<PageResult<Chapters>>, String> {
|
||||||
let offset = (page - 1) * limit;
|
let offset = (page - 1) * limit;
|
||||||
let total: i64 =
|
let total: i64 =
|
||||||
sqlx::query_scalar::<_, i64>("SELECT COUNT(id) FROM chapters WHERE book_id = ?")
|
sqlx::query_scalar::<_, i64>("SELECT COUNT(id) FROM chapters WHERE book_id = ? AND title LIKE ?")
|
||||||
.bind(book_id)
|
.bind(book_id)
|
||||||
|
.bind(format!("%{}%", keyword))
|
||||||
.fetch_one(&*pool)
|
.fetch_one(&*pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
let chapters =
|
let chapters =
|
||||||
sqlx::query_as::<_, Chapters>("SELECT * FROM chapters WHERE book_id = ? LIMIT ?,?")
|
sqlx::query_as::<_, Chapters>("SELECT * FROM chapters WHERE book_id = ? AND title LIKE ? LIMIT ?,?")
|
||||||
.bind(book_id)
|
.bind(book_id)
|
||||||
|
.bind(format!("%{}%", keyword))
|
||||||
.bind(offset)
|
.bind(offset)
|
||||||
.bind(limit)
|
.bind(limit)
|
||||||
.fetch_all(&*pool)
|
.fetch_all(&*pool)
|
||||||
|
|
|
||||||
|
|
@ -45,12 +45,13 @@ const BookApi = {
|
||||||
/**
|
/**
|
||||||
* 获取书籍章节列表
|
* 获取书籍章节列表
|
||||||
* @param bookId 书籍ID
|
* @param bookId 书籍ID
|
||||||
|
* @param keyword 章节标题模糊查询
|
||||||
* @param page 页码
|
* @param page 页码
|
||||||
* @param limit 每页数量
|
* @param limit 每页数量
|
||||||
* @returns 书籍章节列表
|
* @returns 书籍章节列表
|
||||||
*/
|
*/
|
||||||
chapters: async (bookId: number, page: number, limit: number) => {
|
chapters: async (bookId: number, keyword: string, page: number, limit: number) => {
|
||||||
return await invoke<ApiResponse<PageResult<Chapters>>>("chapter_page", { bookId, page, limit })
|
return await invoke<ApiResponse<PageResult<Chapters>>>("chapter_page", { bookId, keyword, page, limit })
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* 获取章节详情
|
* 获取章节详情
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,18 @@
|
||||||
|
|
||||||
<section class="chapter-section">
|
<section class="chapter-section">
|
||||||
<div class="chapter-header">
|
<div class="chapter-header">
|
||||||
<span class="chapter-name">章节列表</span>
|
<div class="chapter-header-left">
|
||||||
<span class="chapter-count">共 {{ total }} 章</span>
|
<span class="chapter-name">章节列表</span>
|
||||||
|
<span class="chapter-count">共 {{ total }} 章</span>
|
||||||
|
</div>
|
||||||
|
<n-input
|
||||||
|
v-model:value="keyword"
|
||||||
|
class="chapter-search"
|
||||||
|
placeholder="搜索章节"
|
||||||
|
clearable
|
||||||
|
size="small"
|
||||||
|
@update:value="loadChapters"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<n-scrollbar class="chapter-scrollbar">
|
<n-scrollbar class="chapter-scrollbar">
|
||||||
|
|
@ -122,6 +132,7 @@ const total = ref(0)
|
||||||
const page = ref(1)
|
const page = ref(1)
|
||||||
const limit = ref(50)
|
const limit = ref(50)
|
||||||
const loadingChapters = ref(false)
|
const loadingChapters = ref(false)
|
||||||
|
const keyword = ref("")
|
||||||
|
|
||||||
const goBack = () => router.back()
|
const goBack = () => router.back()
|
||||||
|
|
||||||
|
|
@ -163,7 +174,7 @@ const loadChapters = async () => {
|
||||||
loadingChapters.value = true
|
loadingChapters.value = true
|
||||||
try {
|
try {
|
||||||
const res = await withLoading(
|
const res = await withLoading(
|
||||||
() => BookApi.chapters(bookId.value, page.value, limit.value),
|
() => BookApi.chapters(bookId.value, keyword.value, page.value, limit.value),
|
||||||
"加载章节中..."
|
"加载章节中..."
|
||||||
)
|
)
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
|
|
@ -304,11 +315,24 @@ onMounted(() => {
|
||||||
.chapter-header {
|
.chapter-header {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: baseline;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chapter-header-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 8px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chapter-search {
|
||||||
|
width: 220px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.chapter-name {
|
.chapter-name {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
@ -337,6 +361,7 @@ onMounted(() => {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
border-radius: var(--radius-sm);
|
border-radius: var(--radius-sm);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue