refactor(game): 重构项目结构,优化链路, 移动端适配
- 移除 useGameBoardVm 数据层实施说明文档 - 移除核心玩法与前端规则摘要文档 - 移除游戏模块数据与界面分层第一阶段实施稿文档 - 清理与数据层重构相关的技术方案说明 - 删除关于 PC 和 Mobile 界面分离的设计规划 - 移除 view-model hooks 架构设计相关内容
This commit is contained in:
181
src/modal/mobile/mobile-finance-records-tab.tsx
Normal file
181
src/modal/mobile/mobile-finance-records-tab.tsx
Normal file
@@ -0,0 +1,181 @@
|
||||
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 { useFinanceRecordsVm } from '@/hooks/use-finance-records-vm'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
function maskOrderNo(value: string) {
|
||||
const text = value.trim()
|
||||
|
||||
if (text.length <= 12) {
|
||||
return text
|
||||
}
|
||||
|
||||
return `${text.slice(0, 6)}**${text.slice(-4)}`
|
||||
}
|
||||
|
||||
function MobileFinanceRecordsTab({ enabled }: { enabled: boolean }) {
|
||||
const vm = useFinanceRecordsVm({ enabled })
|
||||
const parentRef = useRef<HTMLDivElement | null>(null)
|
||||
const rowVirtualizer = useVirtualizer({
|
||||
count: vm.items.length + (vm.hasNextPage ? 1 : 0),
|
||||
estimateSize: () => 52,
|
||||
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 min-h-0 w-full flex-col p-design-4">
|
||||
<div className="mb-design-8 flex shrink-0 items-center justify-between gap-design-8 rounded-md border border-[#3EAFC7]/40 bg-[#062E39]/80 px-design-8 py-design-7">
|
||||
<div className="relative grid min-w-0 grid-cols-2 overflow-hidden rounded-md border border-[#3EAFC7]/30 bg-[#031B24]/75 p-design-3">
|
||||
{vm.recordTypes.map((recordType) => {
|
||||
const isActive = recordType.key === vm.recordType
|
||||
|
||||
return (
|
||||
<button
|
||||
key={recordType.key}
|
||||
type="button"
|
||||
aria-pressed={isActive}
|
||||
onClick={() => {
|
||||
vm.selectRecordType(recordType.key)
|
||||
rowVirtualizer.scrollToOffset(0)
|
||||
}}
|
||||
className={cn(
|
||||
'relative h-design-24 min-w-design-82 cursor-pointer rounded-md px-design-8 text-design-12 transition-colors duration-200',
|
||||
isActive
|
||||
? 'text-white'
|
||||
: 'text-[#6CCDCF] hover:bg-[#0A4252] hover:text-white',
|
||||
)}
|
||||
>
|
||||
{isActive ? (
|
||||
<motion.span
|
||||
layoutId="finance-record-type-active"
|
||||
className={
|
||||
'absolute inset-0 rounded-md bg-[linear-gradient(180deg,#3DA5BD,#166477)] shadow-[0_0_calc(var(--design-unit)*10)_rgba(62,175,199,0.26)]'
|
||||
}
|
||||
transition={{
|
||||
type: 'spring',
|
||||
stiffness: 420,
|
||||
damping: 34,
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
<span className="relative z-10 text-design-10">
|
||||
{recordType.label}
|
||||
</span>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="shrink-0 text-design-11 text-[#7ECAD1]">
|
||||
{vm.pageLabel}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="min-h-0 flex-1 overflow-x-auto rounded-md">
|
||||
<div className="min-w-design-330">
|
||||
<div className="grid grid-cols-[minmax(0,1.55fr)_minmax(0,0.9fr)_minmax(0,0.9fr)] gap-design-6 rounded-md border border-[#2B8CA3]/35 bg-[#031B24]/75 px-design-10 py-design-8 text-design-12 text-[#7ECAD1]">
|
||||
<div>{vm.headers.orderNo}</div>
|
||||
<div>{vm.headers.amount}</div>
|
||||
<div>{vm.headers.bonusAmount}</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
ref={parentRef}
|
||||
className="mt-design-7 max-h-[calc(var(--design-unit)*340)] min-h-0 overflow-y-auto pr-design-2"
|
||||
>
|
||||
{vm.isLoading ? (
|
||||
<DataLoadingIndicator label={vm.loadingText} />
|
||||
) : vm.isError ? (
|
||||
<div className="py-design-24 text-center text-design-12 text-[#6CCDCF]">
|
||||
{vm.loadFailedText}
|
||||
</div>
|
||||
) : vm.items.length === 0 ? (
|
||||
<div className="py-design-24 text-center text-design-12 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-7"
|
||||
style={{
|
||||
height: `${virtualRow.size}px`,
|
||||
transform: `translateY(${virtualRow.start}px)`,
|
||||
}}
|
||||
>
|
||||
{item ? (
|
||||
<motion.div
|
||||
className="grid h-design-45 grid-cols-[minmax(0,1.55fr)_minmax(0,0.9fr)_minmax(0,0.9fr)] items-center gap-design-6 rounded-md bg-[#0A4252] px-design-10 py-design-8 text-design-12 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 font-medium text-white"
|
||||
title={item.orderNoLabel}
|
||||
>
|
||||
{maskOrderNo(item.orderNoLabel)}
|
||||
</div>
|
||||
<div className="truncate text-[#FEEEB0]">
|
||||
{item.amountLabel}
|
||||
</div>
|
||||
<div className="truncate text-[#7CFFCF]">
|
||||
{item.bonusAmountLabel}
|
||||
</div>
|
||||
</motion.div>
|
||||
) : (
|
||||
<DataLoadingIndicator
|
||||
compact
|
||||
label={vm.loadingText}
|
||||
className="h-design-45 rounded-md bg-[#0A4252]/60"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MobileFinanceRecordsTab
|
||||
Reference in New Issue
Block a user