功能:单次特效卡支持赠送——送礼弹框、广播给指定用户/全员、公屏系统消息、购买后关闭商店展示特效

This commit is contained in:
2026-02-27 16:19:21 +08:00
parent 1e2c304754
commit 6a8ba4fbc8
4 changed files with 241 additions and 28 deletions
@@ -622,11 +622,17 @@
// DOMContentLoaded 后启动,确保 Vite 编译的 JS 已加载
document.addEventListener('DOMContentLoaded', setupScreenClearedListener);
// ── 全屏特效事件监听(烟花/下雨/雷电)───────────────
// ── 全屏特效事件监听(烟花/下雨/雷电/下雪)─────────
window.addEventListener('chat:effect', (e) => {
const type = e.detail?.type;
const target = e.detail?.target_username; // null = 全员,otherwise 指定昵称
const myName = window.chatContext?.username;
// null 表示全员,或者 target 匹配自己才播放
if (type && typeof EffectManager !== 'undefined') {
EffectManager.play(type);
if (!target || target === myName) {
EffectManager.play(type);
}
}
});
+157 -12
View File
@@ -15,7 +15,7 @@
{{-- ═══════════ 竖向工具条按钮 ═══════════ --}}
<div class="chat-toolbar" id="toolbar-strip">
<div class="tool-btn" onclick="openShopModal()" title="购买道具">🛍商店</div>
<div class="tool-btn" onclick="openShopModal()" title="购买道具">商店</div>
<div class="tool-btn" onclick="saveExp()" title="手动存经验点">存点</div>
<div class="tool-btn" onclick="alert('🚧 娱乐功能开发中,敬请期待!')" title="娱乐(待开发)">娱乐</div>
<div class="tool-btn" onclick="alert('🚧 银行功能开发中,敬请期待!')" title="银行(待开发)">银行</div>
@@ -586,6 +586,79 @@
margin-top: 5px;
min-height: 14px;
}
/* 送礼对话框 */
#gift-dialog {
display: none;
position: absolute;
inset: 0;
background: rgba(0, 0, 0, .55);
z-index: 10001;
justify-content: center;
align-items: center;
border-radius: 8px;
}
#gift-dialog-box {
background: #fff;
border: 1px solid #5a8fc0;
border-radius: 8px;
padding: 16px 14px;
width: 250px;
box-shadow: 0 4px 20px rgba(0, 0, 0, .25);
}
#gift-dialog-box h4 {
font-size: 12px;
font-weight: bold;
color: #336699;
margin: 0 0 3px;
}
#gift-item-name {
font-size: 11px;
color: #555;
display: block;
border-bottom: 1px dashed #cde;
padding-bottom: 6px;
margin-bottom: 10px;
}
.gift-label {
font-size: 11px;
color: #555;
margin-bottom: 3px;
display: block;
}
.gift-input {
width: 100%;
border: 1px solid #aac;
border-radius: 4px;
padding: 5px 8px;
font-size: 12px;
box-sizing: border-box;
margin-bottom: 8px;
outline: none;
}
.gift-input:focus {
border-color: #336699;
}
.gift-hint {
font-size: 10px;
color: #999;
margin: -4px 0 8px;
display: block;
}
#gift-err {
color: #c00;
font-size: 10px;
margin-top: 4px;
min-height: 14px;
}
</style>
<div id="shop-modal">
@@ -619,6 +692,27 @@
<div id="rename-err"></div>
</div>
</div>
{{-- 送礼对话框:填写接收人 + 赠言 --}}
<div id="gift-dialog">
<div id="gift-dialog-box">
<h4>🎁 赠出单次特效卡</h4>
<span id="gift-item-name"></span>
<label class="gift-label">送给谁?</label>
<input id="gift-recipient" class="gift-input" type="text" maxlength="20"
placeholder="用户名(留空 = 全场可见)">
<span class="gift-hint">💡 留空表示所有人;购买者必定可见</span>
<label class="gift-label">说一句话(公屏发送)</label>
<input id="gift-message" class="gift-input" type="text" maxlength="50" placeholder="可不填,百字以内">
<div id="gift-err"></div>
<div class="rename-btn-row" style="margin-top:8px;">
<button onclick="confirmGift()"
style="flex:1;background:#336699;color:#fff;border:none;border-radius:4px;padding:7px;cursor:pointer;font-size:12px;font-weight:bold;">确认赠出</button>
<button onclick="closeGiftDialog()"
style="flex:1;background:#eee;color:#555;border:1px solid #ccc;border-radius:4px;padding:7px;cursor:pointer;font-size:12px;">取消</button>
</div>
</div>
</div>
</div>
</div>
@@ -732,10 +826,16 @@
btn.className = 'shop-btn shop-btn-use';
btn.textContent = '✦ 使用改名卡';
btn.onclick = openRenameModal;
} else {
} else if (item.type === 'instant') {
// 单次卡:打开送礼弹框
btn.className = 'shop-btn';
btn.innerHTML = `🪙 ${Number(item.price).toLocaleString()}`;
btn.onclick = () => buyItem(item.id, item.name, item.price);
btn.onclick = () => openGiftDialog(item);
} else {
// 周卡等其他商品:直接确认购买
btn.className = 'shop-btn';
btn.innerHTML = `🪙 ${Number(item.price).toLocaleString()}`;
btn.onclick = () => buyItem(item.id, item.name, item.price, 'all', '');
}
card.appendChild(btn);
list.appendChild(card);
@@ -743,9 +843,40 @@
});
}
/** 购买商品 */
window.buyItem = function(itemId, name, price) {
if (!confirm(`确定花费 ${Number(price).toLocaleString()} 金币购买【${name}】吗?`)) return;
/**
* 打开送礼弹框(仅单次特效卡)
* 让用户填写:送给谁 + 说一句话
*/
let _giftItem = null;
window.openGiftDialog = function(item) {
_giftItem = item;
// 重置内容
document.getElementById('gift-recipient').value = '';
document.getElementById('gift-message').value = '';
document.getElementById('gift-err').textContent = '';
document.getElementById('gift-item-name').textContent =
`${item.icon} ${item.name}(🪙 ${Number(item.price).toLocaleString()}`;
document.getElementById('gift-dialog').style.display = 'flex';
};
window.closeGiftDialog = function() {
document.getElementById('gift-dialog').style.display = 'none';
_giftItem = null;
};
/** 送礼确认,提交购买 */
window.confirmGift = function() {
if (!_giftItem) return;
const recipient = document.getElementById('gift-recipient').value.trim();
const message = document.getElementById('gift-message').value.trim();
document.getElementById('gift-err').textContent = '';
closeGiftDialog();
buyItem(_giftItem.id, _giftItem.name, _giftItem.price, recipient, message);
};
/** 购买商品(最终执行) */
window.buyItem = function(itemId, name, price, recipient, message) {
const roomId = window.chatContext?.roomId ?? 0;
fetch('{{ route('shop.buy') }}', {
method: 'POST',
headers: {
@@ -754,24 +885,38 @@
'X-CSRF-TOKEN': _csrf()
},
body: JSON.stringify({
item_id: itemId
item_id: itemId,
recipient: recipient || 'all',
message: message || '',
room_id: roomId,
}),
})
.then(r => r.json())
.then(data => {
showShopToast(data.message, data.status === 'success');
if (data.status === 'success') {
// 更新金币
if (data.jjb !== undefined)
document.getElementById('shop-jjb').textContent = Number(data.jjb)
.toLocaleString();
if (data.play_effect && window.EffectManager)
window.EffectManager.play(data.play_effect);
// 刷新商品状态
// 播放本地特效(购买者自己可见)
if (data.play_effect && window.EffectManager) {
const myTarget = data.target_username;
const myName = window.chatContext?.username;
// null = 全员,或者 target 是自己
if (!myTarget || myTarget === myName) {
window.EffectManager.play(data.play_effect);
}
}
// 关闭商店弹窗,让用户欣赏特效
closeShopModal();
// 延迟刷新商品数据
shopLoaded = false;
setTimeout(() => {
fetchShopData();
shopLoaded = true;
}, 800);
}, 1000);
} else {
showShopToast(data.message, false);
}
})
.catch(() => showShopToast('⚠ 网络异常,请重试', false));