- 实现了 AppBootResourceGate 组件用于管理应用启动时的资源加载 - 集成了图片和字体资源的预加载机制 - 添加了启动时的加载进度指示器和动画效果 - 创建了 DataLoadingIndicator 组件用于显示数据加载状态 - 在多个组件中替换原有的加载提示为新的 DataLoadingIndicator - 更新了样式文件以支持新的加载动画和视觉效果
146 lines
5.2 KiB
TypeScript
146 lines
5.2 KiB
TypeScript
import { useVirtualizer } from '@tanstack/react-virtual'
|
|
import { motion } from 'motion/react'
|
|
import { useEffect, useRef } from 'react'
|
|
|
|
import { DataLoadingIndicator } from '@/components/ui/data-loading-indicator'
|
|
import { useWalletRecordsVm } from '@/features/game/hooks/use-wallet-records-vm'
|
|
|
|
function DesktopWalletRecordsTab({ enabled }: { enabled: boolean }) {
|
|
const vm = useWalletRecordsVm({ enabled })
|
|
const parentRef = useRef<HTMLDivElement | null>(null)
|
|
const rowVirtualizer = useVirtualizer({
|
|
count: vm.items.length + (vm.hasNextPage ? 1 : 0),
|
|
estimateSize: () => 72,
|
|
getScrollElement: () => parentRef.current,
|
|
overscan: 6,
|
|
})
|
|
const virtualItems = rowVirtualizer.getVirtualItems()
|
|
|
|
useEffect(() => {
|
|
const lastItem = virtualItems.at(-1)
|
|
|
|
if (
|
|
!lastItem ||
|
|
lastItem.index < vm.items.length - 1 ||
|
|
!vm.hasNextPage ||
|
|
vm.isFetchingNextPage
|
|
) {
|
|
return
|
|
}
|
|
|
|
void vm.fetchNextPage()
|
|
}, [
|
|
virtualItems,
|
|
vm.fetchNextPage,
|
|
vm.hasNextPage,
|
|
vm.isFetchingNextPage,
|
|
vm.items.length,
|
|
])
|
|
|
|
return (
|
|
<div className={'flex h-full w-full flex-col p-design-10'}>
|
|
<div
|
|
className={
|
|
'mb-design-12 flex items-center justify-between gap-design-16 rounded-md border border-[#3EAFC7]/40 bg-[#062E39]/80 px-design-14 py-design-12'
|
|
}
|
|
>
|
|
<div className={'text-design-20 font-medium text-[#BFEAEC]'}>
|
|
{vm.headers.type}
|
|
</div>
|
|
<div className={'text-design-16 text-[#7ECAD1]'}>{vm.pageLabel}</div>
|
|
</div>
|
|
|
|
<div className={'min-h-0 flex-1 rounded-md'}>
|
|
<div
|
|
className={
|
|
'grid grid-cols-[minmax(0,1.1fr)_minmax(0,0.75fr)_minmax(0,0.8fr)_minmax(0,0.8fr)_minmax(0,1fr)] gap-design-10 rounded-md border border-[#2B8CA3]/35 bg-[#031B24]/75 px-design-16 py-design-12 text-design-16 text-[#7ECAD1]'
|
|
}
|
|
>
|
|
<div>{vm.headers.time}</div>
|
|
<div>{vm.headers.amount}</div>
|
|
<div>{vm.headers.balanceBefore}</div>
|
|
<div>{vm.headers.balanceAfter}</div>
|
|
<div>{vm.headers.remark}</div>
|
|
</div>
|
|
|
|
<div
|
|
ref={parentRef}
|
|
className={
|
|
'mt-design-10 max-h-[calc(var(--design-unit)*320)] min-h-0 overflow-auto pr-design-4'
|
|
}
|
|
>
|
|
{vm.isLoading ? (
|
|
<DataLoadingIndicator label={vm.loadingText} />
|
|
) : vm.isError ? (
|
|
<div className={'py-design-30 text-center text-[#6CCDCF]'}>
|
|
{vm.loadFailedText}
|
|
</div>
|
|
) : vm.items.length === 0 ? (
|
|
<div className={'py-design-30 text-center text-[#6CCDCF]'}>
|
|
{vm.emptyText}
|
|
</div>
|
|
) : (
|
|
<div
|
|
className={'relative w-full'}
|
|
style={{ height: `${rowVirtualizer.getTotalSize()}px` }}
|
|
>
|
|
{virtualItems.map((virtualRow) => {
|
|
const item = vm.items[virtualRow.index]
|
|
|
|
return (
|
|
<div
|
|
key={virtualRow.key}
|
|
className={'absolute left-0 top-0 w-full pb-design-10'}
|
|
style={{
|
|
height: `${virtualRow.size}px`,
|
|
transform: `translateY(${virtualRow.start}px)`,
|
|
}}
|
|
>
|
|
{item ? (
|
|
<motion.div
|
|
className={
|
|
'grid h-[calc(var(--design-unit)*62)] grid-cols-[minmax(0,1.1fr)_minmax(0,0.75fr)_minmax(0,0.8fr)_minmax(0,0.8fr)_minmax(0,1fr)] items-center gap-design-10 rounded-md bg-[#0A4252] px-design-16 py-design-14 text-design-17 text-[#C4F2F7] shadow-[inset_0_0_calc(var(--design-unit)*10)_rgba(108,205,207,0.05)]'
|
|
}
|
|
initial={{ opacity: 0, y: 6 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{
|
|
duration: 0.16,
|
|
ease: 'easeOut',
|
|
}}
|
|
>
|
|
<div className={'truncate text-[#BFEAEC]'}>
|
|
{item.timeLabel}
|
|
</div>
|
|
<div className={'truncate font-medium text-[#FEEEB0]'}>
|
|
{item.amountLabel}
|
|
</div>
|
|
<div className={'truncate text-[#86DAE7]'}>
|
|
{item.balanceBeforeLabel}
|
|
</div>
|
|
<div className={'truncate text-[#7CFFCF]'}>
|
|
{item.balanceAfterLabel}
|
|
</div>
|
|
<div className={'truncate text-white'}>
|
|
{item.remarkLabel}
|
|
</div>
|
|
</motion.div>
|
|
) : (
|
|
<DataLoadingIndicator
|
|
compact
|
|
label={vm.loadingText}
|
|
className="h-[calc(var(--design-unit)*62)] rounded-md bg-[#0A4252]/60"
|
|
/>
|
|
)}
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default DesktopWalletRecordsTab
|