refactor(game): 重构项目结构,优化链路, 移动端适配

- 移除 useGameBoardVm 数据层实施说明文档
- 移除核心玩法与前端规则摘要文档
- 移除游戏模块数据与界面分层第一阶段实施稿文档
- 清理与数据层重构相关的技术方案说明
- 删除关于 PC 和 Mobile 界面分离的设计规划
- 移除 view-model hooks 架构设计相关内容
This commit is contained in:
JiaJun
2026-06-03 17:21:13 +08:00
parent 3efcb3bba6
commit bfb4b76611
129 changed files with 4534 additions and 4227 deletions

View File

@@ -0,0 +1,170 @@
import { type ReactNode, useEffect, useRef } from 'react'
import { createPortal } from 'react-dom'
import { useTranslation } from 'react-i18next'
import mobileModalHeader from '@/assets/system/mobile-modal-header.webp'
import modalClose from '@/assets/system/modal-close.webp'
import { acquireBodyScrollLock } from '@/lib/dom/body-scroll-lock'
import { cn } from '@/lib/utils'
interface MobileCenterModalProps {
open: boolean
onClose?: () => void
title?: ReactNode
titleAlign?: 'left' | 'center'
isShowClose?: boolean
isNormalBg?: boolean
children?: ReactNode
className?: string
backdropClassName?: string
}
const MOBILE_MODAL_HEADER_HEIGHT = 'calc(var(--design-unit)*82)'
const MOBILE_MODAL_CONTENT_TOP = 'calc(var(--design-unit)*40)'
export function MobileCenterModal({
open,
onClose,
title,
titleAlign = 'left',
isShowClose = true,
children,
className,
backdropClassName,
}: MobileCenterModalProps) {
const { t } = useTranslation()
const onCloseRef = useRef(onClose)
const handleClose = () => {
onClose?.()
}
useEffect(() => {
onCloseRef.current = onClose
}, [onClose])
useEffect(() => {
if (!open || typeof document === 'undefined') {
return
}
const releaseBodyScrollLock = acquireBodyScrollLock()
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
onCloseRef.current?.()
}
}
window.addEventListener('keydown', handleKeyDown)
return () => {
releaseBodyScrollLock()
window.removeEventListener('keydown', handleKeyDown)
}
}, [open])
if (!open || typeof document === 'undefined') {
return null
}
return createPortal(
<div
className={cn(
'fixed inset-0 z-50 flex items-center justify-center bg-slate-950/80 px-design-12 py-design-14',
backdropClassName,
)}
>
<div
role="dialog"
aria-modal="true"
aria-label={
typeof title === 'string'
? title
: t('commonUi.modal.defaultAriaLabel')
}
className={cn(
'relative flex min-h-0 w-full flex-col overflow-visible rounded-[calc(var(--design-unit)*12)] text-white shadow-[0_0_calc(var(--design-unit)*22)_rgba(21,213,232,0.18)]',
className,
)}
>
<div
className={cn(
'relative h-full min-h-0 w-full overflow-visible rounded-b-[calc(var(--design-unit)*10)] bg-[linear-gradient(180deg,rgba(5,37,47,0.98)_0%,rgba(2,21,31,0.98)_58%,rgba(1,12,20,0.99)_100%)] shadow-[inset_0_0_calc(var(--design-unit)*24)_rgba(30,206,222,0.18)]',
title ? '' : 'pt-design-40',
)}
>
<div
aria-hidden="true"
className="pointer-events-none absolute inset-x-0 bottom-0 z-[1] rounded-b-[calc(var(--design-unit)*10)] border-x border-b border-[rgba(108,205,207,0.72)]"
style={{
top: MOBILE_MODAL_CONTENT_TOP,
}}
/>
<div
aria-hidden="true"
className="pointer-events-none absolute z-20 shrink-0"
style={{
top: 'calc(var(--design-unit)*-2)',
left: 'calc(var(--design-unit)*-3)',
right: 'calc(var(--design-unit)*-3)',
height: 'calc(var(--design-unit)*86)',
backgroundImage: `url(${mobileModalHeader})`,
backgroundPosition: 'top center',
backgroundRepeat: 'no-repeat',
backgroundSize: '100% 100%',
}}
/>
<div
className="pointer-events-none absolute top-0 z-30 shrink-0 px-design-22"
style={{
left: 'calc(var(--design-unit)*-8)',
right: 'calc(var(--design-unit)*-8)',
height: MOBILE_MODAL_HEADER_HEIGHT,
}}
>
{title ? (
<div
className={cn(
'pointer-events-auto flex h-design-44 w-full items-center text-design-22 font-semibold tracking-[0.05em] text-cyan-50',
titleAlign === 'center'
? 'justify-center text-center'
: 'justify-start text-left',
)}
>
{title}
</div>
) : null}
{isShowClose && onClose ? (
<button
type="button"
aria-label={t('commonUi.modal.close')}
onClick={handleClose}
className="pointer-events-auto absolute top-design-11 right-design-15 inline-flex h-design-24 w-design-34 cursor-pointer items-center justify-center rounded-full transition hover:scale-105 active:scale-95"
>
<img
src={modalClose}
alt=""
aria-hidden="true"
className="modal-close-glow relative z-10 h-design-32 w-design-26 object-contain"
/>
</button>
) : null}
</div>
<div
className="relative z-10 min-h-0 w-full overflow-hidden"
style={{
height: `calc(100% - ${MOBILE_MODAL_CONTENT_TOP})`,
marginTop: MOBILE_MODAL_CONTENT_TOP,
}}
>
<div className="h-full min-h-0 w-full overflow-y-auto overscroll-contain">
{children}
</div>
</div>
</div>
</div>
</div>,
document.body,
)
}