mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-09-24 16:05:44 +08:00
refactor(frontend): remove non-virtual DataTable path, always-on virtual scroll
This commit is contained in:
@@ -33,7 +33,7 @@
|
||||
|
||||
<template v-else>
|
||||
<div
|
||||
v-for="(row, index) in displayedData"
|
||||
v-for="(row, index) in sortedData"
|
||||
:key="resolveRowKey(row, index)"
|
||||
class="rounded-lg border border-gray-200 bg-white p-4 dark:border-dark-700 dark:bg-dark-900"
|
||||
>
|
||||
@@ -65,8 +65,7 @@
|
||||
class="table-wrapper hidden md:block"
|
||||
:class="{
|
||||
'actions-expanded': actionsExpanded,
|
||||
'is-scrollable': isScrollable,
|
||||
'virtual-scroll-container': virtualScroll
|
||||
'is-scrollable': isScrollable
|
||||
}"
|
||||
>
|
||||
<table class="min-w-full divide-y divide-gray-200 dark:divide-dark-700">
|
||||
@@ -148,31 +147,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Data rows: non-virtual mode (original behavior) -->
|
||||
<template v-else-if="!virtualScroll">
|
||||
<tr
|
||||
v-for="(row, index) in displayedData"
|
||||
:key="resolveRowKey(row, index)"
|
||||
:data-row-id="resolveRowKey(row, index)"
|
||||
class="hover:bg-gray-50 dark:hover:bg-dark-800"
|
||||
>
|
||||
<td
|
||||
v-for="(column, colIndex) in columns"
|
||||
:key="column.key"
|
||||
:class="[
|
||||
'whitespace-nowrap py-4 text-sm text-gray-900 dark:text-gray-100',
|
||||
getAdaptivePaddingClass(),
|
||||
getStickyColumnClass(column, colIndex)
|
||||
]"
|
||||
>
|
||||
<slot :name="`cell-${column.key}`" :row="row" :value="row[column.key]" :expanded="actionsExpanded">
|
||||
{{ column.formatter ? column.formatter(row[column.key], row) : row[column.key] }}
|
||||
</slot>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<!-- Data rows: virtual scroll mode -->
|
||||
<!-- Data rows (virtual scroll) -->
|
||||
<template v-else>
|
||||
<tr v-if="virtualPaddingTop > 0" aria-hidden="true">
|
||||
<td :colspan="columns.length"
|
||||
@@ -295,9 +270,6 @@ let resizeHandler: (() => void) | null = null
|
||||
onMounted(() => {
|
||||
checkScrollable()
|
||||
checkActionsColumnWidth()
|
||||
if (!props.virtualScroll) {
|
||||
startProgressiveReveal(sortedData.value?.length ?? 0)
|
||||
}
|
||||
if (tableWrapperRef.value && typeof ResizeObserver !== 'undefined') {
|
||||
resizeObserver = new ResizeObserver(() => {
|
||||
checkScrollable()
|
||||
@@ -316,7 +288,6 @@ onMounted(() => {
|
||||
|
||||
onUnmounted(() => {
|
||||
resizeObserver?.disconnect()
|
||||
cancelAnimationFrame(progressiveRAF)
|
||||
if (resizeHandler) {
|
||||
window.removeEventListener('resize', resizeHandler)
|
||||
resizeHandler = null
|
||||
@@ -347,11 +318,9 @@ interface Props {
|
||||
* will emit 'sort' events instead of performing client-side sorting.
|
||||
*/
|
||||
serverSideSort?: boolean
|
||||
/** Enable virtual scrolling (opt-in, default false) */
|
||||
virtualScroll?: boolean
|
||||
/** Estimated row height in px for the virtualizer (default 56) */
|
||||
estimateRowHeight?: number
|
||||
/** Number of rows to render beyond visible area (default 5) */
|
||||
/** Number of rows to render beyond the visible area (default 5) */
|
||||
overscan?: number
|
||||
}
|
||||
|
||||
@@ -555,7 +524,7 @@ const sortedData = computed(() => {
|
||||
|
||||
// --- Virtual scrolling ---
|
||||
const rowVirtualizer = useVirtualizer(computed(() => ({
|
||||
count: props.virtualScroll ? (sortedData.value?.length ?? 0) : 0,
|
||||
count: sortedData.value?.length ?? 0,
|
||||
getScrollElement: () => tableWrapperRef.value,
|
||||
estimateSize: () => props.estimateRowHeight ?? 56,
|
||||
overscan: props.overscan ?? 5,
|
||||
@@ -575,46 +544,11 @@ const virtualPaddingBottom = computed(() => {
|
||||
})
|
||||
|
||||
const measureElement = (el: any) => {
|
||||
if (el && props.virtualScroll) {
|
||||
if (el) {
|
||||
rowVirtualizer.value.measureElement(el as Element)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Progressive rendering: render rows in chunks to avoid blocking the main thread ---
|
||||
const PROGRESSIVE_CHUNK = 30
|
||||
const visibleCount = ref(0)
|
||||
let progressiveRAF = 0
|
||||
|
||||
const displayedData = computed(() => {
|
||||
const all = sortedData.value
|
||||
if (!all || visibleCount.value >= all.length) return all
|
||||
return all.slice(0, visibleCount.value)
|
||||
})
|
||||
|
||||
function startProgressiveReveal(total: number) {
|
||||
cancelAnimationFrame(progressiveRAF)
|
||||
visibleCount.value = Math.min(PROGRESSIVE_CHUNK, total)
|
||||
if (total <= PROGRESSIVE_CHUNK) return
|
||||
const step = () => {
|
||||
if (visibleCount.value < total) {
|
||||
visibleCount.value = Math.min(visibleCount.value + PROGRESSIVE_CHUNK, total)
|
||||
if (visibleCount.value < total) {
|
||||
progressiveRAF = requestAnimationFrame(step)
|
||||
}
|
||||
}
|
||||
}
|
||||
progressiveRAF = requestAnimationFrame(step)
|
||||
}
|
||||
|
||||
// Restart progressive rendering when the underlying data array changes (page switch, filter, reload).
|
||||
// Use shallowRef identity check so sort-only changes (same array reference) don't re-trigger.
|
||||
const dataIdentity = computed(() => props.data)
|
||||
watch(dataIdentity, () => {
|
||||
if (!props.virtualScroll) {
|
||||
startProgressiveReveal(sortedData.value?.length ?? 0)
|
||||
}
|
||||
})
|
||||
|
||||
const hasActionsColumn = computed(() => {
|
||||
return props.columns.some(column => column.key === 'actions')
|
||||
})
|
||||
@@ -726,14 +660,10 @@ defineExpose({
|
||||
--select-col-width: 52px; /* 勾选列宽度:px-6 (24px*2) + checkbox (16px) */
|
||||
position: relative;
|
||||
overflow-x: auto;
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
/* Virtual scroll mode: enable vertical scrolling, fill available height */
|
||||
.table-wrapper.virtual-scroll-container {
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
/* 表头容器,确保在滚动时覆盖表体内容 */
|
||||
|
||||
@@ -142,7 +142,6 @@
|
||||
default-sort-key="name"
|
||||
default-sort-order="asc"
|
||||
:sort-storage-key="ACCOUNT_SORT_STORAGE_KEY"
|
||||
:virtual-scroll="true"
|
||||
:estimate-row-height="72"
|
||||
:overscan="5"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user