迁移后台全局弹窗脚本

This commit is contained in:
2026-04-25 13:40:58 +08:00
parent e876e76ec7
commit 4d337a2e8f
3 changed files with 212 additions and 174 deletions
-174
View File
@@ -295,180 +295,6 @@
}
}
</style>
<script>
/**
* 后台全局 Toast 工厂。
*
* 负责统一渲染右上角成功/失败提示,并提供全局调用入口。
*
* @param initialToasts 初始提示列表
* @returns {object}
*/
function createAdminToastStore(initialToasts = []) {
return {
toasts: [],
visibleIds: [],
nextToastId: Date.now(),
/**
* 初始化 Toast 列表,并挂载全局调用方法。
*/
boot() {
initialToasts.forEach((toast) => this.push(toast));
window.adminToast = {
show: (message, type = 'success', title = null, duration = 4200) => this.push({
message,
type,
title,
duration
}),
success: (message, title = '操作成功', duration = 4200) => this.push({
message,
type: 'success',
title,
duration
}),
error: (message, title = '操作失败', duration = 4200) => this.push({
message,
type: 'error',
title,
duration
}),
};
},
/**
* 追加一条 Toast,并在指定时长后自动关闭。
*
* @param toast 待展示的提示对象
*/
push(toast) {
if (!toast?.message) {
return;
}
const normalizedToast = {
id: this.nextToastId++,
type: toast.type === 'error' ? 'error' : 'success',
title: toast.title,
message: toast.message,
duration: Number(toast.duration) > 0 ? Number(toast.duration) : 4200,
};
this.toasts.push(normalizedToast);
this.visibleIds.push(normalizedToast.id);
window.setTimeout(() => {
this.remove(normalizedToast.id);
}, normalizedToast.duration);
},
/**
* 关闭指定 Toast,并在退场动画后清理节点。
*
* @param {number} toastId
*/
remove(toastId) {
this.visibleIds = this.visibleIds.filter((visibleId) => visibleId !== toastId);
window.setTimeout(() => {
this.toasts = this.toasts.filter((toast) => toast.id !== toastId);
}, 220);
},
};
}
/**
* 后台全局弹窗组件。
*
* 提供 alert / confirm 两种模式,替换原生 alert/confirm。
*/
window.adminDialog = (function() {
const overlay = document.getElementById('admin-dialog-overlay');
const box = document.getElementById('admin-dialog-box');
const elIcon = document.getElementById('admin-dialog-icon');
const elTitle = document.getElementById('admin-dialog-title');
const elMsg = document.getElementById('admin-dialog-msg');
const elBtns = document.getElementById('admin-dialog-btns');
/** 关闭弹窗 */
function close() {
overlay.style.display = 'none';
}
/** 点击遮罩层关闭 */
overlay.addEventListener('click', function(e) {
if (e.target === overlay) close();
});
/**
* 创建按钮元素
*
* @param {string} label 按钮文字
* @param {string} color 按钮背景色
* @param {Function} onClick 点击回调
*/
function makeBtn(label, color, onClick) {
const btn = document.createElement('button');
btn.textContent = label;
btn.style.cssText = `padding:9px 24px; border-radius:8px; border:none; cursor:pointer;
font-size:14px; font-weight:700; color:#fff; background:${color};
transition:opacity .15s; box-shadow:0 3px 10px rgba(0,0,0,.12);`;
btn.onmouseover = () => btn.style.opacity = '.82';
btn.onmouseout = () => btn.style.opacity = '1';
btn.addEventListener('click', () => {
close();
if (onClick) onClick();
});
return btn;
}
/**
* 弹出提示框(仅「确定」按钮)
*
* @param {string} message 消息内容(支持 HTML
* @param {string} title 标题
* @param {string} icon 图标 Emoji
* @param {Function} onOk 确定回调
*/
function alert(message, title = '提示', icon = '️', onOk = null) {
elIcon.textContent = icon;
elTitle.textContent = title;
elMsg.innerHTML = message;
elBtns.innerHTML = '';
elBtns.appendChild(makeBtn('确定', '#4f46e5', onOk));
overlay.style.display = 'flex';
}
/**
* 弹出确认框(「确定」+「取消」按钮)
*
* @param {string} message 消息内容
* @param {string} title 标题
* @param {Function} onConfirm 确认回调
* @param {string} icon 图标 Emoji
*/
function confirm(message, title = '确认操作', onConfirm = null, icon = '⚠️') {
elIcon.textContent = icon;
elTitle.textContent = title;
elMsg.innerHTML = message;
elBtns.innerHTML = '';
const confirmBtn = makeBtn('确定', '#4f46e5', onConfirm);
const cancelBtn = makeBtn('取消', '#94a3b8', null);
elBtns.appendChild(confirmBtn);
elBtns.appendChild(cancelBtn);
overlay.style.display = 'flex';
}
return {
alert,
confirm,
close
};
})();
</script>
</body>
</html>