重构:将聊天室所有原生弹窗替换为全局弹窗,公告增加发送者与时间
- 将设公告、公屏讲话、全员清屏按钮弹窗改为使用 window.chatDialog 全局弹窗 - 所有弹窗改用 .then() 回调注册事件,避免 async/await 行为不一致问题 - 公告内容末尾追加「——发送者 MM-dd HH:mm」,无需新增数据库字段 - 前端编辑公告时自动剥离末尾元信息,用户仅编辑纯内容 - 修复 red-packet-panel.blade.php 中 3 处原生 alert() 残留 - 修复 shop-panel.blade.php 中购买确认 confirm() 原生弹窗残留
This commit is contained in:
@@ -731,7 +731,9 @@ class ChatController extends Controller
|
|||||||
'announcement' => 'required|string|max:500',
|
'announcement' => 'required|string|max:500',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$room->announcement = $request->input('announcement');
|
// 将发送者和发送时间追加到公告文本末尾,持久化存储,无需额外字段
|
||||||
|
$room->announcement = trim($request->input('announcement'))
|
||||||
|
.' ——'.$user->username.' '.now()->format('m-d H:i');
|
||||||
$room->save();
|
$room->save();
|
||||||
|
|
||||||
// 广播公告更新到所有在线用户
|
// 广播公告更新到所有在线用户
|
||||||
|
|||||||
@@ -416,11 +416,13 @@
|
|||||||
|
|
||||||
// 【前置拦截1】如果有时间戳并算出已过期,直接杀死不弹窗
|
// 【前置拦截1】如果有时间戳并算出已过期,直接杀死不弹窗
|
||||||
if (sentAtUnix && Date.now() >= calculatedExpireAt) {
|
if (sentAtUnix && Date.now() >= calculatedExpireAt) {
|
||||||
if (typeof window.chatToast === 'function') {
|
window.chatToast?.show({
|
||||||
window.chatToast('该红包已过期。', 'info');
|
title: '⏰ 礼包已过期',
|
||||||
} else {
|
message: '该红包已过期,无法领取。',
|
||||||
alert('该红包已过期。');
|
icon: '⏰',
|
||||||
}
|
color: '#9ca3af',
|
||||||
|
duration: 4000,
|
||||||
|
});
|
||||||
console.log('红包已准确断定过期,拦截弹窗显示:', envelopeId);
|
console.log('红包已准确断定过期,拦截弹窗显示:', envelopeId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -499,7 +501,7 @@
|
|||||||
|
|
||||||
const modalEl = document.getElementById('red-packet-modal');
|
const modalEl = document.getElementById('red-packet-modal');
|
||||||
if (!modalEl) {
|
if (!modalEl) {
|
||||||
alert('致命错误:红包视图容器 #red-packet-modal 找不到!');
|
window.chatDialog?.alert('致命错误:红包视图容器 #red-packet-modal 找不到!', '系统错误', '#cc4444');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -543,7 +545,7 @@
|
|||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('showRedPacketModal 执行失败:', err);
|
console.error('showRedPacketModal 执行失败:', err);
|
||||||
alert('红包弹窗初始化异常: ' + err.message);
|
window.chatDialog?.alert('红包弹窗初始化异常: ' + err.message, '系统错误', '#cc4444');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 启动倒计时
|
// 启动倒计时
|
||||||
|
|||||||
@@ -986,13 +986,15 @@
|
|||||||
|
|
||||||
|
|
||||||
// ── 设置房间公告 ─────────────────────────────────────
|
// ── 设置房间公告 ─────────────────────────────────────
|
||||||
async function promptAnnouncement() {
|
function promptAnnouncement() {
|
||||||
const currentText = document.getElementById('announcement-text')?.textContent?.trim() || '';
|
// 从 marquee 读取当前公告全文,剥离末尾的「——发送者 日期」元信息,仅预填纯内容
|
||||||
const newText = prompt('请输入新的房间公告/祝福语:', currentText);
|
const fullText = document.getElementById('announcement-text')?.textContent?.trim() || '';
|
||||||
if (newText === null || newText.trim() === '') return;
|
const pureText = fullText.replace(/ ——\S+ \d{2}-\d{2} \d{2}:\d{2}$/, '').trim();
|
||||||
|
// 使用全局弹窗替代原生 prompt(),通过 .then() 注册回调确保事件正确触发
|
||||||
|
window.chatDialog.prompt('请输入新的房间公告/祝福语:', pureText, '设置公告', '#336699').then(newText => {
|
||||||
|
if (newText === null || newText.trim() === '') return;
|
||||||
|
|
||||||
try {
|
fetch(`/room/${window.chatContext.roomId}/announcement`, {
|
||||||
const res = await fetch(`/room/${window.chatContext.roomId}/announcement`, {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute(
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute(
|
||||||
@@ -1003,27 +1005,28 @@
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
announcement: newText.trim()
|
announcement: newText.trim()
|
||||||
})
|
})
|
||||||
|
}).then(res => res.json()).then(data => {
|
||||||
|
if (data.status === 'success') {
|
||||||
|
// 用后端返回的含发送者+时间的完整公告文本更新滚动条
|
||||||
|
const marquee = document.getElementById('announcement-text');
|
||||||
|
if (marquee) marquee.textContent = data.announcement;
|
||||||
|
window.chatDialog.alert('公告已更新!', '提示', '#16a34a');
|
||||||
|
} else {
|
||||||
|
window.chatDialog.alert(data.message || '更新失败', '操作失败', '#cc4444');
|
||||||
|
}
|
||||||
|
}).catch(e => {
|
||||||
|
window.chatDialog.alert('设置公告失败:' + e.message, '操作失败', '#cc4444');
|
||||||
});
|
});
|
||||||
const data = await res.json();
|
});
|
||||||
if (data.status === 'success') {
|
|
||||||
const marquee = document.getElementById('announcement-text');
|
|
||||||
if (marquee) marquee.textContent = newText.trim();
|
|
||||||
window.chatDialog.alert('公告已更新!', '提示', '#16a34a');
|
|
||||||
} else {
|
|
||||||
window.chatDialog.alert(data.message || '更新失败', '操作失败', '#cc4444');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
window.chatDialog.alert('设置公告失败:' + e.message, '操作失败', '#cc4444');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 站长公屏讲话 ─────────────────────────────────────
|
// ── 站长公屏讲话 ─────────────────────────────────────
|
||||||
async function promptAnnounceMessage() {
|
function promptAnnounceMessage() {
|
||||||
const content = prompt('请输入公屏讲话内容:');
|
// 使用全局弹窗替代原生 prompt(),通过 .then() 注册回调确保事件正确触发
|
||||||
if (!content || !content.trim()) return;
|
window.chatDialog.prompt('请输入公屏讲话内容:', '', '📢 公屏讲话', '#7c3aed').then(content => {
|
||||||
|
if (!content || !content.trim()) return;
|
||||||
|
|
||||||
try {
|
fetch('/command/announce', {
|
||||||
const res = await fetch('/command/announce', {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute(
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute(
|
||||||
@@ -1035,22 +1038,23 @@
|
|||||||
content: content.trim(),
|
content: content.trim(),
|
||||||
room_id: window.chatContext.roomId,
|
room_id: window.chatContext.roomId,
|
||||||
})
|
})
|
||||||
|
}).then(res => res.json()).then(data => {
|
||||||
|
if (data.status !== 'success') {
|
||||||
|
window.chatDialog.alert(data.message || '发送失败', '操作失败', '#cc4444');
|
||||||
|
}
|
||||||
|
}).catch(e => {
|
||||||
|
window.chatDialog.alert('发送失败:' + e.message, '操作失败', '#cc4444');
|
||||||
});
|
});
|
||||||
const data = await res.json();
|
});
|
||||||
if (!res.ok || data.status !== 'success') {
|
|
||||||
window.chatDialog.alert(data.message || '发送失败', '操作失败', '#cc4444');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
window.chatDialog.alert('发送失败:' + e.message, '操作失败', '#cc4444');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 管理员全员清屏 ─────────────────────────────────────
|
// ── 管理员全员清屏 ─────────────────────────────────────
|
||||||
async function adminClearScreen() {
|
function adminClearScreen() {
|
||||||
if (!confirm('确定要清除所有人的聊天记录吗?(悄悄话将保留)')) return;
|
// 使用全局弹窗替代原生 confirm(),通过 .then() 注册回调确保事件正确触发
|
||||||
|
window.chatDialog.confirm('确定要清除所有人的聊天记录吗?(悄悄话将保留)', '全员清屏', '#dc2626').then(ok => {
|
||||||
|
if (!ok) return;
|
||||||
|
|
||||||
try {
|
fetch('/command/clear-screen', {
|
||||||
const res = await fetch('/command/clear-screen', {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute(
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute(
|
||||||
@@ -1061,14 +1065,14 @@
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
room_id: window.chatContext.roomId,
|
room_id: window.chatContext.roomId,
|
||||||
})
|
})
|
||||||
|
}).then(res => res.json()).then(data => {
|
||||||
|
if (data.status !== 'success') {
|
||||||
|
window.chatDialog.alert(data.message || '清屏失败', '操作失败', '#cc4444');
|
||||||
|
}
|
||||||
|
}).catch(e => {
|
||||||
|
window.chatDialog.alert('清屏失败:' + e.message, '操作失败', '#cc4444');
|
||||||
});
|
});
|
||||||
const data = await res.json();
|
});
|
||||||
if (!res.ok || data.status !== 'success') {
|
|
||||||
window.chatDialog.alert(data.message || '清屏失败', '操作失败', '#cc4444');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
window.chatDialog.alert('清屏失败:' + e.message, '操作失败', '#cc4444');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 本地清屏(仅限自己的屏幕)───────────────────────────
|
// ── 本地清屏(仅限自己的屏幕)───────────────────────────
|
||||||
@@ -1273,5 +1277,4 @@
|
|||||||
div.textContent = text;
|
div.textContent = text;
|
||||||
return div.innerHTML;
|
return div.innerHTML;
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -452,36 +452,43 @@
|
|||||||
|
|
||||||
/** 购买商品 */
|
/** 购买商品 */
|
||||||
window.buyItem = function(itemId, name, price) {
|
window.buyItem = function(itemId, name, price) {
|
||||||
if (!confirm(`确定花费 ${Number(price).toLocaleString()} 金币购买【${name}】吗?`)) return;
|
// 使用全局弹窗替代原生 confirm(),通过 .then() 注册回调确保事件正确触发
|
||||||
fetch('{{ route('shop.buy') }}', {
|
window.chatDialog.confirm(
|
||||||
method: 'POST',
|
`确定花费 ${Number(price).toLocaleString()} 金币购买【${name}】吗?`,
|
||||||
headers: {
|
'确认购买',
|
||||||
'Accept': 'application/json',
|
'#336699'
|
||||||
'Content-Type': 'application/json',
|
).then(ok => {
|
||||||
'X-CSRF-TOKEN': _csrf()
|
if (!ok) return;
|
||||||
},
|
fetch('{{ route('shop.buy') }}', {
|
||||||
body: JSON.stringify({
|
method: 'POST',
|
||||||
item_id: itemId
|
headers: {
|
||||||
}),
|
'Accept': 'application/json',
|
||||||
})
|
'Content-Type': 'application/json',
|
||||||
.then(r => r.json())
|
'X-CSRF-TOKEN': _csrf()
|
||||||
.then(data => {
|
},
|
||||||
showShopToast(data.message, data.status === 'success');
|
body: JSON.stringify({
|
||||||
if (data.status === 'success') {
|
item_id: itemId
|
||||||
if (data.jjb !== undefined)
|
}),
|
||||||
document.getElementById('shop-jjb').textContent = Number(data.jjb)
|
})
|
||||||
.toLocaleString();
|
.then(r => r.json())
|
||||||
if (data.play_effect && window.EffectManager)
|
.then(data => {
|
||||||
window.EffectManager.play(data.play_effect);
|
showShopToast(data.message, data.status === 'success');
|
||||||
// 刷新商品状态
|
if (data.status === 'success') {
|
||||||
shopLoaded = false;
|
if (data.jjb !== undefined)
|
||||||
setTimeout(() => {
|
document.getElementById('shop-jjb').textContent = Number(data.jjb)
|
||||||
fetchShopData();
|
.toLocaleString();
|
||||||
shopLoaded = true;
|
if (data.play_effect && window.EffectManager)
|
||||||
}, 800);
|
window.EffectManager.play(data.play_effect);
|
||||||
}
|
// 刷新商品状态
|
||||||
})
|
shopLoaded = false;
|
||||||
.catch(() => showShopToast('⚠ 网络异常,请重试', false));
|
setTimeout(() => {
|
||||||
|
fetchShopData();
|
||||||
|
shopLoaded = true;
|
||||||
|
}, 800);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => showShopToast('⚠ 网络异常,请重试', false));
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Toast 通知 */
|
/** Toast 通知 */
|
||||||
|
|||||||
Reference in New Issue
Block a user