mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-05 23:56:42 +08:00
优化可用高度计算
This commit is contained in:
+4
-2
@@ -2,6 +2,8 @@
|
|||||||
<html lang="zh-CN" style="
|
<html lang="zh-CN" style="
|
||||||
overflow: hidden auto;
|
overflow: hidden auto;
|
||||||
min-block-size: calc(100% + env(safe-area-inset-top) + env(safe-area-inset-bottom));
|
min-block-size: calc(100% + env(safe-area-inset-top) + env(safe-area-inset-bottom));
|
||||||
|
--safe-area-inset-bottom: env(safe-area-inset-bottom);
|
||||||
|
--safe-area-inset-top: env(safe-area-inset-top);
|
||||||
background: var(--initial-loader-bg, #fff);
|
background: var(--initial-loader-bg, #fff);
|
||||||
">
|
">
|
||||||
|
|
||||||
@@ -87,11 +89,11 @@
|
|||||||
<link rel="dns-prefetch" href="//image.tmdb.org" />
|
<link rel="dns-prefetch" href="//image.tmdb.org" />
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin />
|
||||||
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
|
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
|
||||||
|
|
||||||
<!-- 预加载关键资源 -->
|
<!-- 预加载关键资源 -->
|
||||||
<link rel="preload" href="/logo.png" as="image" />
|
<link rel="preload" href="/logo.png" as="image" />
|
||||||
<link rel="modulepreload" href="/src/main.ts" />
|
<link rel="modulepreload" href="/src/main.ts" />
|
||||||
|
|
||||||
<!-- 内联关键CSS -->
|
<!-- 内联关键CSS -->
|
||||||
<style>
|
<style>
|
||||||
/* 关键路径CSS - 从loader.css内联 */
|
/* 关键路径CSS - 从loader.css内联 */
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ import FileList from './filebrowser/FileList.vue'
|
|||||||
import FileToolbar from './filebrowser/FileToolbar.vue'
|
import FileToolbar from './filebrowser/FileToolbar.vue'
|
||||||
import FileNavigator from './filebrowser/FileNavigator.vue'
|
import FileNavigator from './filebrowser/FileNavigator.vue'
|
||||||
import type { EndPoints, FileItem, StorageConf } from '@/api/types'
|
import type { EndPoints, FileItem, StorageConf } from '@/api/types'
|
||||||
import { useDisplay } from 'vuetify'
|
|
||||||
import { storageIconDict } from '@/api/constants'
|
import { storageIconDict } from '@/api/constants'
|
||||||
import { usePWA } from '@/composables/usePWA'
|
|
||||||
|
|
||||||
// 输入参数
|
// 输入参数
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -30,13 +28,6 @@ const props = defineProps({
|
|||||||
// 对外事件
|
// 对外事件
|
||||||
const emit = defineEmits(['pathchanged'])
|
const emit = defineEmits(['pathchanged'])
|
||||||
|
|
||||||
// 显示器宽度
|
|
||||||
const display = useDisplay()
|
|
||||||
|
|
||||||
// APP
|
|
||||||
// PWA模式检测
|
|
||||||
const { appMode } = usePWA()
|
|
||||||
|
|
||||||
const fileIcons = {
|
const fileIcons = {
|
||||||
// 压缩包
|
// 压缩包
|
||||||
zip: 'mdi-folder-zip-outline',
|
zip: 'mdi-folder-zip-outline',
|
||||||
@@ -240,20 +231,6 @@ function stopDrag() {
|
|||||||
;(document.body.style as any).webkitUserSelect = ''
|
;(document.body.style as any).webkitUserSelect = ''
|
||||||
;(document.body.style as any).mozUserSelect = ''
|
;(document.body.style as any).mozUserSelect = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// 外层DIV大小控制
|
|
||||||
const scrollStyle = computed(() => {
|
|
||||||
return appMode.value
|
|
||||||
? 'height: calc(100vh - 10.5rem - env(safe-area-inset-bottom) - 7rem)'
|
|
||||||
: 'height: calc(100vh - 10.5rem - env(safe-area-inset-bottom)'
|
|
||||||
})
|
|
||||||
|
|
||||||
// 文件列表大小限制
|
|
||||||
const fileListStyle = computed(() => {
|
|
||||||
return appMode.value
|
|
||||||
? 'height: calc(100vh - 14rem - env(safe-area-inset-bottom) - 7rem)'
|
|
||||||
: 'height: calc(100vh - 14rem - env(safe-area-inset-bottom)'
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -271,7 +248,7 @@ const fileListStyle = computed(() => {
|
|||||||
@foldercreated="refreshPending = true"
|
@foldercreated="refreshPending = true"
|
||||||
@sortchanged="sortChanged"
|
@sortchanged="sortChanged"
|
||||||
/>
|
/>
|
||||||
<div class="flex" :style="scrollStyle">
|
<div class="flex">
|
||||||
<FileNavigator
|
<FileNavigator
|
||||||
v-if="showDirTree"
|
v-if="showDirTree"
|
||||||
:storage="activeStorage"
|
:storage="activeStorage"
|
||||||
@@ -295,7 +272,6 @@ const fileListStyle = computed(() => {
|
|||||||
:axios="axios"
|
:axios="axios"
|
||||||
:refreshpending="refreshPending"
|
:refreshpending="refreshPending"
|
||||||
:sort="sort"
|
:sort="sort"
|
||||||
:listStyle="fileListStyle"
|
|
||||||
:showTree="showDirTree"
|
:showTree="showDirTree"
|
||||||
:style="{ flex: 1 }"
|
:style="{ flex: 1 }"
|
||||||
@pathchanged="pathChanged"
|
@pathchanged="pathChanged"
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { useDisplay } from 'vuetify'
|
|||||||
import MediaInfoDialog from '../dialog/MediaInfoDialog.vue'
|
import MediaInfoDialog from '../dialog/MediaInfoDialog.vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization'
|
import { useBackgroundOptimization } from '@/composables/useBackgroundOptimization'
|
||||||
|
import { usePWA } from '@/composables/usePWA'
|
||||||
|
|
||||||
// 国际化
|
// 国际化
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
@@ -20,6 +21,8 @@ const { useProgressSSE } = useBackgroundOptimization()
|
|||||||
// 显示器宽度
|
// 显示器宽度
|
||||||
const display = useDisplay()
|
const display = useDisplay()
|
||||||
|
|
||||||
|
const { appMode } = usePWA()
|
||||||
|
|
||||||
// 输入参数
|
// 输入参数
|
||||||
const inProps = defineProps({
|
const inProps = defineProps({
|
||||||
icons: Object,
|
icons: Object,
|
||||||
@@ -35,7 +38,6 @@ const inProps = defineProps({
|
|||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
sort: String,
|
sort: String,
|
||||||
listStyle: String,
|
|
||||||
showTree: Boolean,
|
showTree: Boolean,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -125,6 +127,30 @@ const transferItems = ref<FileItem[]>([])
|
|||||||
// 当前图片地址
|
// 当前图片地址
|
||||||
const currentImgLink = ref('')
|
const currentImgLink = ref('')
|
||||||
|
|
||||||
|
// 计算列表可用高度
|
||||||
|
const listAvailableHeight = computed(() => {
|
||||||
|
// 获取视口高度
|
||||||
|
const viewportHeight = window.innerHeight || document.documentElement.clientHeight
|
||||||
|
|
||||||
|
// navbar高度
|
||||||
|
const navbarHeight = 72
|
||||||
|
// 工具栏高度(包含搜索框和按钮)
|
||||||
|
const toolbarHeight = 64
|
||||||
|
// 底部导航栏高度
|
||||||
|
const footerHeight = appMode.value ? 80 : 16
|
||||||
|
// 安全区域高度
|
||||||
|
const safeAreaHeight =
|
||||||
|
parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--safe-area-inset-bottom')) ||
|
||||||
|
parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--safe-area-inset-top')) ||
|
||||||
|
0
|
||||||
|
|
||||||
|
// 计算可用高度,预留一些边距
|
||||||
|
const availableHeight = viewportHeight - navbarHeight - toolbarHeight - footerHeight - safeAreaHeight - 40
|
||||||
|
|
||||||
|
// 确保最小高度
|
||||||
|
return Math.max(availableHeight, 300)
|
||||||
|
})
|
||||||
|
|
||||||
// 是否为图片文件
|
// 是否为图片文件
|
||||||
const isImage = computed(() => {
|
const isImage = computed(() => {
|
||||||
const ext = inProps.item.path?.split('.').pop()?.toLowerCase()
|
const ext = inProps.item.path?.split('.').pop()?.toLowerCase()
|
||||||
@@ -546,7 +572,7 @@ const progressSSE = useProgressSSE(
|
|||||||
`${import.meta.env.VITE_API_BASE_URL}system/progress/batchrename`,
|
`${import.meta.env.VITE_API_BASE_URL}system/progress/batchrename`,
|
||||||
handleProgressMessage,
|
handleProgressMessage,
|
||||||
'file-batch-rename-progress',
|
'file-batch-rename-progress',
|
||||||
progressActive
|
progressActive,
|
||||||
)
|
)
|
||||||
|
|
||||||
// 使用SSE监听加载进度
|
// 使用SSE监听加载进度
|
||||||
@@ -567,6 +593,14 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.file-list-container {
|
||||||
|
overflow: hidden auto;
|
||||||
|
block-size: 100%;
|
||||||
|
max-block-size: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<VCard class="d-flex flex-column w-full h-full rounded-t-0" :class="{ 'rounded-s-0': showTree }">
|
<VCard class="d-flex flex-column w-full h-full rounded-t-0" :class="{ 'rounded-s-0': showTree }">
|
||||||
@@ -635,9 +669,12 @@ onMounted(() => {
|
|||||||
<VImg :src="currentImgLink" max-width="100%" max-height="100%" />
|
<VImg :src="currentImgLink" max-width="100%" max-height="100%" />
|
||||||
</VCardText>
|
</VCardText>
|
||||||
<!-- 目录和文件列表 -->
|
<!-- 目录和文件列表 -->
|
||||||
<VCardText v-else-if="dirs.length || files.length" class="p-0">
|
<VCardText v-else-if="dirs.length || files.length" class="p-0 flex-grow-1 overflow-hidden">
|
||||||
<VList class="text-high-emphasis">
|
<VList
|
||||||
<VVirtualScroll :items="[...dirs, ...files]" :style="listStyle">
|
class="text-high-emphasis file-list-container"
|
||||||
|
:style="{ height: `${listAvailableHeight}px`, maxHeight: `${listAvailableHeight}px` }"
|
||||||
|
>
|
||||||
|
<VVirtualScroll :items="[...dirs, ...files]" style="block-size: 100%">
|
||||||
<template #default="{ item }">
|
<template #default="{ item }">
|
||||||
<VHover>
|
<VHover>
|
||||||
<template #default="hover">
|
<template #default="hover">
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import type { FileItem } from '@/api/types'
|
|||||||
import { useDisplay } from 'vuetify'
|
import { useDisplay } from 'vuetify'
|
||||||
import type { AxiosRequestConfig } from 'axios'
|
import type { AxiosRequestConfig } from 'axios'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { usePWA } from '@/composables/usePWA'
|
||||||
|
|
||||||
// 国际化
|
// 国际化
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
@@ -11,6 +12,32 @@ const { t } = useI18n()
|
|||||||
// 显示器宽度
|
// 显示器宽度
|
||||||
const display = useDisplay()
|
const display = useDisplay()
|
||||||
|
|
||||||
|
const { appMode } = usePWA()
|
||||||
|
|
||||||
|
// 计算列表可用高度
|
||||||
|
const availableHeight = computed(() => {
|
||||||
|
// 获取视口高度
|
||||||
|
const viewportHeight = window.innerHeight || document.documentElement.clientHeight
|
||||||
|
|
||||||
|
// navbar高度
|
||||||
|
const navbarHeight = 72
|
||||||
|
// 工具栏高度
|
||||||
|
const toolbarHeight = 25
|
||||||
|
// 底部导航栏高度
|
||||||
|
const footerHeight = appMode.value ? 80 : 16
|
||||||
|
// 安全区域高度
|
||||||
|
const safeAreaHeight =
|
||||||
|
parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--safe-area-inset-bottom')) ||
|
||||||
|
parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--safe-area-inset-top')) ||
|
||||||
|
0
|
||||||
|
|
||||||
|
// 计算可用高度,预留一些边距
|
||||||
|
const availableHeight = viewportHeight - navbarHeight - toolbarHeight - footerHeight - safeAreaHeight - 40
|
||||||
|
|
||||||
|
// 确保最小高度
|
||||||
|
return Math.max(availableHeight, 300)
|
||||||
|
})
|
||||||
|
|
||||||
// 输入参数
|
// 输入参数
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
storage: {
|
storage: {
|
||||||
@@ -125,11 +152,6 @@ async function loadRootDirectories() {
|
|||||||
await loadSubdirectories('/')
|
await loadSubdirectories('/')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取目录层级深度
|
|
||||||
function getDirectoryDepth(path: string) {
|
|
||||||
return path.split('/').filter(p => p).length
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检索所有目录节点
|
// 检索所有目录节点
|
||||||
function getAllDirectories() {
|
function getAllDirectories() {
|
||||||
const allDirs: { dir: FileItem; level: number; parentPath: string }[] = []
|
const allDirs: { dir: FileItem; level: number; parentPath: string }[] = []
|
||||||
@@ -227,11 +249,6 @@ const flattenedDirectories = computed(() => {
|
|||||||
return getAllDirectories()
|
return getAllDirectories()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 组件挂载时初始加载
|
|
||||||
onMounted(async () => {
|
|
||||||
await loadRootDirectories()
|
|
||||||
})
|
|
||||||
|
|
||||||
// 检查路径是否为指定目录的子目录或后代
|
// 检查路径是否为指定目录的子目录或后代
|
||||||
function isChildOrDescendant(path: string, ancestorPath: string) {
|
function isChildOrDescendant(path: string, ancestorPath: string) {
|
||||||
if (!path || !ancestorPath) return false
|
if (!path || !ancestorPath) return false
|
||||||
@@ -260,10 +277,19 @@ function getIndentLevel(path: string, ancestorPath: string) {
|
|||||||
|
|
||||||
return pathParts - ancestorParts
|
return pathParts - ancestorParts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 组件挂载时初始加载
|
||||||
|
onMounted(async () => {
|
||||||
|
await loadRootDirectories()
|
||||||
|
})
|
||||||
|
|
||||||
|
onActivated(() => {
|
||||||
|
updateHeight()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VCard class="file-navigator rounded-e-0 rounded-t-0" v-if="!isMobile">
|
<VCard class="file-navigator rounded-e-0 rounded-t-0" v-if="!isMobile" :height="`${availableHeight}px`">
|
||||||
<div class="tree-container">
|
<div class="tree-container">
|
||||||
<!-- 根目录项 -->
|
<!-- 根目录项 -->
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ declare let self: ServiceWorkerGlobalScope & {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 缓存版本控制
|
// 缓存版本控制
|
||||||
const CACHE_VERSION = 'v1.0.1'
|
const CACHE_VERSION = 'v1.0.2'
|
||||||
const CACHE_NAMES = {
|
const CACHE_NAMES = {
|
||||||
appShell: `app-shell-${CACHE_VERSION}`,
|
appShell: `app-shell-${CACHE_VERSION}`,
|
||||||
static: `static-resources-${CACHE_VERSION}`,
|
static: `static-resources-${CACHE_VERSION}`,
|
||||||
|
|||||||
@@ -214,10 +214,28 @@ const TransferDict: { [key: string]: string } = {
|
|||||||
rclone_move: t('transferHistory.transferMode.rclone_move'),
|
rclone_move: t('transferHistory.transferMode.rclone_move'),
|
||||||
}
|
}
|
||||||
|
|
||||||
const tableStyle = computed(() => {
|
// 计算列表可用高度
|
||||||
return appMode.value
|
const availableHeight = computed(() => {
|
||||||
? 'height: calc(100vh - 15rem - env(safe-area-inset-bottom) - 7rem)'
|
// 获取视口高度
|
||||||
: 'height: calc(100vh - 15rem - env(safe-area-inset-bottom)'
|
const viewportHeight = window.innerHeight || document.documentElement.clientHeight
|
||||||
|
|
||||||
|
// navbar高度
|
||||||
|
const navbarHeight = 72
|
||||||
|
// 工具栏高度
|
||||||
|
const toolbarHeight = 88
|
||||||
|
// 底部导航栏高度
|
||||||
|
const footerHeight = appMode.value ? 80 : 16
|
||||||
|
// 安全区域高度
|
||||||
|
const safeAreaHeight =
|
||||||
|
parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--safe-area-inset-bottom')) ||
|
||||||
|
parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--safe-area-inset-top')) ||
|
||||||
|
0
|
||||||
|
|
||||||
|
// 计算可用高度,预留一些边距
|
||||||
|
const availableHeight = viewportHeight - navbarHeight - toolbarHeight - footerHeight - safeAreaHeight - 48
|
||||||
|
|
||||||
|
// 确保最小高度
|
||||||
|
return Math.max(availableHeight, 300)
|
||||||
})
|
})
|
||||||
|
|
||||||
// 分页提示
|
// 分页提示
|
||||||
@@ -509,7 +527,7 @@ onMounted(() => {
|
|||||||
show-select
|
show-select
|
||||||
:loading-text="t('transferHistory.loading')"
|
:loading-text="t('transferHistory.loading')"
|
||||||
hover
|
hover
|
||||||
:style="tableStyle"
|
:style="{ height: `${availableHeight}px` }"
|
||||||
>
|
>
|
||||||
<template #header.data-table-group>
|
<template #header.data-table-group>
|
||||||
<span>{{ t('transferHistory.titleColumn') }}</span>
|
<span>{{ t('transferHistory.titleColumn') }}</span>
|
||||||
@@ -608,7 +626,7 @@ onMounted(() => {
|
|||||||
show-select
|
show-select
|
||||||
:loading-text="t('transferHistory.loading')"
|
:loading-text="t('transferHistory.loading')"
|
||||||
hover
|
hover
|
||||||
:style="tableStyle"
|
:style="{ height: `${availableHeight}px` }"
|
||||||
>
|
>
|
||||||
<template #item.title="{ item }">
|
<template #item.title="{ item }">
|
||||||
<div class="d-flex align-center">
|
<div class="d-flex align-center">
|
||||||
|
|||||||
Reference in New Issue
Block a user