fix(frontend): avoid mounting hidden mobile table

This commit is contained in:
qingyuzhang
2026-04-13 06:55:57 +08:00
parent ad64190bec
commit 3a11348119

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="md:hidden space-y-3"> <div v-if="!isDesktopViewport" class="space-y-3">
<template v-if="loading"> <template v-if="loading">
<div v-for="i in 5" :key="i" class="rounded-lg border border-gray-200 bg-white p-4 dark:border-dark-700 dark:bg-dark-900"> <div v-for="i in 5" :key="i" class="rounded-lg border border-gray-200 bg-white p-4 dark:border-dark-700 dark:bg-dark-900">
<div class="space-y-3"> <div class="space-y-3">
@@ -61,8 +61,9 @@
</div> </div>
<div <div
v-else
ref="tableWrapperRef" ref="tableWrapperRef"
class="table-wrapper hidden md:block" class="table-wrapper"
:class="{ :class="{
'actions-expanded': actionsExpanded, 'actions-expanded': actionsExpanded,
'is-scrollable': isScrollable 'is-scrollable': isScrollable
@@ -203,6 +204,11 @@ import Icon from '@/components/icons/Icon.vue'
const { t } = useI18n() const { t } = useI18n()
const desktopViewportQuery = '(min-width: 768px)'
const isDesktopViewport = ref(
typeof window === 'undefined' ? true : window.matchMedia(desktopViewportQuery).matches
)
const emit = defineEmits<{ const emit = defineEmits<{
sort: [key: string, order: 'asc' | 'desc'] sort: [key: string, order: 'asc' | 'desc']
}>() }>()
@@ -268,8 +274,19 @@ const checkActionsColumnWidth = () => {
// 监听尺寸变化 // 监听尺寸变化
let resizeObserver: ResizeObserver | null = null let resizeObserver: ResizeObserver | null = null
let resizeHandler: (() => void) | null = null let resizeHandler: (() => void) | null = null
let desktopViewportMediaQuery: MediaQueryList | null = null
let desktopViewportListener: ((event: MediaQueryListEvent) => void) | null = null
onMounted(() => { const detachDesktopTableTracking = () => {
resizeObserver?.disconnect()
resizeObserver = null
if (resizeHandler) {
window.removeEventListener('resize', resizeHandler)
resizeHandler = null
}
}
const attachDesktopTableTracking = () => {
checkScrollable() checkScrollable()
checkActionsColumnWidth() checkActionsColumnWidth()
if (tableWrapperRef.value && typeof ResizeObserver !== 'undefined') { if (tableWrapperRef.value && typeof ResizeObserver !== 'undefined') {
@@ -286,14 +303,34 @@ onMounted(() => {
} }
window.addEventListener('resize', resizeHandler) window.addEventListener('resize', resizeHandler)
} }
}
onMounted(() => {
if (typeof window !== 'undefined') {
desktopViewportMediaQuery = window.matchMedia(desktopViewportQuery)
isDesktopViewport.value = desktopViewportMediaQuery.matches
desktopViewportListener = (event: MediaQueryListEvent) => {
isDesktopViewport.value = event.matches
}
if (typeof desktopViewportMediaQuery.addEventListener === 'function') {
desktopViewportMediaQuery.addEventListener('change', desktopViewportListener)
} else {
desktopViewportMediaQuery.addListener(desktopViewportListener)
}
}
}) })
onUnmounted(() => { onUnmounted(() => {
resizeObserver?.disconnect() detachDesktopTableTracking()
if (resizeHandler) { if (desktopViewportMediaQuery && desktopViewportListener) {
window.removeEventListener('resize', resizeHandler) if (typeof desktopViewportMediaQuery.removeEventListener === 'function') {
resizeHandler = null desktopViewportMediaQuery.removeEventListener('change', desktopViewportListener)
} else {
desktopViewportMediaQuery.removeListener(desktopViewportListener)
}
desktopViewportListener = null
} }
desktopViewportMediaQuery = null
}) })
interface Props { interface Props {
@@ -470,6 +507,17 @@ const columnsSignature = computed(() =>
props.columns.map((column) => `${column.key}:${column.sortable ? '1' : '0'}`).join('|') props.columns.map((column) => `${column.key}:${column.sortable ? '1' : '0'}`).join('|')
) )
watch(
isDesktopViewport,
async (isDesktop) => {
detachDesktopTableTracking()
if (!isDesktop) return
await nextTick()
attachDesktopTableTracking()
},
{ immediate: true, flush: 'post' }
)
// 数据/列变化时重新检查滚动状态 // 数据/列变化时重新检查滚动状态
// 注意:不能监听 actionsExpanded因为 checkActionsColumnWidth 会临时修改它,会导致无限循环 // 注意:不能监听 actionsExpanded因为 checkActionsColumnWidth 会临时修改它,会导致无限循环
watch( watch(
@@ -526,7 +574,7 @@ const sortedData = computed(() => {
// --- Virtual scrolling --- // --- Virtual scrolling ---
const rowVirtualizer = useVirtualizer(computed(() => ({ const rowVirtualizer = useVirtualizer(computed(() => ({
count: sortedData.value?.length ?? 0, count: isDesktopViewport.value ? (sortedData.value?.length ?? 0) : 0,
getScrollElement: () => tableWrapperRef.value, getScrollElement: () => tableWrapperRef.value,
estimateSize: () => props.estimateRowHeight ?? 56, estimateSize: () => props.estimateRowHeight ?? 56,
overscan: props.overscan ?? 5, overscan: props.overscan ?? 5,