mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-05 23:56:42 +08:00
添加媒体信息中的合集类型和ID,优化媒体卡片的跳转逻辑,增加搜索系列合集的功能
This commit is contained in:
+3
-1
@@ -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
|
||||||
// 媒体原发行标题
|
// 媒体原发行标题
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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')">
|
||||||
|
|||||||
Reference in New Issue
Block a user