feat(game): 重构游戏组件并新增自动设置模态框

- 将多个组件中的背景图片样式替换为 SmartBackground 组件
- 在 CenterModal 中新增 isShowClose 属性控制关闭按钮显示
- 新增 DesktopAutoSettingModal 组件实现自动设置功能
- 新增 DesktopProceduresModal 和 DesktopWithdrawTopupModal 组件
- 配置 shadcn/ui 并集成 Geist 字体和动画库
- 更新 CSS 样式添加暗色主题变量和输入框样式
- 修复 utils 导入路径错误问题
This commit is contained in:
JiaJun
2026-05-08 17:56:06 +08:00
parent 9ee3192c49
commit 7622d4121f
35 changed files with 4184 additions and 137 deletions

View File

@@ -1,5 +1,5 @@
import { SmartImage } from '@/components/smart-image'
import { cn } from '@/lib/untils'
import { cn } from '@/lib/utils'
const animalModules = import.meta.glob('../../../../assets/animal/*.webp', {
eager: true,

View File

@@ -9,9 +9,10 @@ import controlBg from '@/assets/game/control-bg.png'
import leftBottomBg from '@/assets/game/left-bg.webp'
import reduce from '@/assets/game/reduce.webp'
import totalBg from '@/assets/game/total-bg.webp'
import { SmartBackground } from '@/components/smart-background.tsx'
import { SmartImage } from '@/components/smart-image.tsx'
import { ACTION_OPTIONS, CHIP_OPTIONS } from '@/constants'
import { cn } from '@/lib/untils.ts'
import { cn } from '@/lib/utils'
export function DesktopControl() {
const [chips, setChips] = useState(CHIP_OPTIONS)
@@ -66,14 +67,12 @@ export function DesktopControl() {
'flex h-design-100 w-full items-center gap-design-12 overflow-hidden'
}
>
<div
<SmartBackground
src={leftBottomBg}
size="100% 100%"
className={
'flex flex-col items-center gap-1 justify-center h-full w-design-110 shrink-0 bg-center bg-no-repeat'
}
style={{
backgroundImage: `url(${leftBottomBg})`,
backgroundSize: '100% 100%',
}}
>
<div className={'flex flex-col items-center justify-center'}>
<div>TREBD</div>
@@ -84,21 +83,17 @@ export function DesktopControl() {
alt={`animal`}
className={'w-design-20 h-design-10'}
/>
</div>
<div
</SmartBackground>
<SmartBackground
src={chipBg}
size="100% 100%"
className={
'desktop-control-chip relative z-10 flex h-full w-design-710 shrink-0 items-center gap-design-10 pl-design-20 pr-design-40 bg-center bg-no-repeat'
}
style={{
backgroundImage: `url(${chipBg})`,
backgroundSize: '100% 100%',
}}
>
<div
style={{
backgroundImage: `url(${chipLineBg})`,
backgroundSize: '100% 100%',
}}
<SmartBackground
src={chipLineBg}
size="100% 100%"
className={
'flex min-w-0 flex-1 items-center gap-design-10 overflow-visible'
}
@@ -215,7 +210,7 @@ export function DesktopControl() {
</motion.button>
)
})}
</div>
</SmartBackground>
<div
className={
@@ -238,27 +233,23 @@ export function DesktopControl() {
className={'w-design-40 h-design-40'}
/>
</div>
</div>
<div
</SmartBackground>
<SmartBackground
src={totalBg}
size="100% 100%"
className={
'desktop-control-total relative flex flex-col items-center justify-center z-10 h-full w-design-435 shrink-0 bg-center bg-no-repeat'
}
style={{
backgroundImage: `url(${totalBg})`,
backgroundSize: '100% 100%',
}}
>
<div>SELECTED:3/5</div>
<div>Total Bet150</div>
</div>
<div
</SmartBackground>
<SmartBackground
src={controlBg}
size="100% 100%"
className={cn(
'desktop-control-actions relative z-10 flex h-full w-design-385 shrink-0 items-center bg-center bg-no-repeat pl-design-15',
)}
style={{
backgroundImage: `url(${controlBg})`,
backgroundSize: '100% 100%',
}}
>
{ACTION_OPTIONS.map(({ id, label, Icon, bg }) => {
const isClicked = clickedId === id
@@ -278,25 +269,25 @@ export function DesktopControl() {
)}
>
{showBg && (
<motion.span
<SmartBackground
as={motion.span}
initial={{ opacity: 0 }}
animate={isClicked ? { opacity: 1 } : { opacity: 0 }}
transition={{
duration: isClicked ? 0.15 : 0.18,
ease: 'easeOut',
}}
src={bg}
className={cn(
'pointer-events-none absolute bg-center inset-0 bg-no-repeat h-design-100',
)}
style={{
backgroundImage: `url(${bg})`,
backgroundSize:
id === 'clear'
? '110% 90%'
: id === 'repeat'
? '98% 80%'
: '96% 80%',
}}
size={
id === 'clear'
? '110% 90%'
: id === 'repeat'
? '98% 80%'
: '96% 80%'
}
/>
)}
<motion.div
@@ -331,8 +322,11 @@ export function DesktopControl() {
</motion.button>
)
})}
</div>
<motion.button
</SmartBackground>
<SmartBackground
as={motion.button}
src={confirmBg}
size="100% 100%"
type="button"
onClick={handleConfirmClick}
whileHover={{ scale: 1.01 }}
@@ -340,22 +334,17 @@ export function DesktopControl() {
className={cn(
'relative z-10 h-full w-design-260 shrink-0 cursor-pointer bg-center bg-no-repeat flex items-center justify-center text-design-32 font-bold',
)}
style={{
backgroundImage: `url(${confirmBg})`,
backgroundSize: '100% 100%',
}}
>
{confirmClicked && (
<motion.span
<SmartBackground
as={motion.span}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.15 }}
className="pointer-events-none absolute inset-0 bg-center bg-no-repeat"
style={{
backgroundImage: `url(${confirmBg})`,
backgroundSize: '100% 100%',
}}
src={confirmBg}
size="100% 100%"
/>
)}
<motion.span
@@ -365,7 +354,7 @@ export function DesktopControl() {
>
confirm
</motion.span>
</motion.button>
</SmartBackground>
</div>
)
}

