Files
SMANAGE/backup/Manage/lib/toast/script.js
T

307 lines
7.3 KiB
JavaScript

// toast.js - 统一 Toast 管理
// 全局 Toast 管理器
class ToastManager {
constructor() {
this.toasts = new Map();
this.nextId = 1;
this.setupParentListener();
}
// 设置父页面监听器
setupParentListener() {
if (this.isTopWindow()) {
window.addEventListener('message', this.handleMessage.bind(this));
}
}
// 处理跨页面消息
handleMessage(event) {
const { action, toastId, options } = event.data;
switch (action) {
case 'create':
this.createToast(options);
break;
case 'close':
this.closeToast(toastId);
break;
case 'closeAllFromSource':
this.closeAllFromSource(event.source);
break;
}
}
// 创建 Toast
createToast(options) {
const toastId = this.nextId++;
const container = this.getToastContainer();
const toast = document.createElement('div');
// 设置基本类
toast.className = `toast enter toast-${options.type || 'info'}`;
toast.dataset.toastId = toastId;
// 设置图标
const icons = {
success: 'fa-check-circle',
info: 'fa-info-circle',
error: 'fa-times-circle',
warning: 'fa-exclamation-circle',
debug: 'fa-bug',
unknown: 'fa-question-circle'
};
const icon = icons[options.type] || icons['unknown'];
// 构建 Toast 内容
toast.innerHTML = `
<div class="toast-header">
<div class="toast-title">
<i class="fas ${icon}"></i>
<span>${options.title || '通知'}</span>
</div>
<button class="toast-close">
<i class="fas fa-times"></i>
</button>
</div>
${options.subtitle ? `<div class="toast-subtitle">${options.subtitle}</div>` : ''}
<div class="toast-progress">
<div class="toast-progress-bar"></div>
</div>
`;
// 添加到容器
container.appendChild(toast);
// 强制重绘
toast.offsetHeight;
// 移除进入动画类
setTimeout(() => {
toast.classList.remove('enter');
}, 300);
// 自动关闭
let timeoutId;
const startAutoClose = () => {
if (options.duration !== 0) {
const duration = options.duration || 3000;
timeoutId = setTimeout(() => {
this.closeToast(toastId);
}, duration);
// 进度条动画
const progressBar = toast.querySelector('.toast-progress-bar');
progressBar.style.width = '0%';
}
};
// 存储 Toast 信息
this.toasts.set(toastId, {
element: toast,
timeoutId,
source: options.source,
close: () => {
toast.classList.add('exit');
setTimeout(() => {
toast.classList.add('hiding');
setTimeout(() => {
toast.remove();
this.toasts.delete(toastId);
}, 300);
}, 300);
}
});
// 开始自动关闭
startAutoClose();
// 点击关闭按钮
const closeButton = toast.querySelector('.toast-close');
closeButton.addEventListener('click', () => {
this.closeToast(toastId);
});
return toastId;
}
// 关闭 Toast
closeToast(toastId) {
const toastInfo = this.toasts.get(toastId);
if (toastInfo) {
clearTimeout(toastInfo.timeoutId);
toastInfo.close();
}
}
// 关闭来自特定源的所有 Toast
closeAllFromSource(source) {
for (const [toastId, toastInfo] of this.toasts) {
if (toastInfo.source === source) {
this.closeToast(toastId);
}
}
}
// 获取或创建 Toast 容器
getToastContainer() {
let container = document.getElementById('toast-container');
if (!container) {
container = document.createElement('div');
container.className = 'toast-container';
container.id = 'toast-container';
document.body.appendChild(container);
}
return container;
}
// 检查是否是顶层窗口
isTopWindow() {
try {
return window.self === window.top;
} catch (e) {
return true;
}
}
}
// 全局 Toast 管理器实例
const toastManager = new ToastManager();
// 获取正确的父窗口
function getParentWindow() {
try {
// 尝试获取父窗口的父窗口
if (window.parent && window.parent.parent && window.parent.parent !== window) {
return window.parent.parent;
}
// 否则使用直接父窗口
return window.parent;
} catch (e) {
console.warn('无法访问父窗口,使用当前窗口:', e);
return window;
}
}
// Toast API
const Toast = {
// 显示 Toast
show: function(options) {
// 添加来源信息
const source = window.name || 'unknown';
options = { ...options, source };
if (toastManager.isTopWindow()) {
// 在顶层窗口直接创建
return toastManager.createToast(options);
} else {
try {
// 获取正确的父窗口
const parent = getParentWindow();
// 在子窗口通过消息传递创建
parent.postMessage({
action: 'create',
options
}, '*');
// 设置页面卸载时清理 Toast
window.addEventListener('beforeunload', () => {
parent.postMessage({
action: 'closeAllFromSource',
source
}, '*');
});
// 返回一个代理对象
return {
close: () => {
parent.postMessage({
action: 'close',
toastId: options.toastId
}, '*');
}
};
} catch (e) {
console.error('Toast跨页面通信失败:', e);
return toastManager.createToast(options);
}
}
},
// 快捷方法
success: function(title, subtitle, duration) {
return this.show({
type: 'success',
title,
subtitle,
duration
});
},
info: function(title, subtitle, duration) {
return this.show({
type: 'info',
title,
subtitle,
duration
});
},
error: function(title, subtitle, duration) {
return this.show({
type: 'error',
title,
subtitle,
duration
});
},
warning: function(title, subtitle, duration) {
return this.show({
type: 'warning',
title,
subtitle,
duration
});
},
debug: function(title, subtitle, duration) {
return this.show({
type: 'debug',
title,
subtitle,
duration
});
},
unknown: function(title, subtitle, duration) {
return this.show({
type: 'unknown',
title,
subtitle,
duration
});
}
};
// 导出 Toast API
window.Toast = Toast;
// 如果是在子页面,确保父页面加载了 Toast 脚本
if (!toastManager.isTopWindow()) {
try {
// 获取正确的父窗口
const parent = getParentWindow();
// 检查父页面是否有 Toast 管理器
if (!parent || !parent.Toast) {
console.warn('父页面未加载Toast脚本,Toast功能可能受限');
}
} catch (e) {
console.warn('无法访问父页面,Toast功能可能受限:', e);
}
}