fix(game): 修复移动端动物组件渲染和奖励数据显示问题

- 修复了GitNexus索引统计信息中的符号、关系和执行流数量
- 为桌面端动物组件的投注开始警告添加了z-index类以确保正确显示层级
- 修复了游戏回合存储中奖励金额和类型的返回逻辑,避免显示旧奖励数据
- 调整了移动端金币揭示效果的内边距样式以改善视觉效果
- 重构了移动端动物网格组件,添加了容器引用并修复了揭示动画定位问题
- 清理了未使用的移动揭示帧偏移常量并优化了网格布局结构
- 更新了移动端控制组件中的内边距和间距以改善UI布局
- 在移动端头部组件中添加了新的图像资源和CSS工具函数
- 调整了移动端头部组件中的按钮大小、间距和对齐方式
- 优化了移动端工具栏按钮的样式类以实现更好的响应式设计
This commit is contained in:
JiaJun
2026-06-05 16:10:44 +08:00
parent 7999a5d709
commit d41e8e7f1c
10 changed files with 291 additions and 257 deletions

View File

@@ -1,7 +1,7 @@
<!-- gitnexus:start --> <!-- gitnexus:start -->
# GitNexus — Code Intelligence # GitNexus — Code Intelligence
This project is indexed by GitNexus as **36-character-flower** (2879 symbols, 5735 relationships, 245 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. This project is indexed by GitNexus as **36-character-flower** (3072 symbols, 6072 relationships, 262 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first. > If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.

View File

@@ -1,7 +1,7 @@
<!-- gitnexus:start --> <!-- gitnexus:start -->
# GitNexus — Code Intelligence # GitNexus — Code Intelligence
This project is indexed by GitNexus as **36-character-flower** (2879 symbols, 5735 relationships, 245 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. This project is indexed by GitNexus as **36-character-flower** (3072 symbols, 6072 relationships, 262 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely.
> If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first. > If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first.

View File

@@ -470,7 +470,7 @@ export function DesktopAnimal({
) : null} ) : null}
<DesktopAnimalOverlay showStopOverlay={showStopOverlay} /> <DesktopAnimalOverlay showStopOverlay={showStopOverlay} />
<RoundBettingStartAlert /> <RoundBettingStartAlert className="z-50" />
{showStandbyState ? ( {showStandbyState ? (
<button <button

View File

@@ -18,7 +18,6 @@ const SETTLEMENT_REVEAL_SETTLE_DURATION_MS = 800
const SETTLEMENT_REVEAL_RESULT_HOLD_MS = 1_000 const SETTLEMENT_REVEAL_RESULT_HOLD_MS = 1_000
const SETTLEMENT_REVEAL_MIN_STEP_MS = 180 const SETTLEMENT_REVEAL_MIN_STEP_MS = 180
const SETTLEMENT_REVEAL_MAX_STEP_MS = 960 const SETTLEMENT_REVEAL_MAX_STEP_MS = 960
const MOBILE_REVEAL_FRAME_OFFSET_PX = 2
function getRandomAnimalId(ids: number[], currentId: number | null) { function getRandomAnimalId(ids: number[], currentId: number | null) {
if (ids.length === 0) { if (ids.length === 0) {
@@ -66,6 +65,7 @@ export function MobileAnimal({
const prefersReducedMotion = useReducedMotion() const prefersReducedMotion = useReducedMotion()
const animalIds = useMemo(() => FLOWER_IMAGE_LIST.map((item) => item.id), []) const animalIds = useMemo(() => FLOWER_IMAGE_LIST.map((item) => item.id), [])
const containerRef = useRef<HTMLElement | null>(null) const containerRef = useRef<HTMLElement | null>(null)
const animalGridRef = useRef<HTMLDivElement | null>(null)
const cellRefs = useRef(new Map<number, HTMLButtonElement>()) const cellRefs = useRef(new Map<number, HTMLButtonElement>())
const [revealCellId, setRevealCellId] = useState<number | null>(null) const [revealCellId, setRevealCellId] = useState<number | null>(null)
const [revealFrame, setRevealFrame] = useState<{ const [revealFrame, setRevealFrame] = useState<{
@@ -214,7 +214,7 @@ export function MobileAnimal({
} }
const syncRevealFrame = () => { const syncRevealFrame = () => {
const container = containerRef.current const container = animalGridRef.current
const cell = cellRefs.current.get(revealCellId) const cell = cellRefs.current.get(revealCellId)
if (!container || !cell) { if (!container || !cell) {
@@ -251,7 +251,8 @@ export function MobileAnimal({
> >
<MobileStatusLine /> <MobileStatusLine />
<div className="relative z-10 grid w-full grid-cols-4 gap-design-3"> <div ref={animalGridRef} className="relative z-10 w-full">
<div className="grid w-full grid-cols-4 gap-design-3">
{FLOWER_IMAGE_LIST.map((item) => { {FLOWER_IMAGE_LIST.map((item) => {
const selectionMeta = selectionByCell[item.id] const selectionMeta = selectionByCell[item.id]
const hasPlacedSelection = Boolean(selectionMeta) const hasPlacedSelection = Boolean(selectionMeta)
@@ -441,7 +442,7 @@ export function MobileAnimal({
<div <div
aria-hidden="true" aria-hidden="true"
className={cn( className={cn(
'pointer-events-none absolute left-0 top-0 z-40 transform-gpu transition-[height,transform,width]', 'pointer-events-none absolute left-0 top-0 z-30 transform-gpu transition-[height,transform,width]',
prefersReducedMotion prefersReducedMotion
? 'duration-0' ? 'duration-0'
: isRevealSettlingResult : isRevealSettlingResult
@@ -449,13 +450,22 @@ export function MobileAnimal({
: 'duration-[160ms] ease-[cubic-bezier(0.22,1,0.36,1)]', : 'duration-[160ms] ease-[cubic-bezier(0.22,1,0.36,1)]',
)} )}
style={{ style={{
height: revealFrame.height + MOBILE_REVEAL_FRAME_OFFSET_PX * 2, height: revealFrame.height,
transform: `translate(${revealFrame.left - MOBILE_REVEAL_FRAME_OFFSET_PX}px, ${revealFrame.top - MOBILE_REVEAL_FRAME_OFFSET_PX}px)`, transform: `translate(${revealFrame.left}px, ${revealFrame.top}px)`,
width: revealFrame.width + MOBILE_REVEAL_FRAME_OFFSET_PX * 2, width: revealFrame.width,
}} }}
> >
<SmartImage
src={animalBorderImage}
alt=""
aria-hidden="true"
priority
showSkeleton={false}
className="pointer-events-none absolute inset-0 z-30 h-full w-full"
imgClassName="object-fill"
/>
<div <div
className="mobile-gold-reveal-glow rounded-[calc(var(--design-unit)*7)]" className="mobile-gold-reveal-glow z-10 rounded-[calc(var(--design-unit)*7)]"
style={ style={
prefersReducedMotion prefersReducedMotion
? { ? {
@@ -466,15 +476,16 @@ export function MobileAnimal({
: undefined : undefined
} }
/> />
<div className="mobile-gold-reveal-static-border rounded-[calc(var(--design-unit)*7)]" /> <div className="mobile-gold-reveal-static-border z-20 rounded-[calc(var(--design-unit)*7)]" />
<div <div
className="mobile-gold-reveal-shell rounded-[calc(var(--design-unit)*7)]" className="mobile-gold-reveal-shell z-20 rounded-[calc(var(--design-unit)*7)]"
style={prefersReducedMotion ? { animation: 'none' } : undefined} style={prefersReducedMotion ? { animation: 'none' } : undefined}
/> />
</div> </div>
) : null} ) : null}
<MobileAnimalOverlay showStopOverlay={showStopOverlay} /> <MobileAnimalOverlay showStopOverlay={showStopOverlay} />
</div>
{showStandbyState ? ( {showStandbyState ? (
<button <button

View File

@@ -148,12 +148,12 @@ export function MobileControl() {
<SmartBackground <SmartBackground
src={chipBg} src={chipBg}
size="100% 100%" size="100% 100%"
className="relative z-10 flex h-full min-w-0 flex-1 items-center bg-center bg-no-repeat px-design-8" className="relative z-10 flex h-full min-w-0 flex-1 items-center bg-center bg-no-repeat pl-design-8 pr-design-1"
> >
<SmartBackground <SmartBackground
src={chipLineBg} src={chipLineBg}
size="100% 100%" size="100% 100%"
className="flex h-design-32 min-w-0 flex-1 items-center justify-between gap-design-2 overflow-visible px-design-4" className="flex h-design-32 min-w-0 items-center justify-between gap-design-7 overflow-visible"
> >
{chips.map((chip) => { {chips.map((chip) => {
const isSelected = chip.id === selectedChipId const isSelected = chip.id === selectedChipId
@@ -290,7 +290,7 @@ export function MobileControl() {
<SmartBackground <SmartBackground
src={mobileAddReduceBg} src={mobileAddReduceBg}
size="100% 100%" size="100% 100%"
className="relative z-20 -ml-design-6 flex h-full w-design-103 shrink-0 items-center justify-center gap-design-6 bg-center bg-no-repeat px-design-9 text-design-13 font-bold" className="relative z-20 !ml-[calc(var(--design-unit)*-8)] flex h-full w-design-103 shrink-0 items-center justify-center gap-design-5 bg-center bg-no-repeat px-design-7 text-design-13 font-bold"
> >
<button <button
type="button" type="button"

View File

@@ -8,12 +8,14 @@ import {
} from 'lucide-react' } from 'lucide-react'
import { motion } from 'motion/react' import { motion } from 'motion/react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import add from '@/assets/game/add.webp'
import avatar from '@/assets/system/avatar.webp' import avatar from '@/assets/system/avatar.webp'
import chatImage from '@/assets/system/chat.webp' import chatImage from '@/assets/system/chat.webp'
import diamond from '@/assets/system/diamond.webp' import diamond from '@/assets/system/diamond.webp'
import logo from '@/assets/system/logo.webp' import logo from '@/assets/system/logo.webp'
import { SmartImage } from '@/components/smart-image.tsx' import { SmartImage } from '@/components/smart-image.tsx'
import { useHeaderClockLabel, useHeaderVm } from '@/hooks/use-header-vm' import { useHeaderClockLabel, useHeaderVm } from '@/hooks/use-header-vm'
import { cn } from '@/lib/utils'
import { useModalStore } from '@/store' import { useModalStore } from '@/store'
function MobileHeaderClock() { function MobileHeaderClock() {
@@ -87,6 +89,15 @@ export function MobileHeader() {
'common-neon-inset flex h-design-19 shrink-0 cursor-pointer items-center justify-center gap-design-5 !rounded-[3px] !px-design-5 !py-0 text-design-7 leading-none transition-opacity hover:opacity-85' 'common-neon-inset flex h-design-19 shrink-0 cursor-pointer items-center justify-center gap-design-5 !rounded-[3px] !px-design-5 !py-0 text-design-7 leading-none transition-opacity hover:opacity-85'
const accountButtonClassName = const accountButtonClassName =
'common-neon-inset flex h-design-19 items-center justify-end !rounded-[3px] !py-0 text-design-7 leading-none transition-[opacity,transform] duration-150 group-hover:opacity-90 group-active:scale-[0.98]' 'common-neon-inset flex h-design-19 items-center justify-end !rounded-[3px] !py-0 text-design-7 leading-none transition-[opacity,transform] duration-150 group-hover:opacity-90 group-active:scale-[0.98]'
const toolbarButtonClassName = cn(
actionButtonClassName,
'min-w-0 shrink gap-design-3 !px-design-3',
)
const compactToolbarButtonClassName = cn(toolbarButtonClassName, 'flex-[0.8]')
const wideToolbarButtonClassName = cn(
toolbarButtonClassName,
'flex-[1.2] !px-design-4',
)
return ( return (
<header className="relative z-30 h-design-72"> <header className="relative z-30 h-design-72">
@@ -101,7 +112,7 @@ export function MobileHeader() {
/> />
</div> </div>
{authStatus === 'authenticated' ? ( {authStatus === 'authenticated' ? (
<div className="flex h-full min-w-0 flex-1 items-center justify-end gap-design-7 px-design-9"> <div className="flex h-full min-w-0 flex-1 items-center justify-end gap-design-20 px-design-9">
<button <button
type="button" type="button"
onClick={onOpenUserInfo} onClick={onOpenUserInfo}
@@ -111,10 +122,10 @@ export function MobileHeader() {
src={avatar} src={avatar}
alt="avatar" alt="avatar"
priority priority
className="absolute left-0 z-20 h-design-25 w-design-25 rounded-full" className="absolute left-0 z-20 h-design-25 w-design-25 -translate-x-1/2 rounded-full"
/> />
<div <div
className={`${accountButtonClassName} w-design-92 pl-design-28 pr-design-6`} className={`${accountButtonClassName} w-design-80 pl-design-28 pr-design-6`}
> >
<span className="truncate"> <span className="truncate">
{currentUser?.username || '--'} {currentUser?.username || '--'}
@@ -131,22 +142,20 @@ export function MobileHeader() {
src={diamond} src={diamond}
alt="diamond" alt="diamond"
priority priority
className="absolute left-0 z-20 h-design-25 w-design-25" className="absolute left-0 z-20 h-design-25 w-design-25 -translate-x-1/2"
/> />
<div <div
className={`${accountButtonClassName} w-design-90 gap-design-4 pl-design-26 pr-design-4`} className={`${accountButtonClassName} w-design-80 gap-design-4 !pr-design-4`}
> >
<span className="min-w-0 truncate"> <span className="min-w-0 truncate">
{currentUser?.coin || '--'} {currentUser?.coin || '--'}
</span> </span>
{/*<div className="common-neon-inset flex h-design-15 w-design-15 shrink-0 items-center justify-center !rounded-[3px] !p-0">*/} <SmartImage
{/* <Plus*/} src={add}
{/* aria-hidden="true"*/} alt="add"
{/* className="h-design-10 w-design-10 shrink-0"*/} priority
{/* color="#57B8BF"*/} className="h-design-13 w-design-13 shrink-0 cursor-pointer"
{/* strokeWidth={2.5}*/} />
{/* />*/}
{/*</div>*/}
</div> </div>
</button> </button>
</div> </div>
@@ -197,9 +206,9 @@ export function MobileHeader() {
</motion.button> </motion.button>
</div> </div>
<div className={'w-full px-design-10 '}> <div className="w-full px-design-10">
<div className="flex h-design-29 w-full items-center gap-design-5 my-design-5 rounded-sm border-[#0A353E] border-1"> <div className="my-design-5 flex h-design-29 w-full min-w-0 items-center gap-design-5 rounded-sm border-1 border-[#0A353E] px-design-5">
<div className="flex h-design-19 w-design-43 shrink-0 items-center justify-center !rounded-[3px] !px-design-6 !py-0"> <div className="flex h-design-19 w-design-40 shrink-0 items-center justify-center !rounded-[3px]">
<div className={signalPresentation.toneClassName}> <div className={signalPresentation.toneClassName}>
<SignalBars <SignalBars
activeBars={signalPresentation.activeBars} activeBars={signalPresentation.activeBars}
@@ -224,7 +233,7 @@ export function MobileHeader() {
<button <button
type="button" type="button"
onClick={onOpenRules} onClick={onOpenRules}
className={`${actionButtonClassName} !px-design-8`} className={compactToolbarButtonClassName}
> >
<Info className="h-design-8 w-design-8 shrink-0" color="#57B8BF" /> <Info className="h-design-8 w-design-8 shrink-0" color="#57B8BF" />
<div className="min-w-0 truncate"> <div className="min-w-0 truncate">
@@ -235,7 +244,7 @@ export function MobileHeader() {
<button <button
type="button" type="button"
onClick={onOpenNotice} onClick={onOpenNotice}
className={`${actionButtonClassName} !px-design-8`} className={wideToolbarButtonClassName}
> >
<Mail className="h-design-8 w-design-8 shrink-0" color="#57B8BF" /> <Mail className="h-design-8 w-design-8 shrink-0" color="#57B8BF" />
<div className="min-w-0 truncate"> <div className="min-w-0 truncate">
@@ -246,7 +255,7 @@ export function MobileHeader() {
<button <button
type="button" type="button"
onClick={toggleSoundEnabled} onClick={toggleSoundEnabled}
className={`${actionButtonClassName} !px-design-8`} className={compactToolbarButtonClassName}
> >
{isSoundEnabled ? ( {isSoundEnabled ? (
<Volume2 <Volume2
@@ -266,7 +275,7 @@ export function MobileHeader() {
<button <button
type="button" type="button"
onClick={onOpenLanguage} onClick={onOpenLanguage}
className={`${actionButtonClassName} !px-design-8 justify-between`} className={wideToolbarButtonClassName}
> >
<SmartImage <SmartImage
src={currentLanguageOption.icon} src={currentLanguageOption.icon}

View File

@@ -159,7 +159,8 @@ export function MobileStatusLine() {
export function MobileStatusMetrics() { export function MobileStatusMetrics() {
const { t } = useTranslation() const { t } = useTranslation()
const { limitLabel, oddsLabel, streakLabel } = useGameStatusVm() const { limitLabel, oddsLabel, streakLabel, streakValue } = useGameStatusVm()
const showStreakLimitOnly = typeof streakValue === 'number' && streakValue > 1
return ( return (
<SmartBackground <SmartBackground
@@ -168,24 +169,35 @@ export function MobileStatusMetrics() {
className="relative flex h-design-26 w-full items-center overflow-visible bg-center bg-no-repeat px-design-10 font-bold leading-none text-[#CBD3D5]" className="relative flex h-design-26 w-full items-center overflow-visible bg-center bg-no-repeat px-design-10 font-bold leading-none text-[#CBD3D5]"
style={{ fontSize: 'calc(var(--design-unit) * 7)' }} style={{ fontSize: 'calc(var(--design-unit) * 7)' }}
> >
{showStreakLimitOnly ? (
<div <div
className="pointer-events-none absolute inset-y-design-1 left-0 w-[68%] bg-center bg-no-repeat opacity-80" className="pointer-events-none absolute left-0 right-0 z-0 bg-center bg-no-repeat opacity-95"
style={{ style={{
backgroundImage: `url(${streakBg})`, backgroundImage: `url(${streakBg})`,
backgroundSize: '118% 155%', backgroundSize: '120% 150%',
bottom: 'calc(var(--design-unit)*-5)',
top: 'calc(var(--design-unit)*-6)',
}} }}
/> />
) : (
<>
<div className="relative z-10 flex min-w-0 flex-1 items-center justify-center"> <div className="relative z-10 flex min-w-0 flex-1 items-center justify-center">
{t('gameDesktop.status.odds')}: {t('gameDesktop.status.odds')}:
<span className="ml-design-3 text-[#E3D171]">{oddsLabel}</span> <span className="ml-design-3 text-[#E3D171]">{oddsLabel}</span>
</div> </div>
<div className="relative z-10 flex min-w-0 flex-1 items-center justify-center gap-design-2"> <div className="relative z-10 flex min-w-0 flex-1 items-center justify-center gap-design-2">
<SmartImage className="h-design-11 w-design-9" alt="fire" src={fire} /> <SmartImage
className="h-design-11 w-design-9"
alt="fire"
src={fire}
/>
<span>{t('gameDesktop.status.streak')}:</span> <span>{t('gameDesktop.status.streak')}:</span>
<span className="bg-gradient-to-b from-[#EBA661] to-[#FCC785] bg-clip-text text-transparent"> <span className="bg-gradient-to-b from-[#EBA661] to-[#FCC785] bg-clip-text text-transparent">
{streakLabel} {streakLabel}
</span> </span>
</div> </div>
</>
)}
<div className="relative z-10 flex min-w-0 flex-1 items-center justify-center gap-design-2"> <div className="relative z-10 flex min-w-0 flex-1 items-center justify-center gap-design-2">
<SmartImage className="h-design-10 w-design-8" alt="lock" src={lock} /> <SmartImage className="h-design-10 w-design-8" alt="lock" src={lock} />
<span>{t('gameDesktop.status.limit')}:</span> <span>{t('gameDesktop.status.limit')}:</span>

View File

@@ -256,6 +256,7 @@ function applyBetWinMessage(message: GameSocketMessage) {
return return
} }
if (betWinData.is_win) {
useGameRoundStore.getState().setPendingBetWinReward({ useGameRoundStore.getState().setPendingBetWinReward({
isJackpot: betWinData.is_jackpot, isJackpot: betWinData.is_jackpot,
revealKey: `${betWinData.period_no}:${betWinData.result_number ?? 'pending'}:${betWinData.total_win}`, revealKey: `${betWinData.period_no}:${betWinData.result_number ?? 'pending'}:${betWinData.total_win}`,
@@ -267,6 +268,7 @@ function applyBetWinMessage(message: GameSocketMessage) {
isJackpot: betWinData.is_jackpot, isJackpot: betWinData.is_jackpot,
singleWinAmount: toOptionalNumber(betWinData.total_win) ?? null, singleWinAmount: toOptionalNumber(betWinData.total_win) ?? null,
}) })
}
if (!currentUser) { if (!currentUser) {
return return

View File

@@ -208,10 +208,10 @@ export const useGameRoundStore = create<GameRoundStoreState>()((set) => ({
: state.revealAnimation.revealKey, : state.revealAnimation.revealKey,
rewardAmount: hasPendingReward rewardAmount: hasPendingReward
? state.revealAnimation.pendingRewardAmount ? state.revealAnimation.pendingRewardAmount
: state.revealAnimation.rewardAmount, : null,
rewardType: hasPendingReward rewardType: hasPendingReward
? state.revealAnimation.pendingRewardType ? state.revealAnimation.pendingRewardType
: state.revealAnimation.rewardType, : 'none',
roundId: hasPendingReward roundId: hasPendingReward
? state.revealAnimation.pendingRewardRoundId ? state.revealAnimation.pendingRewardRoundId
: state.revealAnimation.roundId, : state.revealAnimation.roundId,

View File

@@ -640,7 +640,7 @@
.mobile-gold-reveal-shell { .mobile-gold-reveal-shell {
--gold-angle: 0deg; --gold-angle: 0deg;
position: absolute; position: absolute;
inset: calc(var(--design-unit) * 0.2); inset: calc(var(--design-unit) * 2.2);
border-radius: calc(var(--design-unit) * 7); border-radius: calc(var(--design-unit) * 7);
overflow: hidden; overflow: hidden;
clip-path: inset(0 round calc(var(--design-unit) * 7)); clip-path: inset(0 round calc(var(--design-unit) * 7));
@@ -698,7 +698,7 @@
.mobile-gold-reveal-static-border { .mobile-gold-reveal-static-border {
position: absolute; position: absolute;
inset: calc(var(--design-unit) * 0.7); inset: calc(var(--design-unit) * 2.7);
border-radius: calc(var(--design-unit) * 7); border-radius: calc(var(--design-unit) * 7);
border: calc(var(--design-unit) * 2.8) solid rgba(181, 138, 40, 0.98); border: calc(var(--design-unit) * 2.8) solid rgba(181, 138, 40, 0.98);
box-shadow: box-shadow:
@@ -709,7 +709,7 @@
.mobile-gold-reveal-glow { .mobile-gold-reveal-glow {
position: absolute; position: absolute;
inset: 0; inset: calc(var(--design-unit) * 1.4);
border-radius: calc(var(--design-unit) * 8); border-radius: calc(var(--design-unit) * 8);
background: radial-gradient( background: radial-gradient(
circle at center, circle at center,