新增:双色球彩票前台 UI(阶段二)
🎟️ lottery-panel.blade.php 彩票面板 - 红球 1~12(12宫格选3)/ 蓝球 1~6(骰子点数选1) - 购物车机制:可加入多注后一次性购买 - 机选按钮(单注/3注) - 本期我的购票记录展示(含中奖标记) - 最近8期历史开奖号码表格 - 规则折叠说明(奖级表格) - 停售/已开奖状态自动切换 - 内联购票结果提示(3秒自动消失) 🎮 游戏大厅集成 - game-hall 注入 lottery 开关状态 - GAME_HALL_GAMES 追加双色球卡片(动态展示奖池/倒计时/超级期徽章) - frame.blade.php 引入 lottery-panel 🗺️ 路由 /games/enabled 已含 lottery 键
This commit is contained in:
@@ -147,6 +147,8 @@
|
||||
@include('chat.partials.horse-race-panel')
|
||||
{{-- ═══════════ 神秘占卜游戏面板 ═══════════ --}}
|
||||
@include('chat.partials.fortune-panel')
|
||||
{{-- ═══════════ 双色球彩票面板 ═══════════ --}}
|
||||
@include('chat.partials.lottery-panel')
|
||||
{{-- ═══════════ 娱乐游戏大厅弹窗 ═══════════ --}}
|
||||
@include('chat.partials.game-hall')
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
'horse_racing' => \App\Models\GameConfig::isEnabled('horse_racing'),
|
||||
'fortune_telling' => \App\Models\GameConfig::isEnabled('fortune_telling'),
|
||||
'fishing' => \App\Models\GameConfig::isEnabled('fishing'),
|
||||
'lottery' => \App\Models\GameConfig::isEnabled('lottery'),
|
||||
];
|
||||
@endphp
|
||||
<script>
|
||||
@@ -254,6 +255,47 @@
|
||||
}),
|
||||
btnLabel: () => '🎣 去钓鱼',
|
||||
},
|
||||
{
|
||||
id: 'lottery',
|
||||
name: '🎟️ 双色球',
|
||||
desc: '每日20:00开奖,选3红1蓝,按奖池比例派奖,无一等奖滚存累积',
|
||||
accentColor: '#dc2626',
|
||||
fetchUrl: '/lottery/current',
|
||||
openFn: () => {
|
||||
closeGameHall();
|
||||
if (typeof openLotteryPanel === 'function') openLotteryPanel();
|
||||
},
|
||||
renderStatus: (data) => {
|
||||
if (!data?.issue) return {
|
||||
badge: '⏸ 等待开期',
|
||||
badgeStyle: 'background:#e8f0f8; color:#336699; border:1px solid #b8d0e8',
|
||||
detail: '暂无进行中期次'
|
||||
};
|
||||
const iss = data.issue;
|
||||
const pool = Number(iss.pool_amount || 0).toLocaleString();
|
||||
if (data.is_open) {
|
||||
const h = Math.floor(iss.seconds_left / 3600);
|
||||
const m = Math.floor((iss.seconds_left % 3600) / 60);
|
||||
const timeStr = h > 0 ? `${h}h ${m}m` : `${m}m`;
|
||||
return {
|
||||
badge: iss.is_super_issue ? '🎊 超级期购票中' : '🟢 购票中',
|
||||
badgeStyle: 'background:#fef2f2; color:#b91c1c; border:1px solid #fca5a5',
|
||||
detail: `💰 奖池 ${pool} 金 | 距开奖 ${timeStr} | 第 ${iss.issue_no} 期`
|
||||
};
|
||||
}
|
||||
if (iss.status === 'settled') return {
|
||||
badge: '✅ 已开奖',
|
||||
badgeStyle: 'background:#d1fae5; color:#065f46; border:1px solid #6ee7b7',
|
||||
detail: '本期已开奖,下期购票中'
|
||||
};
|
||||
return {
|
||||
badge: '🔴 已停售',
|
||||
badgeStyle: 'background:#fee2e2; color:#b91c1c; border:1px solid #fecaca',
|
||||
detail: `💰 奖池 ${pool} 金 | 等待开奖中…`
|
||||
};
|
||||
},
|
||||
btnLabel: (data) => data?.is_open ? '🎟️ 立即购票' : '📊 查看结果',
|
||||
},
|
||||
|
||||
|
||||
];
|
||||
|
||||
@@ -0,0 +1,618 @@
|
||||
{{--
|
||||
文件功能:双色球彩票前台弹窗组件
|
||||
|
||||
选号购票面板:
|
||||
- 红球 1~12 选3 + 蓝球 1~6 选1,可机选
|
||||
- 支持多注购买(单次最多10注)和追加购买
|
||||
- 展示当期奖池、倒计时、本期已购记录
|
||||
- 展示最近10期历史开奖号码
|
||||
- 规则折叠说明卡片
|
||||
|
||||
@author ChatRoom Laravel
|
||||
@version 1.0.0
|
||||
--}}
|
||||
|
||||
{{-- ─── 彩票面板遮罩 ─── --}}
|
||||
<div id="lottery-panel" x-data="lotteryPanel()" x-show="show" x-cloak
|
||||
style="position:fixed; inset:0; background:rgba(0,0,0,.55); z-index:9935;
|
||||
display:flex; align-items:center; justify-content:center;">
|
||||
|
||||
<div x-transition:enter="transition ease-out duration-250" x-transition:enter-start="opacity-0 scale-95"
|
||||
x-transition:enter-end="opacity-100 scale-100" x-transition:leave="transition ease-in duration-180"
|
||||
x-transition:leave-start="opacity-100 scale-100" x-transition:leave-end="opacity-0 scale-95"
|
||||
style="width:480px; max-width:96vw; max-height:92vh; border-radius:10px; overflow:hidden;
|
||||
box-shadow:0 8px 32px rgba(0,0,0,.35); font-family:'Microsoft YaHei',SimSun,sans-serif;
|
||||
background:#fff; display:flex; flex-direction:column;">
|
||||
|
||||
{{-- ─── 标题栏 ─── --}}
|
||||
<div
|
||||
style="background:linear-gradient(135deg,#dc2626,#ef4444); padding:10px 16px;
|
||||
display:flex; align-items:center; justify-content:space-between; flex-shrink:0;">
|
||||
<div>
|
||||
<div style="color:#fff; font-weight:bold; font-size:14px;">🎟️ 双色球彩票</div>
|
||||
<div style="color:rgba(255,255,255,.8); font-size:11px; margin-top:1px;">
|
||||
第 <span x-text="issueNo">--</span> 期 |
|
||||
<span x-text="isOpen ? '🟢 购票中' : (status === 'settled' ? '✅ 已开奖' : '🔴 已停售')"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align:right;">
|
||||
{{-- 奖池金额 --}}
|
||||
<div style="color:#fef08a; font-size:18px; font-weight:900; line-height:1;"
|
||||
x-text="'💰 ' + poolAmount.toLocaleString()"></div>
|
||||
{{-- 超级期标记 --}}
|
||||
<div x-show="isSuperIssue" style="color:#fcd34d; font-size:10px; font-weight:bold; margin-top:2px;">🎊
|
||||
超级期</div>
|
||||
{{-- 距开奖倒计时 --}}
|
||||
<div x-show="!isSuperIssue && isOpen"
|
||||
style="color:rgba(255,255,255,.8); font-size:10px; margin-top:2px;" x-text="'距开奖 ' + countdownText">
|
||||
</div>
|
||||
</div>
|
||||
<span onclick="closeLotteryPanel()"
|
||||
style="cursor:pointer; font-size:20px; color:#fff; opacity:.8; line-height:1; margin-left:12px;"
|
||||
onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=.8">×</span>
|
||||
</div>
|
||||
|
||||
{{-- ─── 内容区(可滚动)─── --}}
|
||||
<div style="flex:1; overflow-y:auto; background:#fafafa;">
|
||||
|
||||
{{-- 加载中 --}}
|
||||
<div x-show="loading" style="text-align:center; padding:40px 0; color:#dc2626; font-size:13px;">
|
||||
<div style="font-size:28px; margin-bottom:8px;">⏳</div>加载中…
|
||||
</div>
|
||||
|
||||
<div x-show="!loading">
|
||||
|
||||
{{-- ─── 规则说明折叠区 ─── --}}
|
||||
<div style="border-bottom:1px solid #fee2e2; background:#fff7f7;">
|
||||
<div @click="ruleOpen=!ruleOpen"
|
||||
style="padding:8px 14px; display:flex; align-items:center; justify-content:space-between;
|
||||
cursor:pointer; font-size:12px; color:#b91c1c; font-weight:bold; user-select:none;">
|
||||
<span>📖 玩法规则</span>
|
||||
<span x-text="ruleOpen ? '▲ 收起' : '▼ 展开'" style="font-size:10px; opacity:.7;"></span>
|
||||
</div>
|
||||
<div x-show="ruleOpen" x-collapse
|
||||
style="padding:0 14px 10px; font-size:11px; color:#6b7280; line-height:1.8;">
|
||||
<div>🔴 <b>红球</b> 1~12 选3 · 🎲 <b>蓝球</b> 1~6 选1,每注 <b style="color:#dc2626;">100 金币</b></div>
|
||||
<div>每期单人最多购 <b>50 注</b>,可随时追加(停售前均可)</div>
|
||||
<table style="width:100%; margin-top:6px; border-collapse:collapse;">
|
||||
<tr style="background:#fee2e2;">
|
||||
<th style="padding:3px 6px; text-align:left; font-size:10px;">奖级</th>
|
||||
<th style="padding:3px 6px; text-align:left; font-size:10px;">条件</th>
|
||||
<th style="padding:3px 6px; text-align:left; font-size:10px;">奖励</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:2px 6px;">🏆 一等奖</td>
|
||||
<td>3红+蓝</td>
|
||||
<td style="color:#dc2626;">奖池 60% 均分</td>
|
||||
</tr>
|
||||
<tr style="background:#fff7f7;">
|
||||
<td style="padding:2px 6px;">🥇 二等奖</td>
|
||||
<td>3红</td>
|
||||
<td style="color:#dc2626;">奖池 20% 均分</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:2px 6px;">🥈 三等奖</td>
|
||||
<td>2红+蓝</td>
|
||||
<td>奖池 10% 均分</td>
|
||||
</tr>
|
||||
<tr style="background:#fff7f7;">
|
||||
<td style="padding:2px 6px;">🥉 四等奖</td>
|
||||
<td>2红</td>
|
||||
<td>固定 150 金/注</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding:2px 6px;">🎫 五等奖</td>
|
||||
<td>1红+蓝</td>
|
||||
<td>固定 50 金/注</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="margin-top:6px; color:#9ca3af;">无一等奖 → 奖池滚存至下期;连续3期无人中 → 超级期系统注入奖池</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 已开奖结果展示 --}}
|
||||
<div x-show="status === 'settled' && drawRed1"
|
||||
style="padding:10px 14px; background:#fff5f5; border-bottom:1px solid #fee2e2; text-align:center;">
|
||||
<div style="font-size:12px; color:#b91c1c; font-weight:bold; margin-bottom:6px;">🎊 开奖结果</div>
|
||||
<div style="display:flex; justify-content:center; gap:8px; align-items:center; flex-wrap:wrap;">
|
||||
<template x-for="n in [drawRed1, drawRed2, drawRed3]" :key="n">
|
||||
<span
|
||||
style="width:32px; height:32px; border-radius:50%; background:#dc2626;
|
||||
color:#fff; font-weight:bold; font-size:14px;
|
||||
display:flex; align-items:center; justify-content:center;"
|
||||
x-text="String(n).padStart(2,'0')"></span>
|
||||
</template>
|
||||
<span style="color:#6b7280; font-size:18px; font-weight:bold;">+</span>
|
||||
<span
|
||||
style="width:32px; height:32px; border-radius:50%; background:#2563eb;
|
||||
color:#fff; font-weight:bold; font-size:14px;
|
||||
display:flex; align-items:center; justify-content:center;"
|
||||
x-text="drawBlue"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ─── 选号区 ─── --}}
|
||||
<div x-show="isOpen" style="padding:12px 14px;">
|
||||
|
||||
{{-- 红球选择 --}}
|
||||
<div style="margin-bottom:10px;">
|
||||
<div style="font-size:12px; font-weight:bold; color:#b91c1c; margin-bottom:6px;">
|
||||
🔴 红球(已选 <span x-text="selectedReds.length" style="color:#dc2626;"></span>/3)
|
||||
</div>
|
||||
<div style="display:grid; grid-template-columns:repeat(6,1fr); gap:5px;">
|
||||
<template x-for="n in 12" :key="n">
|
||||
<button @click="toggleRed(n)"
|
||||
:style="selectedReds.includes(n) ?
|
||||
'background:#dc2626; color:#fff; border:2px solid #b91c1c; font-weight:bold;' :
|
||||
'background:#fff; color:#dc2626; border:2px solid #fca5a5;'"
|
||||
style="border-radius:50%; width:38px; height:38px; font-size:13px;
|
||||
cursor:pointer; transition:all .15s; display:flex; align-items:center; justify-content:center;"
|
||||
x-text="String(n).padStart(2,'0')">
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 蓝球选择 --}}
|
||||
<div style="margin-bottom:12px;">
|
||||
<div style="font-size:12px; font-weight:bold; color:#1d4ed8; margin-bottom:6px;">
|
||||
🎲 蓝球(骰子点数,已选 <span x-text="selectedBlue ?? '—'" style="color:#2563eb;"></span>)
|
||||
</div>
|
||||
<div style="display:flex; gap:6px; flex-wrap:wrap;">
|
||||
<template x-for="n in 6" :key="n">
|
||||
<button @click="selectedBlue = (selectedBlue === n ? null : n)"
|
||||
:style="selectedBlue === n ?
|
||||
'background:#2563eb; color:#fff; border:2px solid #1d4ed8; font-weight:bold;' :
|
||||
'background:#fff; color:#2563eb; border:2px solid #93c5fd;'"
|
||||
style="border-radius:50%; width:38px; height:38px; font-size:16px;
|
||||
cursor:pointer; transition:all .15s;"
|
||||
x-text="['🎲','⚀','⚁','⚂','⚃','⚄','⚅'][n]">
|
||||
</button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- 当前选号预览 --}}
|
||||
<div x-show="selectedReds.length > 0 || selectedBlue"
|
||||
style="background:#fff7f7; border:1px dashed #fca5a5; border-radius:8px;
|
||||
padding:8px 12px; margin-bottom:10px; font-size:12px; color:#6b7280;">
|
||||
已选:
|
||||
<template x-for="n in [...selectedReds].sort((a,b)=>a-b)" :key="n">
|
||||
<span
|
||||
style="display:inline-block; width:26px; height:26px; border-radius:50%; background:#dc2626;
|
||||
color:#fff; font-size:11px; font-weight:bold; text-align:center; line-height:26px; margin:0 2px;"
|
||||
x-text="String(n).padStart(2,'0')"></span>
|
||||
</template>
|
||||
<span x-show="selectedBlue"
|
||||
style="display:inline-block; width:26px; height:26px; border-radius:50%; background:#2563eb;
|
||||
color:#fff; font-size:11px; font-weight:bold; text-align:center; line-height:26px; margin-left:6px;"
|
||||
x-text="selectedBlue">
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{{-- 操作按钮行(机选 + 清除 + 加入购物车 + 立即购买) --}}
|
||||
<div style="display:flex; gap:6px; margin-bottom:10px;">
|
||||
<button @click="doQuickPick(1)"
|
||||
style="flex:1; padding:8px; background:#f3f4f6; border:1px solid #d1d5db;
|
||||
border-radius:6px; font-size:12px; color:#374151; cursor:pointer;">
|
||||
🎲 机选
|
||||
</button>
|
||||
<button @click="selectedReds=[]; selectedBlue=null;"
|
||||
style="flex:1; padding:8px; background:#f3f4f6; border:1px solid #d1d5db;
|
||||
border-radius:6px; font-size:12px; color:#374151; cursor:pointer;">
|
||||
🗑 清除
|
||||
</button>
|
||||
<button @click="addToCart()"
|
||||
style="flex:1; padding:8px; background:#fef3c7; border:1px solid #fde68a;
|
||||
border-radius:6px; font-size:12px; color:#92400e; cursor:pointer; font-weight:bold;">
|
||||
➕ 加入
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{{-- 购物车(待购清单) --}}
|
||||
<div x-show="cart.length > 0"
|
||||
style="background:#fff7f7; border:1px solid #fecaca; border-radius:8px; padding:10px; margin-bottom:10px;">
|
||||
<div style="font-size:12px; font-weight:bold; color:#b91c1c; margin-bottom:6px;">
|
||||
📋 待购清单(<span x-text="cart.length"></span> 注,
|
||||
💰 <span x-text="cart.length * 100"></span> 金币)
|
||||
</div>
|
||||
<div style="max-height:100px; overflow-y:auto;">
|
||||
<template x-for="(item, idx) in cart" :key="idx">
|
||||
<div
|
||||
style="display:flex; align-items:center; justify-content:space-between;
|
||||
font-size:11px; color:#6b7280; padding:2px 0; border-bottom:1px dashed #fee2e2;">
|
||||
<span>
|
||||
注<span x-text="idx+1"></span>:
|
||||
<template x-for="r in item.reds" :key="r">
|
||||
<span
|
||||
style="display:inline-block; width:20px; height:20px; border-radius:50%; background:#dc2626;
|
||||
color:#fff; font-size:10px; text-align:center; line-height:20px; margin:0 1px;"
|
||||
x-text="String(r).padStart(2,'0')"></span>
|
||||
</template>
|
||||
<span
|
||||
style="display:inline-block; width:20px; height:20px; border-radius:50%; background:#2563eb;
|
||||
color:#fff; font-size:10px; text-align:center; line-height:20px; margin-left:4px;"
|
||||
x-text="item.blue"></span>
|
||||
</span>
|
||||
<button @click="cart.splice(idx,1)"
|
||||
style="color:#dc2626; background:none; border:none; cursor:pointer; font-size:12px;">✕</button>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<button @click="submitCart()" :disabled="buying"
|
||||
style="width:100%; margin-top:8px; padding:9px; background:linear-gradient(135deg,#dc2626,#ef4444);
|
||||
color:#fff; border:none; border-radius:6px; font-size:13px; font-weight:bold;
|
||||
cursor:pointer; transition:opacity .15s;"
|
||||
:style="buying ? 'opacity:.6;cursor:not-allowed;' : ''">
|
||||
<span
|
||||
x-text="buying ? '购买中…' : '🎟️ 确认购买 ' + cart.length + ' 注(' + cart.length*100 + ' 金币)'"></span>
|
||||
</button>
|
||||
{{-- 内联购买结果提示 --}}
|
||||
<div id="lottery-buy-msg"
|
||||
style="display:none; border-radius:8px; padding:8px 12px; text-align:center;
|
||||
margin-top:8px; font-size:12px; font-weight:bold;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{-- 停售/已开奖时的提示 --}}
|
||||
<div x-show="!isOpen && status !== 'settled'"
|
||||
style="padding:16px 14px; text-align:center; color:#b91c1c; font-size:13px;">
|
||||
🔴 本期已停售,等待开奖中…
|
||||
</div>
|
||||
|
||||
{{-- ─── 本期我的购票记录 ─── --}}
|
||||
<div x-show="myTickets.length > 0" style="padding:10px 14px; border-top:1px solid #fee2e2;">
|
||||
<div style="font-size:12px; font-weight:bold; color:#b91c1c; margin-bottom:6px;">
|
||||
📋 本期我的购票(<span x-text="myTickets.length"></span> 注)
|
||||
</div>
|
||||
<div style="max-height:120px; overflow-y:auto;">
|
||||
<template x-for="(t, idx) in myTickets" :key="t.id">
|
||||
<div
|
||||
style="display:flex; align-items:center; justify-content:space-between;
|
||||
font-size:11px; padding:4px 0; border-bottom:1px dashed #fee2e2;">
|
||||
<div style="display:flex; align-items:center; gap:3px;">
|
||||
<span style="color:#9ca3af; min-width:28px;" x-text="'注'+(idx+1)"></span>
|
||||
<template x-for="r in [t.red1,t.red2,t.red3]" :key="r">
|
||||
<span
|
||||
style="width:20px; height:20px; border-radius:50%; background:#dc2626;
|
||||
color:#fff; font-size:10px; text-align:center; line-height:20px;"
|
||||
x-text="String(r).padStart(2,'0')"></span>
|
||||
</template>
|
||||
<span
|
||||
style="width:20px; height:20px; border-radius:50%; background:#2563eb;
|
||||
color:#fff; font-size:11px; text-align:center; line-height:20px; margin-left:2px;"
|
||||
x-text="t.blue"></span>
|
||||
<span x-show="t.is_quick" style="color:#9ca3af; font-size:10px;">机选</span>
|
||||
</div>
|
||||
{{-- 中奖标记 --}}
|
||||
<span x-show="t.prize_level > 0"
|
||||
style="color:#dc2626; font-weight:bold; font-size:11px;"
|
||||
x-text="['','🏆一等','🥇二等','🥈三等','🥉四等','🎫五等'][t.prize_level] + ' +' + t.payout.toLocaleString()"></span>
|
||||
<span x-show="t.prize_level === 0 && status === 'settled'"
|
||||
style="color:#9ca3af; font-size:11px;">未中奖</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- ─── 历史开奖记录 ─── --}}
|
||||
<div style="padding:10px 14px; border-top:1px solid #fee2e2;">
|
||||
<div style="font-size:12px; font-weight:bold; color:#b91c1c; margin-bottom:6px;">📜 近期开奖</div>
|
||||
<div x-show="history.length === 0"
|
||||
style="font-size:11px; color:#9ca3af; text-align:center; padding:8px 0;">
|
||||
暂无历史记录
|
||||
</div>
|
||||
<div style="overflow-x:auto;">
|
||||
<table style="width:100%; font-size:10px; border-collapse:collapse; min-width:300px;">
|
||||
<tr style="background:#fee2e2; color:#b91c1c;">
|
||||
<th style="padding:3px 5px; text-align:left;">期号</th>
|
||||
<th style="padding:3px 5px;">开奖号码</th>
|
||||
<th style="padding:3px 5px; text-align:right;">奖池</th>
|
||||
</tr>
|
||||
<template x-for="(h, i) in history.slice(0,8)" :key="h.issue_no">
|
||||
<tr :style="i % 2 === 0 ? '' : 'background:#fff7f7;'">
|
||||
<td style="padding:3px 5px; color:#6b7280;" x-text="h.issue_no"></td>
|
||||
<td style="padding:3px 5px; text-align:center;">
|
||||
<template x-for="r in [h.red1,h.red2,h.red3]" :key="r">
|
||||
<span
|
||||
style="display:inline-block; width:18px; height:18px; border-radius:50%;
|
||||
background:#dc2626; color:#fff; font-size:9px; font-weight:bold;
|
||||
text-align:center; line-height:18px; margin:0 1px;"
|
||||
x-text="String(r).padStart(2,'0')"></span>
|
||||
</template>
|
||||
<span
|
||||
style="display:inline-block; width:18px; height:18px; border-radius:50%;
|
||||
background:#2563eb; color:#fff; font-size:9px; font-weight:bold;
|
||||
text-align:center; line-height:18px; margin-left:3px;"
|
||||
x-text="h.blue"></span>
|
||||
</td>
|
||||
<td style="padding:3px 5px; text-align:right; color:#dc2626;"
|
||||
x-text="'💰' + Number(h.pool_amount).toLocaleString()"></td>
|
||||
</tr>
|
||||
</template>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>{{-- /!loading --}}
|
||||
</div>{{-- /内容区 --}}
|
||||
|
||||
{{-- ─── 底部操作栏 ─── --}}
|
||||
<div
|
||||
style="padding:8px 14px; background:#fff; border-top:1px solid #fee2e2;
|
||||
display:flex; justify-content:space-between; align-items:center; flex-shrink:0;">
|
||||
<button @click="doQuickPick(3)" x-show="isOpen"
|
||||
style="padding:6px 14px; background:#fef3c7; border:1px solid #fde68a;
|
||||
border-radius:6px; font-size:12px; color:#92400e; cursor:pointer;">
|
||||
🎲 一次机选3注
|
||||
</button>
|
||||
<button @click="loadData()"
|
||||
style="padding:6px 14px; background:#f3f4f6; border:1px solid #e5e7eb;
|
||||
border-radius:6px; font-size:12px; color:#6b7280; cursor:pointer;">
|
||||
🔄 刷新
|
||||
</button>
|
||||
<button @click="closeLotteryPanel()"
|
||||
style="padding:6px 14px; background:#f0f6ff; border:1px solid #b0d0ee;
|
||||
border-radius:6px; font-size:12px; color:#336699; cursor:pointer;">
|
||||
关闭
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 双色球彩票面板 Alpine.js 组件
|
||||
*/
|
||||
function lotteryPanel() {
|
||||
return {
|
||||
// ─── 状态 ───
|
||||
show: false,
|
||||
loading: true,
|
||||
ruleOpen: false,
|
||||
buying: false,
|
||||
|
||||
// ─── 期次数据 ───
|
||||
issueNo: '--',
|
||||
status: 'open',
|
||||
isOpen: false,
|
||||
isSuperIssue: false,
|
||||
poolAmount: 0,
|
||||
secondsLeft: 0,
|
||||
drawAt: null,
|
||||
drawRed1: null,
|
||||
drawRed2: null,
|
||||
drawRed3: null,
|
||||
drawBlue: null,
|
||||
|
||||
// ─── 选号 ───
|
||||
selectedReds: [],
|
||||
selectedBlue: null,
|
||||
cart: [], // 待购清单
|
||||
|
||||
// ─── 数据 ───
|
||||
myTickets: [],
|
||||
history: [],
|
||||
|
||||
// ─── 倒计时 ───
|
||||
_timer: null,
|
||||
|
||||
/**
|
||||
* 格式化倒计时文字(如 4h 22m 或 01:59)
|
||||
*/
|
||||
get countdownText() {
|
||||
const s = this.secondsLeft;
|
||||
if (s <= 0) return '即将开奖';
|
||||
if (s >= 3600) return `${Math.floor(s/3600)}h ${Math.floor((s%3600)/60)}m`;
|
||||
const m = Math.floor(s / 60);
|
||||
const sec = s % 60;
|
||||
return `${String(m).padStart(2,'0')}:${String(sec).padStart(2,'0')}`;
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开面板并加载数据
|
||||
*/
|
||||
async open() {
|
||||
this.show = true;
|
||||
await this.loadData();
|
||||
this.startTimer();
|
||||
},
|
||||
|
||||
/**
|
||||
* 加载当期状态、我的购票、历史开奖
|
||||
*/
|
||||
async loadData() {
|
||||
this.loading = true;
|
||||
try {
|
||||
const [currentRes, histRes] = await Promise.all([
|
||||
fetch('/lottery/current', {
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
}),
|
||||
fetch('/lottery/history', {
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
}),
|
||||
]);
|
||||
const current = await currentRes.json();
|
||||
const hist = await histRes.json();
|
||||
|
||||
if (current.issue) {
|
||||
const iss = current.issue;
|
||||
this.issueNo = iss.issue_no;
|
||||
this.status = iss.status;
|
||||
this.isOpen = current.is_open;
|
||||
this.isSuperIssue = iss.is_super_issue;
|
||||
this.poolAmount = iss.pool_amount;
|
||||
this.secondsLeft = iss.seconds_left;
|
||||
this.drawRed1 = iss.red1;
|
||||
this.drawRed2 = iss.red2;
|
||||
this.drawRed3 = iss.red3;
|
||||
this.drawBlue = iss.blue;
|
||||
}
|
||||
this.myTickets = current.my_tickets ?? [];
|
||||
this.history = hist.issues ?? [];
|
||||
} catch (e) {
|
||||
// 网络异常静默处理
|
||||
}
|
||||
this.loading = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* 启动倒计时 ticker
|
||||
*/
|
||||
startTimer() {
|
||||
clearInterval(this._timer);
|
||||
this._timer = setInterval(() => {
|
||||
if (this.secondsLeft > 0) {
|
||||
this.secondsLeft--;
|
||||
}
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
/**
|
||||
* 切换红球选择状态
|
||||
*/
|
||||
toggleRed(n) {
|
||||
if (this.selectedReds.includes(n)) {
|
||||
this.selectedReds = this.selectedReds.filter(r => r !== n);
|
||||
} else if (this.selectedReds.length < 3) {
|
||||
this.selectedReds = [...this.selectedReds, n];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 服务端机选号码(立即加入购物车)
|
||||
*/
|
||||
async doQuickPick(count = 1) {
|
||||
try {
|
||||
const res = await fetch(`/lottery/quick-pick?count=${count}`);
|
||||
const data = await res.json();
|
||||
for (const num of data.numbers) {
|
||||
if (this.cart.length < 10) {
|
||||
this.cart.push({
|
||||
reds: num.reds,
|
||||
blue: num.blue,
|
||||
quick: true
|
||||
});
|
||||
}
|
||||
}
|
||||
// 清空当前选号
|
||||
this.selectedReds = [];
|
||||
this.selectedBlue = null;
|
||||
} catch {}
|
||||
},
|
||||
|
||||
/**
|
||||
* 将当前选号加入购物车
|
||||
*/
|
||||
addToCart() {
|
||||
if (this.selectedReds.length !== 3 || !this.selectedBlue) {
|
||||
showLotteryMsg('⚠️ 请选满 3 个红球和 1 个蓝球', false);
|
||||
return;
|
||||
}
|
||||
if (this.cart.length >= 10) {
|
||||
showLotteryMsg('⚠️ 单次最多加入 10 注', false);
|
||||
return;
|
||||
}
|
||||
this.cart.push({
|
||||
reds: [...this.selectedReds].sort((a, b) => a - b),
|
||||
blue: this.selectedBlue,
|
||||
quick: false
|
||||
});
|
||||
// 清空选号等待下一注
|
||||
this.selectedReds = [];
|
||||
this.selectedBlue = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* 提交购物车(批量购买)
|
||||
*/
|
||||
async submitCart() {
|
||||
if (this.cart.length === 0 || this.buying) return;
|
||||
this.buying = true;
|
||||
try {
|
||||
const res = await fetch('/lottery/buy', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
numbers: this.cart.map(c => ({
|
||||
reds: c.reds,
|
||||
blue: c.blue
|
||||
})),
|
||||
quick_pick: false,
|
||||
}),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (res.ok && data.status === 'success') {
|
||||
showLotteryMsg('✅ ' + data.message, true);
|
||||
this.cart = [];
|
||||
|
||||
// 更新金币余额显示
|
||||
if (window.chatContext) {
|
||||
window.chatContext.userJjb = Math.max(0, (window.chatContext.userJjb ?? 0) - data
|
||||
.count * 100);
|
||||
}
|
||||
|
||||
// 刷新我的购票列表
|
||||
await this.loadData();
|
||||
} else {
|
||||
showLotteryMsg('❌ ' + (data.message || '购票失败'), false);
|
||||
}
|
||||
} catch {
|
||||
showLotteryMsg('🌐 网络异常,请稍后重试', false);
|
||||
}
|
||||
this.buying = false;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示彩票面板内联消息(3s 后自动消失)
|
||||
*
|
||||
* @param {string} msg
|
||||
* @param {boolean} success
|
||||
*/
|
||||
function showLotteryMsg(msg, success) {
|
||||
const el = document.getElementById('lottery-buy-msg');
|
||||
if (!el) return;
|
||||
el.style.background = success ? '#f0fdf4' : '#fff5f5';
|
||||
el.style.border = success ? '1px solid #86efac' : '1px solid #fecaca';
|
||||
el.style.color = success ? '#16a34a' : '#dc2626';
|
||||
el.textContent = msg;
|
||||
el.style.display = 'block';
|
||||
el.style.opacity = '1';
|
||||
clearTimeout(el._t);
|
||||
el._t = setTimeout(() => {
|
||||
el.style.opacity = '0';
|
||||
setTimeout(() => {
|
||||
el.style.display = 'none';
|
||||
}, 400);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
/** 打开彩票面板 */
|
||||
window.openLotteryPanel = function() {
|
||||
const panel = document.getElementById('lottery-panel');
|
||||
if (panel) Alpine.$data(panel).open();
|
||||
};
|
||||
|
||||
/** 关闭彩票面板 */
|
||||
window.closeLotteryPanel = function() {
|
||||
const panel = document.getElementById('lottery-panel');
|
||||
if (panel) {
|
||||
const data = Alpine.$data(panel);
|
||||
data.show = false;
|
||||
clearInterval(data._timer);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user