mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-07 00:36:41 +08:00
feat:重构推荐页面,添加推荐数据源接口并更新路由和视图
This commit is contained in:
@@ -1244,3 +1244,11 @@ export interface DiscoverSource {
|
|||||||
// 过滤参数UI配置
|
// 过滤参数UI配置
|
||||||
filter_ui: RenderProps[]
|
filter_ui: RenderProps[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 推荐的数据源
|
||||||
|
export interface RecommendSource {
|
||||||
|
// 数据源名称
|
||||||
|
name: string
|
||||||
|
// 媒体数据源API地址
|
||||||
|
api_path: string
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const route = useRoute()
|
|||||||
const activeState = computed(() => {
|
const activeState = computed(() => {
|
||||||
return {
|
return {
|
||||||
home: route.path === '/dashboard',
|
home: route.path === '/dashboard',
|
||||||
ranking: route.path === '/ranking',
|
recommend: route.path === '/recommend',
|
||||||
movie: route.path === '/subscribe/movie',
|
movie: route.path === '/subscribe/movie',
|
||||||
tv: route.path === '/subscribe/tv',
|
tv: route.path === '/subscribe/tv',
|
||||||
apps: route.path === '/apps',
|
apps: route.path === '/apps',
|
||||||
@@ -31,8 +31,8 @@ const activeState = computed(() => {
|
|||||||
<VIcon v-if="activeState.home" size="28">mdi-home</VIcon>
|
<VIcon v-if="activeState.home" size="28">mdi-home</VIcon>
|
||||||
<VIcon v-else size="28">mdi-home-outline</VIcon>
|
<VIcon v-else size="28">mdi-home-outline</VIcon>
|
||||||
</VBtn>
|
</VBtn>
|
||||||
<VBtn to="/ranking" :ripple="false">
|
<VBtn to="/recommend" :ripple="false">
|
||||||
<VIcon v-if="activeState.ranking" size="28">mdi-star</VIcon>
|
<VIcon v-if="activeState.recommend" size="28">mdi-star</VIcon>
|
||||||
<VIcon v-else size="28">mdi-star-outline</VIcon>
|
<VIcon v-else size="28">mdi-star-outline</VIcon>
|
||||||
</VBtn>
|
</VBtn>
|
||||||
<VBtn to="/subscribe/movie" :ripple="false">
|
<VBtn to="/subscribe/movie" :ripple="false">
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import MediaCardSlideView from '@/views/discover/MediaCardSlideView.vue'
|
|
||||||
|
|
||||||
const viewList = reactive<{ apipath: string; linkurl: string; title: string }[]>([
|
|
||||||
{
|
|
||||||
apipath: 'tmdb/trending',
|
|
||||||
linkurl: '/browse/tmdb/trending?title=流行趋势',
|
|
||||||
title: '流行趋势',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
apipath: 'douban/showing',
|
|
||||||
linkurl: '/browse/douban/showing?title=正在热映',
|
|
||||||
title: '正在热映',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
apipath: 'bangumi/calendar',
|
|
||||||
linkurl: '/browse/bangumi/calendar?title=Bangumi每日放送',
|
|
||||||
title: 'Bangumi每日放送',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
apipath: 'tmdb/movies',
|
|
||||||
linkurl: '/browse/tmdb/movies?title=TMDB热门电影',
|
|
||||||
title: 'TMDB热门电影',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
apipath: 'tmdb/tvs?with_original_language=zh|en|ja|ko',
|
|
||||||
linkurl: '/browse/tmdb/tvs??with_original_language=zh|en|ja|ko&title=TMDB热门电视剧',
|
|
||||||
title: 'TMDB热门电视剧',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
apipath: 'douban/movie_hot',
|
|
||||||
linkurl: '/browse/douban/movie_hot?title=豆瓣热门电影',
|
|
||||||
title: '豆瓣热门电影',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
apipath: 'douban/tv_hot',
|
|
||||||
linkurl: '/browse/douban/tv_hot?title=豆瓣热门电视剧',
|
|
||||||
title: '豆瓣热门电视剧',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
apipath: 'douban/tv_animation',
|
|
||||||
linkurl: '/browse/douban/tv_animation?title=豆瓣热门动漫',
|
|
||||||
title: '豆瓣热门动漫',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
apipath: 'douban/movies',
|
|
||||||
linkurl: '/browse/douban/movies?title=豆瓣最新电影',
|
|
||||||
title: '豆瓣最新电影',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
apipath: 'douban/tvs',
|
|
||||||
linkurl: '/browse/douban/tvs?title=豆瓣最新电视剧',
|
|
||||||
title: '豆瓣最新电视剧',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
apipath: 'douban/movie_top250',
|
|
||||||
linkurl: '/browse/douban/movie_top250?title=电影TOP250',
|
|
||||||
title: '豆瓣电影TOP250',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
apipath: 'douban/tv_weekly_chinese',
|
|
||||||
linkurl: '/browse/douban/tv_weekly_chinese?title=豆瓣国产剧集榜',
|
|
||||||
title: '豆瓣国产剧集榜',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
apipath: 'douban/tv_weekly_global',
|
|
||||||
linkurl: '/browse/douban/tv_weekly_global?title=豆瓣全球剧集榜',
|
|
||||||
title: '豆瓣全球剧集榜',
|
|
||||||
},
|
|
||||||
])
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<MediaCardSlideView v-for="(item, index) in viewList" :key="index" v-bind="item" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import api from '@/api'
|
||||||
|
import { RecommendSource } from '@/api/types'
|
||||||
|
import MediaCardSlideView from '@/views/discover/MediaCardSlideView.vue'
|
||||||
|
import { useDisplay } from 'vuetify'
|
||||||
|
|
||||||
|
// APP
|
||||||
|
const display = useDisplay()
|
||||||
|
const appMode = inject('pwaMode') && display.mdAndDown.value
|
||||||
|
|
||||||
|
const viewList = reactive<{ apipath: string; linkurl: string; title: string }[]>([
|
||||||
|
{
|
||||||
|
apipath: 'recommend/tmdb_trending',
|
||||||
|
linkurl: '/browse/recommend/tmdb_trending?title=流行趋势',
|
||||||
|
title: '流行趋势',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apipath: 'recommend/douban_showing',
|
||||||
|
linkurl: '/browse/recommend/douban_showing?title=正在热映',
|
||||||
|
title: '正在热映',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apipath: 'bangumi/calendar',
|
||||||
|
linkurl: '/browse/bangumi/calendar?title=Bangumi每日放送',
|
||||||
|
title: 'Bangumi每日放送',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apipath: 'recommend/tmdb_movies',
|
||||||
|
linkurl: '/browse/recommend/tmdb_movies?title=TMDB热门电影',
|
||||||
|
title: 'TMDB热门电影',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apipath: 'recommend/tmdb_tvs?with_original_language=zh|en|ja|ko',
|
||||||
|
linkurl: '/browse/recommend/tmdb_tvs??with_original_language=zh|en|ja|ko&title=TMDB热门电视剧',
|
||||||
|
title: 'TMDB热门电视剧',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apipath: 'recommend/douban_movie_hot',
|
||||||
|
linkurl: '/browse/recommend/douban_movie_hot?title=豆瓣热门电影',
|
||||||
|
title: '豆瓣热门电影',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apipath: 'recommend/douban_tv_hot',
|
||||||
|
linkurl: '/browse/recommend/douban_tv_hot?title=豆瓣热门电视剧',
|
||||||
|
title: '豆瓣热门电视剧',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apipath: 'recommend/douban_tv_animation',
|
||||||
|
linkurl: '/browse/recommend/douban_tv_animation?title=豆瓣热门动漫',
|
||||||
|
title: '豆瓣热门动漫',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apipath: 'recommend/douban_movies',
|
||||||
|
linkurl: '/browse/recommend/douban_movies?title=豆瓣最新电影',
|
||||||
|
title: '豆瓣最新电影',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apipath: 'recommend/douban_tvs',
|
||||||
|
linkurl: '/browse/recommend/douban_tvs?title=豆瓣最新电视剧',
|
||||||
|
title: '豆瓣最新电视剧',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apipath: 'recommend/douban_movie_top250',
|
||||||
|
linkurl: '/browse/recommend/douban_movie_top250?title=电影TOP250',
|
||||||
|
title: '豆瓣电影TOP250',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apipath: 'recommend/douban_tv_weekly_chinese',
|
||||||
|
linkurl: '/browse/recommend/douban_tv_weekly_chinese?title=豆瓣国产剧集榜',
|
||||||
|
title: '豆瓣国产剧集榜',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
apipath: 'recommend/douban_tv_weekly_global',
|
||||||
|
linkurl: '/browse/recommend/douban_tv_weekly_global?title=豆瓣全球剧集榜',
|
||||||
|
title: '豆瓣全球剧集榜',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
// 计算启用的视图
|
||||||
|
const enabledViews = computed(() => viewList.filter(item => enableConfig.value[item.title]))
|
||||||
|
|
||||||
|
// 榜单启用配置, 以title为key
|
||||||
|
const enableConfig = ref<{ [key: string]: boolean }>({
|
||||||
|
...Object.fromEntries(viewList.map(item => [item.title, true])),
|
||||||
|
})
|
||||||
|
|
||||||
|
// 弹窗
|
||||||
|
const dialog = ref(false)
|
||||||
|
|
||||||
|
// 额外的数据源
|
||||||
|
const extraRecommendSources = ref<RecommendSource[]>([])
|
||||||
|
|
||||||
|
// 加载额外的发现数据源
|
||||||
|
async function loadExtraRecommendSources() {
|
||||||
|
try {
|
||||||
|
extraRecommendSources.value = await api.get('recommend/source')
|
||||||
|
if (extraRecommendSources.value.length > 0) {
|
||||||
|
viewList.push(
|
||||||
|
...extraRecommendSources.value.map(source => ({
|
||||||
|
apipath: source.api_path,
|
||||||
|
linkurl: `/browse/recommend/${source.api_path}?title=${source.name}`,
|
||||||
|
title: source.name,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载面板配置
|
||||||
|
async function loadConfig() {
|
||||||
|
// 显示配置
|
||||||
|
const local_enable = localStorage.getItem('MP_RECOMMEND')
|
||||||
|
if (local_enable) {
|
||||||
|
enableConfig.value = JSON.parse(local_enable)
|
||||||
|
} else {
|
||||||
|
const response = await api.get('/user/config/Recommend')
|
||||||
|
if (response && response.data && response.data.value) {
|
||||||
|
enableConfig.value = response.data.value
|
||||||
|
localStorage.setItem('MP_RECOMMEND', JSON.stringify(response.data.value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置项目
|
||||||
|
async function saveConfig() {
|
||||||
|
// 启用配置
|
||||||
|
const enableString = JSON.stringify(enableConfig.value)
|
||||||
|
localStorage.setItem('MP_RECOMMEND', enableString)
|
||||||
|
|
||||||
|
// 保存到服务端
|
||||||
|
try {
|
||||||
|
await api.post('/user/config/Recommend', enableConfig.value)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
dialog.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
await loadConfig()
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await loadExtraRecommendSources()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<MediaCardSlideView v-for="(item, index) in enabledViews" :key="index" v-bind="item" />
|
||||||
|
<!-- 弹窗,根据配置生成选项 -->
|
||||||
|
<VDialog v-if="dialog" v-model="dialog" max-width="35rem" scrollable>
|
||||||
|
<VCard>
|
||||||
|
<VCardItem>
|
||||||
|
<VCardTitle>设置推荐榜单</VCardTitle>
|
||||||
|
</VCardItem>
|
||||||
|
<VDivider />
|
||||||
|
<VCardText>
|
||||||
|
<VRow>
|
||||||
|
<VCol v-for="item in viewList" :key="item.title" cols="6" md="4" sm="4">
|
||||||
|
<VCheckbox v-model="enableConfig[item.title]" :label="item.title" />
|
||||||
|
</VCol>
|
||||||
|
</VRow>
|
||||||
|
</VCardText>
|
||||||
|
<VDivider />
|
||||||
|
<VCardText class="pt-5 text-end">
|
||||||
|
<VSpacer />
|
||||||
|
<VBtn variant="outlined" color="secondary" class="me-4" @click="dialog = false"> 关闭 </VBtn>
|
||||||
|
<VBtn @click="saveConfig">
|
||||||
|
<template #prepend>
|
||||||
|
<VIcon icon="mdi-content-save" />
|
||||||
|
</template>
|
||||||
|
保存
|
||||||
|
</VBtn>
|
||||||
|
</VCardText>
|
||||||
|
</VCard>
|
||||||
|
</VDialog>
|
||||||
|
</div>
|
||||||
|
<!-- 底部操作按钮 -->
|
||||||
|
<VFab
|
||||||
|
icon="mdi-text-box-edit"
|
||||||
|
location="bottom"
|
||||||
|
size="x-large"
|
||||||
|
fixed
|
||||||
|
app
|
||||||
|
appear
|
||||||
|
@click="dialog = true"
|
||||||
|
:class="{ 'mb-12': appMode }"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
+2
-2
@@ -28,8 +28,8 @@ const router = createRouter({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/ranking',
|
path: '/recommend',
|
||||||
component: () => import('../pages/ranking.vue'),
|
component: () => import('../pages/recommend.vue'),
|
||||||
meta: {
|
meta: {
|
||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ export const SystemNavMenus = [
|
|||||||
{
|
{
|
||||||
title: '推荐',
|
title: '推荐',
|
||||||
icon: 'mdi-star-outline',
|
icon: 'mdi-star-outline',
|
||||||
to: '/ranking',
|
to: '/recommend',
|
||||||
header: '发现',
|
header: '发现',
|
||||||
admin: false,
|
admin: false,
|
||||||
footer: true,
|
footer: true,
|
||||||
|
|||||||
@@ -102,6 +102,6 @@ watch([filterParams], () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<MediaCardListView :key="currentKey" apipath="bangumi/subjects" :params="filterParams" />
|
<MediaCardListView :key="currentKey" apipath="discover/bangumi" :params="filterParams" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -197,6 +197,6 @@ watch([type, filterParams], () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<MediaCardListView :key="currentKey" :apipath="`douban/${type}`" :params="filterParams" />
|
<MediaCardListView :key="currentKey" :apipath="`discover/douban_${type}`" :params="filterParams" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -173,6 +173,6 @@ watch([type, filterParams], () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<MediaCardListView :key="currentKey" :apipath="`tmdb/${type}`" :params="filterParams" />
|
<MediaCardListView :key="currentKey" :apipath="`discover/tmdb_${type}`" :params="filterParams" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
+1
-1
@@ -101,7 +101,7 @@ export default defineConfig({
|
|||||||
'shortcuts': [
|
'shortcuts': [
|
||||||
{
|
{
|
||||||
'name': '推荐',
|
'name': '推荐',
|
||||||
'url': './ranking',
|
'url': './recommend',
|
||||||
'icons': [
|
'icons': [
|
||||||
{
|
{
|
||||||
'src': './sparkles-icon-192x192.png',
|
'src': './sparkles-icon-192x192.png',
|
||||||
|
|||||||
Reference in New Issue
Block a user