Files
chatroom/resources/js/admin/admin-ui.js
T
2026-04-25 13:40:58 +08:00

212 lines
5.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 后台全局 UI 能力,提供 Toast 状态工厂和 adminDialog 弹窗入口。
/**
* 创建后台 Toast 状态工厂。
*
* @param {Array<object>} initialToasts
* @returns {object}
*/
function createAdminToastStore(initialToasts = []) {
return {
toasts: [],
visibleIds: [],
nextToastId: Date.now(),
/**
* 初始化 Toast 列表,并挂载全局调用方法。
*
* @returns {void}
*/
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 {object} toast
* @returns {void}
*/
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
* @returns {void}
*/
remove(toastId) {
this.visibleIds = this.visibleIds.filter((visibleId) => visibleId !== toastId);
window.setTimeout(() => {
this.toasts = this.toasts.filter((toast) => toast.id !== toastId);
}, 220);
},
};
}
/**
* 创建后台全局弹窗组件。
*
* @returns {{alert: Function, confirm: Function, close: Function}|null}
*/
function createAdminDialog() {
const overlay = document.getElementById("admin-dialog-overlay");
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");
if (!overlay || !elIcon || !elTitle || !elMsg || !elBtns) {
return null;
}
/**
* 关闭当前弹窗。
*
* @returns {void}
*/
function close() {
overlay.style.display = "none";
}
overlay.addEventListener("click", (event) => {
if (event.target === overlay) {
close();
}
});
/**
* 创建弹窗按钮。
*
* @param {string} label
* @param {string} color
* @param {Function|null} onClick
* @returns {HTMLButtonElement}
*/
function makeButton(label, color, onClick) {
const button = document.createElement("button");
button.textContent = label;
button.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);`;
button.addEventListener("mouseover", () => {
button.style.opacity = ".82";
});
button.addEventListener("mouseout", () => {
button.style.opacity = "1";
});
button.addEventListener("click", () => {
close();
if (onClick) {
onClick();
}
});
return button;
}
/**
* 弹出提示框。
*
* @param {string} message
* @param {string} title
* @param {string} icon
* @param {Function|null} onOk
* @returns {void}
*/
function alert(message, title = "提示", icon = "️", onOk = null) {
elIcon.textContent = icon;
elTitle.textContent = title;
elMsg.innerHTML = message;
elBtns.innerHTML = "";
elBtns.appendChild(makeButton("确定", "#4f46e5", onOk));
overlay.style.display = "flex";
}
/**
* 弹出确认框。
*
* @param {string} message
* @param {string} title
* @param {Function|null} onConfirm
* @param {string} icon
* @returns {void}
*/
function confirm(message, title = "确认操作", onConfirm = null, icon = "⚠️") {
elIcon.textContent = icon;
elTitle.textContent = title;
elMsg.innerHTML = message;
elBtns.innerHTML = "";
elBtns.appendChild(makeButton("确定", "#4f46e5", onConfirm));
elBtns.appendChild(makeButton("取消", "#94a3b8", null));
overlay.style.display = "flex";
}
return {
alert,
confirm,
close,
};
}
/**
* DOM 就绪后初始化后台全局弹窗。
*
* @returns {void}
*/
function bootAdminDialog() {
const adminDialog = createAdminDialog();
if (adminDialog) {
window.adminDialog = adminDialog;
}
}
window.createAdminToastStore = createAdminToastStore;
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", bootAdminDialog, { once: true });
} else {
bootAdminDialog();
}