新增订阅功能的状态筛选选项

This commit is contained in:
jxxghp
2025-07-21 09:38:59 +08:00
parent 6f315a408a
commit 752231086d
5 changed files with 151 additions and 10 deletions
+8
View File
@@ -41,6 +41,7 @@ export default {
details: 'Details', details: 'Details',
files: 'Files', files: 'Files',
share: 'Share', share: 'Share',
subscribe: 'Subscribe',
unsubscribe: 'Unsubscribe', unsubscribe: 'Unsubscribe',
media: 'Media', media: 'Media',
unknown: 'Unknown', unknown: 'Unknown',
@@ -60,6 +61,7 @@ export default {
networkOffline: 'Network Offline', networkOffline: 'Network Offline',
serviceAvailable: 'Service Available', serviceAvailable: 'Service Available',
serviceUnavailable: 'Service Unavailable', serviceUnavailable: 'Service Unavailable',
status: 'Status',
}, },
mediaType: { mediaType: {
movie: 'Movie', movie: 'Movie',
@@ -812,6 +814,12 @@ export default {
totalReuseCount: 'Total Reuse Count', totalReuseCount: 'Total Reuse Count',
ranking: 'Ranking', ranking: 'Ranking',
noStatisticsData: 'No share statistics data available', noStatisticsData: 'No share statistics data available',
bestVersion: 'Version Upgrading',
completed: 'Completed',
subscribing: 'Subscribing',
notStarted: 'Not Started',
pending: 'Pending',
paused: 'Paused',
}, },
recommend: { recommend: {
all: 'All', all: 'All',
+8
View File
@@ -41,6 +41,7 @@ export default {
details: '详情', details: '详情',
files: '文件', files: '文件',
share: '分享', share: '分享',
subscribe: '订阅',
unsubscribe: '取消订阅', unsubscribe: '取消订阅',
media: '媒体', media: '媒体',
unknown: '未知', unknown: '未知',
@@ -60,6 +61,7 @@ export default {
networkOffline: '网络离线', networkOffline: '网络离线',
serviceAvailable: '服务可用', serviceAvailable: '服务可用',
serviceUnavailable: '服务不可用', serviceUnavailable: '服务不可用',
status: '状态',
}, },
mediaType: { mediaType: {
movie: '电影', movie: '电影',
@@ -808,6 +810,12 @@ export default {
totalReuseCount: '次复用', totalReuseCount: '次复用',
ranking: '排名', ranking: '排名',
noStatisticsData: '暂无分享统计数据', noStatisticsData: '暂无分享统计数据',
bestVersion: '洗版中',
completed: '订阅完成',
subscribing: '订阅中',
notStarted: '未开始',
pending: '待定',
paused: '暂停',
}, },
recommend: { recommend: {
all: '全部', all: '全部',
+8
View File
@@ -41,6 +41,7 @@ export default {
details: '詳情', details: '詳情',
files: '文件', files: '文件',
share: '分享', share: '分享',
subscribe: '訂閱',
unsubscribe: '取消訂閱', unsubscribe: '取消訂閱',
media: '媒體', media: '媒體',
unknown: '未知', unknown: '未知',
@@ -60,6 +61,7 @@ export default {
networkOffline: '網絡離線', networkOffline: '網絡離線',
serviceAvailable: '服務可用', serviceAvailable: '服務可用',
serviceUnavailable: '服務不可用', serviceUnavailable: '服務不可用',
status: '狀態',
}, },
mediaType: { mediaType: {
movie: '電影', movie: '電影',
@@ -806,6 +808,12 @@ export default {
totalReuseCount: '次複用', totalReuseCount: '次複用',
ranking: '排名', ranking: '排名',
noStatisticsData: '暫無分享統計數據', noStatisticsData: '暫無分享統計數據',
bestVersion: '洗版中',
completed: '訂閱完成',
subscribing: '訂閱中',
notStarted: '未開始',
pending: '待定',
paused: '暫停',
}, },
recommend: { recommend: {
all: '全部', all: '全部',
+65 -4
View File
@@ -43,6 +43,9 @@ const shareStatisticsDialog = ref(false)
// 订阅过滤词 // 订阅过滤词
const subscribeFilter = ref('') const subscribeFilter = ref('')
// 订阅状态筛选
const subscribeStatusFilter = ref<string | null>(null)
// 分享搜索词 // 分享搜索词
const shareKeyword = ref('') const shareKeyword = ref('')
@@ -52,6 +55,41 @@ const searchShares = () => {
shareViewKey.value++ shareViewKey.value++
} }
// 筛选选项
const filterOptions = computed(() => {
const baseOptions = [
{ value: 'all', label: t('common.all'), icon: 'mdi-format-list-bulleted' },
{ value: 'best_version', label: t('subscribe.bestVersion'), icon: 'mdi-refresh', color: 'warning' },
]
// 电影只显示基本选项和状态选项
if (subType === '电影') {
return [
...baseOptions,
{ value: 'pending', label: t('subscribe.pending'), icon: 'mdi-help-circle', color: 'secondary' },
{ value: 'paused', label: t('subscribe.paused'), icon: 'mdi-pause-circle', color: 'error' },
]
}
// 电视剧显示所有选项
return [
...baseOptions,
{ value: 'not_started', label: t('subscribe.notStarted'), icon: 'mdi-clock-outline', color: 'secondary' },
{ value: 'subscribing', label: t('subscribe.subscribing'), icon: 'mdi-download', color: 'info' },
{ value: 'pending', label: t('subscribe.pending'), icon: 'mdi-help-circle', color: 'secondary' },
{ value: 'paused', label: t('subscribe.paused'), icon: 'mdi-pause-circle', color: 'error' },
{ value: 'completed', label: t('subscribe.completed'), icon: 'mdi-check-circle', color: 'success' },
]
})
// 计算筛选按钮颜色
const filterButtonColor = computed(() => {
if (subscribeFilter.value || (subscribeStatusFilter.value && subscribeStatusFilter.value !== 'all')) {
return 'primary'
}
return 'gray'
})
// VMenu activator选择器 // VMenu activator选择器
const filterActivator = computed(() => '[data-menu-activator="filter-btn"]') const filterActivator = computed(() => '[data-menu-activator="filter-btn"]')
const searchActivator = computed(() => '[data-menu-activator="search-btn"]') const searchActivator = computed(() => '[data-menu-activator="search-btn"]')
@@ -67,7 +105,7 @@ registerHeaderTab({
{ {
icon: 'mdi-filter-multiple-outline', icon: 'mdi-filter-multiple-outline',
variant: 'text', variant: 'text',
color: computed(() => (subscribeFilter.value ? 'primary' : 'gray')), color: filterButtonColor,
class: 'settings-icon-button', class: 'settings-icon-button',
dataAttr: 'filter-btn', dataAttr: 'filter-btn',
action: () => { action: () => {
@@ -125,7 +163,12 @@ onMounted(() => {
<VWindowItem value="mysub"> <VWindowItem value="mysub">
<transition name="fade-slide" appear> <transition name="fade-slide" appear>
<div> <div>
<SubscribeListView :type="subType" :subid="subId" :keyword="subscribeFilter" /> <SubscribeListView
:type="subType"
:subid="subId"
:keyword="subscribeFilter"
:status-filter="subscribeStatusFilter ?? ''"
/>
</div> </div>
</transition> </transition>
</VWindowItem> </VWindowItem>
@@ -149,7 +192,7 @@ onMounted(() => {
<Teleport to="body" v-if="filterSubscribeDialog"> <Teleport to="body" v-if="filterSubscribeDialog">
<VMenu <VMenu
v-model="filterSubscribeDialog" v-model="filterSubscribeDialog"
width="20rem" width="25rem"
:close-on-content-click="false" :close-on-content-click="false"
:activator="filterActivator" :activator="filterActivator"
location="bottom end" location="bottom end"
@@ -163,7 +206,25 @@ onMounted(() => {
<VDialogCloseBtn @click="filterSubscribeDialog = false" /> <VDialogCloseBtn @click="filterSubscribeDialog = false" />
</VCardItem> </VCardItem>
<VCardText> <VCardText>
<VTextField v-model="subscribeFilter" :label="t('subscribe.name')" clearable density="comfortable" /> <VRow>
<!-- 名称筛选 -->
<VCol cols="6">
<VTextField v-model="subscribeFilter" :label="t('subscribe.name')" clearable density="comfortable" />
</VCol>
<!-- 状态筛选 -->
<VCol cols="6">
<VSelect
v-model="subscribeStatusFilter"
:items="filterOptions"
item-title="label"
item-value="value"
:label="t('common.status')"
density="comfortable"
clearable
/>
</VCol>
</VRow>
</VCardText> </VCardText>
</VCard> </VCard>
</VMenu> </VMenu>
+62 -6
View File
@@ -31,6 +31,7 @@ const props = defineProps({
type: String, type: String,
subid: String, subid: String,
keyword: String, keyword: String,
statusFilter: String,
}) })
// 是否刷新过 // 是否刷新过
@@ -51,22 +52,61 @@ const orderConfig = ref<{ id: number }[]>([])
// 显示的订阅列表 // 显示的订阅列表
const displayList = ref<Subscribe[]>([]) const displayList = ref<Subscribe[]>([])
// 根据订阅数据判断订阅状态
function getSubscribeStatus(subscribe: Subscribe) {
// 洗版中
if (subscribe.best_version) {
return 'best_version'
}
// 根据订阅状态判断
if (subscribe.state === 'P') {
return 'pending' // 待定
} else if (subscribe.state === 'S') {
return 'paused' // 暂停
}
// 如果是电影,只有洗版和状态
if (subscribe.type === '电影') {
return 'all'
}
// 电视剧根据集数情况判断
if (subscribe.total_episode && subscribe.total_episode > 0) {
const lackEpisode = subscribe.lack_episode || 0
const completedEpisode = subscribe.total_episode - lackEpisode
if (lackEpisode === 0) {
return 'completed' // 订阅完成
} else if (completedEpisode > 0) {
return 'subscribing' // 订阅中
} else {
return 'not_started' // 未开始
}
}
return 'not_started' // 默认未开始
}
// API请求键值(计算属性) // API请求键值(计算属性)
const orderRequestKey = computed(() => (props.type === '电影' ? 'SubscribeMovieOrder' : 'SubscribeTvOrder')) const orderRequestKey = computed(() => (props.type === '电影' ? 'SubscribeMovieOrder' : 'SubscribeTvOrder'))
// 监听dataList变化,同步更新displayList // 监听dataList变化,同步更新displayList
watch([dataList, () => props.keyword], () => { watch([dataList, () => props.keyword, () => props.statusFilter], () => {
if (superUser) if (superUser)
displayList.value = dataList.value.filter( displayList.value = dataList.value.filter(
data => data =>
data.type === props.type && (!props.keyword || data.name.toLowerCase().includes(props.keyword.toLowerCase())), data.type === props.type &&
(!props.keyword || data.name.toLowerCase().includes(props.keyword.toLowerCase())) &&
(!props.statusFilter || getSubscribeStatus(data) === props.statusFilter),
) )
else else
displayList.value = dataList.value.filter( displayList.value = dataList.value.filter(
data => data =>
data.type === props.type && data.type === props.type &&
data.username === userName && data.username === userName &&
(!props.keyword || data.name.toLowerCase().includes(props.keyword.toLowerCase())), (!props.keyword || data.name.toLowerCase().includes(props.keyword.toLowerCase())) &&
(!props.statusFilter || getSubscribeStatus(data) === props.statusFilter),
) )
// 排序 // 排序
sortSubscribeOrder() sortSubscribeOrder()
@@ -133,6 +173,22 @@ function historyDone() {
fetchData() fetchData()
} }
// 错误描述
const errorDescription = computed(() => {
if ((props.statusFilter && props.statusFilter !== 'all') || props.keyword) {
return t('common.tryChangingFilters')
}
return t('subscribe.noSubscribeData')
})
// 错误标题
const errorTitle = computed(() => {
if ((props.statusFilter && props.statusFilter !== 'all') || props.keyword) {
return t('common.noMatchingData')
}
return t('common.noData')
})
onMounted(async () => { onMounted(async () => {
await fetchData() await fetchData()
if (props.subid) { if (props.subid) {
@@ -161,7 +217,6 @@ useDynamicButton({
</script> </script>
<template> <template>
<VPageContentTitle v-if="keyword" :title="`${t('subscribe.filterSubscriptions')}${keyword}`" />
<LoadingBanner v-if="!isRefreshed" class="mt-12" /> <LoadingBanner v-if="!isRefreshed" class="mt-12" />
<draggable <draggable
v-if="displayList.length > 0" v-if="displayList.length > 0"
@@ -171,6 +226,7 @@ useDynamicButton({
item-key="id" item-key="id"
tag="div" tag="div"
:component-data="{ class: 'grid gap-4 grid-subscribe-card px-2' }" :component-data="{ class: 'grid gap-4 grid-subscribe-card px-2' }"
:disabled="props.statusFilter !== 'all'"
> >
<template #item="{ element }"> <template #item="{ element }">
<SubscribeCard :key="element.id" :media="element" @remove="fetchData" @save="fetchData" /> <SubscribeCard :key="element.id" :media="element" @remove="fetchData" @save="fetchData" />
@@ -179,8 +235,8 @@ useDynamicButton({
<NoDataFound <NoDataFound
v-if="displayList.length === 0 && isRefreshed" v-if="displayList.length === 0 && isRefreshed"
error-code="404" error-code="404"
:error-title="t('common.noData')" :error-title="errorTitle"
:error-description="keyword ? t('subscribe.noFilterData') : t('subscribe.noSubscribeData')" :error-description="errorDescription"
/> />
<!-- 底部操作按钮 --> <!-- 底部操作按钮 -->
<Teleport to="body" v-if="route.path.startsWith(`/subscribe/${props.type === '电影' ? 'movie' : 'tv'}`)"> <Teleport to="body" v-if="route.path.startsWith(`/subscribe/${props.type === '电影' ? 'movie' : 'tv'}`)">