mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-05-22 00:39:43 +08:00
feat(Search): 添加站点搜索功能,支持选择和过滤
This commit is contained in:
@@ -5,7 +5,7 @@ import SubscribeEditDialog from '../dialog/SubscribeEditDialog.vue'
|
||||
import { formatSeason, formatRating } from '@/@core/utils/formatters'
|
||||
import api from '@/api'
|
||||
import { doneNProgress, startNProgress } from '@/api/nprogress'
|
||||
import type { MediaInfo, NotExistMediaInfo, Subscribe, MediaSeason } from '@/api/types'
|
||||
import type { MediaInfo, NotExistMediaInfo, Subscribe, MediaSeason, Site } from '@/api/types'
|
||||
import router, { registerAbortController } from '@/router'
|
||||
import noImage from '@images/no-image.jpeg'
|
||||
import tmdbImage from '@images/logos/tmdb.png'
|
||||
@@ -73,6 +73,38 @@ const mediaCardRef = ref<HTMLElement | null>(null)
|
||||
// 创建Intersection Observer实例
|
||||
const observer = ref<IntersectionObserver | null>(null)
|
||||
|
||||
// 所有站点
|
||||
const allSites = ref<Site[]>([])
|
||||
|
||||
// 选中的站点
|
||||
const selectedSites = ref<number[]>([])
|
||||
|
||||
// 搜索菜单显示状态
|
||||
const searchMenuShow = ref(false)
|
||||
|
||||
// 查询所有站点
|
||||
async function querySites() {
|
||||
try {
|
||||
const data: Site[] = await api.get('site/')
|
||||
|
||||
// 过滤站点,只有启用的站点才显示
|
||||
allSites.value = data.filter(item => item.is_active)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
// 查询用户选中的站点
|
||||
async function querySelectedSites() {
|
||||
try {
|
||||
const result: { [key: string]: any } = await api.get('system/setting/IndexerSites')
|
||||
|
||||
selectedSites.value = result.data?.value ?? []
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
// 获得mediaid
|
||||
function getMediaId() {
|
||||
if (props.media?.tmdb_id) return `tmdb:${props.media?.tmdb_id}`
|
||||
@@ -375,6 +407,13 @@ function goMediaDetail(isHovering = false) {
|
||||
}
|
||||
}
|
||||
|
||||
// 点击搜索
|
||||
async function clickSearch() {
|
||||
if (allSites.value?.length > 0) return
|
||||
querySites()
|
||||
querySelectedSites()
|
||||
}
|
||||
|
||||
// 开始搜索
|
||||
function handleSearch() {
|
||||
router.push({
|
||||
@@ -386,6 +425,7 @@ function handleSearch() {
|
||||
title: props.media?.title,
|
||||
year: props.media?.year,
|
||||
season: props.media?.season,
|
||||
sites: selectedSites.value.join(','),
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -499,7 +539,7 @@ function onRemoveSubscribe() {
|
||||
</VImg>
|
||||
<!-- 详情 -->
|
||||
<VCardText
|
||||
v-show="hover.isHovering || imageLoadError"
|
||||
v-show="hover.isHovering || imageLoadError || searchMenuShow"
|
||||
class="w-full h-full flex flex-col flex-wrap justify-end align-left text-white absolute bottom-0 cursor-pointer pa-2"
|
||||
style="background: linear-gradient(rgba(45, 55, 72, 40%) 0%, rgba(45, 55, 72, 90%) 100%)"
|
||||
>
|
||||
@@ -512,7 +552,31 @@ function onRemoveSubscribe() {
|
||||
</p>
|
||||
<div v-if="props.media?.collection_id" class="mb-3" @click.stop=""></div>
|
||||
<div v-else class="flex align-center justify-between">
|
||||
<IconBtn icon="mdi-magnify" color="white" @click.stop="handleSearch" />
|
||||
<VMenu close-on-content-click v-model="searchMenuShow" max-width="450">
|
||||
<template v-slot:activator="{ props }">
|
||||
<IconBtn v-bind="props" icon="mdi-magnify" color="white" @click.stop="clickSearch" />
|
||||
</template>
|
||||
<VList>
|
||||
<VListItem>
|
||||
<VChipGroup v-model="selectedSites" column multiple @click.stop>
|
||||
<VChip
|
||||
v-for="site in allSites"
|
||||
:key="site.id"
|
||||
:color="selectedSites.includes(site.id) ? 'primary' : ''"
|
||||
filter
|
||||
variant="outlined"
|
||||
:value="site.id"
|
||||
size="small"
|
||||
>
|
||||
{{ site.name }}
|
||||
</VChip>
|
||||
</VChipGroup>
|
||||
</VListItem>
|
||||
<VListItem>
|
||||
<VBtn @click="handleSearch" block>搜索</VBtn>
|
||||
</VListItem>
|
||||
</VList>
|
||||
</VMenu>
|
||||
<IconBtn icon="mdi-heart" :color="isSubscribed ? 'error' : 'white'" @click.stop="handleSubscribe" />
|
||||
</div>
|
||||
</VCardText>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useToast } from 'vue-toast-notification'
|
||||
import PersonCardSlideView from './PersonCardSlideView.vue'
|
||||
import MediaCardSlideView from './MediaCardSlideView.vue'
|
||||
import api from '@/api'
|
||||
import type { MediaInfo, NotExistMediaInfo, Subscribe, TmdbEpisode } from '@/api/types'
|
||||
import type { MediaInfo, NotExistMediaInfo, Site, Subscribe, TmdbEpisode } from '@/api/types'
|
||||
import NoDataFound from '@/components/NoDataFound.vue'
|
||||
import { doneNProgress, startNProgress } from '@/api/nprogress'
|
||||
import { formatSeason } from '@/@core/utils/formatters'
|
||||
@@ -57,6 +57,38 @@ const seasonsSubscribed = ref<{ [key: number]: boolean }>({})
|
||||
// 订阅编号
|
||||
const subscribeId = ref<number>()
|
||||
|
||||
// 所有站点
|
||||
const allSites = ref<Site[]>([])
|
||||
|
||||
// 选中的站点
|
||||
const selectedSites = ref<number[]>([])
|
||||
|
||||
// 搜索方式 title/imdbid
|
||||
const searchType = ref('title')
|
||||
|
||||
// 查询所有站点
|
||||
async function querySites() {
|
||||
try {
|
||||
const data: Site[] = await api.get('site/')
|
||||
|
||||
// 过滤站点,只有启用的站点才显示
|
||||
allSites.value = data.filter(item => item.is_active)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
// 查询用户选中的站点
|
||||
async function querySelectedSites() {
|
||||
try {
|
||||
const result: { [key: string]: any } = await api.get('system/setting/IndexerSites')
|
||||
|
||||
selectedSites.value = result.data?.value ?? []
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
// 获得mediaid
|
||||
function getMediaId() {
|
||||
if (mediaDetail.value?.tmdb_id) return `tmdb:${mediaDetail.value?.tmdb_id}`
|
||||
@@ -398,17 +430,18 @@ function joinArray(arr: string[]) {
|
||||
}
|
||||
|
||||
// 开始搜索
|
||||
function handleSearch(area: string) {
|
||||
function handleSearch() {
|
||||
const keyword = getMediaId()
|
||||
router.push({
|
||||
path: '/resource',
|
||||
query: {
|
||||
keyword,
|
||||
type: mediaDetail.value.type,
|
||||
area,
|
||||
area: searchType.value,
|
||||
title: mediaDetail.value.title,
|
||||
year: mediaDetail.value.year,
|
||||
season: mediaDetail.value.season,
|
||||
sites: selectedSites.value.join(','),
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -455,6 +488,13 @@ function onSubscribeEditRemove() {
|
||||
else checkSeasonsSubscribed()
|
||||
}
|
||||
|
||||
// 点击搜索
|
||||
async function clickSearch() {
|
||||
if (allSites.value?.length > 0) return
|
||||
querySites()
|
||||
querySelectedSites()
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
getMediaDetail()
|
||||
})
|
||||
@@ -512,38 +552,45 @@ onBeforeMount(() => {
|
||||
</div>
|
||||
<div class="media-actions">
|
||||
<VBtn
|
||||
v-if="(mediaDetail.tmdb_id || mediaDetail.douban_id || mediaDetail.bangumi_id) && mediaDetail.imdb_id"
|
||||
v-if="mediaDetail.tmdb_id || mediaDetail.douban_id || mediaDetail.bangumi_id"
|
||||
variant="tonal"
|
||||
color="info"
|
||||
class="mb-2"
|
||||
@click="clickSearch"
|
||||
>
|
||||
<template #prepend>
|
||||
<VIcon icon="mdi-magnify" />
|
||||
</template>
|
||||
搜索资源
|
||||
<VMenu activator="parent" close-on-content-click>
|
||||
<VMenu activator="parent" close-on-content-click max-width="450">
|
||||
<VList>
|
||||
<VListItem variant="plain" @click="handleSearch('title')">
|
||||
<VListItemTitle>标题</VListItemTitle>
|
||||
<VListItem>
|
||||
<VBtnToggle v-model="searchType" color="primary" @click.stop>
|
||||
<VBtn value="title">标题</VBtn>
|
||||
<VBtn value="imdbid" v-show="mediaDetail.imdb_id">IMDB链接</VBtn>
|
||||
</VBtnToggle>
|
||||
</VListItem>
|
||||
<VListItem v-show="mediaDetail.imdb_id" variant="plain" @click="handleSearch('imdbid')">
|
||||
<VListItemTitle>IMDB链接</VListItemTitle>
|
||||
<VListItem>
|
||||
<VChipGroup v-model="selectedSites" column multiple @click.stop>
|
||||
<VChip
|
||||
v-for="site in allSites"
|
||||
:key="site.id"
|
||||
:color="selectedSites.includes(site.id) ? 'primary' : ''"
|
||||
filter
|
||||
variant="outlined"
|
||||
:value="site.id"
|
||||
size="small"
|
||||
>
|
||||
{{ site.name }}
|
||||
</VChip>
|
||||
</VChipGroup>
|
||||
</VListItem>
|
||||
<VListItem>
|
||||
<VBtn @click="handleSearch" block>搜索</VBtn>
|
||||
</VListItem>
|
||||
</VList>
|
||||
</VMenu>
|
||||
</VBtn>
|
||||
<VBtn
|
||||
v-if="(mediaDetail.tmdb_id || mediaDetail.douban_id || mediaDetail.bangumi_id) && !mediaDetail.imdb_id"
|
||||
variant="tonal"
|
||||
color="info"
|
||||
class="mb-2"
|
||||
@click="handleSearch('title')"
|
||||
>
|
||||
<template #prepend>
|
||||
<VIcon icon="mdi-magnify" />
|
||||
</template>
|
||||
搜索资源
|
||||
</VBtn>
|
||||
<VBtn
|
||||
v-if="mediaDetail.type === '电影' || mediaDetail.douban_id || mediaDetail.bangumi_id"
|
||||
class="ms-2 mb-2"
|
||||
|
||||
@@ -346,7 +346,7 @@ onMounted(() => {
|
||||
<VListItemTitle class="break-words whitespace-break-spaces">
|
||||
搜索 <span class="font-bold">{{ searchWord }}</span> 相关的【站点资源】 ...
|
||||
</VListItemTitle>
|
||||
<VChipGroup v-if="hover.isHovering" v-model="selectedSites" column multiple @click.stop>
|
||||
<VChipGroup v-if="hover.isHovering" v-model="selectedSites" column multiple>
|
||||
<VChip
|
||||
v-for="site in allSites"
|
||||
:key="site.id"
|
||||
@@ -354,6 +354,8 @@ onMounted(() => {
|
||||
filter
|
||||
variant="outlined"
|
||||
:value="site.id"
|
||||
size="small"
|
||||
@click.stop
|
||||
>
|
||||
{{ site.name }}
|
||||
</VChip>
|
||||
|
||||
Reference in New Issue
Block a user