refactor: adjust TransferHistoryView layout offset and apply code style improvements

This commit is contained in:
jxxghp
2026-04-08 13:47:04 +08:00
parent 2d50bd7536
commit 660338688a
2 changed files with 41 additions and 37 deletions

View File

@@ -1,4 +1,4 @@
import { computed, ref, onMounted, onUnmounted } from 'vue'
import { computed, ref, watch, onMounted, onUnmounted, nextTick } from 'vue'
import { usePWA } from '@/composables/usePWA'
/**
@@ -35,19 +35,19 @@ export function useAvailableHeight(
layoutPaddingBottom.value = parseFloat(style.paddingBottom) || 24
}
// 测量 Footer Dock 的实际高度
// .footer-nav-container 是 position:fixed, padding-block-end 含 env(safe-area-inset-bottom)
// offsetHeight 是元素自身的渲染高度(含 padding即 Dock 遮挡的区域大小
if (appMode.value) {
const footerEl = document.querySelector('.footer-nav-container') as HTMLElement | null
footerDockMeasuredHeight.value = footerEl ? footerEl.offsetHeight : 70
} else {
footerDockMeasuredHeight.value = 0
}
// 直接查询 Footer Dock DOM无论 appMode 状态
// Dock 通过 Teleport 挂载到 body存在即测量不存在即为 0
const footerEl = document.querySelector('.footer-nav-container') as HTMLElement | null
footerDockMeasuredHeight.value = footerEl ? footerEl.offsetHeight : 0
}
// appMode 异步变化时PWA 检测完成、屏幕尺寸变化等Dock 会出现/消失
// 需要等 DOM 更新后重新测量
watch(appMode, () => {
nextTick(updateMeasurements)
})
onMounted(() => {
// 初始测量nextTick 确保 DOM 已渲染)
nextTick(updateMeasurements)
window.addEventListener('resize', updateMeasurements)
@@ -72,7 +72,7 @@ export function useAvailableHeight(
// 布局底部 padding
const bottomPadding = layoutPaddingBottom.value
// 底部 Dock 栏遮挡高度(appMode 下通过 DOM 测量,含 safe-area-inset-bottom
// 底部 Dock 栏遮挡高度(通过 DOM 测量,含 safe-area-inset-bottom
const footerDockHeight = footerDockMeasuredHeight.value
const available = vh - topPadding - bottomPadding - footerDockHeight - componentOffset

View File

@@ -23,8 +23,8 @@ const display = useDisplay()
const { appMode } = usePWA()
// 计算列表可用高度
// componentOffset = VCardItem搜索栏(80) + VDivider(1) + 分页栏(52) + VCard边距(4) = 137
const { availableHeight } = useAvailableHeight(137, 300)
// componentOffset = VCardItem搜索栏(68) + VDivider(1) + 分页栏(40) + VCard边距(2) = 111
const { availableHeight } = useAvailableHeight(125, 300)
// 提示框
const $toast = useToast()
@@ -51,21 +51,21 @@ const redoTargetStorage = ref<string>()
// 已选中的数据
const selected = ref<TransferHistory[]>([])
const getNum = (s?: string) => (s ? parseInt(s.replace(/[^0-9]/g, ''), 10) || 0 : 0);
const getNum = (s?: string) => (s ? parseInt(s.replace(/[^0-9]/g, ''), 10) || 0 : 0)
function sortByTitle(a: TransferHistory, b: TransferHistory) {
if (a.type !== b.type) {
return (a.type ?? '').localeCompare(b.type ?? '');
return (a.type ?? '').localeCompare(b.type ?? '')
}
if (a.title !== b.title) {
return (a.title ?? '').toLocaleLowerCase().localeCompare((b.title ?? '').toLocaleLowerCase());
return (a.title ?? '').toLocaleLowerCase().localeCompare((b.title ?? '').toLocaleLowerCase())
}
if (a.type === '电视剧') {
if (a.seasons !== b.seasons) {
return getNum(a.seasons) - getNum(b.seasons);
return getNum(a.seasons) - getNum(b.seasons)
}
if (a.episodes !== b.episodes) {
return getNum(a.episodes) - getNum(b.episodes);
return getNum(a.episodes) - getNum(b.episodes)
}
}
return 0
@@ -231,10 +231,13 @@ async function loadStorages() {
// 存储字典
const storageDict = computed(() => {
return storages.value.reduce((dict, item) => {
dict[item.type] = item.name
return dict
}, {} as Record<string, string>)
return storages.value.reduce(
(dict, item) => {
dict[item.type] = item.name
return dict
},
{} as Record<string, string>,
)
})
// 转移方式字典
@@ -247,8 +250,6 @@ const TransferDict: { [key: string]: string } = {
rclone_move: t('transferHistory.transferMode.rclone_move'),
}
// 分页提示
const pageTip = computed(() => {
const begin = itemsPerPage.value * (currentPage.value - 1) + 1
@@ -488,23 +489,26 @@ function ensureNumber(value: any, defaultValue: number = 0) {
// 按标题分组后的选中数量统计,键为标题,值为对应分组的选中数
const selectedCountsGroupedByTitle = computed(() => {
return selected.value.reduce((acc, item) => {
const title = item.title || '';
acc[title] = (acc[title] || 0) + 1;
return acc;
}, {} as Record<string, number>);
});
return selected.value.reduce(
(acc, item) => {
const title = item.title || ''
acc[title] = (acc[title] || 0) + 1
return acc
},
{} as Record<string, number>,
)
})
// 控制分组内所有子项的选中状态
const toggleGroupSelection = (checked: boolean | null, items: readonly any[]) => {
const values = items.map(item => item.value);
const values = items.map(item => item.value)
if (checked) {
selected.value = [...new Set([...selected.value, ...values])];
selected.value = [...new Set([...selected.value, ...values])]
} else {
const itemsSet = new Set(values);
selected.value = selected.value.filter(item => !itemsSet.has(item));
const itemsSet = new Set(values)
selected.value = selected.value.filter(item => !itemsSet.has(item))
}
};
}
// 初始加载数据
onMounted(() => {
@@ -579,7 +583,7 @@ onMounted(() => {
<VCheckbox
:model-value="selectedCountsGroupedByTitle[item.value] == item.items.length"
:indeterminate="selectedCountsGroupedByTitle[item.value] < item.items.length"
@update:modelValue="(checked) => toggleGroupSelection(checked, item.items)"
@update:modelValue="checked => toggleGroupSelection(checked, item.items)"
/>
{{ item.value }}
</div>