mirror of
https://github.com/jxxghp/MoviePilot-Frontend.git
synced 2026-09-05 23:56:42 +08:00
refactor: adjust TransferHistoryView layout offset and apply code style improvements
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user