动态Tab组件

This commit is contained in:
jxxghp
2025-07-05 12:13:53 +08:00
parent 09942ec946
commit da0756adf0
5 changed files with 351 additions and 98 deletions
+27 -14
View File
@@ -119,6 +119,9 @@ async function saveTabOrder() {
}
}
// 使用动态标签页
const { registerHeaderTab } = useDynamicHeaderTab()
onBeforeMount(async () => {
initDiscoverTabs()
await loadOrderConfig()
@@ -130,28 +133,38 @@ onBeforeMount(async () => {
}
})
onMounted(() => {
// 注册动态标签页
registerHeaderTab({
items: discoverTabItems.value,
modelValue: activeTab,
appendButtons: [
{
icon: 'mdi-order-alphabetical-ascending',
variant: 'text',
color: 'grey',
class: 'settings-icon-button',
action: () => {
orderConfigDialog.value = true
},
},
],
})
})
onActivated(async () => {
await loadExtraDiscoverSources()
sortSubscribeOrder()
})
function useDynamicHeaderTab(): { registerHeaderTab: any } {
throw new Error('Function not implemented.')
}
</script>
<template>
<div>
<VHeaderTab :items="discoverTabItems" v-model="activeTab">
<template #append>
<VBtn
icon="mdi-order-alphabetical-ascending"
variant="text"
color="grey"
size="default"
class="settings-icon-button"
@click="orderConfigDialog = true"
/>
</template>
</VHeaderTab>
<VWindow v-model="activeTab" class="mt-5 disable-tab-transition" :touch="false">
<VWindow v-model="activeTab" class="disable-tab-transition" :touch="false">
<VWindowItem value="themoviedb">
<transition name="fade-slide" appear>
<div>
+114 -80
View File
@@ -4,6 +4,7 @@ import SubscribePopularView from '@/views/subscribe/SubscribePopularView.vue'
import SubscribeShareView from '@/views/subscribe/SubscribeShareView.vue'
import SubscribeEditDialog from '@/components/dialog/SubscribeEditDialog.vue'
import { useI18n } from 'vue-i18n'
import { useDynamicHeaderTab } from '@/composables/useDynamicHeaderTab'
import { getSubscribeMovieTabs, getSubscribeTvTabs } from '@/router/i18n-menu'
@@ -14,7 +15,7 @@ const route = useRoute()
const subType = route.meta.subType?.toString()
const subId = ref(route.query.id as string)
const activeTab = ref(route.query.tab)
const activeTab = ref((route.query.tab as string) || '')
const shareViewKey = ref(0)
// 获取标签页
@@ -46,89 +47,64 @@ const searchShares = () => {
searchShareDialog.value = false
shareViewKey.value++
}
// VMenu activator选择器
const filterActivator = computed(() => '[data-menu-activator="filter-btn"]')
const searchActivator = computed(() => '[data-menu-activator="search-btn"]')
// 使用动态标签页
const { registerHeaderTab } = useDynamicHeaderTab()
// 注册动态标签页
onMounted(() => {
// 设置初始activeTab值
if (!activeTab.value && subscribeTabs.value.length > 0) {
activeTab.value = subscribeTabs.value[0].tab
}
registerHeaderTab({
items: subscribeTabs.value,
modelValue: activeTab,
appendButtons: [
{
icon: 'mdi-filter-multiple-outline',
variant: 'text',
color: computed(() => (subscribeFilter.value ? 'primary' : 'gray')),
class: 'settings-icon-button',
dataAttr: 'filter-btn',
action: () => {
filterSubscribeDialog.value = true
},
show: computed(() => activeTab.value === 'mysub'),
},
{
icon: 'mdi-movie-search-outline',
variant: 'text',
color: computed(() => (shareKeyword.value ? 'primary' : 'gray')),
class: 'settings-icon-button',
dataAttr: 'search-btn',
action: () => {
searchShareDialog.value = true
},
show: computed(() => activeTab.value === 'share'),
},
{
icon: 'mdi-clipboard-edit-outline',
variant: 'text',
color: 'gray',
class: 'settings-icon-button',
action: () => {
subscribeEditDialog.value = true
},
show: computed(() => activeTab.value === 'mysub'),
},
],
})
})
</script>
<template>
<div>
<VHeaderTab :items="subscribeTabs" v-model="activeTab">
<template #append>
<VMenu
v-if="activeTab === 'mysub'"
v-model="filterSubscribeDialog"
width="20rem"
:close-on-content-click="false"
scrim
>
<template #activator="{ props }">
<VBtn
icon="mdi-filter-multiple-outline"
variant="text"
:color="subscribeFilter ? 'primary' : 'gray'"
size="default"
class="settings-icon-button"
v-bind="props"
/>
</template>
<VCard>
<VCardItem>
<VCardTitle>
<VIcon icon="mdi-filter-multiple-outline" class="mr-2" />
{{ t('subscribe.filterSubscriptions') }}
</VCardTitle>
<VDialogCloseBtn @click="filterSubscribeDialog = false" />
</VCardItem>
<VCardText>
<VTextField v-model="subscribeFilter" :label="t('subscribe.name')" clearable density="comfortable" />
</VCardText>
</VCard>
</VMenu>
<VMenu
v-if="activeTab === 'share'"
v-model="searchShareDialog"
width="25rem"
:close-on-content-click="false"
scrim
>
<template #activator="{ props }">
<VBtn
icon="mdi-movie-search-outline"
variant="text"
:color="shareKeyword ? 'primary' : 'gray'"
size="default"
class="settings-icon-button"
v-bind="props"
/>
</template>
<VCard>
<VCardItem>
<VCardTitle>
<VIcon icon="mdi-movie-search-outline" class="mr-2" />
{{ t('subscribe.searchShares') }}
</VCardTitle>
<VDialogCloseBtn @click="searchShareDialog = false" />
</VCardItem>
<VCardText>
<VTextField v-model="shareKeyword" :label="t('subscribe.keyword')" clearable density="comfortable">
<template #append>
<VBtn prepend-icon="mdi-magnify" color="primary" @click="searchShares">{{ t('common.search') }}</VBtn>
</template>
</VTextField>
</VCardText>
</VCard>
</VMenu>
<VBtn
v-if="activeTab === 'mysub'"
icon="mdi-clipboard-edit-outline"
variant="text"
color="gray"
size="default"
class="settings-icon-button"
@click="subscribeEditDialog = true"
/>
</template>
</VHeaderTab>
<VWindow v-model="activeTab" class="disable-tab-transition" :touch="false">
<VWindow v-model="activeTab" class="disable-tab-transition content-window" :touch="false">
<VWindowItem value="mysub">
<transition name="fade-slide" appear>
<div>
@@ -152,6 +128,58 @@ const searchShares = () => {
</VWindowItem>
</VWindow>
<!-- 订阅过滤弹窗 -->
<Teleport to="body" v-if="filterSubscribeDialog">
<VMenu
v-model="filterSubscribeDialog"
width="20rem"
:close-on-content-click="false"
:activator="filterActivator"
location="bottom end"
>
<VCard>
<VCardItem>
<VCardTitle>
<VIcon icon="mdi-filter-multiple-outline" class="mr-2" />
{{ t('subscribe.filterSubscriptions') }}
</VCardTitle>
<VDialogCloseBtn @click="filterSubscribeDialog = false" />
</VCardItem>
<VCardText>
<VTextField v-model="subscribeFilter" :label="t('subscribe.name')" clearable density="comfortable" />
</VCardText>
</VCard>
</VMenu>
</Teleport>
<!-- 搜索订阅分享弹窗 -->
<Teleport to="body" v-if="searchShareDialog">
<VMenu
v-model="searchShareDialog"
width="25rem"
:close-on-content-click="false"
:activator="searchActivator"
location="bottom end"
>
<VCard>
<VCardItem>
<VCardTitle>
<VIcon icon="mdi-movie-search-outline" class="mr-2" />
{{ t('subscribe.searchShares') }}
</VCardTitle>
<VDialogCloseBtn @click="searchShareDialog = false" />
</VCardItem>
<VCardText>
<VTextField v-model="shareKeyword" :label="t('subscribe.keyword')" clearable density="comfortable">
<template #append>
<VBtn prepend-icon="mdi-magnify" color="primary" @click="searchShares">{{ t('common.search') }}</VBtn>
</template>
</VTextField>
</VCardText>
</VCard>
</VMenu>
</Teleport>
<!-- 订阅编辑弹窗 -->
<SubscribeEditDialog
v-if="subscribeEditDialog"
@@ -163,3 +191,9 @@ const searchShares = () => {
/>
</div>
</template>
<style scoped>
.content-window {
margin-block-start: 0;
}
</style>