fix keepalive

This commit is contained in:
jxxghp
2024-06-15 18:10:56 +08:00
parent 28b307fb98
commit be1a44ad61
12 changed files with 115 additions and 179 deletions

View File

@@ -13,8 +13,8 @@ const activeState = computed(() => {
return {
home: route.path === '/dashboard',
ranking: route.path === '/ranking',
movie: route.path === '/subscribe-movie',
tv: route.path === '/subscribe-tv',
movie: route.path === '/subscribe/movie',
tv: route.path === '/subscribe/tv',
apps: route.path === '/apps',
}
})
@@ -30,24 +30,24 @@ const activeState = computed(() => {
style="block-size: calc(3.5rem + env(safe-area-inset-bottom))"
>
<VBtn to="/dashboard" :ripple="false">
<VIcon v-if="activeState.home" size="32">mdi-home</VIcon>
<VIcon v-else size="32">mdi-home-outline</VIcon>
<VIcon v-if="activeState.home" size="28">mdi-home</VIcon>
<VIcon v-else size="28">mdi-home-outline</VIcon>
</VBtn>
<VBtn to="/ranking" :ripple="false">
<VIcon v-if="activeState.ranking" size="32">mdi-star</VIcon>
<VIcon v-else size="32">mdi-star-outline</VIcon>
<VIcon v-if="activeState.ranking" size="28">mdi-star</VIcon>
<VIcon v-else size="28">mdi-star-outline</VIcon>
</VBtn>
<VBtn to="/subscribe-movie?tab=mysub" :ripple="false">
<VIcon v-if="activeState.movie" size="32">mdi-movie-open</VIcon>
<VIcon v-else size="32">mdi-movie-open-outline</VIcon>
<VBtn to="/subscribe/movie" :ripple="false">
<VIcon v-if="activeState.movie" size="28">mdi-movie-open</VIcon>
<VIcon v-else size="28">mdi-movie-open-outline</VIcon>
</VBtn>
<VBtn to="/subscribe-tv?tab=mysub" :ripple="false">
<VIcon v-if="activeState.tv" size="32">mdi-television-play</VIcon>
<VIcon v-else size="32">mdi-television</VIcon>
<VBtn to="/subscribe/tv" :ripple="false">
<VIcon v-if="activeState.tv" size="28">mdi-television-play</VIcon>
<VIcon v-else size="28">mdi-television</VIcon>
</VBtn>
<VBtn to="/apps" :ripple="false">
<VIcon v-if="activeState.apps" size="32">mdi-dots-horizontal-circle</VIcon>
<VIcon v-else size="32">mdi-dots-horizontal</VIcon>
<VIcon v-if="activeState.apps" size="28">mdi-dots-horizontal-circle</VIcon>
<VIcon v-else size="28">mdi-dots-horizontal</VIcon>
</VBtn>
</VBottomNavigation>
</div>

View File

@@ -1,41 +0,0 @@
<script setup lang="ts">
import SubscribeListView from '@/views/subscribe/SubscribeListView.vue'
import SubscribePopularView from '@/views/subscribe/SubscribePopularView.vue'
import router from '@/router'
import { SubscribeTvTabs } from '@/router/menu'
const route = useRoute()
const activeTab = ref(route.query.tab)
// 订阅ID参数
const subId = ref(route.query.id as string)
// 跳转tab
function jumpTab(tab: string) {
router.push('/subscribe-tv?tab=' + tab)
}
</script>
<template>
<div>
<VTabs v-model="activeTab">
<VTab v-for="item in SubscribeTvTabs" :value="item.tab" @click="jumpTab(item.tab)">
<span class="mx-5">{{ item.title }}</span>
</VTab>
</VTabs>
<VWindow v-model="activeTab" class="mt-5 disable-tab-transition" :touch="false">
<VWindowItem value="mysub">
<transition name="fade-slide" appear>
<SubscribeListView type="电视剧" :subid="subId" />
</transition>
</VWindowItem>
<VWindowItem value="popular">
<transition name="fade-slide" appear>
<SubscribePopularView type="电视剧" :subid="subId" />
</transition>
</VWindowItem>
</VWindow>
</div>
</template>

View File

@@ -1,27 +1,24 @@
<script setup lang="ts">
import SubscribeListView from '@/views/subscribe/SubscribeListView.vue'
import SubscribePopularView from '@/views/subscribe/SubscribePopularView.vue'
import router from '@/router'
import { SubscribeMovieTabs } from '@/router/menu'
import router from '@/router'
const route = useRoute()
// ID
const subType = route.meta.subType?.toString()
const subId = ref(route.query.id as string)
//
const activeTab = ref(route.query.tab)
// tab
function jumpTab(tab: string) {
router.push('/subscribe-movie?tab=' + tab)
router.push('/subscribe/movie?tab=' + tab)
}
</script>
<template>
<div>
<VTabs v-model="activeTab">
<VTab v-for="item in SubscribeMovieTabs" :value="item.tab" @click="jumpTab(item.tab)">
<VTab v-for="item in SubscribeMovieTabs" :value="item.tab" @to="jumpTab(item.tab)">
<span class="mx-5">{{ item.title }}</span>
</VTab>
</VTabs>
@@ -29,12 +26,12 @@ function jumpTab(tab: string) {
<VWindow v-model="activeTab" class="mt-5 disable-tab-transition" :touch="false">
<VWindowItem value="mysub">
<transition name="fade-slide" appear>
<SubscribeListView type="电影" :subid="subId" />
<SubscribeListView :type="subType" :subid="subId" />
</transition>
</VWindowItem>
<VWindowItem value="popular">
<transition name="fade-slide" appear>
<SubscribePopularView type="电影" :subid="subId" />
<SubscribePopularView :type="subType" />
</transition>
</VWindowItem>
</VWindow>

View File

@@ -23,6 +23,7 @@ const router = createRouter({
path: '/dashboard',
component: () => import('../pages/dashboard.vue'),
meta: {
keepAlive: true,
requiresAuth: true,
},
},
@@ -42,23 +43,28 @@ const router = createRouter({
},
},
{
path: '/subscribe-movie',
component: () => import('../pages/subscribe-movie.vue'),
path: '/subscribe/movie',
component: () => import('../pages/subscribe.vue'),
meta: {
keepAlive: true,
requiresAuth: true,
subType: '电影',
},
},
{
path: '/subscribe-tv',
component: () => import('../pages/subscribe-tv.vue'),
path: '/subscribe/tv',
component: () => import('../pages/subscribe.vue'),
meta: {
keepAlive: true,
requiresAuth: true,
subType: '电视剧',
},
},
{
path: '/calendar',
component: () => import('../pages/calendar.vue'),
meta: {
keepAlive: true,
requiresAuth: true,
},
},
@@ -80,6 +86,7 @@ const router = createRouter({
path: '/site',
component: () => import('../pages/site.vue'),
meta: {
keepAlive: true,
requiresAuth: true,
},
},
@@ -87,6 +94,7 @@ const router = createRouter({
path: '/plugins',
component: () => import('../pages/plugin.vue'),
meta: {
keepAlive: true,
requiresAuth: true,
},
},
@@ -120,6 +128,7 @@ const router = createRouter({
component: () => import('../pages/person.vue'),
props: true,
meta: {
keepAlive: true,
requiresAuth: true,
},
},
@@ -127,6 +136,7 @@ const router = createRouter({
path: '/media',
component: () => import('../pages/media.vue'),
meta: {
keepAlive: true,
requiresAuth: true,
},
},

View File

@@ -25,7 +25,7 @@ export const SystemNavMenus = [
title: '电影',
full_title: '电影订阅',
icon: 'mdi-movie-open-outline',
to: '/subscribe-movie?tab=mysub',
to: '/subscribe/movie',
header: '订阅',
admin: false,
},
@@ -33,7 +33,7 @@ export const SystemNavMenus = [
title: '电视剧',
full_title: '电视剧订阅',
icon: 'mdi-television',
to: '/subscribe-tv?tab=mysub',
to: '/subscribe/tv',
header: '订阅',
admin: false,
},
@@ -69,7 +69,7 @@ export const SystemNavMenus = [
{
title: '插件',
icon: 'mdi-apps',
to: '/plugins?tab=installed',
to: '/plugins',
header: '系统',
admin: true,
},
@@ -186,12 +186,12 @@ export const SubscribeMovieTabs = [
{
title: '我的订阅',
tab: 'mysub',
icon: 'mdi-movie-roll',
icon: 'mdi-movie-open-outline',
},
{
title: '热门订阅',
tab: 'popular',
icon: 'mdi-movie-roll',
icon: 'mdi-movie-open-outline',
},
]
@@ -200,12 +200,12 @@ export const SubscribeTvTabs = [
{
title: '我的订阅',
tab: 'mysub',
icon: 'mdi-television-classic',
icon: 'mdi-television',
},
{
title: '热门订阅',
tab: 'popular',
icon: 'mdi-television-classic',
icon: 'mdi-television',
},
]

View File

@@ -8,7 +8,6 @@ import PluginCard from '@/components/cards/PluginCard.vue'
import noImage from '@images/logos/plugin.png'
import { useDisplay } from 'vuetify'
import { isNullOrEmptyObject } from '@/@core/utils'
import router from '@/router'
import { PluginTabs } from '@/router/menu'
const route = useRoute()
@@ -39,6 +38,9 @@ const sortOptions = [
{ title: '最新发布', value: 'add_time' },
]
// 加载中
const loading = ref(false)
// 已安装插件列表
const dataList = ref<Plugin[]>([])
@@ -198,11 +200,13 @@ const filterPlugins = computed(() => {
// 获取插件列表数据
async function fetchInstalledPlugins() {
try {
loading.value = true
dataList.value = await api.get('plugin/', {
params: {
state: 'installed',
},
})
loading.value = false
isRefreshed.value = true
} catch (error) {
console.error(error)
@@ -212,6 +216,7 @@ async function fetchInstalledPlugins() {
// 获取未安装插件列表数据
async function fetchUninstalledPlugins() {
try {
loading.value = true
uninstalledList.value = await api.get('plugin/', {
params: {
state: 'market',
@@ -227,6 +232,8 @@ async function fetchUninstalledPlugins() {
}
}
}
loading.value = false
isRefreshed.value = true
// 更新插件市场列表
// 排除已安装且有更新的,上面的问题在于“本地存在未安装的旧版本插件且云端有更新时”不会在插件市场展示
marketList.value = uninstalledList.value.filter(item => !(item.has_update && item.installed))
@@ -312,11 +319,6 @@ function handleRepoUrl(url: string | undefined) {
return url.replace('https://github.com/', '').replace('https://raw.githubusercontent.com/', '')
}
// 跳转tab
function jumpTab(tab: string) {
router.push('/plugins?tab=' + tab)
}
// 加载时获取数据
onBeforeMount(async () => {
await refreshData()
@@ -334,7 +336,7 @@ onBeforeMount(async () => {
<template>
<div>
<VTabs v-model="activeTab">
<VTab v-for="item in PluginTabs" :value="item.tab" @click="jumpTab(item.tab)">
<VTab v-for="item in PluginTabs" :value="item.tab">
<span class="mx-5">{{ item.title }}</span>
</VTab>
</VTabs>

View File

@@ -6,6 +6,7 @@ import SiteCard from '@/components/cards/SiteCard.vue'
import NoDataFound from '@/components/NoDataFound.vue'
import SiteAddEditDialog from '@/components/dialog/SiteAddEditDialog.vue'
import { useDisplay } from 'vuetify'
import { isLength } from 'lodash'
// 显示器宽度
const display = useDisplay()
@@ -21,13 +22,18 @@ const dataList = ref<Site[]>([])
// 是否刷新过
const isRefreshed = ref(false)
// 是否加载中
const loading = ref(false)
// 新增站点对话框
const siteAddDialog = ref(false)
// 获取站点列表数据
async function fetchData() {
try {
loading.value = true
dataList.value = await api.get('site/')
loading.value = false
isRefreshed.value = true
} catch (error) {
console.error(error)
@@ -50,6 +56,12 @@ async function savaSitesPriority() {
// 加载时获取数据
onBeforeMount(fetchData)
onActivated(() => {
if (!loading.value) {
fetchData()
}
})
</script>
<template>

View File

@@ -1,4 +1,4 @@
<script lang='ts' setup>
<script lang="ts" setup>
import type { CalendarOptions, EventSourceInput } from '@fullcalendar/core'
import dayGridPlugin from '@fullcalendar/daygrid'
import interactionPlugin from '@fullcalendar/interaction'
@@ -8,6 +8,13 @@ import type { Ref } from 'vue'
import type { MediaInfo, Subscribe, TmdbEpisode } from '@/api/types'
import api from '@/api'
import { formatEp, parseDate } from '@/@core/utils/formatters'
import ProgressDialog from '@/components/dialog/ProgressDialog.vue'
// 进度框
const progressDialog = ref(false)
// 加载中
const loading = ref(false)
// 日历属性
const calendarOptions: Ref<CalendarOptions> = ref({
@@ -51,12 +58,9 @@ async function eventsHander(subscribe: Subscribe) {
mediaType: subscribe.type,
len: 1,
}
}
else {
} else {
// 调用API查询集信息
const episodes: TmdbEpisode[] = await api.get(
`tmdb/${subscribe.tmdbid}/${subscribe.season}`,
)
const episodes: TmdbEpisode[] = await api.get(`tmdb/${subscribe.tmdbid}/${subscribe.season}`)
interface EpisodeInfo {
title: string
@@ -78,8 +82,7 @@ async function eventsHander(subscribe: Subscribe) {
if (dictEpisode[air_date]) {
dictEpisode[air_date].subtitle += `,${episode.episode_number}`
dictEpisode[air_date].len++
}
else {
} else {
dictEpisode[air_date] = {
title: subscribe.name,
subtitle: `${episode.episode_number}`,
@@ -100,25 +103,30 @@ async function eventsHander(subscribe: Subscribe) {
// 调用API查询所有订阅
async function getSubscribes() {
progressDialog.value = true
try {
// 订阅
loading.value = true
const subscribes: Subscribe[] = await api.get('subscribe/')
const subEvents = await Promise.all(
subscribes.map(async sub => eventsHander(sub)),
)
loading.value = false
const subEvents = await Promise.all(subscribes.map(async sub => eventsHander(sub)))
calendarOptions.value.events = subEvents.flat().filter(event => event.start) as EventSourceInput
}
catch (error) {
} catch (error) {
console.error(error)
}
progressDialog.value = false
}
// 页面加载时调用API查询所有订阅
onMounted(() => {
getSubscribes()
})
onActivated(() => {
if (!loading.value) {
getSubscribes()
}
})
</script>
<template>
@@ -186,9 +194,10 @@ onMounted(() => {
</div>
</template>
</FullCalendar>
<ProgressDialog v-if="progressDialog" v-model="progressDialog" text="正在加载 ..." />
</template>
<style lang='scss'>
<style lang="scss">
.v-application .fc {
--fc-today-bg-color: rgba(var(--v-theme-on-surface), 0.04);
--fc-border-color: rgba(var(--v-border-color), var(--v-border-opacity));
@@ -200,7 +209,7 @@ onMounted(() => {
// 当天背景渐变
.fc-day-today {
background-image: linear-gradient(to bottom, #AF85FD ,rgba(var(--v-theme-on-surface), 0.04));
background-image: linear-gradient(to bottom, #af85fd, rgba(var(--v-theme-on-surface), 0.04));
}
.v-application .fc a {
@@ -295,11 +304,7 @@ onMounted(() => {
.v-application .fc .fc-toolbar-chunk .fc-button-group .fc-button-primary,
.v-application .fc .fc-toolbar-chunk .fc-button-group .fc-button-primary:hover,
.v-application
.fc
.fc-toolbar-chunk
.fc-button-group
.fc-button-primary:not(.disabled):active {
.v-application .fc .fc-toolbar-chunk .fc-button-group .fc-button-primary:not(.disabled):active {
border-color: transparent;
background-color: transparent;
color: rgba(var(--v-theme-on-surface), var(--v-high-emphasis-opacity));
@@ -323,19 +328,11 @@ onMounted(() => {
text-transform: uppercase;
}
.v-application
.fc
.fc-toolbar-chunk:last-child
.fc-button-group
.fc-button:not(:last-child) {
.v-application .fc .fc-toolbar-chunk:last-child .fc-button-group .fc-button:not(:last-child) {
border-inline-end: 0.0625rem solid rgba(var(--v-theme-primary), var(--v-overlay-scrim-opacity));
}
.v-application
.fc
.fc-toolbar-chunk:last-child
.fc-button-group
.fc-button.fc-button-active {
.v-application .fc .fc-toolbar-chunk:last-child .fc-button-group .fc-button.fc-button-active {
background-color: rgba(var(--v-theme-primary), var(--v-activated-opacity));
color: rgb(var(--v-theme-primary));
}
@@ -371,7 +368,7 @@ onMounted(() => {
padding-inline: 0.25rem;
}
.v-application .fc tbody[role="rowgroup"] > tr > td[role="presentation"] {
.v-application .fc tbody[role='rowgroup'] > tr > td[role='presentation'] {
border: none;
}
@@ -400,9 +397,8 @@ onMounted(() => {
.v-application .fc .fc-popover {
border-radius: 6px;
box-shadow: 0 4px 14px -4px var(--v-shadow-key-umbra-opacity),
0 4px 8px -4px var(--v-shadow-key-penumbra-opacity),
0 4px 8px -4px var(--v-shadow-key-ambient-opacity);
box-shadow: 0 4px 14px -4px var(--v-shadow-key-umbra-opacity), 0 4px 8px -4px var(--v-shadow-key-penumbra-opacity),
0 4px 8px -4px var(--v-shadow-key-ambient-opacity);
}
.v-application .fc .fc-popover .fc-popover-header,
@@ -441,12 +437,7 @@ onMounted(() => {
}
}
.v-theme--dark
.v-application
.fc
.fc-toolbar-chunk
.fc-button-group
.fc-drawerToggler-button {
.v-theme--dark .v-application .fc .fc-toolbar-chunk .fc-button-group .fc-drawerToggler-button {
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' stroke='rgba(232,232,241,0.68)' stroke-width='2' fill='none' stroke-linecap='round' stroke-linejoin='round' class='css-i6dzq1'%3E%3Cpath d='M3 12h18M3 6h18M3 18h18'/%3E%3C/svg%3E");
}

View File

@@ -24,7 +24,7 @@ const props = defineProps({
})
// 是否刷新过
const isRefreshed = ref(false)
let isRefreshed = ref(false)
// 数据列表
const dataList = ref<Subscribe[]>([])
@@ -38,7 +38,9 @@ const historyDialog = ref(false)
// 获取订阅列表数据
async function fetchData() {
try {
loading.value = true
dataList.value = await api.get('subscribe/')
loading.value = false
isRefreshed.value = true
} catch (error) {
console.error(error)
@@ -49,10 +51,9 @@ async function fetchData() {
const loading = ref(false)
// 下拉刷新
function onRefresh() {
loading.value = true
fetchData()
loading.value = false
async function onRefresh({ done }: { done: any }) {
await fetchData()
done('ok')
}
// 过滤数据,管理员用户显示全部,非管理员只显示自己的订阅
@@ -75,6 +76,12 @@ onMounted(async () => {
}
}
})
onActivated(async () => {
if (!loading.value) {
fetchData()
}
})
</script>
<template>

View File

@@ -76,13 +76,13 @@ async function fetchData({ done }: { done: any }) {
done('ok')
}
} else {
// 加载一次
// 设置加载中
loading.value = true
// 请求API
currData.value = await api.get(apipath, {
params: getParams(),
})
loading.value = false
// 标计为已请求完成
isRefreshed.value = true
if (currData.value.length === 0) {
@@ -97,8 +97,6 @@ async function fetchData({ done }: { done: any }) {
done('ok')
}
}
// 取消加载中
loading.value = false
} catch (error) {
console.error(error)
// 返回加载失败

View File

@@ -1,14 +1,7 @@
<script setup lang="ts">
import api from '@/api'
import type { Plugin, Subscribe } from '@/api/types'
import {
SystemNavMenus,
UserfulMenus,
SubscribeMovieTabs,
SubscribeTvTabs,
PluginTabs,
SettingTabs,
} from '@/router/menu'
import { SystemNavMenus, UserfulMenus, PluginTabs, SettingTabs } from '@/router/menu'
import { NavMenu } from '@/@layouts/types'
// 路由
@@ -54,14 +47,14 @@ function getMenus(): NavMenu[] {
item =>
item &&
menus.push({
title: item.title,
title: item.full_title ?? item.title,
icon: item.icon,
to: item.to,
header: item.header,
admin: item.admin,
}),
)
// 各类标签页
// 设置标签页
SettingTabs.forEach(
item =>
item &&
@@ -74,39 +67,6 @@ function getMenus(): NavMenu[] {
description: item.description,
}),
)
SubscribeMovieTabs.forEach(
item =>
item &&
menus.push({
title: '电影 -> ' + item.title,
icon: item.icon,
to: `/subscribe-movie?tab=${item.tab}`,
header: '',
admin: false,
}),
)
SubscribeTvTabs.forEach(
item =>
item &&
menus.push({
title: '电视剧 -> ' + item.title,
icon: item.icon,
to: `/subscribe-tv?tab=${item.tab}`,
header: '',
admin: false,
}),
)
PluginTabs.forEach(
item =>
item &&
menus.push({
title: '插件 -> ' + item.title,
icon: item.icon,
to: `/plugins?tab=${item.tab}`,
header: '',
admin: true,
}),
)
return menus
}
@@ -236,14 +196,14 @@ function goPage(to: string) {
function goSubscribe(subscribe: Subscribe) {
if (subscribe.type === '电影') {
router.push({
path: '/subscribe-movie',
path: '/subscribe/movie',
query: {
id: subscribe.id,
},
})
} else {
router.push({
path: '/subscribe-tv',
path: '/subscribe/tv',
query: {
id: subscribe.id,
},

View File

@@ -90,7 +90,7 @@ export default defineConfig({
},
{
'name': '电影订阅',
'url': './subscribe-movie?tab=mysub',
'url': './subscribe/movie',
'icons': [
{
'src': './clock-icon-192x192.png',
@@ -101,7 +101,7 @@ export default defineConfig({
},
{
'name': '电视剧订阅',
'url': './subscribe-tv?tab=mysub',
'url': './subscribe/tv',
'icons': [
{
'src': './clock-icon-192x192.png',