351 lines
35 KiB
PHP
351 lines
35 KiB
PHP
<?php use App\Core\I18n; I18n::init(); $t = function($k,$p=[]){return I18n::t($k,$p);}; $gameId = $game['id'] ?? 0; ?>
|
|
<!DOCTYPE html><html lang="<?=I18n::getLang()?>">
|
|
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
|
|
<title><?=$t('pk10_title')?></title>
|
|
<link rel="stylesheet" href="/Static/css/app.css">
|
|
<style>
|
|
/* 赛车赛道 — 保留3D透视 */
|
|
.race-wrapper{perspective:800px;perspective-origin:50% 100%}
|
|
.race-track{background:linear-gradient(180deg,#2d5a2d 0%,#1e4a1e 50%,#163a16 100%);border-radius:12px;overflow:hidden;position:relative;transform:rotateX(25deg);transform-origin:50% 100%;box-shadow:0 -20px 60px rgba(0,128,0,.08),inset 0 0 80px rgba(0,0,0,.3)}
|
|
.race-track::before{content:'';position:absolute;top:0;left:0;right:0;bottom:0;background:repeating-linear-gradient(90deg,transparent,transparent calc(10% - 1px),rgba(255,255,255,.05) calc(10% - 1px),rgba(255,255,255,.05) 10%);pointer-events:none;z-index:1}
|
|
.finish-line{position:absolute;right:5%;top:0;bottom:0;width:4px;background:repeating-linear-gradient(180deg,#fff 0,#fff 4px,#000 4px,#000 8px);opacity:.4;z-index:2}
|
|
.start-line{position:absolute;left:5%;top:0;bottom:0;width:2px;background:rgba(255,255,255,.2);z-index:2}
|
|
.track-lane{height:36px;display:flex;align-items:center;border-bottom:1px solid rgba(255,255,255,.06);padding:0 8px;position:relative;overflow:hidden}
|
|
.track-lane:nth-child(odd){background:rgba(255,255,255,.02)}
|
|
.track-lane .progress-bar{position:absolute;left:0;top:0;height:100%;opacity:.15;width:5%;border-radius:0 4px 4px 0;transition:width .3s ease}
|
|
.car-container{position:absolute;left:3%;top:50%;transform:translateY(-50%);z-index:10;display:flex;align-items:center;gap:4px;transition:left 0.3s ease}
|
|
.race-car{width:32px;height:24px;border-radius:4px 12px 12px 4px;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:11px;color:#fff;flex-shrink:0;position:relative;box-shadow:2px 2px 6px rgba(0,0,0,.5),inset 0 1px 0 rgba(255,255,255,.3);text-shadow:0 1px 2px rgba(0,0,0,.5)}
|
|
.race-car::after{content:'';position:absolute;right:-6px;top:50%;transform:translateY(-50%);width:0;height:0;border-left:6px solid currentColor;border-top:4px solid transparent;border-bottom:4px solid transparent;opacity:.4}
|
|
.rc1{background:linear-gradient(180deg,#ff6b6b,#c0392b);color:#c0392b}.rc2{background:linear-gradient(180deg,#74b9ff,#2980b9);color:#2980b9}
|
|
.rc3{background:linear-gradient(180deg,#55efc4,#27ae60);color:#27ae60}.rc4{background:linear-gradient(180deg,#ffeaa7,#f39c12);color:#f39c12}
|
|
.rc5{background:linear-gradient(180deg,#dda0dd,#8e44ad);color:#8e44ad}.rc6{background:linear-gradient(180deg,#81ecec,#16a085);color:#16a085}
|
|
.rc7{background:linear-gradient(180deg,#fab1a0,#e67e22);color:#e67e22}.rc8{background:linear-gradient(180deg,#fd79a8,#e91e63);color:#e91e63}
|
|
.rc9{background:linear-gradient(180deg,#a0d2db,#00bcd4);color:#00bcd4}.rc10{background:linear-gradient(180deg,#c8e6c9,#689f38);color:#689f38}
|
|
.exhaust{position:absolute;left:-8px;top:50%;transform:translateY(-50%);font-size:8px;opacity:0;pointer-events:none}
|
|
.car-container.moving .exhaust{animation:exhaustSmoke .8s infinite}
|
|
@keyframes exhaustSmoke{0%{opacity:.6;transform:translateY(-50%) translateX(0) scale(1)}100%{opacity:0;transform:translateY(-50%) translateX(-15px) scale(1.5)}}
|
|
.car-container.sprinting .race-car{box-shadow:2px 2px 6px rgba(0,0,0,.5),inset 0 1px 0 rgba(255,255,255,.3),-8px 0 20px rgba(255,255,255,.1)}
|
|
.car-container.sprinting .exhaust{animation:exhaustSmoke .4s infinite}
|
|
.lane-label{position:absolute;left:4px;top:50%;transform:translateY(-50%);color:rgba(255,255,255,.25);font-size:10px;z-index:0}
|
|
.rank-badge{position:absolute;right:-24px;top:50%;transform:translateY(-50%);font-size:9px;font-weight:700;color:#ffd700;opacity:0;transition:opacity .5s;text-shadow:0 0 4px rgba(255,215,0,.6)}
|
|
.car-container.show-rank .rank-badge{opacity:1}
|
|
@keyframes readyShake{0%,100%{transform:translateY(-50%) translateX(0)}25%{transform:translateY(-50%) translateX(2px)}75%{transform:translateY(-50%) translateX(-2px)}}
|
|
.car-container.ready{animation:readyShake .3s infinite}
|
|
/* 顶部栏 */
|
|
.pk-header{position:sticky;top:0;z-index:40;background:#fff;border-bottom:1px solid #F0F0F0;padding:10px 15px;display:flex;align-items:center;justify-content:space-between}
|
|
.pk-header .title{font-weight:700;font-size:16px;color:#333;display:flex;align-items:center;gap:6px}
|
|
.pk-header .stats-btn{color:#333;font-size:14px;display:flex;align-items:center;gap:4px;text-decoration:none;cursor:pointer;background:none;border:none}
|
|
.pk-header .bal{color:var(--primary);font-weight:700;font-family:monospace;font-size:15px;display:flex;align-items:center;gap:4px}
|
|
/* 上期结果区 */
|
|
.result-bar{background:#fff;padding:10px 15px;border-bottom:1px solid #F0F0F0}
|
|
.result-balls{display:flex;gap:3px;flex-wrap:wrap;margin-top:4px}
|
|
.result-tags{display:flex;gap:3px;flex-wrap:wrap;margin-top:6px}
|
|
.result-tags span{display:inline-block;padding:2px 6px;border:1px solid #ddd;border-radius:4px;font-size:11px;color:#666}
|
|
/* 期号状态栏 */
|
|
.period-bar{background:#fff;padding:10px 15px;border-bottom:1px solid #F0F0F0;display:flex;align-items:center;justify-content:space-between}
|
|
.period-badge{font-size:13px;font-weight:600}
|
|
.period-badge.open{color:var(--success)}.period-badge.closed{color:var(--danger)}.period-badge.drawing{color:var(--warn)}
|
|
.seal-label{font-size:13px;font-weight:700;color:#333}
|
|
.seal-status{color:var(--danger);font-weight:600;font-size:13px}
|
|
.draw-label{font-size:13px;font-weight:700;color:#333}
|
|
.draw-countdown{color:var(--primary);font-weight:700;font-size:15px;font-family:monospace}
|
|
/* 左右布局 */
|
|
.bet-layout{display:flex;min-height:50vh}
|
|
.side-menu{width:72px;background:#fff;border-right:1px solid #F0F0F0;flex-shrink:0}
|
|
.side-menu .mi{display:flex;align-items:center;justify-content:center;height:48px;font-size:13px;color:#666;cursor:pointer;border-left:3px solid transparent;transition:all .2s}
|
|
.side-menu .mi.active{color:var(--primary);font-weight:600;border-left-color:var(--primary);background:#F0F8FF}
|
|
.bet-content{flex:1;overflow-y:auto;max-height:60vh;background:#fff}
|
|
/* 投注行 */
|
|
.bet-section-title{text-align:center;padding:10px 0 6px;font-size:14px;font-weight:600;color:#333;border-bottom:1px solid #F0F0F0;position:relative}
|
|
.bet-section-title::after{content:'▲';font-size:8px;color:#999;margin-left:4px}
|
|
.bet-row{display:flex;border-bottom:1px solid #F5F5F5}
|
|
.bet-cell{flex:1;display:flex;align-items:center;justify-content:center;gap:8px;padding:14px 10px;cursor:pointer;font-size:14px;color:#333;transition:background .15s}
|
|
.bet-cell:first-child{border-right:1px solid #F5F5F5}
|
|
.bet-cell:active,.bet-cell.selected{background:#E8F4FF}
|
|
.bet-cell .odds{color:var(--primary);font-weight:600;font-size:14px}
|
|
/* 筹码 */
|
|
.chip-bar{background:#fff;padding:8px 15px;border-top:1px solid #F0F0F0;display:flex;align-items:center;gap:8px}
|
|
.chip{border-radius:50%;width:40px;height:40px;display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;color:#fff;cursor:pointer;transition:transform .15s}
|
|
.chip.active{transform:scale(1.15);box-shadow:0 0 0 2px var(--primary)}
|
|
/* 提交栏 */
|
|
.submit-bar{background:#fff;padding:10px 15px;border-top:1px solid #F0F0F0;display:flex;align-items:center;gap:10px}
|
|
.submit-bar .info{flex:1;font-size:12px;color:#999}
|
|
.submit-bar .info b{color:var(--primary)}
|
|
.bet-amount-badge{position:absolute;top:-4px;right:-4px;background:var(--danger);color:#fff;font-size:9px;font-weight:700;min-width:18px;height:18px;border-radius:9px;display:flex;align-items:center;justify-content:center;padding:0 4px;z-index:2}
|
|
.bet-cell,.bet-btn{position:relative}
|
|
</style>
|
|
</head>
|
|
<body class="pb-nav" style="background:#F5F5F5">
|
|
|
|
<!-- 顶部栏 -->
|
|
<header class="pk-header">
|
|
<div class="title">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#1E90FF" stroke-width="2"><circle cx="7" cy="17" r="2"/><circle cx="17" cy="17" r="2"/><path d="M5 17H3v-4l2-5h9l4 5h3v4h-2"/><path d="M5 12h14"/></svg>
|
|
<?=$t('pk10_title')?>
|
|
</div>
|
|
<button class="stats-btn" onclick="location.href='/game/pk10/stats'"><?=$t('statistics')?><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/><line x1="6" y1="20" x2="6" y2="14"/></svg></button>
|
|
<div class="bal">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg>
|
|
<span id="balanceDisplay"><?=number_format($user['balance']??0,2)?></span>
|
|
<select onchange="setLang(this.value)" style="padding:2px 4px;border:1px solid #ddd;border-radius:4px;font-size:10px;background:#fff;color:#666">
|
|
<?php foreach(I18n::LANGUAGES as $c=>$n): ?><option value="<?=$c?>" <?=I18n::getLang()===$c?'selected':''?>><?=$n?></option><?php endforeach; ?>
|
|
</select>
|
|
<?php if(!empty($isAgent)): ?><a href="/agent" style="color:#1E90FF;font-size:12px;text-decoration:none"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="8.5" cy="7" r="4"/><line x1="20" y1="8" x2="20" y2="14"/><line x1="23" y1="11" x2="17" y2="11"/></svg></a><?php endif; ?>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- 上期结果区 -->
|
|
<div class="result-bar">
|
|
<div style="display:flex;align-items:center;gap:8px">
|
|
<span style="font-size:13px;font-weight:600;color:#333" id="lastPeriodNum">---</span>
|
|
<span style="font-size:11px;color:#999"><?=$t('last_result')?></span>
|
|
</div>
|
|
<div class="result-balls" id="lastResult"><span style="color:#999">---</span></div>
|
|
<div class="result-tags" id="lastResultTags"></div>
|
|
</div>
|
|
|
|
<!-- 当前期状态栏 -->
|
|
<div class="period-bar">
|
|
<div style="display:flex;align-items:center;gap:6px">
|
|
<span style="font-size:13px;font-weight:600;color:#333" id="periodNum">---</span>
|
|
<span class="seal-label"><?=$t('seal_plate')?></span>
|
|
<span id="statusBadge" class="seal-status"><?=$t('betting')?></span>
|
|
</div>
|
|
<div style="display:flex;align-items:center;gap:6px">
|
|
<span class="draw-label"><?=$t('draw_time')?></span>
|
|
<span class="draw-countdown" id="countdown">--</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 3D赛车动画区 — 完整保留 -->
|
|
<div class="race-wrapper">
|
|
<div class="race-track p-2" id="raceArea">
|
|
<div class="start-line"></div>
|
|
<div class="finish-line"></div>
|
|
<?php for($i=1;$i<=10;$i++): ?>
|
|
<div class="track-lane" id="trackLane<?=$i?>">
|
|
<div class="progress-bar c<?=$i?>" id="bar<?=$i?>"></div>
|
|
<span class="lane-label">#<?=$i?></span>
|
|
<div class="car-container" id="carContainer<?=$i?>">
|
|
<div class="race-car rc<?=$i?>"><?=$i?></div>
|
|
<span class="exhaust">•</span>
|
|
<span class="rank-badge" id="rankBadge<?=$i?>"></span>
|
|
</div>
|
|
</div>
|
|
<?php endfor; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 左右布局投注面板 -->
|
|
<div id="betPanel">
|
|
<div class="bet-layout">
|
|
<!-- 左侧菜单 -->
|
|
<div class="side-menu">
|
|
<div class="mi active" onclick="showBetTab('bs',this)" id="tabBs"><?=$t('two_side')?></div>
|
|
<div class="mi" onclick="showBetTab('rank',this)" id="tabRank"><?=$t('rank_1_10')?></div>
|
|
<div class="mi" onclick="showBetTab('sum',this)" id="tabSum"><?=$t('sum_bs_tab')?></div>
|
|
</div>
|
|
<!-- 右侧投注内容 -->
|
|
<div class="bet-content">
|
|
|
|
<!-- 两面(大小+单双+龙虎)-->
|
|
<div id="panelBs">
|
|
<!-- 冠亚和 大小单双 -->
|
|
<div class="bet-section-title"><?=$t('sum')?></div>
|
|
<div class="bet-row">
|
|
<div class="bet-cell" onclick="addBet('sum_bs','sum_big',this)"><?=$t('sum_bs_big')?> <span class="odds">2.2</span></div>
|
|
<div class="bet-cell" onclick="addBet('sum_bs','sum_small',this)"><?=$t('sum_bs_small')?> <span class="odds">1.79</span></div>
|
|
</div>
|
|
<div class="bet-row">
|
|
<div class="bet-cell" onclick="addBet('sum_bs','sum_odd',this)"><?=$t('sum_bs_odd')?> <span class="odds">1.79</span></div>
|
|
<div class="bet-cell" onclick="addBet('sum_bs','sum_even',this)"><?=$t('sum_bs_even')?> <span class="odds">2.2</span></div>
|
|
</div>
|
|
<?php for($r=1;$r<=10;$r++): $label=$r<=2?($r==1?$t('champion'):$t('runner_up')):$t('rank_n',['n'=>$r]); ?>
|
|
<!-- 名次标题 -->
|
|
<div class="bet-section-title"><?=$label?></div>
|
|
<div class="bet-row">
|
|
<div class="bet-cell" onclick="addBet('bs','rank<?=$r?>_big',this)"><?=$t('big')?> <span class="odds">1.995</span></div>
|
|
<div class="bet-cell" onclick="addBet('bs','rank<?=$r?>_small',this)"><?=$t('small')?> <span class="odds">1.995</span></div>
|
|
</div>
|
|
<div class="bet-row">
|
|
<div class="bet-cell" onclick="addBet('oe','rank<?=$r?>_odd',this)"><?=$t('odd')?> <span class="odds">1.995</span></div>
|
|
<div class="bet-cell" onclick="addBet('oe','rank<?=$r?>_even',this)"><?=$t('even')?> <span class="odds">1.995</span></div>
|
|
</div>
|
|
<?php if($r<=5): $pairs=[[1,10],[2,9],[3,8],[4,7],[5,6]]; $p=$pairs[$r-1]; ?>
|
|
<div class="bet-row">
|
|
<div class="bet-cell" onclick="addBet('dt','dt<?=$r?>_dragon',this)"><?=$t('dragon')?> <span class="odds">1.995</span></div>
|
|
<div class="bet-cell" onclick="addBet('dt','dt<?=$r?>_tiger',this)"><?=$t('tiger')?> <span class="odds">1.995</span></div>
|
|
</div>
|
|
<?php endif; endfor; ?>
|
|
</div>
|
|
|
|
<!-- 1~10名 号码投注 -->
|
|
<div id="panelRank" class="hidden" style="padding:10px">
|
|
<?php for($r=1;$r<=10;$r++): $label=$r<=2?($r==1?$t('champion'):$t('runner_up')):$t('rank_n',['n'=>$r]); ?>
|
|
<div style="margin-bottom:10px">
|
|
<div style="font-size:13px;font-weight:600;color:#333;margin-bottom:6px"><?=$label?></div>
|
|
<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:4px">
|
|
<?php for($c=1;$c<=10;$c++): ?>
|
|
<div onclick="addBet('rank','rank<?=$r?>_<?=$c?>',this)" class="bet-btn" data-odds="9.80">
|
|
<div class="car c<?=$c?>" style="width:24px;height:24px;font-size:11px;margin:0 auto 2px"><?=$c?></div>
|
|
<div style="color:#999;font-size:10px">9.80</div>
|
|
</div>
|
|
<?php endfor; ?>
|
|
</div>
|
|
</div>
|
|
<?php endfor; ?>
|
|
</div>
|
|
|
|
<!-- 冠亚和值 -->
|
|
<div id="panelSum" class="hidden" style="padding:10px">
|
|
<div style="font-size:13px;font-weight:600;color:#333;margin-bottom:8px"><?=$t('sum_value')?> (3-19)</div>
|
|
<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:4px">
|
|
<?php $sumOdds=[3=>140,4=>70,5=>35,6=>23,7=>17.5,8=>14,9=>11.6,10=>10,11=>8.8,12=>8.8,13=>10,14=>11.6,15=>14,16=>17.5,17=>23,18=>35,19=>70];
|
|
foreach($sumOdds as $v=>$o): ?>
|
|
<div onclick="addBet('sum','sum_<?=$v?>',this)" class="bet-btn">
|
|
<div style="font-weight:700"><?=$v?></div><div style="color:#999;font-size:10px"><?=$o?></div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;margin-top:10px">
|
|
<div onclick="addBet('sum_bs','sum_big',this)" class="bet-cell" style="border:1px solid #F0F0F0;border-radius:8px"><?=$t('sum_big')?> <span class="odds">1.95</span></div>
|
|
<div onclick="addBet('sum_bs','sum_small',this)" class="bet-cell" style="border:1px solid #F0F0F0;border-radius:8px"><?=$t('sum_small')?> <span class="odds">1.95</span></div>
|
|
<div onclick="addBet('sum_bs','sum_odd',this)" class="bet-cell" style="border:1px solid #F0F0F0;border-radius:8px"><?=$t('sum_odd')?> <span class="odds">1.95</span></div>
|
|
<div onclick="addBet('sum_bs','sum_even',this)" class="bet-cell" style="border:1px solid #F0F0F0;border-radius:8px"><?=$t('sum_even')?> <span class="odds">1.95</span></div>
|
|
</div>
|
|
</div>
|
|
|
|
</div><!-- /bet-content -->
|
|
</div><!-- /bet-layout -->
|
|
|
|
<!-- 筹码+提交栏 -->
|
|
<div class="chip-bar">
|
|
<div onclick="selectChip(10)" class="chip active" style="background:#e74c3c" data-amount="10">10</div>
|
|
<div onclick="selectChip(50)" class="chip" style="background:#3498db" data-amount="50">50</div>
|
|
<div onclick="selectChip(100)" class="chip" style="background:#2ecc71" data-amount="100">100</div>
|
|
<div onclick="selectChip(500)" class="chip" style="background:#9b59b6" data-amount="500">500</div>
|
|
<div onclick="showCustomChip()" class="chip" style="background:#f39c12" data-amount="custom" id="customChipBtn"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#fff" stroke-width="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg></div>
|
|
</div>
|
|
<div id="customChipInput" class="hidden" style="padding:8px 15px;background:#fff;display:flex;gap:8px">
|
|
<input type="number" id="customAmount" min="1" step="1" placeholder="<?=$t('amount')?>" class="input" style="flex:1;text-align:center">
|
|
<button onclick="applyCustomChip()" class="btn btn-primary btn-sm"><?=$t('confirm')?></button>
|
|
</div>
|
|
<div class="submit-bar">
|
|
<div class="info"><?=$t('total')?>: <b id="betTotal">0</b> x<span id="betCount">0</span></div>
|
|
<button onclick="clearBets()" class="btn btn-outline btn-sm"><?=$t('cancel')?></button>
|
|
<button onclick="submitBets()" id="submitBtn" class="btn btn-primary btn-sm"><?=$t('place_bet')?></button>
|
|
</div>
|
|
|
|
<!-- 本期已下注 -->
|
|
<div style="background:#fff;padding:10px 15px;border-top:1px solid #F0F0F0" id="myBetsPanel">
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">
|
|
<span style="color:#999;font-size:12px"><?=$t('current_bets')?></span>
|
|
<span style="color:#999;font-size:11px" id="myBetsCount">0</span>
|
|
</div>
|
|
<div id="myBetsList" style="max-height:140px;overflow-y:auto">
|
|
<div style="color:#999;font-size:12px;text-align:center;padding:8px"><?=$t('no_data')?></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 上期结果面板 -->
|
|
<div class="hidden" style="background:#fff;padding:10px 15px;border-top:1px solid #F0F0F0" id="lastResultPanel">
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">
|
|
<span style="color:#999;font-size:12px"><?=$t('last_result_label')?></span>
|
|
<span style="font-size:12px;font-weight:700" id="lastProfitDisplay"></span>
|
|
</div>
|
|
<div id="lastMyBetsList" style="max-height:128px;overflow-y:auto"></div>
|
|
</div>
|
|
</div><!-- /betPanel -->
|
|
|
|
<!-- 历史记录 -->
|
|
<div style="background:#fff;margin-top:8px;padding:10px 15px;border-top:1px solid #F0F0F0">
|
|
<div style="color:#999;font-size:12px;margin-bottom:6px"><?=$t('history')?></div>
|
|
<div id="historyList" style="font-size:12px;max-height:200px;overflow-y:auto"></div>
|
|
</div>
|
|
|
|
<!-- 底部导航 -->
|
|
<?php $navActive='game'; include __DIR__.'/_nav.php'; ?>
|
|
|
|
<script>
|
|
const GAME_ID=<?=$gameId?>;
|
|
let selectedChip=10,bets={},periodData=null,pollTimer=null;
|
|
const rankNames={1:'<?=$t('champion')?>',2:'<?=$t('runner_up')?>',3:'<?=$t('rank_n',['n'=>3])?>',4:'<?=$t('rank_n',['n'=>4])?>',5:'<?=$t('rank_n',['n'=>5])?>',6:'<?=$t('rank_n',['n'=>6])?>',7:'<?=$t('rank_n',['n'=>7])?>',8:'<?=$t('rank_n',['n'=>8])?>',9:'<?=$t('rank_n',['n'=>9])?>',10:'<?=$t('rank_n',['n'=>10])?>'};
|
|
const dtPairs={1:[1,10],2:[2,9],3:[3,8],4:[4,7],5:[5,6]};
|
|
const I18N={big:'<?=$t('big')?>',small:'<?=$t('small')?>',odd:'<?=$t('odd')?>',even:'<?=$t('even')?>',dragon:'<?=$t('dragon')?>',tiger:'<?=$t('tiger')?>',sum:'<?=$t('sum')?>',sum_big:'<?=$t('sum_big')?>',sum_small:'<?=$t('sum_small')?>',sum_odd:'<?=$t('sum_odd')?>',sum_even:'<?=$t('sum_even')?>',car:'<?=$t('car_no')?>',no_bets:'<?=$t('no_data')?>',total:'<?=$t('total')?>',items:'',profit:'<?=$t('win')?>',loss:'<?=$t('lose')?>',draw:'<?=$t('pending')?>',net_err:'<?=$t('error')?>',rank_prefix:'<?=$t('rank_n',['n'=>''])?>',drawing:'<?=$t('drawing')?>',settled_label:'<?=$t('settled')?>'};
|
|
function formatBetLabel(type,value){
|
|
let m;
|
|
if(type==='rank'&&(m=value.match(/^rank(\d+)_(\d+)$/))){return(rankNames[+m[1]]||I18N.rank_prefix+m[1])+' #'+m[2];}
|
|
if(type==='bs'&&(m=value.match(/^rank(\d+)_(big|small)$/))){return(rankNames[+m[1]]||I18N.rank_prefix+m[1])+' '+(m[2]==='big'?I18N.big:I18N.small);}
|
|
if(type==='oe'&&(m=value.match(/^rank(\d+)_(odd|even)$/))){return(rankNames[+m[1]]||I18N.rank_prefix+m[1])+' '+(m[2]==='odd'?I18N.odd:I18N.even);}
|
|
if(type==='dt'&&(m=value.match(/^dt(\d+)_(dragon|tiger)$/))){const p=dtPairs[+m[1]]||[0,0];return(rankNames[p[0]]||p[0])+'vs'+(rankNames[p[1]]||p[1])+' '+(m[2]==='dragon'?I18N.dragon:I18N.tiger);}
|
|
if(type==='sum'&&(m=value.match(/^sum_(\d+)$/))){return I18N.sum+' '+m[1];}
|
|
if(type==='sum_bs'){const sm={sum_big:I18N.sum_big,sum_small:I18N.sum_small,sum_odd:I18N.sum_odd,sum_even:I18N.sum_even};return sm[value]||value;}
|
|
return type+':'+value;
|
|
}
|
|
function selectChip(a){selectedChip=a;document.querySelectorAll('.chip').forEach(c=>{const amt=c.dataset.amount;c.classList.toggle('active',amt!=='custom'&&parseInt(amt)===a);});document.getElementById('customChipInput').classList.add('hidden');}
|
|
function showCustomChip(){const b=document.getElementById('customChipInput');b.classList.toggle('hidden');if(!b.classList.contains('hidden'))document.getElementById('customAmount').focus();}
|
|
function applyCustomChip(){const v=parseInt(document.getElementById('customAmount').value);if(!v||v<=0){showToast('<?=$t('error')?>: > 0','error');return;}selectedChip=v;document.querySelectorAll('.chip').forEach(c=>c.classList.remove('active'));const cb=document.getElementById('customChipBtn');cb.classList.add('active');cb.textContent=v>=1000?(v/1000)+'k':v;document.getElementById('customChipInput').classList.add('hidden');}
|
|
function addBet(type,target,el){if(periodData&&periodData.status!=='pending'){showToast('<?=$t('period_closed')?>','error');return;}const key=type+'_'+target;if(!bets[key])bets[key]={type,value:target,amount:0,el:el};bets[key].amount+=selectedChip;el.classList.add('selected');let badge=el.querySelector('.bet-amount-badge');if(!badge){badge=document.createElement('span');badge.className='bet-amount-badge';el.appendChild(badge);}badge.textContent=bets[key].amount>=1000?(bets[key].amount/1000).toFixed(1)+'k':bets[key].amount;updateBetSummary();}
|
|
function clearBets(){bets={};document.querySelectorAll('.bet-btn.selected,.bet-cell.selected').forEach(e=>{e.classList.remove('selected');const b=e.querySelector('.bet-amount-badge');if(b)b.remove();});updateBetSummary();}
|
|
function updateBetSummary(){let total=0,count=0;Object.values(bets).forEach(b=>{total+=b.amount;count++;});document.getElementById('betTotal').textContent=total.toFixed(0);document.getElementById('betCount').textContent=count;}
|
|
async function submitBets(){const arr=Object.values(bets);if(!arr.length){showToast('<?=$t('place_bet')?>','error');return;}if(!periodData||!periodData.period_number){showToast('<?=$t('waiting')?>','error');return;}try{const r=await fetch('/api/bet',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({game_id:GAME_ID,period_number:periodData.period_number,bets:arr})});const d=await r.json();if(d.success){showToast(d.message,'ok');document.getElementById('balanceDisplay').textContent=parseFloat(d.new_balance).toFixed(2);clearBets();pollPeriod();}else{showToast(d.message,'error');}}catch(e){showToast(I18N.net_err,'error');}}
|
|
function showBetTab(tab,btn){['Rank','Bs','Sum'].forEach(t=>{const p=document.getElementById('panel'+t);if(p)p.classList.toggle('hidden',t.toLowerCase()!==tab);});document.querySelectorAll('.side-menu .mi').forEach(m=>m.classList.remove('active'));if(btn)btn.classList.add('active');}
|
|
|
|
// ===== 赛车动画系统(完整保留) =====
|
|
let raceAnimState='idle',lastAnimatedResult=null,idleAnimTimer=null,carPositions={},raceFrameTimer=null;
|
|
const FINISH_LINE=88,START_LINE=3;
|
|
for(let i=1;i<=10;i++)carPositions[i]=START_LINE;
|
|
|
|
function setCarPosition(n,pct,dur){const c=document.getElementById('carContainer'+n);const b=document.getElementById('bar'+n);if(!c)return;c.style.transition='left '+dur+'s ease';c.style.left=pct+'%';if(b){b.style.transition='width '+dur+'s ease';b.style.width=Math.min(pct+3,92)+'%';}carPositions[n]=pct;}
|
|
function setCarMoving(n,moving,sprinting){const c=document.getElementById('carContainer'+n);if(!c)return;c.classList.toggle('moving',moving);c.classList.toggle('sprinting',!!sprinting);c.classList.remove('ready');}
|
|
function setRankBadge(n,rank){const badge=document.getElementById('rankBadge'+n);if(!badge)return;const c=document.getElementById('carContainer'+n);if(rank>0){const labels=['1st','2nd','3rd','4th','5th','6th','7th','8th','9th','10th'];badge.textContent=labels[rank-1]||rank;if(c)c.classList.add('show-rank');}else{badge.textContent='';if(c)c.classList.remove('show-rank');}}
|
|
// ANIM: idle
|
|
function startIdleAnimation(){if(idleAnimTimer)return;raceAnimState='waiting';for(let i=1;i<=10;i++){setCarPosition(i,START_LINE+Math.random()*5,0.5);setCarMoving(i,true,false);setRankBadge(i,0);}idleAnimTimer=setInterval(function(){if(raceAnimState!=='waiting'){stopIdleAnimation();return;}for(let i=1;i<=10;i++){if(Math.random()<0.5){const cur=carPositions[i]||START_LINE;const target=Math.max(START_LINE,Math.min(15,cur+(Math.random()-0.5)*6));setCarPosition(i,target,0.5+Math.random()*0.3);}}},800);}
|
|
function stopIdleAnimation(){if(idleAnimTimer){clearInterval(idleAnimTimer);idleAnimTimer=null;}if(raceFrameTimer){clearInterval(raceFrameTimer);raceFrameTimer=null;}for(let i=1;i<=10;i++){setCarMoving(i,false,false);const c=document.getElementById('carContainer'+i);if(c)c.classList.remove('ready');}}
|
|
function startReadyAnimation(){stopIdleAnimation();raceAnimState='ready';for(let i=1;i<=10;i++){setCarPosition(i,START_LINE,0.8);setCarMoving(i,true,false);setRankBadge(i,0);const c=document.getElementById('carContainer'+i);if(c)c.classList.add('ready');}}
|
|
// ANIM: race
|
|
function animateRace(result){if(!result||result.length<10)return;const rk=result.join(',');if(lastAnimatedResult===rk&&raceAnimState==='finished')return;lastAnimatedResult=rk;stopIdleAnimation();raceAnimState='racing';const rankMap={};for(let i=0;i<10;i++)rankMap[result[i]]=i;const carSpeeds={},carTargets={};for(let i=1;i<=10;i++){const rank=rankMap[i]!==undefined?rankMap[i]:9;carSpeeds[i]=1.0-rank*0.06;carTargets[i]=FINISH_LINE-rank*7.5;setCarMoving(i,true,true);setRankBadge(i,0);}
|
|
for(let i=1;i<=10;i++){const s=carSpeeds[i];setCarPosition(i,15+s*20+Math.random()*5,1.2+Math.random()*0.3);}
|
|
setTimeout(function(){if(raceAnimState!=='racing')return;for(let i=1;i<=10;i++){const s=carSpeeds[i];setCarPosition(i,35+s*30+Math.random()*5,1.5+Math.random()*0.5);}},1500);
|
|
setTimeout(function(){if(raceAnimState!=='racing')return;for(let i=1;i<=10;i++){const dur=1.5+rankMap[i]*0.05+Math.random()*0.2;setCarPosition(i,carTargets[i],dur);}},3500);
|
|
setTimeout(function(){if(raceAnimState!=='racing')return;raceAnimState='finished';for(let i=1;i<=10;i++){setCarMoving(i,false,false);const rank=rankMap[i]!==undefined?rankMap[i]+1:0;setRankBadge(i,rank);}},5500);}
|
|
function resetRace(){stopIdleAnimation();raceAnimState='idle';lastAnimatedResult=null;for(let i=1;i<=10;i++){const c=document.getElementById('carContainer'+i);const b=document.getElementById('bar'+i);if(c){c.style.transition='left 0.5s ease';c.style.left=START_LINE+'%';c.classList.remove('moving','sprinting','ready','show-rank');}if(b){b.style.transition='width 0.5s ease';b.style.width='5%';}carPositions[i]=START_LINE;setRankBadge(i,0);}}
|
|
function renderResult(result,container){if(!result)return;const el=document.getElementById(container);el.innerHTML='';['rank_1','rank_2','rank_3','rank_4','rank_5','rank_6','rank_7','rank_8','rank_9','rank_10'].forEach(k=>{const v=result[k];if(!v)return;const d=document.createElement('div');d.className='car c'+v;d.style.cssText='width:28px;height:28px;font-size:11px';d.textContent=v;el.appendChild(d);});
|
|
const tags=document.getElementById('lastResultTags');if(!tags)return;tags.innerHTML='';const r1=+result.rank_1,r2=+result.rank_2;if(r1&&r2){const sum=r1+r2;const items=[sum,(sum>=12?I18N.big:I18N.small),(sum%2?I18N.odd:I18N.even)];for(let i=1;i<=5;i++){const a=+result['rank_'+i],b=+result['rank_'+(11-i)];if(a&&b)items.push(a>b?I18N.dragon:I18N.tiger);}items.forEach(t=>{const s=document.createElement('span');s.textContent=t;tags.appendChild(s);});}}
|
|
// 投注记录渲染
|
|
function renderMyBets(myBets){const list=document.getElementById('myBetsList');const countEl=document.getElementById('myBetsCount');if(!myBets||!myBets.length){list.innerHTML='<div style="color:var(--text3);font-size:12px;text-align:center;padding:8px">'+I18N.no_bets+'</div>';countEl.textContent='0';return;}countEl.textContent=myBets.length;let total=0,html='';myBets.forEach(b=>{const label=formatBetLabel(b.bet_type,b.bet_value);const amt=parseFloat(b.amount);total+=amt;const sl=b.status==='pending'?'<span style="color:var(--warn)">'+I18N.draw+'</span>':b.status==='win'?'<span style="color:var(--success)">+'+parseFloat(b.win_amount).toFixed(0)+'</span>':'<span style="color:var(--danger)">-'+amt.toFixed(0)+'</span>';html+='<div style="display:flex;justify-content:space-between;align-items:center;font-size:12px;padding:4px 0;border-bottom:1px solid var(--border)"><span style="color:var(--text2)">'+label+'</span><span style="color:var(--primary)">'+amt.toFixed(0)+' <span style="color:var(--text3)">x'+b.odds+'</span></span>'+sl+'</div>';});html+='<div style="display:flex;justify-content:space-between;font-size:12px;padding-top:4px;color:var(--text3)"><span>'+I18N.total+'</span><span style="color:var(--primary);font-weight:700">'+total.toFixed(0)+'</span></div>';list.innerHTML=html;}
|
|
function renderLastMyBets(lastMyBets){const panel=document.getElementById('lastResultPanel');const list=document.getElementById('lastMyBetsList');const profitEl=document.getElementById('lastProfitDisplay');if(!lastMyBets||!lastMyBets.length){panel.classList.add('hidden');return;}panel.classList.remove('hidden');let totalWin=0,totalBet=0,html='';lastMyBets.forEach(b=>{const label=formatBetLabel(b.bet_type,b.bet_value);const amt=parseFloat(b.amount);totalBet+=amt;if(b.status==='win'){const win=parseFloat(b.win_amount);totalWin+=win+amt;html+='<div style="display:flex;justify-content:space-between;font-size:12px;padding:2px 0"><span style="color:var(--text2)">'+label+'</span><span style="color:var(--success)">+'+win.toFixed(0)+'</span></div>';}else{html+='<div style="display:flex;justify-content:space-between;font-size:12px;padding:2px 0"><span style="color:var(--text2)">'+label+'</span><span style="color:var(--danger)">-'+amt.toFixed(0)+'</span></div>';}});list.innerHTML=html;const profit=totalWin-totalBet;if(profit>0){profitEl.style.color='var(--success)';profitEl.textContent='+'+profit.toFixed(0);}else if(profit<0){profitEl.style.color='var(--danger)';profitEl.textContent=profit.toFixed(0);}else{profitEl.style.color='var(--text3)';profitEl.textContent='0';}}
|
|
// 倒计时
|
|
let localCountdown=0,lastPollStatus='',lastPeriodId=0,drawnResultShown=false;
|
|
setInterval(function(){if(localCountdown>0)localCountdown--;const m=Math.floor(localCountdown/60),s=localCountdown%60;document.getElementById('countdown').textContent=localCountdown>0?m+':'+(s<10?'0':'')+s:'--';},1000);
|
|
// 轮询期号
|
|
async function pollPeriod(){try{const r=await fetch('/api/period/current?game_id='+GAME_ID);const d=await r.json();if(!d.success)return;periodData=d.data;
|
|
const serverRemaining=periodData.remaining_seconds||0;const periodChanged=(periodData.id&&periodData.id!==lastPeriodId);const statusChanged=(periodData.status!==lastPollStatus);const driftTooMuch=Math.abs(localCountdown-serverRemaining)>3;
|
|
if(periodChanged||statusChanged||driftTooMuch||localCountdown<=0)localCountdown=serverRemaining;
|
|
if(d.balance!==null&&d.balance!==undefined)document.getElementById('balanceDisplay').textContent=parseFloat(d.balance).toFixed(2);
|
|
// 状态徽章
|
|
const badge=document.getElementById('statusBadge');const statusMap={pending:['<?=$t('betting')?>','seal-status','color:var(--success)'],locked:['<?=$t('sealed')?>','seal-status','color:var(--danger)'],drawn:[I18N.drawing,'seal-status','color:var(--warn)'],settled:[I18N.settled_label,'seal-status','color:var(--warn)']};const s=statusMap[periodData.status]||statusMap.pending;badge.textContent=s[0];badge.className=s[1];badge.style.cssText=s[2];
|
|
// 期号
|
|
const fullPn=periodData.period_number||'---';const shortPn=fullPn.length>8?fullPn.slice(0,-4)+'-'+fullPn.slice(-4):fullPn;const pnEl=document.getElementById('periodNum');pnEl.textContent=shortPn;pnEl.title=fullPn;
|
|
// 提取结果
|
|
function extractResultArr(res){if(!res)return null;const arr=[res.rank_1,res.rank_2,res.rank_3,res.rank_4,res.rank_5,res.rank_6,res.rank_7,res.rank_8,res.rank_9,res.rank_10];return arr.every(v=>v)?arr:null;}
|
|
// 投注中→待机动画
|
|
if(periodData.status==='pending'){if(statusChanged||periodChanged){if(d.last_result&&periodChanged&&raceAnimState!=='finished'){const ra=extractResultArr(d.last_result);if(ra){animateRace(ra);renderResult(d.last_result,'lastResult');const lp=d.last_result.period_number||'';document.getElementById('lastPeriodNum').textContent=lp.length>4?'#'+lp.slice(-4):lp;setTimeout(function(){resetRace();setTimeout(startIdleAnimation,500);},6500);}else{resetRace();setTimeout(startIdleAnimation,600);}}else{resetRace();setTimeout(startIdleAnimation,600);}}else if(raceAnimState==='idle'){startIdleAnimation();}}
|
|
// 封盘→比赛或预备
|
|
if(periodData.status==='locked'){const raceRes=extractResultArr(d.race_result);if(raceRes&&raceAnimState!=='racing'&&raceAnimState!=='finished'){animateRace(raceRes);}else if(!raceRes&&(statusChanged||(raceAnimState!=='ready'&&raceAnimState!=='racing'&&raceAnimState!=='finished'))){startReadyAnimation();}}
|
|
// 开奖/结算
|
|
if(d.last_result&&(periodData.status==='drawn'||periodData.status==='settled')){if(statusChanged||periodChanged){const lp=d.last_result.period_number||'';document.getElementById('lastPeriodNum').textContent=lp.length>4?'#'+lp.slice(-4):lp;const ra=extractResultArr(d.last_result);if(ra&&raceAnimState!=='finished')animateRace(ra);setTimeout(()=>{renderResult(d.last_result,'lastResult');},5500);}}
|
|
// 首次加载
|
|
if(d.last_result&&!lastAnimatedResult&&raceAnimState==='idle'){const ra=extractResultArr(d.last_result);if(ra){const lp=d.last_result.period_number||'';document.getElementById('lastPeriodNum').textContent=lp.length>4?'#'+lp.slice(-4):lp;animateRace(ra);setTimeout(()=>{renderResult(d.last_result,'lastResult');if(periodData.status==='pending')setTimeout(function(){resetRace();setTimeout(startIdleAnimation,500);},1500);},5500);}}
|
|
renderMyBets(d.my_bets||[]);renderLastMyBets(d.last_my_bets||[]);
|
|
// 历史
|
|
if(d.history&&periodChanged){const hl=document.getElementById('historyList');hl.innerHTML='';d.history.forEach(h=>{const row=document.createElement('div');row.className='card';row.style.cssText='padding:6px 8px;margin-bottom:4px';const pn=h.period_number||'';const sp=pn.length>4?'#'+pn.slice(-4):pn;let balls='';['rank_1','rank_2','rank_3','rank_4','rank_5','rank_6','rank_7','rank_8','rank_9','rank_10'].forEach(k=>{if(h[k])balls+='<div class="car c'+h[k]+'" style="width:18px;height:18px;font-size:8px;flex-shrink:0">'+h[k]+'</div>';});row.innerHTML='<div style="color:var(--text3);font-size:10px;margin-bottom:2px">'+sp+'</div><div style="display:flex;gap:2px;flex-wrap:wrap">'+balls+'</div>';hl.appendChild(row);});}
|
|
lastPollStatus=periodData.status;lastPeriodId=periodData.id||0;
|
|
}catch(e){console.error('pollPeriod error:',e);}}
|
|
function showToast(msg,type){const t=document.createElement('div');t.style.cssText='position:fixed;top:80px;left:50%;transform:translateX(-50%);padding:10px 24px;border-radius:24px;font-size:13px;z-index:999;color:#fff;box-shadow:0 4px 16px rgba(0,0,0,.15)';t.style.background=type==='ok'?'var(--success)':'var(--danger)';t.textContent=msg;document.body.appendChild(t);setTimeout(()=>t.remove(),2500);}
|
|
async function setLang(l){await fetch('/api/set-lang',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({lang:l})});location.reload();}
|
|
pollPeriod();
|
|
(function dynamicPoll(){const delay=(lastPollStatus==='locked'||lastPollStatus==='drawn'||lastPollStatus==='settled')?1500:3000;setTimeout(function(){pollPeriod().then(dynamicPoll).catch(dynamicPoll);},delay);})();
|
|
</script>
|
|
</body></html>
|