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
+12 -12
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' import { usePWA } from '@/composables/usePWA'
/** /**
@@ -35,19 +35,19 @@ export function useAvailableHeight(
layoutPaddingBottom.value = parseFloat(style.paddingBottom) || 24 layoutPaddingBottom.value = parseFloat(style.paddingBottom) || 24
} }
// 测量 Footer Dock 的实际高度 // 直接查询 Footer Dock DOM,无论 appMode 状态
// .footer-nav-container 是 position:fixed, padding-block-end 含 env(safe-area-inset-bottom) // Dock 通过 Teleport 挂载到 body,存在即测量,不存在即为 0
// offsetHeight 是元素自身的渲染高度(含 padding),即 Dock 遮挡的区域大小 const footerEl = document.querySelector('.footer-nav-container') as HTMLElement | null
if (appMode.value) { footerDockMeasuredHeight.value = footerEl ? footerEl.offsetHeight : 0
const footerEl = document.querySelector('.footer-nav-container') as HTMLElement | null
footerDockMeasuredHeight.value = footerEl ? footerEl.offsetHeight : 70
} else {
footerDockMeasuredHeight.value = 0
}
} }
// appMode 异步变化时(PWA 检测完成、屏幕尺寸变化等),Dock 会出现/消失
// 需要等 DOM 更新后重新测量
watch(appMode, () => {
nextTick(updateMeasurements)
})
onMounted(() => { onMounted(() => {
// 初始测量(nextTick 确保 DOM 已渲染)
nextTick(updateMeasurements) nextTick(updateMeasurements)
window.addEventListener('resize', updateMeasurements) window.addEventListener('resize', updateMeasurements)
@@ -72,7 +72,7 @@ export function useAvailableHeight(
// 布局底部 padding // 布局底部 padding
const bottomPadding = layoutPaddingBottom.value const bottomPadding = layoutPaddingBottom.value
// 底部 Dock 栏遮挡高度(appMode 下通过 DOM 测量,含 safe-area-inset-bottom // 底部 Dock 栏遮挡高度(通过 DOM 测量,含 safe-area-inset-bottom
const footerDockHeight = footerDockMeasuredHeight.value const footerDockHeight = footerDockMeasuredHeight.value
const available = vh - topPadding - bottomPadding - footerDockHeight - componentOffset const available = vh - topPadding - bottomPadding - footerDockHeight - componentOffset
+29 -25
View File
@@ -23,8 +23,8 @@ const display = useDisplay()
const { appMode } = usePWA() const { appMode } = usePWA()
// 计算列表可用高度 // 计算列表可用高度
// componentOffset = VCardItem搜索栏(80) + VDivider(1) + 分页栏(52) + VCard边距(4) = 137 // componentOffset = VCardItem搜索栏(68) + VDivider(1) + 分页栏(40) + VCard边距(2) = 111
const { availableHeight } = useAvailableHeight(137, 300) const { availableHeight } = useAvailableHeight(125, 300)
// 提示框 // 提示框
const $toast = useToast() const $toast = useToast()
@@ -51,21 +51,21 @@ const redoTargetStorage = ref<string>()
// 已选中的数据 // 已选中的数据
const selected = ref<TransferHistory[]>([]) 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) { function sortByTitle(a: TransferHistory, b: TransferHistory) {
if (a.type !== b.type) { if (a.type !== b.type) {
return (a.type ?? '').localeCompare(b.type ?? ''); return (a.type ?? '').localeCompare(b.type ?? '')
} }
if (a.title !== b.title) { 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.type === '电视剧') {
if (a.seasons !== b.seasons) { if (a.seasons !== b.seasons) {
return getNum(a.seasons) - getNum(b.seasons); return getNum(a.seasons) - getNum(b.seasons)
} }
if (a.episodes !== b.episodes) { if (a.episodes !== b.episodes) {
return getNum(a.episodes) - getNum(b.episodes); return getNum(a.episodes) - getNum(b.episodes)
} }
} }
return 0 return 0
@@ -231,10 +231,13 @@ async function loadStorages() {
// 存储字典 // 存储字典
const storageDict = computed(() => { const storageDict = computed(() => {
return storages.value.reduce((dict, item) => { return storages.value.reduce(
dict[item.type] = item.name (dict, item) => {
return dict dict[item.type] = item.name
}, {} as Record<string, string>) return dict
},
{} as Record<string, string>,
)
}) })
// 转移方式字典 // 转移方式字典
@@ -247,8 +250,6 @@ const TransferDict: { [key: string]: string } = {
rclone_move: t('transferHistory.transferMode.rclone_move'), rclone_move: t('transferHistory.transferMode.rclone_move'),
} }
// 分页提示 // 分页提示
const pageTip = computed(() => { const pageTip = computed(() => {
const begin = itemsPerPage.value * (currentPage.value - 1) + 1 const begin = itemsPerPage.value * (currentPage.value - 1) + 1
@@ -488,23 +489,26 @@ function ensureNumber(value: any, defaultValue: number = 0) {
// 按标题分组后的选中数量统计,键为标题,值为对应分组的选中数 // 按标题分组后的选中数量统计,键为标题,值为对应分组的选中数
const selectedCountsGroupedByTitle = computed(() => { const selectedCountsGroupedByTitle = computed(() => {
return selected.value.reduce((acc, item) => { return selected.value.reduce(
const title = item.title || ''; (acc, item) => {
acc[title] = (acc[title] || 0) + 1; const title = item.title || ''
return acc; acc[title] = (acc[title] || 0) + 1
}, {} as Record<string, number>); return acc
}); },
{} as Record<string, number>,
)
})
// 控制分组内所有子项的选中状态 // 控制分组内所有子项的选中状态
const toggleGroupSelection = (checked: boolean | null, items: readonly any[]) => { const toggleGroupSelection = (checked: boolean | null, items: readonly any[]) => {
const values = items.map(item => item.value); const values = items.map(item => item.value)
if (checked) { if (checked) {
selected.value = [...new Set([...selected.value, ...values])]; selected.value = [...new Set([...selected.value, ...values])]
} else { } else {
const itemsSet = new Set(values); const itemsSet = new Set(values)
selected.value = selected.value.filter(item => !itemsSet.has(item)); selected.value = selected.value.filter(item => !itemsSet.has(item))
} }
}; }
// 初始加载数据 // 初始加载数据
onMounted(() => { onMounted(() => {
@@ -579,7 +583,7 @@ onMounted(() => {
<VCheckbox <VCheckbox
:model-value="selectedCountsGroupedByTitle[item.value] == item.items.length" :model-value="selectedCountsGroupedByTitle[item.value] == item.items.length"
:indeterminate="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 }} {{ item.value }}
</div> </div>