mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-08-28 03:27:54 +08:00
feat(index): 添加页面加载超时提示,修改默认主题设置为跟随系统
fix(service-worker): 优化清理运行时缓存逻辑
This commit is contained in:
+66
-1
@@ -193,6 +193,35 @@
|
|||||||
transform: rotate(1turn);
|
transform: rotate(1turn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 超时通知样式 */
|
||||||
|
#loading-timeout {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 2500;
|
||||||
|
display: none;
|
||||||
|
inset-block-end: 20px;
|
||||||
|
inset-inline-start: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
color: #fff;
|
||||||
|
padding: 12px 24px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
|
||||||
|
white-space: nowrap;
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#timeout-btn {
|
||||||
|
color: var(--initial-loader-color, #9155FD);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-inline-start: 8px;
|
||||||
|
border-bottom: 1px solid var(--initial-loader-color, #9155FD);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -210,7 +239,7 @@
|
|||||||
let primaryColor = localStorage.getItem('materio-initial-loader-color')
|
let primaryColor = localStorage.getItem('materio-initial-loader-color')
|
||||||
|
|
||||||
// 检查主题设置
|
// 检查主题设置
|
||||||
const savedTheme = localStorage.getItem('theme')
|
const savedTheme = localStorage.getItem('theme') || 'auto'
|
||||||
const isAutoTheme = savedTheme === 'auto'
|
const isAutoTheme = savedTheme === 'auto'
|
||||||
|
|
||||||
// 如果是自动主题或者没有保存的背景色,根据系统主题设置背景色
|
// 如果是自动主题或者没有保存的背景色,根据系统主题设置背景色
|
||||||
@@ -243,6 +272,38 @@
|
|||||||
updateSafeArea()
|
updateSafeArea()
|
||||||
window.addEventListener('resize', updateSafeArea)
|
window.addEventListener('resize', updateSafeArea)
|
||||||
window.addEventListener('orientationchange', updateSafeArea)
|
window.addEventListener('orientationchange', updateSafeArea)
|
||||||
|
|
||||||
|
// 清除缓存处理逻辑
|
||||||
|
window.clearAndReload = async function() {
|
||||||
|
try {
|
||||||
|
// 1. 清除所有缓存
|
||||||
|
if ('caches' in window) {
|
||||||
|
const cacheNames = await caches.keys()
|
||||||
|
await Promise.all(cacheNames.map(name => caches.delete(name)))
|
||||||
|
console.log('[VersionChecker] 已清除所有缓存')
|
||||||
|
}
|
||||||
|
// 2. 注销 Service Worker
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
const registrations = await navigator.serviceWorker.getRegistrations()
|
||||||
|
await Promise.all(registrations.map(registration => registration.unregister()))
|
||||||
|
console.log('[VersionChecker] 已注销所有 Service Worker')
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[VersionChecker] 清除缓存时出错:', e)
|
||||||
|
} finally {
|
||||||
|
// 3. 重载页面
|
||||||
|
const url = new URL(window.location.href)
|
||||||
|
url.searchParams.set('_t', Date.now().toString())
|
||||||
|
window.location.replace(url.toString())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
const timeoutEl = document.getElementById('loading-timeout');
|
||||||
|
if (timeoutEl) {
|
||||||
|
timeoutEl.style.display = 'block';
|
||||||
|
}
|
||||||
|
}, 15000); // 15秒后显示超时提示
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@@ -257,6 +318,10 @@
|
|||||||
<div class="effect-2 effects"></div>
|
<div class="effect-2 effects"></div>
|
||||||
<div class="effect-3 effects"></div>
|
<div class="effect-3 effects"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 超时提示 - 默认隐藏 -->
|
||||||
|
<div id="loading-timeout">
|
||||||
|
页面加载似乎遇到了阻碍,请尝试 <a href="javascript:void(0)" onclick="window.clearAndReload()" id="timeout-btn">清除缓存</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
|||||||
+3
-3
@@ -15,7 +15,7 @@ import { themeManager } from '@/utils/themeManager'
|
|||||||
|
|
||||||
// 生效主题
|
// 生效主题
|
||||||
const { global: globalTheme } = useTheme()
|
const { global: globalTheme } = useTheme()
|
||||||
let themeValue = localStorage.getItem('theme') || 'light'
|
let themeValue = localStorage.getItem('theme') || 'auto'
|
||||||
const autoTheme = checkPrefersColorSchemeIsDark() ? 'dark' : 'light'
|
const autoTheme = checkPrefersColorSchemeIsDark() ? 'dark' : 'light'
|
||||||
globalTheme.name.value = themeValue === 'auto' ? autoTheme : themeValue
|
globalTheme.name.value = themeValue === 'auto' ? autoTheme : themeValue
|
||||||
|
|
||||||
@@ -239,8 +239,8 @@ async function loadBackgroundImages(retryCount = 0) {
|
|||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
// 移除URL中的时间戳参数
|
// 移除URL中的时间戳参数
|
||||||
const url = new URL(window.location.href)
|
const url = new URL(window.location.href)
|
||||||
if (url.searchParams.has('t')) {
|
if (url.searchParams.has('_t')) {
|
||||||
url.searchParams.delete('t')
|
url.searchParams.delete('_t')
|
||||||
window.history.replaceState({}, '', url.toString())
|
window.history.replaceState({}, '', url.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ const needsUpdate = computed(() => {
|
|||||||
*/
|
*/
|
||||||
export const reloadWithTimestamp = (): void => {
|
export const reloadWithTimestamp = (): void => {
|
||||||
const url = new URL(window.location.href)
|
const url = new URL(window.location.href)
|
||||||
url.searchParams.set('t', Date.now().toString())
|
url.searchParams.set('_t', Date.now().toString())
|
||||||
window.location.href = url.toString()
|
window.location.replace(url.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -272,7 +272,7 @@ const getUIModeIcon = computed(() => {
|
|||||||
|
|
||||||
// 主题相关功能
|
// 主题相关功能
|
||||||
const { name: themeName, global: globalTheme } = useTheme()
|
const { name: themeName, global: globalTheme } = useTheme()
|
||||||
const savedTheme = ref(localStorage.getItem('theme') ?? themeName)
|
const savedTheme = ref(localStorage.getItem('theme') ?? 'auto')
|
||||||
const currentThemeName = ref(savedTheme.value)
|
const currentThemeName = ref(savedTheme.value)
|
||||||
|
|
||||||
const themes: ThemeSwitcherTheme[] = [
|
const themes: ThemeSwitcherTheme[] = [
|
||||||
|
|||||||
+23
-11
@@ -7,11 +7,13 @@ import * as navigationPreload from 'workbox-navigation-preload'
|
|||||||
|
|
||||||
// Service Worker 类型声明
|
// Service Worker 类型声明
|
||||||
declare let self: ServiceWorkerGlobalScope & {
|
declare let self: ServiceWorkerGlobalScope & {
|
||||||
__WB_MANIFEST: Array<{ url: string; revision?: string }>
|
readonly __WB_MANIFEST: Array<{ url: string; revision?: string }>
|
||||||
}
|
}
|
||||||
|
|
||||||
// 缓存版本控制
|
// 缓存版本控制
|
||||||
const CACHE_VERSION = `${__APP_VERSION__}-${__BUILD_TIME__}`
|
const RESOURCE_VERSION = 'V2'
|
||||||
|
// 开发环境CACHE_VERSION回退到RESOURCE_VERSION
|
||||||
|
const CACHE_VERSION = typeof __APP_VERSION__ !== 'undefined' ? `${__APP_VERSION__}-${__BUILD_TIME__}` : RESOURCE_VERSION
|
||||||
|
|
||||||
// 启用导航预载
|
// 启用导航预载
|
||||||
navigationPreload.enable()
|
navigationPreload.enable()
|
||||||
@@ -111,7 +113,7 @@ registerRoute(
|
|||||||
registerRoute(
|
registerRoute(
|
||||||
({ request }) => request.destination === 'image',
|
({ request }) => request.destination === 'image',
|
||||||
new CacheFirst({
|
new CacheFirst({
|
||||||
cacheName: `image-cache-${CACHE_VERSION}`,
|
cacheName: `image-cache-${RESOURCE_VERSION}`,
|
||||||
plugins: [
|
plugins: [
|
||||||
new CacheableResponsePlugin({
|
new CacheableResponsePlugin({
|
||||||
statuses: [0, 200],
|
statuses: [0, 200],
|
||||||
@@ -128,7 +130,7 @@ registerRoute(
|
|||||||
registerRoute(
|
registerRoute(
|
||||||
({ request }) => request.destination === 'font',
|
({ request }) => request.destination === 'font',
|
||||||
new CacheFirst({
|
new CacheFirst({
|
||||||
cacheName: `font-cache-${CACHE_VERSION}`,
|
cacheName: `font-cache-${RESOURCE_VERSION}`,
|
||||||
plugins: [
|
plugins: [
|
||||||
new CacheableResponsePlugin({
|
new CacheableResponsePlugin({
|
||||||
statuses: [0, 200],
|
statuses: [0, 200],
|
||||||
@@ -145,7 +147,7 @@ registerRoute(
|
|||||||
registerRoute(
|
registerRoute(
|
||||||
({ url }) => url.hostname === 'image.tmdb.org',
|
({ url }) => url.hostname === 'image.tmdb.org',
|
||||||
new CacheFirst({
|
new CacheFirst({
|
||||||
cacheName: `tmdb-image-cache-${CACHE_VERSION}`,
|
cacheName: `tmdb-image-cache-${RESOURCE_VERSION}`,
|
||||||
plugins: [
|
plugins: [
|
||||||
new CacheableResponsePlugin({
|
new CacheableResponsePlugin({
|
||||||
statuses: [0, 200],
|
statuses: [0, 200],
|
||||||
@@ -165,7 +167,8 @@ registerRoute(
|
|||||||
request.method === 'GET' &&
|
request.method === 'GET' &&
|
||||||
!url.pathname.includes('/api/v1/system/message') && // 排除 SSE 长连接
|
!url.pathname.includes('/api/v1/system/message') && // 排除 SSE 长连接
|
||||||
!url.pathname.includes('/api/v1/common/message') && // 排除通用消息
|
!url.pathname.includes('/api/v1/common/message') && // 排除通用消息
|
||||||
!url.pathname.includes('/api/v1/message/'), // 排除所有消息类接口
|
!url.pathname.includes('/api/v1/message/') && // 排除所有消息类接口
|
||||||
|
!url.pathname.includes('/api/v1/system/global'), // 排除global接口
|
||||||
new NetworkFirst({
|
new NetworkFirst({
|
||||||
cacheName: `api-cache-${CACHE_VERSION}`,
|
cacheName: `api-cache-${CACHE_VERSION}`,
|
||||||
networkTimeoutSeconds: 5,
|
networkTimeoutSeconds: 5,
|
||||||
@@ -201,14 +204,23 @@ async function cleanupRuntimeCaches(onlyOld: boolean = false) {
|
|||||||
'font-cache',
|
'font-cache',
|
||||||
'api-cache',
|
'api-cache',
|
||||||
'tmdb-image-cache',
|
'tmdb-image-cache',
|
||||||
'pages-cache',
|
]
|
||||||
|
|
||||||
|
// 当前版本的缓存全名
|
||||||
|
const currentCacheNames = [
|
||||||
|
`app-shell-${CACHE_VERSION}`,
|
||||||
|
`static-resources-${CACHE_VERSION}`,
|
||||||
|
`image-cache-${RESOURCE_VERSION}`,
|
||||||
|
`font-cache-${RESOURCE_VERSION}`,
|
||||||
|
`tmdb-image-cache-${RESOURCE_VERSION}`,
|
||||||
|
`api-cache-${CACHE_VERSION}`,
|
||||||
]
|
]
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
cacheNames.map(cacheName => {
|
cacheNames.map(cacheName => {
|
||||||
const isRuntimeCache = runtimeCachePrefixes.some(prefix => cacheName.startsWith(prefix))
|
const isRuntimeCache = runtimeCachePrefixes.some(prefix => cacheName.startsWith(prefix))
|
||||||
if (isRuntimeCache) {
|
if (isRuntimeCache) {
|
||||||
if (!onlyOld || !cacheName.includes(CACHE_VERSION)) {
|
if (!onlyOld || !currentCacheNames.includes(cacheName)) {
|
||||||
console.log('[SW] Deleting runtime cache:', cacheName)
|
console.log('[SW] Deleting runtime cache:', cacheName)
|
||||||
return caches.delete(cacheName)
|
return caches.delete(cacheName)
|
||||||
}
|
}
|
||||||
@@ -285,9 +297,9 @@ async function updateBadge(count: number) {
|
|||||||
if ('setAppBadge' in self.navigator) {
|
if ('setAppBadge' in self.navigator) {
|
||||||
try {
|
try {
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
await (self.navigator as any).setAppBadge(count)
|
await self.navigator.setAppBadge(count)
|
||||||
} else {
|
} else {
|
||||||
await (self.navigator as any).clearAppBadge()
|
await self.navigator.clearAppBadge()
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to update app badge:', error)
|
console.error('Failed to update app badge:', error)
|
||||||
@@ -298,7 +310,7 @@ async function updateBadge(count: number) {
|
|||||||
async function clearBadge() {
|
async function clearBadge() {
|
||||||
if ('clearAppBadge' in self.navigator) {
|
if ('clearAppBadge' in self.navigator) {
|
||||||
try {
|
try {
|
||||||
await (self.navigator as any).clearAppBadge()
|
await self.navigator.clearAppBadge()
|
||||||
await setStoredUnreadCount(0)
|
await setStoredUnreadCount(0)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to clear app badge:', error)
|
console.error('Failed to clear app badge:', error)
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ const templateTypes = ref([
|
|||||||
|
|
||||||
// 编辑器主题
|
// 编辑器主题
|
||||||
const { name: themeName, global: globalTheme } = useTheme()
|
const { name: themeName, global: globalTheme } = useTheme()
|
||||||
const savedTheme = ref(localStorage.getItem('theme') ?? themeName)
|
const savedTheme = ref(localStorage.getItem('theme') ?? 'auto')
|
||||||
const currentThemeName = ref(savedTheme.value)
|
const currentThemeName = ref(savedTheme.value)
|
||||||
const editorTheme = computed(() => (currentThemeName.value === 'light' ? 'github' : 'monokai'))
|
const editorTheme = computed(() => (currentThemeName.value === 'light' ? 'github' : 'monokai'))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user