添加媒体信息中的合集类型和ID,优化媒体卡片的跳转逻辑,增加搜索系列合集的功能

This commit is contained in:
jxxghp
2025-01-16 17:52:16 +08:00
parent 43d1cdb91c
commit e4af05cd56
3 changed files with 108 additions and 36 deletions
+3 -1
View File
@@ -186,7 +186,7 @@ export interface TransferHistory {
export interface MediaInfo { export interface MediaInfo {
// 来源:themoviedb、douban、bangumi // 来源:themoviedb、douban、bangumi
source?: string source?: string
// 类型 电影、电视剧 // 类型 电影、电视剧、合集
type?: string type?: string
// 媒体标题 // 媒体标题
title?: string title?: string
@@ -206,6 +206,8 @@ export interface MediaInfo {
douban_id?: string douban_id?: string
// Bangumi ID // Bangumi ID
bangumi_id?: string bangumi_id?: string
// 合集ID
collection_id?: number
// 媒体原语种 // 媒体原语种
original_language?: string original_language?: string
// 媒体原发行标题 // 媒体原发行标题
+87 -35
View File
@@ -59,11 +59,11 @@ const seasonInfos = ref<TmdbSeason[]>([])
// 选中的订阅季 // 选中的订阅季
const seasonsSelected = ref<TmdbSeason[]>([]) const seasonsSelected = ref<TmdbSeason[]>([])
let abortController: AbortController | null = null; let abortController: AbortController | null = null
abortController = new AbortController(); abortController = new AbortController()
registerAbortController(abortController); registerAbortController(abortController)
const { signal } = abortController; const { signal } = abortController
// 来源角标字典 // 来源角标字典
const sourceIconDict: { [key: string]: any } = { const sourceIconDict: { [key: string]: any } = {
themoviedb: tmdbImage, themoviedb: tmdbImage,
@@ -219,7 +219,6 @@ async function removeSubscribe() {
// 查询当前媒体是否已订阅 // 查询当前媒体是否已订阅
async function handleCheckSubscribe() { async function handleCheckSubscribe() {
try { try {
const result = await checkSubscribe(props.media?.season) const result = await checkSubscribe(props.media?.season)
if (result) isSubscribed.value = true if (result) isSubscribed.value = true
} catch (error) { } catch (error) {
@@ -230,7 +229,6 @@ async function handleCheckSubscribe() {
// 查询当前媒体是否已入库 // 查询当前媒体是否已入库
async function handleCheckExists() { async function handleCheckExists() {
try { try {
const result: { [key: string]: any } = await api.get('mediaserver/exists', { const result: { [key: string]: any } = await api.get('mediaserver/exists', {
params: { params: {
tmdbid: props.media?.tmdb_id, tmdbid: props.media?.tmdb_id,
@@ -239,7 +237,7 @@ async function handleCheckExists() {
season: props.media?.season, season: props.media?.season,
mtype: props.media?.type, mtype: props.media?.type,
}, },
signal signal,
}) })
if (result.success) isExists.value = true if (result.success) isExists.value = true
@@ -251,7 +249,6 @@ async function handleCheckExists() {
// 调用API检查是否已订阅,电视剧需要指定季 // 调用API检查是否已订阅,电视剧需要指定季
async function checkSubscribe(season = 0) { async function checkSubscribe(season = 0) {
try { try {
const mediaid = getMediaId() const mediaid = getMediaId()
const result: Subscribe = await api.get(`subscribe/media/${mediaid}`, { const result: Subscribe = await api.get(`subscribe/media/${mediaid}`, {
@@ -259,7 +256,7 @@ async function checkSubscribe(season = 0) {
season, season,
title: props.media?.title, title: props.media?.title,
}, },
signal signal,
}) })
return result.id || null return result.id || null
@@ -351,13 +348,24 @@ function getExistText(season: number) {
// 打开详情页 // 打开详情页
function goMediaDetail(isHovering = false) { function goMediaDetail(isHovering = false) {
if (isHovering) { if (isHovering) {
router.push({ if (props.media?.collection_id) {
path: '/media', // 跳转到合集列表
query: { router.push({
mediaid: getMediaId(), path: `/browse/tmdb/collection/${props.media?.collection_id}`,
type: props.media?.type, query: {
}, title: props.media?.title,
}) },
})
} else {
// 跳转到媒体详情页
router.push({
path: '/media',
query: {
mediaid: getMediaId(),
type: props.media?.type,
},
})
}
} }
} }
@@ -376,6 +384,9 @@ function handleSearch() {
// 懒加载检查 // 懒加载检查
function handleCheckLazy() { function handleCheckLazy() {
if (props.media?.collection_id) {
return
}
handleCheckSubscribe() handleCheckSubscribe()
handleCheckExists() handleCheckExists()
} }
@@ -453,13 +464,25 @@ function onRemoveSubscribe() {
<VHover> <VHover>
<template #default="hover"> <template #default="hover">
<div ref="mediaCardRef"> <div ref="mediaCardRef">
<VCard v-bind="hover.props" :height="props.height" :width="props.width" <VCard
class="outline-none shadow ring-gray-500 rounded-lg" :class="{ v-bind="hover.props"
:height="props.height"
:width="props.width"
class="outline-none shadow ring-gray-500 rounded-lg"
:class="{
'transition transform-cpu duration-300 scale-105 shadow-lg': hover.isHovering, 'transition transform-cpu duration-300 scale-105 shadow-lg': hover.isHovering,
'ring-1': isImageLoaded, 'ring-1': isImageLoaded,
}" @click.stop="goMediaDetail(hover.isHovering ?? false)"> }"
<VImg aspect-ratio="2/3" :src="getImgUrl" class="object-cover aspect-w-2 aspect-h-3" cover @click.stop="goMediaDetail(hover.isHovering ?? false)"
@load="isImageLoaded = true" @error="imageLoadError = true"> >
<VImg
aspect-ratio="2/3"
:src="getImgUrl"
class="object-cover aspect-w-2 aspect-h-3"
cover
@load="isImageLoaded = true"
@error="imageLoadError = true"
>
<template #placeholder> <template #placeholder>
<div class="w-full h-full"> <div class="w-full h-full">
<VSkeletonLoader class="object-cover aspect-w-2 aspect-h-3" /> <VSkeletonLoader class="object-cover aspect-w-2 aspect-h-3" />
@@ -467,9 +490,11 @@ function onRemoveSubscribe() {
</template> </template>
</VImg> </VImg>
<!-- 详情 --> <!-- 详情 -->
<VCardText v-show="hover.isHovering || imageLoadError" <VCardText
v-show="hover.isHovering || imageLoadError"
class="w-full h-full flex flex-col flex-wrap justify-end align-left text-white absolute bottom-0 cursor-pointer pa-2" 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%)"> style="background: linear-gradient(rgba(45, 55, 72, 40%) 0%, rgba(45, 55, 72, 90%) 100%)"
>
<span class="font-bold">{{ props.media?.year }}</span> <span class="font-bold">{{ props.media?.year }}</span>
<h1 class="mb-1 text-white font-extrabold text-xl line-clamp-2 overflow-hidden text-ellipsis ..."> <h1 class="mb-1 text-white font-extrabold text-xl line-clamp-2 overflow-hidden text-ellipsis ...">
{{ props.media?.title }} {{ props.media?.title }}
@@ -477,27 +502,42 @@ function onRemoveSubscribe() {
<p class="leading-4 line-clamp-4 overflow-hidden text-ellipsis ..."> <p class="leading-4 line-clamp-4 overflow-hidden text-ellipsis ...">
{{ props.media?.overview }} {{ props.media?.overview }}
</p> </p>
<div class="flex align-center justify-between"> <div v-if="props.media?.collection_id" class="mb-3"></div>
<div v-else class="flex align-center justify-between">
<IconBtn icon="mdi-magnify" color="white" @click.stop="handleSearch" /> <IconBtn icon="mdi-magnify" color="white" @click.stop="handleSearch" />
<IconBtn icon="mdi-heart" :color="isSubscribed ? 'error' : 'white'" @click.stop="handleSubscribe" /> <IconBtn icon="mdi-heart" :color="isSubscribed ? 'error' : 'white'" @click.stop="handleSubscribe" />
</div> </div>
</VCardText> </VCardText>
<!-- 类型角标 --> <!-- 类型角标 -->
<VChip v-show="isImageLoaded" variant="elevated" size="small" :class="getChipColor(props.media?.type || '')" <VChip
class="absolute left-2 top-2 bg-opacity-80 shadow-md text-white font-bold"> v-show="isImageLoaded"
variant="elevated"
size="small"
:class="getChipColor(props.media?.type || '')"
class="absolute left-2 top-2 bg-opacity-80 shadow-md text-white font-bold"
>
{{ props.media?.type }} {{ props.media?.type }}
</VChip> </VChip>
<!-- 本地存在标识 --> <!-- 本地存在标识 -->
<ExistIcon v-if="isExists && !hover.isHovering" /> <ExistIcon v-if="isExists && !hover.isHovering" />
<!-- 评分角标 --> <!-- 评分角标 -->
<VChip v-if="isImageLoaded && props.media?.vote_average && !(isExists && !hover.isHovering)" <VChip
variant="elevated" size="small" :class="getChipColor('rating')" v-if="isImageLoaded && props.media?.vote_average && !(isExists && !hover.isHovering)"
class="absolute right-2 top-2 bg-opacity-80 shadow-md text-white font-bold"> variant="elevated"
size="small"
:class="getChipColor('rating')"
class="absolute right-2 top-2 bg-opacity-80 shadow-md text-white font-bold"
>
{{ props.media?.vote_average }} {{ props.media?.vote_average }}
</VChip> </VChip>
<!--来源图标--> <!--来源图标-->
<VAvatar size="24" density="compact" class="absolute bottom-1 right-1" tile <VAvatar
v-if="!hover.isHovering && isImageLoaded && props.media?.source"> size="24"
density="compact"
class="absolute bottom-1 right-1"
tile
v-if="!hover.isHovering && isImageLoaded && props.media?.source"
>
<VImg cover :src="sourceIconDict[props.media?.source]" class="shadow-lg" /> <VImg cover :src="sourceIconDict[props.media?.source]" class="shadow-lg" />
</VAvatar> </VAvatar>
</VCard> </VCard>
@@ -516,8 +556,14 @@ function onRemoveSubscribe() {
<VList v-model:selected="seasonsSelected" lines="three" select-strategy="classic"> <VList v-model:selected="seasonsSelected" lines="three" select-strategy="classic">
<VListItem v-for="(item, i) in seasonInfos" :key="i" :value="item"> <VListItem v-for="(item, i) in seasonInfos" :key="i" :value="item">
<template #prepend> <template #prepend>
<VImg height="90" width="60" :src="getSeasonPoster(item.poster_path || '')" aspect-ratio="2/3" <VImg
class="object-cover rounded shadow ring-gray-500 me-3" cover> height="90"
width="60"
:src="getSeasonPoster(item.poster_path || '')"
aspect-ratio="2/3"
class="object-cover rounded shadow ring-gray-500 me-3"
cover
>
<template #placeholder> <template #placeholder>
<div class="w-full h-full"> <div class="w-full h-full">
<VSkeletonLoader class="object-cover aspect-w-2 aspect-h-3" /> <VSkeletonLoader class="object-cover aspect-w-2 aspect-h-3" />
@@ -556,6 +602,12 @@ function onRemoveSubscribe() {
</VCard> </VCard>
</VBottomSheet> </VBottomSheet>
<!-- 订阅编辑弹窗 --> <!-- 订阅编辑弹窗 -->
<SubscribeEditDialog v-if="subscribeEditDialog" v-model="subscribeEditDialog" :subid="subscribeId" <SubscribeEditDialog
@close="subscribeEditDialog = false" @save="subscribeEditDialog = false" @remove="onRemoveSubscribe" /> v-if="subscribeEditDialog"
v-model="subscribeEditDialog"
:subid="subscribeId"
@close="subscribeEditDialog = false"
@save="subscribeEditDialog = false"
@remove="onRemoveSubscribe"
/>
</template> </template>
+18
View File
@@ -272,6 +272,24 @@ onMounted(() => {
</VListItem> </VListItem>
</template> </template>
</VHover> </VHover>
<VHover>
<template #default="hover">
<VListItem
prepend-icon="mdi-movie-filter"
density="compact"
link
v-bind="hover.props"
@click="searchMedia('collection')"
>
<VListItemTitle class="break-words whitespace-break-spaces">
搜索 <span class="font-bold">{{ searchWord }} </span> 相关的系列合集 ...
</VListItemTitle>
<template #append>
<VIcon v-if="hover.isHovering" icon="ri-corner-down-left-line" />
</template>
</VListItem>
</template>
</VHover>
<VHover> <VHover>
<template #default="hover"> <template #default="hover">
<VListItem prepend-icon="mdi-account-search" link v-bind="hover.props" @click="searchMedia('person')"> <VListItem prepend-icon="mdi-account-search" link v-bind="hover.props" @click="searchMedia('person')">