feat(modal): 添加居中模态框组件及游戏相关弹窗功能

- 新增 CenterModal 组件,支持标题、关闭按钮、背景图片等功能
- 添加键盘 ESC 键关闭模态框和页面滚动锁定功能
- 创建桌面端登录、注册和用户信息三个业务模态框
- 集成 lottie-web 动画库并创建 LottiePlayer 组件
- 在样式文件中添加模态框相关的 CSS 类和输入框样式
- 更新桌面入口文件集成登录和用户信息模态框
- 调整桌面控制组件间距和样式配置
This commit is contained in:
JiaJun
2026-05-07 16:16:41 +08:00
parent 9ee681168e
commit f98710d375
20 changed files with 1390 additions and 7 deletions

View File

@@ -63,7 +63,7 @@ export function DesktopControl() {
return (
<div
className={
'flex h-design-100 w-full items-center gap-design-16 overflow-hidden'
'flex h-design-100 w-full items-center gap-design-12 overflow-hidden'
}
>
<div

View File

@@ -3,6 +3,9 @@ import { DesktopAnimal } from '@/features/game/components/desktop/desktop-animal
import { DesktopControl } from '@/features/game/components/desktop/desktop-control.tsx'
import { DesktopGameHistory } from '@/features/game/components/desktop/desktop-game-history.tsx'
import { DesktopStatusLine } from '@/features/game/components/desktop/desktop-status.tsx'
import DesktopLoginModal from '@/features/game/modal/desktop/desktop-login-modal.tsx'
import DesktopRegisterModal from '@/features/game/modal/desktop/desktop-register-modal.tsx'
import DesktopUserInfoModal from '@/features/game/modal/desktop/desktop-userInfo-modal.tsx'
export function PcEntry() {
return (
@@ -36,6 +39,12 @@ export function PcEntry() {
>
<DesktopControl />
</div>
{/*登录弹窗*/}
<DesktopLoginModal />
{/*注册弹窗 */}
{/*<DesktopRegisterModal />*/}
{/* 用户信息弹窗 */}
<DesktopUserInfoModal />
</>
)
}

View File

@@ -0,0 +1,101 @@
import { motion } from 'motion/react'
import { useState } from 'react'
import loginBg from '@/assets/system/login-bg.webp'
import rightImg from '@/assets/system/right.webp'
import { CenterModal } from '@/components/center-modal.tsx'
import { SmartImage } from '@/components/smart-image.tsx'
function DesktopLoginModal() {
const [open, setOpen] = useState(false)
function handleSubmit() {
setOpen(false)
}
return (
<CenterModal
open={open}
onClose={() => {}}
title={<div className={'modal-title-glow'}></div>}
titleAlign="center"
className={'w-design-980 h-design-540'}
>
<div
className={
'flex flex-col items-center justify-between gap-design-20 px-design-20'
}
>
<div
className={
'h-design-375 flex flex-col gap-design-45 w-full bg-[#060B0F]/50 p-design-50'
}
>
<div className={'flex items-center'}>
<div
className={
'w-design-160 shrink-0 text-left text-design-24 text-[#58ADAF]'
}
>
Akun/TEL:
</div>
<input
className={'flex-1 text-left'}
placeholder={'Silakan masukkan akun atau nomor ponsel Anda.'}
/>
</div>
<div className={'flex items-center'}>
<div
className={
'w-design-160 shrink-0 text-left text-design-24 text-[#58ADAF]'
}
>
Kata Sandi:
</div>
<input
className={'flex-1 text-left'}
placeholder={'Masukkan Kata Sandi'}
/>
</div>
<div className={'flex items-center justify-around'}>
<div className={'flex items-center gap-design-10'}>
<div
className={
'flex items-center justify-center bg-[#040B0F] border border-[#549195] rounded-md w-design-38 h-design-38'
}
>
<SmartImage alt={'right'} src={rightImg} />
</div>
<div className={'text-[#549195]'}>Daftar Akun</div>
</div>
<div className={'flex items-center gap-design-10'}>
<div
className={
'flex items-center justify-center bg-[#040B0F] border border-[#549195] rounded-md w-design-38 h-design-38'
}
>
<SmartImage alt={'right'} src={rightImg} />
</div>
<div className={'text-[#549195]'}>Ingat Kata Sandi</div>
</div>
</div>
</div>
<motion.div
onClick={handleSubmit}
whileTap={{ scale: 0.95 }}
style={{
backgroundImage: `url(${loginBg})`,
backgroundSize: '100% 100%',
}}
className={
'w-design-390 h-design-110 flex items-center justify-center text-design-32 modal-title-glow font-bold cursor-pointer'
}
>
MASUK
</motion.div>
</div>
</CenterModal>
)
}
export default DesktopLoginModal

View File

@@ -0,0 +1,125 @@
import { motion } from 'motion/react'
import { useState } from 'react'
import loginBg from '@/assets/system/login-bg.webp'
import rightImg from '@/assets/system/right.webp'
import { CenterModal } from '@/components/center-modal.tsx'
import { SmartImage } from '@/components/smart-image.tsx'
function DesktopRegisterModal() {
const [open, setOpen] = useState(true)
function handleSubmit() {
setOpen(false)
}
return (
<CenterModal
open={open}
onClose={() => {}}
title={<div className={'modal-title-glow'}></div>}
titleAlign="center"
className={'w-design-980 h-design-740'}
>
<div
className={'flex flex-col items-center justify-between px-design-20'}
>
<div
className={
'h-design-490 flex flex-col gap-design-30 w-full bg-[#060B0F]/50 p-design-50'
}
>
<div className={'flex items-center'}>
<div
className={
'w-design-160 shrink-0 text-left text-design-24 text-[#58ADAF]'
}
>
Akun/TEL:
</div>
<input
className={'flex-1 text-left'}
placeholder={'Silakan masukkan akun atau nomor ponsel Anda.'}
/>
</div>
<div className={'flex items-center'}>
<div
className={
'w-design-160 shrink-0 text-left text-design-24 text-[#58ADAF]'
}
>
Kata Sandi:
</div>
<input
className={'flex-1 text-left'}
placeholder={'Masukkan Kata Sandi'}
/>
</div>
<div className={'flex items-center'}>
<div
className={
'w-design-160 shrink-0 text-left text-design-24 text-[#58ADAF]'
}
>
Kata Sandi:
</div>
<input
className={'flex-1 text-left'}
placeholder={'Masukkan Kata Sandi'}
/>
</div>
<div className={'flex items-center'}>
<div
className={
'w-design-160 shrink-0 text-left text-design-24 text-[#58ADAF]'
}
>
Kata Sandi:
</div>
<input
className={'flex-1 text-left'}
placeholder={'Masukkan Kata Sandi'}
/>
</div>
<div className={'flex items-center justify-around'}>
<div className={'flex items-center gap-design-10'}>
<div
className={
'flex items-center justify-center bg-[#040B0F] border border-[#549195] rounded-md w-design-38 h-design-38'
}
>
<SmartImage alt={'right'} src={rightImg} />
</div>
<div className={'text-[#549195]'}>Daftar Akun</div>
</div>
<div className={'flex items-center gap-design-10'}>
<div
className={
'flex items-center justify-center bg-[#040B0F] border border-[#549195] rounded-md w-design-38 h-design-38'
}
>
<SmartImage alt={'right'} src={rightImg} />
</div>
<div className={'text-[#549195]'}>Ingat Kata Sandi</div>
</div>
</div>
</div>
<motion.div
onClick={handleSubmit}
whileTap={{ scale: 0.95 }}
style={{
backgroundImage: `url(${loginBg})`,
backgroundSize: '100% 100%',
}}
className={
'w-design-390 h-design-110 flex items-center justify-center text-design-32 modal-title-glow font-bold cursor-pointer'
}
>
MASUK
</motion.div>
</div>
</CenterModal>
)
}
export default DesktopRegisterModal

View File

@@ -0,0 +1,136 @@
import { CircleUserRound, Mail } from 'lucide-react'
import { useState } from 'react'
import userInfoBg from '@/assets/system/userInfo-bg.webp'
import { CenterModal } from '@/components/center-modal.tsx'
import { cn } from '@/lib/untils'
type UserInfoTabKey = 'profile' | 'message'
const USER_INFO_TABS: Array<{
key: UserInfoTabKey
label: string
icon: typeof CircleUserRound
}> = [
{
key: 'profile',
label: '个人信息',
icon: CircleUserRound,
},
{
key: 'message',
label: '站内消息',
icon: Mail,
},
]
function DesktopUserInfoModal() {
const [open, setOpen] = useState(true)
const [activeTab, setActiveTab] = useState<UserInfoTabKey>('profile')
function handleSubmit() {
setOpen(false)
}
return (
<CenterModal
open={open}
onClose={handleSubmit}
title={
<div className={'modal-title-glow text-design-26'}>Biomond Balance</div>
}
isNormalBg={true}
titleAlign="left"
className={'w-design-980 h-design-590'}
>
<div className={'relative flex h-[96%] w-full'}>
<div className={'relative w-design-230 shrink-0'}>
<div
aria-hidden="true"
className="absolute right-0 top-0 h-full w-[calc(var(--design-unit)*2)] bg-[linear-gradient(180deg,rgba(68,244,255,0)_0%,rgba(68,244,255,0.55)_12%,rgba(130,255,255,0.95)_50%,rgba(68,244,255,0.55)_88%,rgba(68,244,255,0)_100%)] shadow-[0_0_calc(var(--design-unit)*8)_rgba(49,208,255,0.45)]"
/>
{USER_INFO_TABS.map((tab) => {
const Icon = tab.icon
const isActive = tab.key === activeTab
return (
<button
key={tab.key}
type="button"
onClick={() => setActiveTab(tab.key)}
className={cn(
'relative flex h-design-150 w-full flex-col items-center justify-center gap-design-10 overflow-hidden transition',
isActive
? 'text-[#FEEEB0]'
: 'text-[#58ADAF] hover:text-[#BFEAEC]',
)}
>
{isActive ? (
<span
aria-hidden="true"
className="absolute inset-y-0 right-0 w-full bg-[linear-gradient(to_left,rgba(254,238,176,0.46)_0%,rgba(254,238,176,0.28)_42%,rgba(254,238,176,0.12)_68%,rgba(254,238,176,0)_100%)]"
/>
) : null}
{isActive ? (
<span
aria-hidden="true"
className="absolute right-0 top-1/2 h-[72%] w-[calc(var(--design-unit)*3)] -translate-y-1/2 rounded-l-full bg-[linear-gradient(180deg,rgba(255,248,214,0.96)_0%,rgba(254,238,176,0.92)_48%,rgba(232,188,112,0.88)_100%)] shadow-[-2px_0_calc(var(--design-unit)*8)_rgba(254,238,176,0.36)]"
/>
) : null}
<Icon
size={60}
className={cn(
'relative z-10 transition',
isActive &&
'drop-shadow-[0_0_calc(var(--design-unit)*8)_rgba(254,238,176,0.5)]',
)}
/>
<div
className={cn(
'relative z-10 text-design-24',
isActive && 'modal-title-gold-glow',
)}
>
{tab.label}
</div>
</button>
)
})}
</div>
<div className={'flex-1'}>
<div
className={
'flex h-full w-full items-start justify-start bg-top bg-no-repeat px-design-40 py-design-32'
}
style={{
backgroundImage: `url(${userInfoBg})`,
backgroundSize: '120% 100%',
}}
>
{activeTab === 'profile' ? (
<div className="space-y-3 text-design-22 text-[#D7FAFF]">
<div className="modal-title-gold-glow text-design-28">
</div>
<div>UID: 10009231</div>
<div>账户等级: VIP 3</div>
<div>绑定手机: 138****8899</div>
</div>
) : (
<div className="space-y-3 text-design-22 text-[#D7FAFF]">
<div className="modal-title-gold-glow text-design-28">
</div>
<div></div>
</div>
)}
</div>
</div>
</div>
</CenterModal>
)
}
export default DesktopUserInfoModal