Skip to content

Commit a225ba6

Browse files
committed
feat: implement responsive filter panel with collapsible search for mobile layout
1 parent 303fe39 commit a225ba6

1 file changed

Lines changed: 175 additions & 93 deletions

File tree

src/components/dialog/SiteResourceDialog.vue

Lines changed: 175 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,15 @@ const resourcePage = ref(1)
4141
// 加载状态
4242
const resourceLoading = ref(false)
4343
44+
// 移动端搜索栏是否展开
45+
const mobileSearchExpanded = ref(false)
46+
4447
// 种子元数据
4548
const torrent = ref<TorrentInfo>()
4649
4750
// 添加下载对话框
4851
const addDownloadDialog = ref(false)
4952
50-
// 移动端每页条数
51-
const mobileItemsPerPage = 8
52-
5353
// 分类选项
5454
const categoryOptions = computed(() => {
5555
return siteCategoryList.value?.map(item => {
@@ -88,17 +88,11 @@ const resultSummaryText = computed(() => {
8888
return `${resourceTotalItems.value} results`
8989
})
9090
91-
// 移动端分页总页数
92-
const mobilePageCount = computed(() => {
93-
return Math.max(1, Math.ceil(resourceTotalItems.value / mobileItemsPerPage))
94-
})
91+
// 是否小屏幕
92+
const isMobileLayout = computed(() => display.smAndDown.value)
9593
9694
// 移动端分页数据
97-
const mobileResourceList = computed(() => {
98-
const start = (resourcePage.value - 1) * mobileItemsPerPage
99-
100-
return resourceDataList.value.slice(start, start + mobileItemsPerPage)
101-
})
95+
const mobileResourceList = computed(() => resourceDataList.value)
10296
10397
// 打开种子详情页面
10498
function openTorrentDetail(page_url: string) {
@@ -154,6 +148,10 @@ async function getResourceList() {
154148
}
155149
156150
resourceLoading.value = false
151+
152+
if (isMobileLayout.value) {
153+
mobileSearchExpanded.value = false
154+
}
157155
}
158156
159157
// 加载站点分类
@@ -174,12 +172,25 @@ watch([resourceItemsPerPage, resourceTotalItems, () => display.mdAndUp.value], (
174172
175173
return
176174
}
177-
178-
if (resourcePage.value > mobilePageCount.value) {
179-
resourcePage.value = mobilePageCount.value
180-
}
181175
})
182176
177+
watch(
178+
() => display.mdAndUp.value,
179+
isDesktop => {
180+
if (isDesktop) {
181+
mobileSearchExpanded.value = false
182+
}
183+
},
184+
)
185+
186+
function toggleMobileSearch() {
187+
mobileSearchExpanded.value = !mobileSearchExpanded.value
188+
}
189+
190+
function closeMobileSearch() {
191+
mobileSearchExpanded.value = false
192+
}
193+
183194
// 装载时查询站点分类和资源
184195
onMounted(() => {
185196
getSiteCategoryList()
@@ -203,67 +214,141 @@ onMounted(() => {
203214
</div>
204215

205216
<div class="pa-3 pb-2">
206-
<VSheet class="site-resource-filter-panel" rounded="lg" border>
207-
<div class="site-resource-filter-panel__inner">
208-
<VRow class="site-resource-filter-row">
209-
<VCol cols="12" md="4">
210-
<VTextField
211-
v-model="keyword"
212-
class="site-resource-filter-input"
213-
size="small"
214-
density="compact"
215-
variant="solo-filled"
216-
flat
217-
:label="keywordFieldLabel"
218-
clearable
219-
prepend-inner-icon="mdi-magnify"
220-
hide-details
221-
@keyup.enter="getResourceList"
222-
/>
223-
</VCol>
224-
<VCol cols="12" md="5">
225-
<VSelect
226-
v-model="selectCategory"
227-
:items="categoryOptions"
228-
class="site-resource-filter-input"
229-
size="small"
230-
density="compact"
231-
variant="solo-filled"
232-
flat
233-
chips
234-
:label="categoryFieldLabel"
235-
multiple
236-
clearable
237-
prepend-inner-icon="mdi-folder"
238-
hide-details
239-
/>
240-
</VCol>
241-
<VCol cols="12" md="3" class="d-flex align-center">
242-
<VBtn
243-
color="primary"
244-
variant="flat"
245-
block
246-
size="default"
247-
rounded="lg"
248-
prepend-icon="mdi-magnify"
249-
class="site-resource-search-btn"
250-
@click="getResourceList"
251-
>
252-
{{ t('dialog.siteResource.search') }}
253-
</VBtn>
254-
</VCol>
255-
</VRow>
256-
257-
<div v-if="resourceTotalItems > 0" class="d-flex justify-space-between align-center flex-wrap gap-2 mt-3">
258-
<div class="text-body-2 text-medium-emphasis">
259-
{{ resultSummaryText }}
217+
<template v-if="!isMobileLayout">
218+
<VSheet class="site-resource-filter-panel" rounded="lg" border>
219+
<div class="site-resource-filter-panel__inner">
220+
<VRow class="site-resource-filter-row">
221+
<VCol cols="12" md="4">
222+
<VTextField
223+
v-model="keyword"
224+
class="site-resource-filter-input"
225+
size="small"
226+
density="compact"
227+
variant="solo-filled"
228+
flat
229+
:label="keywordFieldLabel"
230+
clearable
231+
prepend-inner-icon="mdi-magnify"
232+
hide-details
233+
@keyup.enter="getResourceList"
234+
/>
235+
</VCol>
236+
<VCol cols="12" md="5">
237+
<VSelect
238+
v-model="selectCategory"
239+
:items="categoryOptions"
240+
class="site-resource-filter-input"
241+
size="small"
242+
density="compact"
243+
variant="solo-filled"
244+
flat
245+
chips
246+
:label="categoryFieldLabel"
247+
multiple
248+
clearable
249+
prepend-inner-icon="mdi-folder"
250+
hide-details
251+
/>
252+
</VCol>
253+
<VCol cols="12" md="3" class="d-flex align-center">
254+
<VBtn
255+
color="primary"
256+
variant="flat"
257+
block
258+
size="default"
259+
rounded="lg"
260+
prepend-icon="mdi-magnify"
261+
class="site-resource-search-btn"
262+
@click="getResourceList"
263+
>
264+
{{ t('dialog.siteResource.search') }}
265+
</VBtn>
266+
</VCol>
267+
</VRow>
268+
269+
<div
270+
v-if="resourceTotalItems > 0"
271+
class="d-flex justify-space-between align-center flex-wrap gap-2 mt-3"
272+
>
273+
<div class="text-body-2 text-medium-emphasis">
274+
{{ resultSummaryText }}
275+
</div>
276+
<VChip size="small" color="primary" variant="tonal" class="site-resource-result-chip">
277+
{{ resourceTotalItems }}
278+
</VChip>
260279
</div>
261-
<VChip size="small" color="primary" variant="tonal" class="site-resource-result-chip">
262-
{{ resourceTotalItems }}
263-
</VChip>
280+
</div>
281+
</VSheet>
282+
</template>
283+
284+
<template v-else>
285+
<div class="site-resource-mobile-search">
286+
<VBtn
287+
icon
288+
variant="text"
289+
color="primary"
290+
class="site-resource-mobile-search__toggle"
291+
@click="toggleMobileSearch"
292+
>
293+
<VIcon icon="mdi-magnify" />
294+
</VBtn>
295+
<div v-if="resourceTotalItems > 0" class="text-body-2 text-medium-emphasis">
296+
{{ resultSummaryText }}
264297
</div>
265298
</div>
266-
</VSheet>
299+
300+
<VExpandTransition>
301+
<div v-if="mobileSearchExpanded" class="mt-2">
302+
<VSheet class="site-resource-filter-panel" rounded="lg" border>
303+
<div class="site-resource-filter-panel__inner">
304+
<VRow class="site-resource-filter-row">
305+
<VCol cols="12">
306+
<VTextField
307+
v-model="keyword"
308+
class="site-resource-filter-input"
309+
size="small"
310+
density="compact"
311+
variant="solo-filled"
312+
flat
313+
:label="keywordFieldLabel"
314+
clearable
315+
prepend-inner-icon="mdi-magnify"
316+
hide-details
317+
autofocus
318+
@keyup.enter="getResourceList"
319+
/>
320+
</VCol>
321+
<VCol cols="12">
322+
<VSelect
323+
v-model="selectCategory"
324+
:items="categoryOptions"
325+
class="site-resource-filter-input"
326+
size="small"
327+
density="compact"
328+
variant="solo-filled"
329+
flat
330+
chips
331+
:label="categoryFieldLabel"
332+
multiple
333+
clearable
334+
prepend-inner-icon="mdi-folder"
335+
hide-details
336+
/>
337+
</VCol>
338+
<VCol cols="12" class="d-flex gap-2">
339+
<VBtn color="primary" variant="flat" block rounded="lg" class="site-resource-search-btn" @click="getResourceList">
340+
{{ t('dialog.siteResource.search') }}
341+
</VBtn>
342+
<VBtn variant="text" rounded="lg" @click="closeMobileSearch">
343+
{{ t('common.cancel') }}
344+
</VBtn>
345+
</VCol>
346+
</VRow>
347+
</div>
348+
</VSheet>
349+
</div>
350+
</VExpandTransition>
351+
</template>
267352
</div>
268353

269354
<VCardText class="site-resource-content px-0 py-0 my-0">
@@ -384,9 +469,7 @@ onMounted(() => {
384469
<VCard
385470
v-for="(item, index) in mobileResourceList"
386471
:key="item.page_url || item.enclosure || `${item.title}-${index}`"
387-
class="site-resource-card mb-3"
388-
rounded="lg"
389-
variant="tonal"
472+
class="mb-3"
390473
>
391474
<VCardText class="pa-4">
392475
<button type="button" class="site-resource-title-btn text-start" @click="addDownload(item)">
@@ -474,14 +557,6 @@ onMounted(() => {
474557
</VCardText>
475558
</VCard>
476559

477-
<VPagination
478-
v-if="mobilePageCount > 1"
479-
v-model="resourcePage"
480-
:length="mobilePageCount"
481-
:total-visible="0"
482-
rounded="circle"
483-
class="mt-2"
484-
/>
485560
</div>
486561

487562
<div v-else class="px-4 py-10 text-center text-medium-emphasis">
@@ -545,6 +620,17 @@ onMounted(() => {
545620
font-weight: 600;
546621
}
547622
623+
.site-resource-mobile-search {
624+
display: flex;
625+
align-items: center;
626+
justify-content: space-between;
627+
gap: 0.75rem;
628+
}
629+
630+
.site-resource-mobile-search__toggle {
631+
flex: 0 0 auto;
632+
}
633+
548634
.site-resource-title-btn {
549635
padding: 0;
550636
border: 0;
@@ -587,14 +673,6 @@ onMounted(() => {
587673
white-space: nowrap;
588674
}
589675
590-
.site-resource-card {
591-
border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
592-
background:
593-
linear-gradient(180deg, rgba(var(--v-theme-surface), 0.98), rgba(var(--v-theme-surface), 0.92)),
594-
linear-gradient(135deg, rgba(var(--v-theme-primary), 0.05), rgba(var(--v-theme-secondary), 0.03));
595-
box-shadow: 0 10px 30px rgba(15, 23, 42, 6%);
596-
}
597-
598676
.site-resource-card__description {
599677
display: -webkit-box;
600678
overflow: hidden;
@@ -651,5 +729,9 @@ onMounted(() => {
651729
.site-resource-filter-panel__inner {
652730
padding: 0.7rem 0.75rem;
653731
}
732+
733+
.site-resource-mobile-search {
734+
min-block-size: 2.5rem;
735+
}
654736
}
655737
</style>

0 commit comments

Comments
 (0)