新增:双色球彩票前台 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.horse-race-panel')
|
||||||
{{-- ═══════════ 神秘占卜游戏面板 ═══════════ --}}
|
{{-- ═══════════ 神秘占卜游戏面板 ═══════════ --}}
|
||||||
@include('chat.partials.fortune-panel')
|
@include('chat.partials.fortune-panel')
|
||||||
|
{{-- ═══════════ 双色球彩票面板 ═══════════ --}}
|
||||||
|
@include('chat.partials.lottery-panel')
|
||||||
{{-- ═══════════ 娱乐游戏大厅弹窗 ═══════════ --}}
|
{{-- ═══════════ 娱乐游戏大厅弹窗 ═══════════ --}}
|
||||||
@include('chat.partials.game-hall')
|
@include('chat.partials.game-hall')
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
'horse_racing' => \App\Models\GameConfig::isEnabled('horse_racing'),
|
'horse_racing' => \App\Models\GameConfig::isEnabled('horse_racing'),
|
||||||
'fortune_telling' => \App\Models\GameConfig::isEnabled('fortune_telling'),
|
'fortune_telling' => \App\Models\GameConfig::isEnabled('fortune_telling'),
|
||||||
'fishing' => \App\Models\GameConfig::isEnabled('fishing'),
|
'fishing' => \App\Models\GameConfig::isEnabled('fishing'),
|
||||||
|
'lottery' => \App\Models\GameConfig::isEnabled('lottery'),
|
||||||
];
|
];
|
||||||
@endphp
|
@endphp
|
||||||
<script>
|
<script>
|
||||||
@@ -254,6 +255,47 @@
|
|||||||
}),
|
}),
|
||||||
btnLabel: () => '🎣 去钓鱼',
|
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