2026-03-01 00:34:11 +08:00
|
|
|
|
{{--
|
2026-03-09 11:30:11 +08:00
|
|
|
|
文件功能:全局自定义弹窗组件(替代原生 alert / confirm / prompt)
|
2026-03-01 00:34:11 +08:00
|
|
|
|
|
|
|
|
|
|
提供全局 JS API:
|
2026-03-09 11:30:11 +08:00
|
|
|
|
- window.chatDialog.alert(message, title?, color?) → Promise<void>
|
|
|
|
|
|
- window.chatDialog.confirm(message, title?, color?) → Promise<boolean>
|
|
|
|
|
|
- window.chatDialog.prompt(message, defaultVal?, title?, color?) → Promise<string|null>
|
2026-03-01 00:34:11 +08:00
|
|
|
|
|
|
|
|
|
|
任何 JS 代码(Alpine.js 组件、toolbar、scripts 等)均可直接调用,
|
|
|
|
|
|
无需使用浏览器原生弹窗,避免 Chrome/Edge 兼容性问题。
|
|
|
|
|
|
|
|
|
|
|
|
@author ChatRoom Laravel
|
2026-03-09 11:30:11 +08:00
|
|
|
|
@version 2.0.0
|
2026-03-01 00:34:11 +08:00
|
|
|
|
--}}
|
|
|
|
|
|
|
|
|
|
|
|
{{-- ─── 全局弹窗遮罩 ─── --}}
|
|
|
|
|
|
<div id="global-dialog-modal"
|
|
|
|
|
|
style="display:none; position:fixed; inset:0; background:rgba(0,0,0,.5);
|
|
|
|
|
|
z-index:999999; justify-content:center; align-items:center;">
|
|
|
|
|
|
<div id="global-dialog-box"
|
|
|
|
|
|
style="background:#fff; border-radius:10px; width:320px; max-width:90vw;
|
|
|
|
|
|
box-shadow:0 12px 40px rgba(0,0,0,.35); overflow:hidden;
|
|
|
|
|
|
animation:gdSlideIn .18s ease;">
|
|
|
|
|
|
|
|
|
|
|
|
{{-- 标题栏(颜色由 JS 动态设置) --}}
|
|
|
|
|
|
<div id="global-dialog-header" style="padding:13px 18px; font-size:14px; font-weight:bold; color:#fff;">
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{{-- 内容区 --}}
|
|
|
|
|
|
<div id="global-dialog-message"
|
2026-03-09 11:30:11 +08:00
|
|
|
|
style="padding:18px 18px 8px; font-size:13px; color:#374151; white-space:pre-wrap;
|
2026-03-01 00:34:11 +08:00
|
|
|
|
line-height:1.6; word-break:break-word;">
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-03-09 11:30:11 +08:00
|
|
|
|
{{-- 输入框(prompt 模式专用) --}}
|
|
|
|
|
|
<div id="global-dialog-input-wrap" style="display:none; padding:0 18px 12px;">
|
|
|
|
|
|
<textarea id="global-dialog-input"
|
|
|
|
|
|
style="width:100%; box-sizing:border-box; border:1px solid #d1d5db; border-radius:6px;
|
|
|
|
|
|
padding:8px 10px; font-size:13px; color:#374151; resize:vertical;
|
|
|
|
|
|
min-height:72px; line-height:1.5; outline:none; font-family:inherit;
|
|
|
|
|
|
transition:border-color .15s;"
|
|
|
|
|
|
placeholder="请输入内容…"></textarea>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-03-01 00:34:11 +08:00
|
|
|
|
{{-- 按钮区 --}}
|
|
|
|
|
|
<div style="display:flex; gap:10px; padding:0 18px 16px;">
|
2026-04-25 03:54:23 +08:00
|
|
|
|
<button id="global-dialog-cancel-btn" data-chat-dialog-action="cancel"
|
2026-03-01 00:34:11 +08:00
|
|
|
|
style="flex:1; padding:9px; background:#f3f4f6; color:#555;
|
|
|
|
|
|
border:1px solid #d1d5db; border-radius:6px;
|
|
|
|
|
|
font-size:13px; cursor:pointer; transition:background .15s;">
|
|
|
|
|
|
取消
|
|
|
|
|
|
</button>
|
2026-04-25 03:54:23 +08:00
|
|
|
|
<button id="global-dialog-confirm-btn" data-chat-dialog-action="confirm"
|
2026-03-01 00:34:11 +08:00
|
|
|
|
style="flex:1; padding:9px; color:#fff; border:none;
|
|
|
|
|
|
border-radius:6px; font-size:13px; font-weight:bold;
|
|
|
|
|
|
cursor:pointer; transition:opacity .15s;">
|
|
|
|
|
|
确定
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
|
@keyframes gdSlideIn {
|
|
|
|
|
|
from {
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transform: scale(.92) translateY(-8px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
to {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
transform: scale(1) translateY(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-01 00:48:51 +08:00
|
|
|
|
@keyframes fdSlideIn {
|
|
|
|
|
|
from {
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
transform: translateY(16px);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
to {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
transform: translateY(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-01 00:34:11 +08:00
|
|
|
|
#global-dialog-cancel-btn:hover {
|
|
|
|
|
|
background: #e5e7eb !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#global-dialog-confirm-btn:hover {
|
|
|
|
|
|
opacity: .88;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 全局弹窗工具,提供 alert / confirm 两种模式。
|
|
|
|
|
|
* 替代浏览器原生弹窗,兼容 Chrome/Edge/Safari。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 用法:
|
|
|
|
|
|
* await window.chatDialog.alert('操作成功!', '提示', '#16a34a');
|
|
|
|
|
|
* const yes = await window.chatDialog.confirm('确定要删除吗?', '请确认');
|
|
|
|
|
|
* if (yes) { ... }
|
|
|
|
|
|
*/
|
|
|
|
|
|
window.chatDialog = (function() {
|
|
|
|
|
|
let _resolve = null;
|
2026-03-09 11:30:11 +08:00
|
|
|
|
let _currentType = 'alert';
|
2026-03-01 00:34:11 +08:00
|
|
|
|
|
|
|
|
|
|
const $ = id => document.getElementById(id);
|
|
|
|
|
|
|
|
|
|
|
|
/** 打开弹窗内部方法 */
|
2026-03-09 11:30:11 +08:00
|
|
|
|
function _open({ message, title, color, type, defaultVal }) {
|
|
|
|
|
|
_currentType = type;
|
|
|
|
|
|
|
2026-03-01 00:34:11 +08:00
|
|
|
|
$('global-dialog-header').textContent = title;
|
|
|
|
|
|
$('global-dialog-header').style.background = color;
|
|
|
|
|
|
$('global-dialog-message').textContent = message;
|
|
|
|
|
|
$('global-dialog-confirm-btn').style.background = color;
|
|
|
|
|
|
|
2026-03-09 11:30:11 +08:00
|
|
|
|
// prompt 模式显示输入框,并填入默认值
|
|
|
|
|
|
const inputWrap = $('global-dialog-input-wrap');
|
|
|
|
|
|
const inputEl = $('global-dialog-input');
|
|
|
|
|
|
if (type === 'prompt') {
|
|
|
|
|
|
inputEl.value = defaultVal ?? '';
|
|
|
|
|
|
inputWrap.style.display = '';
|
|
|
|
|
|
// 弹出后自动聚焦
|
|
|
|
|
|
setTimeout(() => { inputEl.focus(); inputEl.select(); }, 80);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
inputWrap.style.display = 'none';
|
|
|
|
|
|
inputEl.value = '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// alert 模式隐藏取消按钮
|
2026-03-01 00:34:11 +08:00
|
|
|
|
const cancelBtn = $('global-dialog-cancel-btn');
|
2026-03-09 11:30:11 +08:00
|
|
|
|
cancelBtn.style.display = (type === 'confirm' || type === 'prompt') ? '' : 'none';
|
2026-03-01 00:34:11 +08:00
|
|
|
|
|
2026-03-09 11:30:11 +08:00
|
|
|
|
// alert 模式确认按钮撑满
|
|
|
|
|
|
$('global-dialog-confirm-btn').style.flex = type === 'alert' ? '1 1 100%' : '1';
|
2026-03-01 00:34:11 +08:00
|
|
|
|
|
|
|
|
|
|
$('global-dialog-modal').style.display = 'flex';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 显示提示弹窗(替代 alert)。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {string} message 提示内容
|
|
|
|
|
|
* @param {string} title 标题(默认:提示)
|
|
|
|
|
|
* @param {string} color 标题栏颜色(默认蓝色)
|
|
|
|
|
|
* @return {Promise<void>}
|
|
|
|
|
|
*/
|
|
|
|
|
|
alert(message, title = '提示', color = '#336699') {
|
|
|
|
|
|
return new Promise(resolve => {
|
|
|
|
|
|
_resolve = resolve;
|
2026-03-09 11:30:11 +08:00
|
|
|
|
_open({ message, title, color, type: 'alert' });
|
2026-03-01 00:34:11 +08:00
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 显示确认弹窗(替代 confirm)。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {string} message 确认内容
|
|
|
|
|
|
* @param {string} title 标题(默认:请确认)
|
|
|
|
|
|
* @param {string} color 标题栏颜色(默认红色)
|
|
|
|
|
|
* @return {Promise<boolean>} 用户点确定返回 true,取消返回 false
|
|
|
|
|
|
*/
|
|
|
|
|
|
confirm(message, title = '请确认', color = '#cc4444') {
|
|
|
|
|
|
return new Promise(resolve => {
|
|
|
|
|
|
_resolve = resolve;
|
2026-03-09 11:30:11 +08:00
|
|
|
|
_open({ message, title, color, type: 'confirm' });
|
|
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 显示文本输入弹窗(替代 prompt)。
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param {string} message 提示文字
|
|
|
|
|
|
* @param {string} defaultVal 输入框默认值
|
|
|
|
|
|
* @param {string} title 标题(默认:请输入)
|
|
|
|
|
|
* @param {string} color 标题栏颜色(默认蓝色)
|
|
|
|
|
|
* @return {Promise<string|null>} 确定返回输入内容,取消返回 null
|
|
|
|
|
|
*/
|
|
|
|
|
|
prompt(message, defaultVal = '', title = '请输入', color = '#336699') {
|
|
|
|
|
|
return new Promise(resolve => {
|
|
|
|
|
|
_resolve = resolve;
|
|
|
|
|
|
_open({ message, title, color, type: 'prompt', defaultVal });
|
2026-03-01 00:34:11 +08:00
|
|
|
|
});
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/** 点确定按钮 */
|
|
|
|
|
|
_confirm() {
|
2026-03-09 11:30:11 +08:00
|
|
|
|
if (_currentType === 'prompt') {
|
|
|
|
|
|
// prompt 模式:返回输入框内容
|
|
|
|
|
|
_resolve?.($('global-dialog-input').value);
|
|
|
|
|
|
} else if (_currentType === 'confirm') {
|
|
|
|
|
|
_resolve?.(true);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
_resolve?.();
|
|
|
|
|
|
}
|
2026-03-01 00:34:11 +08:00
|
|
|
|
this._hide();
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/** 点取消按钮 */
|
|
|
|
|
|
_cancel() {
|
2026-03-09 11:30:11 +08:00
|
|
|
|
if (_currentType === 'prompt') {
|
|
|
|
|
|
_resolve?.(null);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
_resolve?.(false);
|
|
|
|
|
|
}
|
2026-03-01 00:34:11 +08:00
|
|
|
|
this._hide();
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/** 关闭弹窗 */
|
|
|
|
|
|
_hide() {
|
|
|
|
|
|
$('global-dialog-modal').style.display = 'none';
|
|
|
|
|
|
_resolve = null;
|
2026-03-09 11:30:11 +08:00
|
|
|
|
_currentType = 'alert';
|
2026-03-01 00:34:11 +08:00
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
})();
|
|
|
|
|
|
</script>
|