View File

@@ -1,7 +1,7 @@
import dayjs from 'dayjs'
import duration from 'dayjs/plugin/duration'
import { useEffect, useMemo, useState } from 'react'
import { cn } from '@/lib/untils'
import { cn } from '@/lib/utils'
dayjs.extend(duration)

View File

@@ -1,4 +1,5 @@
import historyBg from '@/assets/system/history-bg.png'
import { SmartBackground } from '@/components/smart-background.tsx'
export function DesktopGameHistory() {
const data = [
@@ -115,12 +116,10 @@ export function DesktopGameHistory() {
]
return (
<div
<SmartBackground
src={historyBg}
size="100% 100%"
className="desktop-game-history-glow flex h-full min-h-0 w-full flex-col items-center overflow-hidden bg-center bg-no-repeat py-2"
style={{
backgroundImage: `url(${historyBg})`,
backgroundSize: '100% 100%',
}}
>
<div
className={
@@ -178,6 +177,6 @@ export function DesktopGameHistory() {
)
})}
</div>
</div>
</SmartBackground>
)
}

View File

@@ -1,26 +1,26 @@
import statusCenter from '@/assets/system/status-center.webp'
import statusLine from '@/assets/system/status-line.webp'
import { SmartBackground } from '@/components/smart-background.tsx'
import { DesktopCountdown } from '@/features/game/components/desktop/desktop-countdown.tsx'
import { DesktopTitle } from '@/features/game/components/desktop/desktop-title.tsx'
export function DesktopStatusLine() {
return (
<div className={'relative w-full flex flex-col text-design-22'}>
<div
<SmartBackground
src={statusLine}
size="100% 100%"
className="w-full h-design-60 bg-no-repeat bg-center flex items-center justify-center"
style={{
backgroundImage: `url(${statusLine})`,
backgroundSize: '100% 100%',
}}
>
<div className={'flex-1 flex items-center justify-center'}>
<div>Odds: 1:33</div>
<div>Streak: X2</div>
<div>Limit: 100</div>
</div>
<div
<SmartBackground
src={statusCenter}
className="w-design-360 h-[105px] font-countdown bg-no-repeat bg-center bg-contain flex items-center justify-center"
style={{ backgroundImage: `url(${statusCenter})` }}
size="contain"
>
<DesktopCountdown
initialSeconds={30}
@@ -28,7 +28,7 @@ export function DesktopStatusLine() {
console.log('countdown finished')
}}
/>
</div>
</SmartBackground>
<div className={'flex-1 flex items-center justify-center gap-10'}>
<div>Round ID:20241026120</div>
<div className={'flex items-center gap-2'}>
@@ -41,7 +41,7 @@ export function DesktopStatusLine() {
<div>(Menerima Taruhan)</div>
</div>
</div>
</div>
</SmartBackground>
<div
className={
'absolute top-design-60 left-1/2 -translate-x-1/2 -z-10 w-full px-design-16'

View File

@@ -0,0 +1,5 @@
function DesktopTopup() {
return <div>DesktopTopup</div>
}
export default DesktopTopup

View File

@@ -0,0 +1,5 @@
function DesktopWithdraw() {
return <div>DesktopWithdraw</div>
}
export default DesktopWithdraw

View File

@@ -1,6 +1,6 @@
import type { ReactNode } from 'react'
import { cn } from '@/lib/untils'
import { cn } from '@/lib/utils'
type GameTone = 'neutral' | 'brand' | 'success' | 'warning' | 'danger'

View File

@@ -3,10 +3,10 @@ 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 DesktopNoticeModal from '@/features/game/modal/desktop/desktop-notice-modal.tsx'
import DesktopRegisterModal from '@/features/game/modal/desktop/desktop-register-modal.tsx'
import DesktopUserInfoModal from '@/features/game/modal/desktop/desktop-userInfo-modal.tsx'
import DesktopAutoSettingModal from '@/features/game/modal/desktop/desktop-auto-setting-modal.tsx'
import DesktopProceduresModal from '@/features/game/modal/desktop/desktop-procedures-modal.tsx'
import DesktopWithdrawTopupModal from '@/features/game/modal/desktop/desktop-withdraw-topup-modal.tsx'
import DesktopRegisterModal from '../modal/desktop/desktop-register-modal'
export function PcEntry() {
return (
@@ -47,7 +47,13 @@ export function PcEntry() {
{/* 用户信息弹窗 */}
{/*<DesktopUserInfoModal />*/}
{/*公告弹窗*/}
<DesktopNoticeModal />
{/*<DesktopNoticeModal />*/}
{/*自动托管弹窗*/}
{/* <DesktopAutoSettingModal/>*/}
{/* 充值提现前置选择弹窗*/}
<DesktopProceduresModal />
{/* 充值和提现弹窗 */}
{/*<DesktopWithdrawTopupModal/>*/}
</>
)
}

View File

@@ -0,0 +1,104 @@
import { useState } from 'react'
import lengthBlueBtn from '@/assets/system/length-blue-btn.webp'
import { CenterModal } from '@/components/center-modal.tsx'
import { SmartBackground } from '@/components/smart-background.tsx'
import { Switch } from '@/components/ui/switch.tsx'
const AUTO_STOP_ROWS = [
{
label: 'Stop if balance lower than',
value: '0',
checked: false,
},
{
label: 'Stop if single win exceeds',
value: '50000',
checked: true,
},
{
label: 'Stop on any Jackpot',
checked: false,
},
] as const
function DesktopAutoSettingModal() {
const [open, setOpen] = useState(true)
function handleSubmit() {
setOpen(false)
}
return (
<CenterModal
open={open}
onClose={handleSubmit}
title={
<div className={'modal-title-glow text-design-26 uppercase'}>
Biomond Balance
</div>
}
isNormalBg={true}
titleAlign="left"
className={'w-design-835 !h-design-500'}
>
<div
className={
'flex h-full w-full flex-col justify-between px-design-18 pt-design-30 pb-design-60'
}
>
<div className={'flex w-full flex-col gap-design-26'}>
{AUTO_STOP_ROWS.map((row) => (
<div
key={row.label}
className={'flex items-center justify-between gap-design-30'}
>
<div
className={
'w-design-300 shrink-0 text-design-22 leading-[1.1] text-[#9CF7FF]'
}
>
{row.label}
</div>
{row.value ? (
<div
className={
'game-setting-input-shell flex h-design-58 w-design-410 items-center justify-between pl-design-18 pr-design-10'
}
>
<input
defaultValue={row.value}
className={
'game-setting-input h-full w-design-280 text-design-18'
}
/>
<Switch size={'sm'} defaultChecked={row.checked} />
</div>
) : (
<div className={'flex w-design-410 justify-end pr-design-2'}>
<Switch size={'sm'} defaultChecked={row.checked} />
</div>
)}
</div>
))}
</div>
<div className={'flex w-full justify-center'}>
<SmartBackground
src={lengthBlueBtn}
size="100% 100%"
repeat="no-repeat"
position="center"
className={
'w-design-300 h-design-72 pb-design-4 flex items-center justify-center text-design-24 font-bold tracking-wide text-[#E7FBFF]'
}
>
START AUTO-SPIN
</SmartBackground>
</div>
</div>
</CenterModal>
)
}
export default DesktopAutoSettingModal

View File

@@ -3,6 +3,7 @@ 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 { SmartBackground } from '@/components/smart-background.tsx'
import { SmartImage } from '@/components/smart-image.tsx'
function DesktopLoginModal() {
@@ -80,19 +81,18 @@ function DesktopLoginModal() {
</div>
</div>
<motion.div
<SmartBackground
as={motion.div}
onClick={handleSubmit}
whileTap={{ scale: 0.95 }}
style={{
backgroundImage: `url(${loginBg})`,
backgroundSize: '100% 100%',
}}
src={loginBg}
size="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>
</SmartBackground>
</div>
</CenterModal>
)

View File

@@ -3,6 +3,7 @@ import lengthBlueBtn from '@/assets/system/length-blue-btn.webp'
import lengthGreenBtn from '@/assets/system/length-green-btn.webp'
import noticeBg from '@/assets/system/notice-bg.webp'
import { CenterModal } from '@/components/center-modal.tsx'
import { SmartBackground } from '@/components/smart-background.tsx'
import { SmartImage } from '@/components/smart-image.tsx'
function DesktopNoticeModal() {
@@ -49,33 +50,29 @@ function DesktopNoticeModal() {
</div>
</div>
<div className={'w-full flex justify-around'}>
<div
style={{
backgroundImage: `url(${lengthGreenBtn})`,
backgroundSize: '106% 108%',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
}}
<SmartBackground
src={lengthGreenBtn}
size="106% 108%"
repeat="no-repeat"
position="center"
className={
'w-design-270 h-design-72 pb-design-5 flex items-center justify-center text-design-20 font-bold'
}
>
Memeriksa
</div>
</SmartBackground>
<div
style={{
backgroundImage: `url(${lengthBlueBtn})`,
backgroundSize: '100% 90%',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
}}
<SmartBackground
src={lengthBlueBtn}
size="100% 90%"
repeat="no-repeat"
position="center"
className={
'w-design-270 h-design-72 pb-design-5 flex items-center justify-center text-design-20 font-bold'
}
>
Memeriksa
</div>
</SmartBackground>
</div>
</div>
</CenterModal>

View File

@@ -0,0 +1,60 @@
import { useState } from 'react'
import proceduresBg from '@/assets/system/procedures-bg.webp'
import topupBtnBg from '@/assets/system/topup.webp'
import withdrawBtnBg from '@/assets/system/withdraw.webp'
import { CenterModal } from '@/components/center-modal.tsx'
import { SmartBackground } from '@/components/smart-background.tsx'
function DesktopProceduresModal() {
const [open, setOpen] = useState(true)
function handleSubmit() {
setOpen(false)
}
return (
<CenterModal
open={open}
onClose={handleSubmit}
title={
<div className={'modal-title-glow text-design-26 uppercase'}>
Biomond Balance
</div>
}
isNormalBg={true}
titleAlign="left"
className={'w-design-1000 h-design-610'}
>
<SmartBackground
src={proceduresBg}
repeat="no-repeat"
size="cover"
className={
'h-[95%] w-full rounded-md flex flex-col items-center justify-between'
}
>
<div className={'mt-design-190'}>111</div>
<div className={'flex items-center ml-design-180'}>
<SmartBackground
src={withdrawBtnBg}
className={
'w-design-400 h-design-195 flex items-center justify-center pb-design-10 text-design-32 font-bold'
}
>
</SmartBackground>
<SmartBackground
src={topupBtnBg}
className={
'w-design-400 h-design-195 flex items-center justify-center pb-design-20 text-design-32 font-bold'
}
>
</SmartBackground>
</div>
</SmartBackground>
</CenterModal>
)
}
export default DesktopProceduresModal

View File

@@ -3,6 +3,7 @@ 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 { SmartBackground } from '@/components/smart-background.tsx'
import { SmartImage } from '@/components/smart-image.tsx'
function DesktopRegisterModal() {
@@ -104,19 +105,18 @@ function DesktopRegisterModal() {
</div>
</div>
<motion.div
<SmartBackground
as={motion.div}
onClick={handleSubmit}
whileTap={{ scale: 0.95 }}
style={{
backgroundImage: `url(${loginBg})`,
backgroundSize: '100% 100%',
}}
src={loginBg}
size="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>
</SmartBackground>
</div>
</CenterModal>
)

View File

@@ -5,8 +5,9 @@ import blueBtnBg from '@/assets/system/blue-btn.webp'
import lengthBtnBg from '@/assets/system/length-blue-btn.webp'
import userInfoBg from '@/assets/system/userInfo-bg.webp'
import { CenterModal } from '@/components/center-modal.tsx'
import { SmartBackground } from '@/components/smart-background.tsx'
import { SmartImage } from '@/components/smart-image.tsx'
import { cn } from '@/lib/untils'
import { cn } from '@/lib/utils'
type UserInfoTabKey = 'profile' | 'message'
@@ -104,14 +105,12 @@ function DesktopUserInfoModal() {
<div className={'flex-1'}>
{activeTab === 'profile' ? (
<div
<SmartBackground
src={userInfoBg}
size="120% 100%"
className={
'flex flex-col h-full w-full items-start justify-between bg-top bg-no-repeat px-design-40 py-design-32 text-[#6CCDCF] text-design-24'
}
style={{
backgroundImage: `url(${userInfoBg})`,
backgroundSize: '120% 100%',
}}
>
<div className={'flex items-center gap-design-30'}>
<SmartImage
@@ -145,7 +144,7 @@ function DesktopUserInfoModal() {
mus
</div>
</div>
</div>
</SmartBackground>
) : (
<div
className={
@@ -172,17 +171,15 @@ function DesktopUserInfoModal() {
2026, dapatkan pengembalian ...
</div>
</div>
<div
style={{
backgroundImage: `url(${blueBtnBg})`,
backgroundSize: '100% 100%',
}}
<SmartBackground
src={blueBtnBg}
size="100% 100%"
className={
'w-design-150 h-design-64 flex items-center justify-center text-design-20 font-bold'
}
>
Memeriksa
</div>
</SmartBackground>
</div>
))}
</div>
@@ -192,17 +189,15 @@ function DesktopUserInfoModal() {
'absolute bottom-0 left-0 w-full bg-[#266477]/70 rounded-md h-design-85 flex items-center justify-center'
}
>
<div
style={{
backgroundImage: `url(${lengthBtnBg})`,
backgroundSize: '100% 100%',
}}
<SmartBackground
src={lengthBtnBg}
size="100% 100%"
className={
'w-design-275 h-design-65 flex items-center justify-center text-design-22 font-bold'
}
>
</div>
</SmartBackground>
</div>
</div>
)}

View File

@@ -0,0 +1,34 @@
import { useState } from 'react'
import { CenterModal } from '@/components/center-modal.tsx'
import DesktopTopup from '@/features/game/components/desktop/desktop-topup.tsx'
import DesktopWithdraw from '@/features/game/components/desktop/desktop-withdraw.tsx'
type WithdrawType = 'withdraw' | 'topup'
function DesktopWithdrawTopupModal() {
const [open, setOpen] = useState(true)
const [type, setType] = useState<WithdrawType>('withdraw')
function handleSubmit() {
setOpen(false)
}
return (
<CenterModal
open={open}
onClose={handleSubmit}
title={
<div className={'modal-title-glow text-design-26 uppercase'}>
{type}
</div>
}
isShowClose={false}
isNormalBg={true}
titleAlign="left"
className={'w-design-835 h-design-500'}
>
<div>{type ? <DesktopWithdraw /> : <DesktopTopup />}</div>
</CenterModal>
)
}
export default DesktopWithdrawTopupModal