mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-05-30 21:00:43 +08:00
优化浏览器警告
This commit is contained in:
@@ -71,7 +71,7 @@ export const checkPWAStatus = async () => {
|
||||
}
|
||||
|
||||
// 检测是否为移动设备
|
||||
const isMobileDevice = (): boolean => {
|
||||
export const isMobileDevice = (): boolean => {
|
||||
// 检查用户代理字符串
|
||||
const userAgent = navigator.userAgent || ''
|
||||
const mobileRegex = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile|mobile|CriOS/i
|
||||
|
||||
@@ -48,16 +48,18 @@ const getImgUrl = computed(() => {
|
||||
<VSkeletonLoader class="object-cover aspect-w-3 aspect-h-2" />
|
||||
</div>
|
||||
</template>
|
||||
<VCardText
|
||||
class="w-full flex flex-col flex-wrap justify-end align-left text-white absolute bottom-0 cursor-pointer pa-2"
|
||||
>
|
||||
<h1
|
||||
class="mb-1 text-white text-shadow font-extrabold text-xl line-clamp-2 overflow-hidden text-ellipsis ..."
|
||||
<template #default>
|
||||
<VCardText
|
||||
class="w-full flex flex-col flex-wrap justify-end align-left text-white absolute bottom-0 cursor-pointer pa-2"
|
||||
>
|
||||
{{ props.media?.title }}
|
||||
</h1>
|
||||
<span class="text-shadow">{{ props.media?.subtitle }}</span>
|
||||
</VCardText>
|
||||
<h1
|
||||
class="mb-1 text-white text-shadow font-extrabold text-xl line-clamp-2 overflow-hidden text-ellipsis ..."
|
||||
>
|
||||
{{ props.media?.title }}
|
||||
</h1>
|
||||
<span class="text-shadow">{{ props.media?.subtitle }}</span>
|
||||
</VCardText>
|
||||
</template>
|
||||
</VImg>
|
||||
</template>
|
||||
<div class="w-full absolute bottom-0">
|
||||
|
||||
@@ -170,13 +170,15 @@ onMounted(async () => {
|
||||
<VSkeletonLoader class="object-cover aspect-w-3 aspect-h-2" />
|
||||
</div>
|
||||
</template>
|
||||
<VCardText
|
||||
class="w-full flex flex-col flex-wrap justify-end align-center text-white absolute bottom-0 cursor-pointer pa-2"
|
||||
>
|
||||
<h1 class="mb-1 text-white text-shadow font-bold line-clamp-2 overflow-hidden text-ellipsis ...">
|
||||
{{ props.media?.name }}
|
||||
</h1>
|
||||
</VCardText>
|
||||
<template #default>
|
||||
<VCardText
|
||||
class="w-full flex flex-col flex-wrap justify-end align-center text-white absolute bottom-0 cursor-pointer pa-2"
|
||||
>
|
||||
<h1 class="mb-1 text-white text-shadow font-bold line-clamp-2 overflow-hidden text-ellipsis ...">
|
||||
{{ props.media?.name }}
|
||||
</h1>
|
||||
</VCardText>
|
||||
</template>
|
||||
</VImg>
|
||||
</template>
|
||||
</VCard>
|
||||
|
||||
@@ -170,7 +170,9 @@ const iconPath: Ref<string> = computed(() => {
|
||||
if (imageLoadError.value) return noImage
|
||||
// 如果是网络图片则使用代理后返回
|
||||
if (props.plugin?.plugin_icon?.startsWith('http'))
|
||||
return `${import.meta.env.VITE_API_BASE_URL}system/img/1?imgurl=${encodeURIComponent(props.plugin?.plugin_icon)}&cache=true`
|
||||
return `${import.meta.env.VITE_API_BASE_URL}system/img/1?imgurl=${encodeURIComponent(
|
||||
props.plugin?.plugin_icon,
|
||||
)}&cache=true`
|
||||
|
||||
return `./plugin_icon/${props.plugin?.plugin_icon}`
|
||||
})
|
||||
@@ -475,7 +477,9 @@ watch(
|
||||
<div class="flex flex-nowrap items-center w-full pe-10">
|
||||
<div class="flex flex-nowrap max-w-40 items-center align-middle">
|
||||
<VImg :src="authorPath" class="author-avatar" @load="isAvatarLoaded = true">
|
||||
<VIcon v-if="!isAvatarLoaded" size="small" icon="mdi-github" class="me-1" />
|
||||
<template #default>
|
||||
<VIcon v-if="!isAvatarLoaded" size="small" icon="mdi-github" class="me-1" />
|
||||
</template>
|
||||
</VImg>
|
||||
<a
|
||||
:href="props.plugin?.author_url"
|
||||
|
||||
@@ -346,7 +346,9 @@ function onSubscribeEditRemove() {
|
||||
<VSkeletonLoader class="object-cover aspect-w-3 aspect-h-2" />
|
||||
</div>
|
||||
</template>
|
||||
<div class="absolute inset-0 outline-none subscribe-card-background"></div>
|
||||
<template #default>
|
||||
<div class="absolute inset-0 outline-none subscribe-card-background"></div>
|
||||
</template>
|
||||
</VImg>
|
||||
<div
|
||||
v-if="subscribeState === 'P'"
|
||||
|
||||
@@ -121,7 +121,9 @@ function doDelete() {
|
||||
<VSkeletonLoader class="object-cover aspect-w-3 aspect-h-2" />
|
||||
</div>
|
||||
</template>
|
||||
<div class="absolute inset-0 subscribe-card-background"></div>
|
||||
<template #default>
|
||||
<div class="absolute inset-0 subscribe-card-background"></div>
|
||||
</template>
|
||||
</VImg>
|
||||
</template>
|
||||
<div class="h-full flex flex-col">
|
||||
|
||||
@@ -148,7 +148,6 @@ async function doDelete() {
|
||||
<template>
|
||||
<VDialog max-width="40rem" scrollable>
|
||||
<VCard>
|
||||
<VDialogCloseBtn @click="emit('close')" />
|
||||
<VCardText>
|
||||
<VCol>
|
||||
<div class="d-flex justify-space-between flex-wrap flex-md-nowrap flex-column flex-md-row">
|
||||
@@ -242,6 +241,7 @@ async function doDelete() {
|
||||
</div>
|
||||
</VCol>
|
||||
</VCardText>
|
||||
<VDialogCloseBtn @click="emit('close')" />
|
||||
</VCard>
|
||||
</VDialog>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useTabStateRestore } from '@/composables/useStateRestore'
|
||||
import { isMobileDevice } from '@/@core/utils/navigator'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
@@ -77,6 +78,11 @@ const scrollTabs = (direction: 'left' | 'right') => {
|
||||
left: scrollPosition,
|
||||
behavior: 'smooth',
|
||||
})
|
||||
|
||||
// 滚动完成后更新指示器状态
|
||||
setTimeout(() => {
|
||||
updateTabsIndicator()
|
||||
}, 300) // 等待滚动动画完成
|
||||
}
|
||||
|
||||
// Function to check and update the indicator state
|
||||
@@ -84,14 +90,17 @@ const updateTabsIndicator = () => {
|
||||
const el = tabsContainerRef.value
|
||||
if (!el) return
|
||||
|
||||
// 在移动端不显示滚动指示器
|
||||
const isMobile = isMobileDevice()
|
||||
|
||||
const tolerance = 1 // Allow 1px tolerance
|
||||
const hasOverflow = el.scrollWidth > el.clientWidth + tolerance
|
||||
const isScrolledToEnd = el.scrollLeft + el.clientWidth >= el.scrollWidth - tolerance
|
||||
const isScrolledToStart = el.scrollLeft <= tolerance
|
||||
|
||||
showTabsScrollIndicator.value = hasOverflow && !isScrolledToEnd
|
||||
showLeftButton.value = hasOverflow && !isScrolledToStart
|
||||
showRightButton.value = hasOverflow && !isScrolledToEnd
|
||||
showTabsScrollIndicator.value = hasOverflow && !isScrolledToEnd && !isMobile
|
||||
showLeftButton.value = hasOverflow && !isScrolledToStart && !isMobile
|
||||
showRightButton.value = hasOverflow && !isScrolledToEnd && !isMobile
|
||||
}
|
||||
|
||||
// Debounce resize handler
|
||||
@@ -106,6 +115,8 @@ const handleResize = () => {
|
||||
onMounted(async () => {
|
||||
// Add resize listener for tabs indicator
|
||||
window.addEventListener('resize', handleResize)
|
||||
// Add scroll listener for tabs container
|
||||
tabsContainerRef.value?.addEventListener('scroll', updateTabsIndicator)
|
||||
// Initial check for tabs indicator after DOM update
|
||||
await nextTick() // Ensure element is rendered
|
||||
updateTabsIndicator()
|
||||
@@ -173,6 +184,11 @@ onUnmounted(() => {
|
||||
&.right-button {
|
||||
margin-inline-start: 6px;
|
||||
}
|
||||
|
||||
// 在移动端隐藏滚动按钮
|
||||
@media (width <= 768px) {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.header-tabs {
|
||||
@@ -209,6 +225,18 @@ onUnmounted(() => {
|
||||
pointer-events: none; // Allow interaction with content behind it
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
// Show indicator when class is applied
|
||||
&.show-indicator::after {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
// 在移动端隐藏渐变指示器
|
||||
@media (width <= 768px) {
|
||||
&::after {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-tab-icon {
|
||||
|
||||
Reference in New Issue
Block a user