refactor: 优化代码格式,简化部分逻辑,提升可读性

This commit is contained in:
jxxghp
2026-06-07 18:10:10 +08:00
parent 87d780d985
commit e239c0c5ea
9 changed files with 153 additions and 180 deletions
+4 -5
View File
@@ -161,15 +161,15 @@ const isDragging = ref(false)
const dragStartX = ref(0) const dragStartX = ref(0)
const dragStartWidth = ref(0) const dragStartWidth = ref(0)
watch(sort, (val) => { watch(sort, val => {
localStorage.setItem(SORT_KEY, val) localStorage.setItem(SORT_KEY, val)
}) })
watch(showDirTree, (val) => { watch(showDirTree, val => {
localStorage.setItem(SHOW_TREE_KEY, String(val)) localStorage.setItem(SHOW_TREE_KEY, String(val))
}) })
watch(navigatorWidth, (val) => { watch(navigatorWidth, val => {
localStorage.setItem(NAV_WIDTH_KEY, String(val)) localStorage.setItem(NAV_WIDTH_KEY, String(val))
}) })
@@ -182,7 +182,6 @@ const storagesArray = computed(() => {
})) }))
}) })
// 方法 // 方法
function loadingChanged(isLoading: number) { function loadingChanged(isLoading: number) {
if (isLoading) loading.value++ if (isLoading) loading.value++
@@ -272,7 +271,7 @@ function stopDrag() {
</script> </script>
<template> <template>
<div class="mx-auto" :loading="loading > 0"> <div class="mx-auto overflow-hidden" :loading="loading > 0">
<div v-if="item"> <div v-if="item">
<FileToolbar <FileToolbar
ref="toolbarRef" ref="toolbarRef"
+6 -2
View File
@@ -288,7 +288,10 @@ async function handleResetSettings() {
v-bind="customizerContainerProps" v-bind="customizerContainerProps"
:style="customizerContainerStyle" :style="customizerContainerStyle"
> >
<div class="theme-customizer-panel" :class="{ 'theme-customizer-panel--dialog': appMode, 'app-surface': appMode }"> <div
class="theme-customizer-panel"
:class="{ 'theme-customizer-panel--dialog': appMode, 'app-surface': appMode }"
>
<div class="theme-customizer-header py-5 px-4"> <div class="theme-customizer-header py-5 px-4">
<div> <div>
<h2 class="theme-customizer-title">{{ t('theme.customizer.title') }}</h2> <h2 class="theme-customizer-title">{{ t('theme.customizer.title') }}</h2>
@@ -493,7 +496,7 @@ async function handleResetSettings() {
overflow: hidden; overflow: hidden;
block-size: 100dvh !important; block-size: 100dvh !important;
border-inline-start: 1px solid rgba(var(--v-theme-on-surface), 0.08) !important; border-inline-start: 1px solid rgba(var(--v-theme-on-surface), 0.08) !important;
box-shadow: -2px 0 6px rgba(0, 0, 0, 10%) !important; box-shadow: var(--app-surface-shadow) !important;
inset-block: 0 !important; inset-block: 0 !important;
inset-inline-end: 0 !important; inset-inline-end: 0 !important;
max-block-size: 100dvh !important; max-block-size: 100dvh !important;
@@ -534,6 +537,7 @@ async function handleResetSettings() {
background: rgb(var(--v-theme-surface)); background: rgb(var(--v-theme-surface));
block-size: var(--theme-customizer-viewport-height, 100dvh); block-size: var(--theme-customizer-viewport-height, 100dvh);
max-block-size: var(--theme-customizer-viewport-height, 100dvh); max-block-size: var(--theme-customizer-viewport-height, 100dvh);
/* fullscreen dialog 会贴到 viewport-fit=cover 顶部,iOS 需要在面板内部避开系统状态栏。 */ /* fullscreen dialog 会贴到 viewport-fit=cover 顶部,iOS 需要在面板内部避开系统状态栏。 */
padding-block-start: env(safe-area-inset-top); padding-block-start: env(safe-area-inset-top);
} }
+60 -70
View File
@@ -71,82 +71,72 @@ async function deleteDownload() {
</script> </script>
<template> <template>
<VCard <VHover>
v-if="cardState" <template #default="hover">
:key="props.info?.hash" <VCard
class="downloading-card flex flex-col h-full overflow-hidden" v-if="cardState"
min-height="150" v-bind="hover.props"
> :key="props.info?.hash"
<template #image> class="downloading-card app-surface flex flex-col h-full overflow-hidden"
<VImg :class="{
:src="props.info?.media.image" 'transition transform-cpu duration-300 -translate-y-1': hover.isHovering,
class="downloading-card-image" }"
aspect-ratio="2/3" min-height="150"
cover
@load="imageLoadHandler"
position="top"
> >
<template #placeholder> <template #image>
<div class="w-full h-full"> <VImg
<VSkeletonLoader class="object-cover aspect-w-2 aspect-h-3" /> :src="props.info?.media.image"
</div> class="downloading-card-image"
aspect-ratio="2/3"
cover
@load="imageLoadHandler"
position="top"
>
<template #placeholder>
<div class="w-full h-full">
<VSkeletonLoader class="object-cover aspect-w-2 aspect-h-3" />
</div>
</template>
<template #default>
<div class="absolute inset-0 outline-none downloading-card-background"></div>
</template>
</VImg>
</template> </template>
<template #default>
<div class="absolute inset-0 outline-none downloading-card-background"></div> <div>
</template> <VCardTitle class="break-words whitespace-normal text-white">
</VImg> {{ props.info?.media.title || props.info?.name }}
{{
props.info?.media.episode
? `${props.info?.media.season} ${props.info?.media.episode}`
: props.info?.season_episode
}}
</VCardTitle>
<VCardSubtitle class="break-words whitespace-normal text-white">
{{ props.info?.title }}
</VCardSubtitle>
<VCardText class="text-subtitle-1 pt-3 pb-1 text-white">
{{ getSpeedText() }}
</VCardText>
<VCardText v-if="getPercentage() > 0" class="text-white">
<VProgressLinear :model-value="getPercentage()" bg-color="success" color="success" />
</VCardText>
<VCardActions class="justify-space-between">
<VBtn :icon="`${isDownloading ? 'mdi-pause' : 'mdi-play'}`" @click="toggleDownload" />
<VBtn color="error" icon="mdi-trash-can-outline" @click="deleteDownload" />
</VCardActions>
</div>
</VCard>
</template> </template>
</VHover>
<div>
<VCardTitle class="break-words whitespace-normal text-white">
{{ props.info?.media.title || props.info?.name }}
{{
props.info?.media.episode
? `${props.info?.media.season} ${props.info?.media.episode}`
: props.info?.season_episode
}}
</VCardTitle>
<VCardSubtitle class="break-words whitespace-normal text-white">
{{ props.info?.title }}
</VCardSubtitle>
<VCardText class="text-subtitle-1 pt-3 pb-1 text-white">
{{ getSpeedText() }}
</VCardText>
<VCardText v-if="getPercentage() > 0" class="text-white">
<VProgressLinear :model-value="getPercentage()" bg-color="success" color="success" />
</VCardText>
<VCardActions class="justify-space-between">
<VBtn :icon="`${isDownloading ? 'mdi-pause' : 'mdi-play'}`" @click="toggleDownload" />
<VBtn color="error" icon="mdi-trash-can-outline" @click="deleteDownload" />
</VCardActions>
</div>
</VCard>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.downloading-card { /* stylelint-disable selector-pseudo-class-no-unknown */
position: relative;
isolation: isolate;
background-color: rgb(31, 41, 55) !important;
}
// 图片槽和渐变遮罩统一继承卡片圆角,避免阴影增强后四角露出页面底色。
.downloading-card :deep(.v-card__image),
.downloading-card :deep(.v-responsive),
.downloading-card :deep(.v-img),
.downloading-card :deep(.v-img__img),
.downloading-card :deep(.v-responsive__content) {
overflow: hidden;
border-radius: inherit;
}
.downloading-card :deep(.v-card__image) {
background-color: rgb(31, 41, 55);
}
.downloading-card-image { .downloading-card-image {
block-size: 100%; block-size: 100%;
+18 -37
View File
@@ -418,17 +418,14 @@ watch(
) )
// 切换媒体类型或识别源时,非 TMDB 电视剧不保留剧集组选择。 // 切换媒体类型或识别源时,非 TMDB 电视剧不保留剧集组选择。
watch( watch([() => transferForm.type_name, () => mediaSource.value], ([typeName, source]) => {
[() => transferForm.type_name, () => mediaSource.value], if (typeName === '电视剧' && source === 'themoviedb' && transferForm.tmdbid) {
([typeName, source]) => { getEpisodeGroups(transferForm.tmdbid)
if (typeName === '电视剧' && source === 'themoviedb' && transferForm.tmdbid) { return
getEpisodeGroups(transferForm.tmdbid) }
return transferForm.episode_group = null
} episodeGroups.value = []
transferForm.episode_group = null })
episodeGroups.value = []
},
)
watch( watch(
() => transferForm.episode_group, () => transferForm.episode_group,
@@ -500,9 +497,7 @@ function normalizeTargetPath(path?: string | null) {
} }
// 归一化剧集组值,兼容历史对象态值。 // 归一化剧集组值,兼容历史对象态值。
function normalizeEpisodeGroup( function normalizeEpisodeGroup(episodeGroup?: string | { value?: string | null } | null) {
episodeGroup?: string | { value?: string | null } | null,
) {
if (!episodeGroup) return null if (!episodeGroup) return null
if (typeof episodeGroup === 'string') { if (typeof episodeGroup === 'string') {
const normalizedEpisodeGroup = episodeGroup.trim() const normalizedEpisodeGroup = episodeGroup.trim()
@@ -632,11 +627,7 @@ const previewFileRows = computed(() => {
// 标准化预览项中的识别词命中详情 // 标准化预览项中的识别词命中详情
function getPreviewApplyWords(item: ManualTransferPreviewItem) { function getPreviewApplyWords(item: ManualTransferPreviewItem) {
return [ return [
...new Set( ...new Set((item.apply_words ?? []).map(word => word?.trim()).filter((word): word is string => Boolean(word))),
(item.apply_words ?? [])
.map(word => word?.trim())
.filter((word): word is string => Boolean(word)),
),
] ]
} }
@@ -765,9 +756,7 @@ const episodeFormatRecommendSelectedFileItems = computed(() => {
const episodeFormatRecommendHasValidSelectedFiles = computed(() => { const episodeFormatRecommendHasValidSelectedFiles = computed(() => {
if (episodeFormatRecommendSelectedFileItems.value.length <= 1) return false if (episodeFormatRecommendSelectedFileItems.value.length <= 1) return false
const directoryKeys = new Set( const directoryKeys = new Set(episodeFormatRecommendSelectedFileItems.value.map(item => getFileParentKey(item)))
episodeFormatRecommendSelectedFileItems.value.map(item => getFileParentKey(item)),
)
return directoryKeys.size === 1 return directoryKeys.size === 1
}) })
@@ -778,8 +767,7 @@ const episodeFormatRecommendSourceItem = computed<FileItem | undefined>(() => {
const canRecommendEpisodeFormat = computed(() => { const canRecommendEpisodeFormat = computed(() => {
return ( return (
(Boolean(episodeFormatRecommendSourceItem.value?.path) || (Boolean(episodeFormatRecommendSourceItem.value?.path) || episodeFormatRecommendHasValidSelectedFiles.value) &&
episodeFormatRecommendHasValidSelectedFiles.value) &&
!progressDialog.value && !progressDialog.value &&
!episodeFormatRecommendState.loading !episodeFormatRecommendState.loading
) )
@@ -793,10 +781,7 @@ const episodeFormatRecommendSelectionKey = computed(() => {
const episodeFormatRecommendTooltip = computed(() => { const episodeFormatRecommendTooltip = computed(() => {
if (episodeFormatRecommendState.loading) return t('dialog.reorganize.episodeFormatRecommendLoading') if (episodeFormatRecommendState.loading) return t('dialog.reorganize.episodeFormatRecommendLoading')
if ( if (normalizedItems.value.length > 1 && !episodeFormatRecommendHasValidSelectedFiles.value) {
normalizedItems.value.length > 1 &&
!episodeFormatRecommendHasValidSelectedFiles.value
) {
return t('dialog.reorganize.episodeFormatRecommendInvalidSelection') return t('dialog.reorganize.episodeFormatRecommendInvalidSelection')
} }
if (!episodeFormatRecommendSourceItem.value?.path && !episodeFormatRecommendHasValidSelectedFiles.value) { if (!episodeFormatRecommendSourceItem.value?.path && !episodeFormatRecommendHasValidSelectedFiles.value) {
@@ -832,11 +817,7 @@ function getBatchItemsLabel(items: FileItem[]) {
// 构造整理请求 // 构造整理请求
function createTransferPayload(options: { item?: FileItem; items?: FileItem[]; logid?: number; preview?: boolean }) { function createTransferPayload(options: { item?: FileItem; items?: FileItem[]; logid?: number; preview?: boolean }) {
const sourceItem = const sourceItem = options.item ?? (options.items?.length ? options.items[0] : ({} as FileItem))
options.item ??
(options.items?.length
? options.items[0]
: ({} as FileItem))
const payload: ManualTransferPayload = { const payload: ManualTransferPayload = {
...transferForm, ...transferForm,
fileitem: sourceItem, fileitem: sourceItem,
@@ -1702,7 +1683,7 @@ onUnmounted(() => {
<div <div
v-for="(item, index) in pagedPreviewRows" v-for="(item, index) in pagedPreviewRows"
:key="`${item.source}-${item.target}-${index}`" :key="`${item.source}-${item.target}-${index}`"
class="preview-file-row" class="preview-file-row app-surface-shape"
:class="{ 'preview-file-row--failed': item.success === false }" :class="{ 'preview-file-row--failed': item.success === false }"
> >
<div class="preview-file-row__card preview-file-row__card--source"> <div class="preview-file-row__card preview-file-row__card--source">
@@ -1983,18 +1964,18 @@ onUnmounted(() => {
} }
.preview-custom-words__source { .preview-custom-words__source {
overflow-wrap: anywhere;
color: rgb(var(--v-theme-on-surface)); color: rgb(var(--v-theme-on-surface));
font-size: 0.8125rem; font-size: 0.8125rem;
font-weight: 600; font-weight: 600;
line-height: 1.4; line-height: 1.4;
overflow-wrap: anywhere;
} }
.preview-custom-words__original { .preview-custom-words__original {
overflow-wrap: anywhere;
color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity)); color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity));
font-size: 0.75rem; font-size: 0.75rem;
line-height: 1.4; line-height: 1.4;
overflow-wrap: anywhere;
} }
.preview-custom-words__chips { .preview-custom-words__chips {
@@ -2107,10 +2088,10 @@ onUnmounted(() => {
.preview-file-row__path { .preview-file-row__path {
overflow: visible; overflow: visible;
overflow-wrap: anywhere;
color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity)); color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity));
font-size: 0.8125rem; font-size: 0.8125rem;
line-height: 1.4; line-height: 1.4;
overflow-wrap: anywhere;
white-space: normal; white-space: normal;
word-break: break-all; word-break: break-all;
} }
+21 -15
View File
@@ -30,7 +30,7 @@ const { appMode } = usePWA()
// 计算列表可用高度 // 计算列表可用高度
// componentOffset = FileToolbar(48) + FileList操作栏(40) + VCard边距(4) = 92 // componentOffset = FileToolbar(48) + FileList操作栏(40) + VCard边距(4) = 92
const { availableHeight: listAvailableHeight } = useAvailableHeight(92, 300) const { availableHeight: listAvailableHeight } = useAvailableHeight(89, 300)
// 输入参数 // 输入参数
const inProps = defineProps({ const inProps = defineProps({
@@ -267,7 +267,7 @@ async function list_files(context: KeepAliveRefreshContext = {}) {
} }
// 加载数据 // 加载数据
const data = ((await inProps.axios.request<FileItem[], FileItem[]>(config))) ?? [] const data = (await inProps.axios.request<FileItem[], FileItem[]>(config)) ?? []
// 如果当前路径已经变化,则放弃此次加载结果 // 如果当前路径已经变化,则放弃此次加载结果
if (prevURI !== takeURISnapshot()) { if (prevURI !== takeURISnapshot()) {
return return
@@ -381,7 +381,7 @@ async function download(item: FileItem) {
responseType: 'blob', responseType: 'blob',
} }
// 加载数据 // 加载数据
const result: Blob = (await inProps.axios.request<Blob, Blob>(config)) const result: Blob = await inProps.axios.request<Blob, Blob>(config)
if (result) { if (result) {
const downloadUrl = URL.createObjectURL(result) const downloadUrl = URL.createObjectURL(result)
window.open(downloadUrl, '_blank') window.open(downloadUrl, '_blank')
@@ -402,7 +402,7 @@ async function getImgLink(item: FileItem) {
responseType: 'blob', responseType: 'blob',
} }
// 加载二进制数据 // 加载二进制数据
const result: Blob = (await inProps.axios.request<Blob, Blob>(config)) const result: Blob = await inProps.axios.request<Blob, Blob>(config)
if (result) { if (result) {
// 创建图片地址 // 创建图片地址
revokeCurrentImgLink() revokeCurrentImgLink()
@@ -508,7 +508,7 @@ async function rename() {
method: inProps.endpoints?.rename.method || 'post', method: inProps.endpoints?.rename.method || 'post',
data: currentItem.value, data: currentItem.value,
} }
const result: { [key: string]: any } = (await inProps.axios?.request<any, { [key: string]: any }>(config)) const result: { [key: string]: any } = await inProps.axios?.request<any, { [key: string]: any }>(config)
if (!result.success) { if (!result.success) {
$toast.error(result.message) $toast.error(result.message)
} }
@@ -768,17 +768,9 @@ onUnmounted(() => {
}) })
</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"> <VCard class="d-flex flex-column w-full h-full file-list">
<div v-if="!loading" class="flex"> <div v-if="!loading" class="flex">
<IconBtn v-if="display.mdAndUp.value"> <IconBtn v-if="display.mdAndUp.value">
<VIcon v-if="showTree" icon="mdi-file-tree" @click="switchFileTree(false)" /> <VIcon v-if="showTree" icon="mdi-file-tree" @click="switchFileTree(false)" />
@@ -792,7 +784,7 @@ onUnmounted(() => {
density="compact" density="compact"
variant="plain" variant="plain"
:placeholder="t('file.filterPlaceholder')" :placeholder="t('file.filterPlaceholder')"
:prepend-inner-icon="(filter.includes('*') || filter.includes('?')) ? 'mdi-asterisk' : 'mdi-filter-outline'" :prepend-inner-icon="filter.includes('*') || filter.includes('?') ? 'mdi-asterisk' : 'mdi-filter-outline'"
class="mx-2" class="mx-2"
rounded rounded
/> />
@@ -931,3 +923,17 @@ onUnmounted(() => {
</VCard> </VCard>
</div> </div>
</template> </template>
<style scoped>
.file-list {
border-radius: 0 !important;
box-shadow: none !important;
}
.file-list-container {
overflow: hidden auto;
border-radius: 0 !important;
block-size: 100%;
max-block-size: 100%;
}
</style>
+4 -10
View File
@@ -116,7 +116,7 @@ async function loadSubdirectories(path: string) {
data: fakeItem, data: fakeItem,
} }
const result = (await props.axios?.request(config)) const result = await props.axios?.request(config)
if (result && Array.isArray(result)) { if (result && Array.isArray(result)) {
// 过滤出目录项 // 过滤出目录项
const dirs = result.filter(item => item.type === 'dir') const dirs = result.filter(item => item.type === 'dir')
@@ -249,7 +249,6 @@ function getTreeRowStyle(level: number) {
onMounted(async () => { onMounted(async () => {
await loadRootDirectories() await loadRootDirectories()
}) })
</script> </script>
<template> <template>
@@ -296,13 +295,7 @@ onMounted(async () => {
:style="getTreeRowStyle(item.level)" :style="getTreeRowStyle(item.level)"
> >
<div class="folder-toggle" @click.stop="toggleFolder(item.dir.path || '')"> <div class="folder-toggle" @click.stop="toggleFolder(item.dir.path || '')">
<VProgressCircular <VProgressCircular v-if="loading[item.dir.path || '']" indeterminate size="14" width="2" color="primary" />
v-if="loading[item.dir.path || '']"
indeterminate
size="14"
width="2"
color="primary"
/>
<VIcon <VIcon
v-else v-else
size="small" size="small"
@@ -332,9 +325,10 @@ onMounted(async () => {
overflow: hidden; overflow: hidden;
flex-direction: column; flex-direction: column;
flex-shrink: 0; flex-shrink: 0;
border-radius: 0 !important;
background: rgb(var(--v-table-header-background)); background: rgb(var(--v-table-header-background));
block-size: 100%; block-size: 100%;
border-end-start-radius: 12px; box-shadow: none !important;
inline-size: 240px; inline-size: 240px;
} }
+16 -11
View File
@@ -141,7 +141,6 @@ html[data-theme-shadow='high'] {
.v-card, .v-card,
.v-sheet, .v-sheet,
.v-list, .v-list,
.v-table,
.v-expansion-panel { .v-expansion-panel {
border: var(--app-surface-border); border: var(--app-surface-border);
border-radius: var(--app-surface-radius) !important; border-radius: var(--app-surface-radius) !important;
@@ -159,7 +158,6 @@ html[data-theme-shadow='high'] {
.v-card:hover, .v-card:hover,
.v-sheet:hover, .v-sheet:hover,
.v-list:hover, .v-list:hover,
.v-table:hover,
.v-expansion-panel:hover { .v-expansion-panel:hover {
box-shadow: var(--app-surface-hover-shadow) !important; box-shadow: var(--app-surface-hover-shadow) !important;
} }
@@ -190,6 +188,16 @@ html[data-theme-shadow='high'] {
transition: border-radius 0.2s ease, box-shadow 0.2s ease; transition: border-radius 0.2s ease, box-shadow 0.2s ease;
} }
.v-btn:not(.v-btn--variant-plain, .v-btn--variant-text) {
box-shadow: var(--app-surface-shadow) !important;
transition: box-shadow 0.2s ease;
}
.v-btn:not(.v-btn--rounded, [class^='rounded-'], [class*=' rounded-']) {
border-radius: var(--app-surface-radius);
transition: border-radius 0.2s ease;
}
// 只给外层 surface 加边框和阴影,卡片内部的列表、表格等子组件保持平面,避免层级噪声。 // 只给外层 surface 加边框和阴影,卡片内部的列表、表格等子组件保持平面,避免层级噪声。
.v-card .v-list:not(.app-surface), .v-card .v-list:not(.app-surface),
.v-card .v-sheet:not(.app-surface), .v-card .v-sheet:not(.app-surface),
@@ -213,15 +221,6 @@ html[data-theme-shadow='high'] {
--app-surface-shadow: none; --app-surface-shadow: none;
} }
// 菜单只让直接承载层拥有弹出阴影,内部嵌套 surface 不再重复制造层级。
.v-menu
> .v-overlay__content
> :where(.v-card, .v-sheet, .v-list)
:where(.v-card, .v-sheet, .v-list, .v-table, .v-expansion-panel):not(.app-card-colorful) {
--app-surface-hover-shadow: none;
--app-surface-shadow: none;
}
// 主题定制器的 bordered 皮肤:保持原布局密度,只给非 Vuetify 外壳增加清晰边界。 // 主题定制器的 bordered 皮肤:保持原布局密度,只给非 Vuetify 外壳增加清晰边界。
html[data-theme-skin='bordered'] { html[data-theme-skin='bordered'] {
.layout-vertical-nav { .layout-vertical-nav {
@@ -619,10 +618,16 @@ html[data-theme="transparent"] .app-card-colorful,
// 组件样式 // 组件样式
.v-alert--variant-elevated, .v-alert--variant-flat { .v-alert--variant-elevated, .v-alert--variant-flat {
border-radius: var(--app-surface-radius);
background: rgb(var(--v-table-header-background)); background: rgb(var(--v-table-header-background));
color: rgba(var(--v-theme-on-surface), var(--v-high-emphasis-opacity)); color: rgba(var(--v-theme-on-surface), var(--v-high-emphasis-opacity));
} }
.Vue-Toastification__toast {
border-radius: var(--app-surface-radius);
box-shadow: var(--app-overlay-shadow);
}
.backdrop-blur { .backdrop-blur {
--tw-backdrop-blur: blur(8px)!important; --tw-backdrop-blur: blur(8px)!important;
+20 -21
View File
@@ -77,14 +77,14 @@ function findCommonPath(paths: string[]): string {
const STORAGE_KEY = 'fileBrowserView.activeStorage' const STORAGE_KEY = 'fileBrowserView.activeStorage'
interface BrowserInitialParams { interface BrowserInitialParams {
storage: string; storage: string
path: string; path: string
name: string; name: string
} }
// determine which entry to select initially // determine which entry to select initially
function determineBrowserInitialParams(downloadDirectories: TransferDirectoryConf[]): BrowserInitialParams { function determineBrowserInitialParams(downloadDirectories: TransferDirectoryConf[]): BrowserInitialParams {
const isAvailable = (storage: string) => storageTypes.value.includes(storage); const isAvailable = (storage: string) => storageTypes.value.includes(storage)
const buckets = downloadDirectories.reduce<Map<string, string[]>>((dict, item) => { const buckets = downloadDirectories.reduce<Map<string, string[]>>((dict, item) => {
// filter out directories whose storage is not available // filter out directories whose storage is not available
if (!isAvailable(item.storage)) { if (!isAvailable(item.storage)) {
return dict return dict
@@ -98,37 +98,35 @@ function determineBrowserInitialParams(downloadDirectories: TransferDirectoryCon
dict.get(item.storage)!.push(item.download_path) dict.get(item.storage)!.push(item.download_path)
} }
return dict return dict
}, new Map()); }, new Map())
const cachedStorage = localStorage.getItem(STORAGE_KEY) || ''; const cachedStorage = localStorage.getItem(STORAGE_KEY) || ''
// if no download directories are configured, fall back to cached storage or first available storage // if no download directories are configured, fall back to cached storage or first available storage
if (buckets.size === 0) { if (buckets.size === 0) {
return { return {
storage: isAvailable(cachedStorage) storage: isAvailable(cachedStorage) ? cachedStorage : storageTypes.value[0] || 'local',
? cachedStorage
: (storageTypes.value[0] || 'local'),
path: '/', path: '/',
name: '/', name: '/',
} }
} }
let selectedEntry: [string, string[]]; let selectedEntry: [string, string[]]
if (cachedStorage && buckets.has(cachedStorage)) { if (cachedStorage && buckets.has(cachedStorage)) {
selectedEntry = [cachedStorage, buckets.get(cachedStorage)!]; selectedEntry = [cachedStorage, buckets.get(cachedStorage)!]
} else { } else {
// if no storage selected previously, use the most populous one // if no storage selected previously, use the most populous one
selectedEntry = Array.from(buckets.entries()).reduce((prev, curr) => { selectedEntry = Array.from(buckets.entries()).reduce((prev, curr) => {
return curr[1].length > prev[1].length ? curr : prev; return curr[1].length > prev[1].length ? curr : prev
}); })
} }
const path = findCommonPath(selectedEntry[1]); const path = findCommonPath(selectedEntry[1])
return { return {
storage: selectedEntry[0], storage: selectedEntry[0],
path, path,
name: path.split('/').filter(Boolean).pop() ?? '', name: path.split('/').filter(Boolean).pop() ?? '',
} }
} }
// 查询下载目录 // 查询下载目录
async function loadDownloadDirectories() { async function loadDownloadDirectories() {
try { try {
@@ -138,7 +136,7 @@ async function loadDownloadDirectories() {
const result: { [key: string]: any } = await api.get('system/setting/Directories') const result: { [key: string]: any } = await api.get('system/setting/Directories')
if (result.success && result.data?.value) { if (result.success && result.data?.value) {
const { storage, path, name } = determineBrowserInitialParams(result.data.value); const { storage, path, name } = determineBrowserInitialParams(result.data.value)
// operItem初始化 // operItem初始化
operItem.value = { operItem.value = {
type: 'dir', type: 'dir',
@@ -154,8 +152,8 @@ async function loadDownloadDirectories() {
name: '/', name: '/',
path: '/', path: '/',
fileid: 'root', fileid: 'root',
} },
]; ]
// 将初始数据拆分到堆栈中 // 将初始数据拆分到堆栈中
const paths = path.split('/').filter(Boolean) const paths = path.split('/').filter(Boolean)
paths.map((name, index) => { paths.map((name, index) => {
@@ -206,7 +204,7 @@ onMounted(loadDownloadDirectories)
</script> </script>
<template> <template>
<div class="file-browser-view"> <div class="file-browser-view app-surface">
<FileBrowser <FileBrowser
v-if="operItem" v-if="operItem"
:storages="storages" :storages="storages"
@@ -223,6 +221,7 @@ onMounted(loadDownloadDirectories)
<style lang="scss" scoped> <style lang="scss" scoped>
.file-browser-view { .file-browser-view {
position: relative; position: relative;
overflow: hidden;
block-size: 100%; block-size: 100%;
} }
</style> </style>
+4 -9
View File
@@ -144,8 +144,8 @@ onMounted(getModules)
isChecking isChecking
? t('moduleTest.checking') ? t('moduleTest.checking')
: checkComplete : checkComplete
? t('moduleTest.complete') ? t('moduleTest.complete')
: t('moduleTest.preparing') : t('moduleTest.preparing')
}} }}
</h3> </h3>
</div> </div>
@@ -230,16 +230,15 @@ onMounted(getModules)
<style scoped> <style scoped>
.system-health-check { .system-health-check {
display: flex; display: flex;
flex-direction: column; overflow: hidden;
flex: 1 1 auto; flex: 1 1 auto;
flex-direction: column;
block-size: 100%; block-size: 100%;
min-block-size: 0; min-block-size: 0;
overflow: hidden;
} }
.progress-container { .progress-container {
flex-shrink: 0; flex-shrink: 0;
background: var(--v-surface-variant);
} }
.progress-card { .progress-card {
@@ -336,10 +335,6 @@ onMounted(getModules)
transition: all 0.3s ease; transition: all 0.3s ease;
} }
.module-item:hover {
transform: translateY(-2px);
}
.module-item.success { .module-item.success {
border-color: #4caf50; border-color: #4caf50;
background: linear-gradient(135deg, #f8fff9 0%, #e8f5e8 100%); background: linear-gradient(135deg, #f8fff9 0%, #e8f5e8 100%);