Skip to content

Commit 9db175a

Browse files
author
Taois
committed
feat: 搜索翻页
py支持搜索翻页。搜索结果仅含list的情况
1 parent a1e166c commit 9db175a

File tree

5 files changed

+166
-34
lines changed

5 files changed

+166
-34
lines changed

dashboard/src/api/services/video.js

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,8 @@ class VideoService {
229229
videos: (response.list || []).map(this.formatVideoInfo),
230230
pagination: this.createPagination(response, page),
231231
keyword: keyword.trim(),
232-
total: response.total || 0
232+
total: response.total || 0,
233+
rawResponse: response // 添加原始响应数据用于调试
233234
}
234235

235236
return result
@@ -405,10 +406,44 @@ class VideoService {
405406
const pagination = createPaginationInfo()
406407

407408
pagination.page = currentPage
408-
pagination.total = response.total || 0
409-
pagination.pageSize = response.limit || 20
410-
pagination.totalPages = Math.ceil(pagination.total / pagination.pageSize)
411-
pagination.hasNext = currentPage < pagination.totalPages
409+
410+
// 处理不同的API响应格式
411+
const total = response.total || response.recordcount || 0
412+
const pageCount = response.pagecount || response.totalPages || 0
413+
const pageSize = response.limit || response.pagesize || 20
414+
const currentList = response.list || []
415+
416+
pagination.total = total
417+
pagination.pageSize = pageSize
418+
419+
// 如果API返回了总页数,直接使用
420+
if (pageCount > 0) {
421+
pagination.totalPages = pageCount
422+
pagination.hasNext = currentPage < pageCount
423+
} else if (total > 0) {
424+
// 否则根据总数计算
425+
pagination.totalPages = Math.ceil(total / pageSize)
426+
pagination.hasNext = currentPage < pagination.totalPages
427+
} else {
428+
// 如果没有总数信息,根据当前返回的数据判断
429+
// 检查是否有"no_data"标识
430+
const hasNoDataFlag = currentList.some(item =>
431+
item.vod_id === 'no_data' ||
432+
item.vod_name === 'no_data' ||
433+
(typeof item === 'string' && item.includes('no_data'))
434+
)
435+
436+
if (hasNoDataFlag || currentList.length === 0) {
437+
// 如果有no_data标识或列表为空,表示没有更多数据
438+
pagination.hasNext = false
439+
pagination.totalPages = currentPage
440+
} else {
441+
// 否则假设还有下一页,需要实际请求下一页来确认
442+
pagination.hasNext = true
443+
pagination.totalPages = currentPage + 1
444+
}
445+
}
446+
412447
pagination.hasPrev = currentPage > 1
413448

414449
return pagination

dashboard/src/components/Breadcrumb.vue

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121
<!-- 中间搜索框 -->
2222
<div class="header-center">
2323
<a-input-search
24+
v-model="searchValue"
2425
placeholder="搜索视频"
2526
enter-button
2627
@search="onSearch"
28+
@press-enter="onSearch"
2729
/>
2830
</div>
2931

@@ -57,6 +59,8 @@
5759
</template>
5860

5961
<script setup>
62+
import { ref } from 'vue'
63+
6064
const props = defineProps({
6165
navigation_title: {
6266
type: String,
@@ -74,14 +78,20 @@ const emit = defineEmits([
7478
"onSearch",
7579
]);
7680
81+
const searchValue = ref('')
82+
7783
const handleOpenForm = () => {
7884
emit("handleOpenForm");
7985
};
8086
const refreshPage = () => {
8187
emit("refreshPage");
8288
};
8389
const onSearch = (value) => {
84-
emit("onSearch", value);
90+
// 如果value是字符串,使用它;否则使用当前输入框的值
91+
const searchTerm = typeof value === 'string' ? value : searchValue.value
92+
if (searchTerm && typeof searchTerm === 'string' && searchTerm.trim()) {
93+
emit("onSearch", searchTerm.trim());
94+
}
8595
};
8696
8797
const closeWindow = () => {

dashboard/src/components/FilterSection.vue

Lines changed: 84 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,23 @@
3131
:key="filterGroup.key"
3232
class="filter-group"
3333
>
34-
<div class="filter-group-title">{{ filterGroup.name }}</div>
35-
<div class="filter-options">
36-
<a-tag
37-
v-for="option in filterGroup.value"
38-
:key="option.v"
39-
:color="isSelected(filterGroup.key, option.v) ? 'green' : ''"
40-
:checkable="true"
41-
:checked="isSelected(filterGroup.key, option.v)"
42-
@check="handleToggleFilter(filterGroup.key, option.v, option.n)"
43-
class="filter-option-tag"
44-
>
45-
{{ option.n }}
46-
</a-tag>
34+
<div class="filter-group-row">
35+
<div class="filter-group-title">{{ filterGroup.name }}</div>
36+
<div class="filter-options-container">
37+
<div class="filter-options">
38+
<a-tag
39+
v-for="option in filterGroup.value"
40+
:key="option.v"
41+
:color="isSelected(filterGroup.key, option.v) ? 'green' : ''"
42+
:checkable="true"
43+
:checked="isSelected(filterGroup.key, option.v)"
44+
@check="handleToggleFilter(filterGroup.key, option.v, option.n)"
45+
class="filter-option-tag"
46+
>
47+
{{ option.n }}
48+
</a-tag>
49+
</div>
50+
</div>
4751
</div>
4852
</div>
4953
</div>
@@ -126,33 +130,93 @@ const handleResetFilters = () => {
126130
}
127131
128132
.filter-content {
129-
padding: 0 16px 16px;
133+
padding: 0 16px 8px;
130134
}
131135
132136
.filter-group {
133-
margin-bottom: 16px;
137+
margin-bottom: 4px;
138+
padding: 4px 12px;
139+
background: var(--color-fill-1);
140+
border-radius: 6px;
141+
border-left: 3px solid var(--color-primary-light-3);
134142
}
135143
136144
.filter-group:last-child {
137145
margin-bottom: 0;
138146
}
139147
148+
.filter-group-row {
149+
display: flex;
150+
align-items: center;
151+
gap: 16px;
152+
min-height: 28px;
153+
}
154+
140155
.filter-group-title {
141-
font-size: 14px;
142-
font-weight: 500;
143-
color: var(--color-text-1);
144-
margin-bottom: 8px;
156+
font-size: 13px;
157+
font-weight: 600;
158+
color: var(--color-text-2);
159+
white-space: nowrap;
160+
flex-shrink: 0;
161+
min-width: 70px;
162+
text-align: left;
163+
background: var(--color-fill-3);
164+
padding: 4px 8px 4px 20px;
165+
border-radius: 4px;
166+
border: 1px solid var(--color-border-2);
167+
position: relative;
168+
}
169+
170+
.filter-group-title::before {
171+
content: '';
172+
position: absolute;
173+
left: 6px;
174+
top: 50%;
175+
transform: translateY(-50%);
176+
width: 8px;
177+
height: 8px;
178+
background: var(--color-primary-light-4);
179+
border-radius: 2px;
180+
border: 1px solid var(--color-primary-light-2);
181+
}
182+
183+
.filter-options-container {
184+
flex: 1;
185+
overflow: hidden;
145186
}
146187
147188
.filter-options {
148189
display: flex;
149-
flex-wrap: wrap;
150190
gap: 8px;
191+
overflow-x: auto;
192+
overflow-y: hidden;
193+
padding: 2px 0;
194+
scrollbar-width: thin;
195+
scrollbar-color: var(--color-border-3) transparent;
196+
}
197+
198+
.filter-options::-webkit-scrollbar {
199+
height: 4px;
200+
}
201+
202+
.filter-options::-webkit-scrollbar-track {
203+
background: transparent;
204+
}
205+
206+
.filter-options::-webkit-scrollbar-thumb {
207+
background: var(--color-border-3);
208+
border-radius: 2px;
209+
}
210+
211+
.filter-options::-webkit-scrollbar-thumb:hover {
212+
background: var(--color-border-2);
151213
}
152214
153215
.filter-option-tag {
154216
cursor: pointer;
155217
transition: all 0.2s ease;
218+
white-space: nowrap;
219+
flex-shrink: 0;
156220
}
157221
158222
.filter-option-tag:hover {

dashboard/src/views/Video.vue

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,6 @@ const onSearch = async (value) => {
253253
searchState.currentPage = 1;
254254
255255
try {
256-
console.log("开始搜索:", keyword, "当前源:", form.now_site);
257-
258256
if (!form.now_site || !form.now_site.key) {
259257
throw new Error("请先选择数据源");
260258
}
@@ -270,8 +268,6 @@ const onSearch = async (value) => {
270268
searchState.searchResults = searchData.videos || [];
271269
searchState.totalPages = searchData.pagination?.totalPages || 1;
272270
searchState.hasMore = searchData.pagination?.hasNext || false;
273-
274-
console.log("搜索结果:", searchState.searchResults);
275271
} catch (error) {
276272
console.error("搜索失败:", error);
277273
searchState.searchError = error.message || "搜索失败,请重试";
@@ -296,12 +292,30 @@ const onSearchLoadMore = async () => {
296292
apiUrl: form.now_site.api
297293
});
298294
295+
296+
299297
// 追加新的搜索结果到现有结果中
300298
const newVideos = searchData.videos || [];
301-
searchState.searchResults = [...searchState.searchResults, ...newVideos];
299+
300+
// 检查是否有重复数据或no_data标识
301+
const existingIds = new Set(searchState.searchResults.map(v => v.vod_id));
302+
const uniqueNewVideos = newVideos.filter(video => {
303+
// 过滤掉重复的视频和no_data标识
304+
return !existingIds.has(video.vod_id) &&
305+
video.vod_id !== 'no_data' &&
306+
video.vod_name !== 'no_data';
307+
});
308+
309+
// 如果新数据为空或全部重复,表示没有更多数据
310+
if (uniqueNewVideos.length === 0) {
311+
searchState.hasMore = false;
312+
} else {
313+
searchState.searchResults = [...searchState.searchResults, ...uniqueNewVideos];
314+
searchState.hasMore = searchData.pagination?.hasNext !== false; // 只有明确返回false才停止
315+
}
316+
302317
searchState.currentPage = nextPage;
303-
searchState.totalPages = searchData.pagination?.totalPages || 1;
304-
searchState.hasMore = searchData.pagination?.hasNext || false;
318+
searchState.totalPages = searchData.pagination?.totalPages || searchState.totalPages;
305319
} catch (error) {
306320
console.error("搜索加载更多失败:", error);
307321
searchState.searchError = error.message || "加载失败,请重试";
@@ -319,6 +333,11 @@ const exitSearch = () => {
319333
searchState.currentPage = 1;
320334
};
321335
336+
// 处理视频点击事件
337+
const handleVideoClick = (video) => {
338+
// 这里可以添加视频点击后的处理逻辑,比如跳转到播放页面
339+
};
340+
322341
323342
324343
const handleOpenForm = () => {

readme.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,8 @@ https://juejin.cn/post/7301193497247727652
4141

4242
https://drplayer.playdreamer.cn/
4343

44-
https://hipy.playdreamer.cn/
44+
https://hipy.playdreamer.cn/
45+
46+
# AI加量包购买
47+
48+
[点此访问](https://www.trae.ai/account-setting?purchase=1#usage)

0 commit comments

Comments
 (0)