Files
ai_dianshang/web/assets/js/toast.js
2025-11-28 15:18:10 +08:00

497 lines
15 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.

/**
* Toast 消息提醒组件 - iOS风格
* 使用方法:
* Toast.show('消息内容')
* Toast.success('成功消息')
* Toast.error('错误消息')
* Toast.warning('警告消息')
* Toast.info('提示消息')
*/
const Toast = (function() {
// 消息类型配置
const TYPES = {
success: {
icon: '✓',
color: '#34C759',
iconBg: 'rgba(52, 199, 89, 0.15)'
},
error: {
icon: '✕',
color: '#FF3B30',
iconBg: 'rgba(255, 59, 48, 0.15)'
},
warning: {
icon: '!',
color: '#FF9500',
iconBg: 'rgba(255, 149, 0, 0.15)'
},
info: {
icon: 'i',
color: '#007AFF',
iconBg: 'rgba(0, 122, 255, 0.15)'
}
};
// 默认配置
const DEFAULT_OPTIONS = {
duration: 2500, // 显示时长(毫秒)
position: 'top-center', // 位置top-center, top-right, bottom-center, center
showIcon: true, // 是否显示图标
animation: true // 是否使用动画
};
// 当前显示的toast队列
let toastQueue = [];
let container = null;
/**
* 初始化容器
*/
function initContainer() {
if (!container) {
container = document.createElement('div');
container.className = 'toast-container';
document.body.appendChild(container);
}
return container;
}
/**
* 创建Toast元素
*/
function createToast(message, type = 'info', options = {}) {
const opts = { ...DEFAULT_OPTIONS, ...options };
const config = TYPES[type] || TYPES.info;
const toast = document.createElement('div');
toast.className = `toast toast-${type} toast-${opts.position}`;
if (opts.animation) {
toast.classList.add('toast-enter');
}
// 构建toast内容
let html = '<div class="toast-content">';
if (opts.showIcon) {
html += `
<div class="toast-icon" style="background-color: ${config.iconBg}; color: ${config.color}">
<span>${config.icon}</span>
</div>
`;
}
html += `
<div class="toast-message">${message}</div>
</div>`;
toast.innerHTML = html;
return { element: toast, duration: opts.duration };
}
/**
* 显示Toast
*/
function show(message, type = 'info', options = {}) {
const toastContainer = initContainer();
const { element, duration } = createToast(message, type, options);
// 添加到DOM
toastContainer.appendChild(element);
toastQueue.push(element);
// 触发入场动画
setTimeout(() => {
element.classList.remove('toast-enter');
element.classList.add('toast-visible');
}, 10);
// 自动移除
setTimeout(() => {
hide(element);
}, duration);
return element;
}
/**
* 隐藏Toast
*/
function hide(toastElement) {
if (!toastElement || !toastElement.parentNode) return;
toastElement.classList.remove('toast-visible');
toastElement.classList.add('toast-exit');
setTimeout(() => {
if (toastElement.parentNode) {
toastElement.parentNode.removeChild(toastElement);
}
// 从队列中移除
const index = toastQueue.indexOf(toastElement);
if (index > -1) {
toastQueue.splice(index, 1);
}
// 如果队列为空,移除容器
if (toastQueue.length === 0 && container) {
container.remove();
container = null;
}
}, 300);
}
/**
* 清除所有Toast
*/
function clear() {
toastQueue.forEach(toast => hide(toast));
toastQueue = [];
}
// 快捷方法
function success(message, options) {
return show(message, 'success', options);
}
function error(message, options) {
return show(message, 'error', options);
}
function warning(message, options) {
return show(message, 'warning', options);
}
function info(message, options) {
return show(message, 'info', options);
}
/**
* 提示对话框iOS风格只有一个确定按钮
*/
function alert(options = {}) {
// 支持直接传字符串或对象
if (typeof options === 'string') {
options = { message: options };
}
return new Promise((resolve) => {
const opts = {
title: options.title || '',
message: options.message || '',
confirmText: options.confirmText || i18n?.t?.('confirm') || '确定',
confirmColor: options.confirmColor || '#007AFF',
...options
};
// 创建遮罩层
const overlay = document.createElement('div');
overlay.className = 'toast-overlay';
// 创建对话框
const dialog = document.createElement('div');
dialog.className = 'toast-dialog';
let html = '<div class="toast-dialog-content">';
if (opts.title) {
html += `<div class="toast-dialog-title">${opts.title}</div>`;
}
if (opts.message) {
html += `<div class="toast-dialog-message">${opts.message}</div>`;
}
// 只有一个确定按钮
html += `
<div class="toast-dialog-buttons toast-dialog-single-button">
<button class="toast-dialog-button toast-dialog-confirm" style="color: ${opts.confirmColor}">
${opts.confirmText}
</button>
</div>
</div>`;
dialog.innerHTML = html;
// 添加到DOM
overlay.appendChild(dialog);
document.body.appendChild(overlay);
// 触发动画
setTimeout(() => {
overlay.classList.add('toast-overlay-visible');
dialog.classList.add('toast-dialog-visible');
}, 10);
// 按钮事件
const confirmBtn = dialog.querySelector('.toast-dialog-confirm');
function close() {
overlay.classList.remove('toast-overlay-visible');
dialog.classList.remove('toast-dialog-visible');
setTimeout(() => {
overlay.remove();
}, 300);
}
confirmBtn.addEventListener('click', () => {
close();
resolve(true);
});
// 点击遮罩关闭
if (opts.closeOnClickOverlay !== false) {
overlay.addEventListener('click', (e) => {
if (e.target === overlay) {
close();
resolve(true);
}
});
}
});
}
/**
* 确认对话框iOS风格
*/
function confirm(options = {}) {
return new Promise((resolve, reject) => {
const opts = {
title: options.title || '',
message: options.message || '',
confirmText: options.confirmText || i18n.t('confirm') || '确定',
cancelText: options.cancelText || i18n.t('cancel') || '取消',
confirmColor: options.confirmColor || '#007AFF',
cancelColor: options.cancelColor || '#8E8E93',
...options
};
// 创建遮罩层
const overlay = document.createElement('div');
overlay.className = 'toast-overlay';
// 创建对话框
const dialog = document.createElement('div');
dialog.className = 'toast-dialog';
let html = '<div class="toast-dialog-content">';
if (opts.title) {
html += `<div class="toast-dialog-title">${opts.title}</div>`;
}
if (opts.message) {
html += `<div class="toast-dialog-message">${opts.message}</div>`;
}
html += `
<div class="toast-dialog-buttons">
<button class="toast-dialog-button toast-dialog-cancel" style="color: ${opts.cancelColor}">
${opts.cancelText}
</button>
<button class="toast-dialog-button toast-dialog-confirm" style="color: ${opts.confirmColor}">
${opts.confirmText}
</button>
</div>
</div>`;
dialog.innerHTML = html;
// 添加到DOM
overlay.appendChild(dialog);
document.body.appendChild(overlay);
// 触发动画
setTimeout(() => {
overlay.classList.add('toast-overlay-visible');
dialog.classList.add('toast-dialog-visible');
}, 10);
// 按钮事件
const confirmBtn = dialog.querySelector('.toast-dialog-confirm');
const cancelBtn = dialog.querySelector('.toast-dialog-cancel');
function close() {
overlay.classList.remove('toast-overlay-visible');
dialog.classList.remove('toast-dialog-visible');
setTimeout(() => {
overlay.remove();
}, 300);
}
confirmBtn.addEventListener('click', () => {
close();
resolve(true);
});
cancelBtn.addEventListener('click', () => {
close();
resolve(false);
});
// 点击遮罩关闭
if (opts.closeOnClickOverlay !== false) {
overlay.addEventListener('click', (e) => {
if (e.target === overlay) {
close();
resolve(false);
}
});
}
});
}
/**
* 输入对话框iOS风格
*/
function prompt(options = {}) {
return new Promise((resolve, reject) => {
const opts = {
title: options.title || '',
message: options.message || '',
placeholder: options.placeholder || '',
defaultValue: options.defaultValue || '',
inputType: options.inputType || 'text',
confirmText: options.confirmText || i18n.t('confirm') || '确定',
cancelText: options.cancelText || i18n.t('cancel') || '取消',
confirmColor: options.confirmColor || '#007AFF',
cancelColor: options.cancelColor || '#8E8E93',
maxLength: options.maxLength || null,
...options
};
// 创建遮罩层
const overlay = document.createElement('div');
overlay.className = 'toast-overlay';
// 创建对话框
const dialog = document.createElement('div');
dialog.className = 'toast-dialog toast-prompt-dialog';
let html = '<div class="toast-dialog-content">';
if (opts.title) {
html += `<div class="toast-dialog-title">${opts.title}</div>`;
}
if (opts.message) {
html += `<div class="toast-dialog-message">${opts.message}</div>`;
}
// 输入框
const maxLengthAttr = opts.maxLength ? `maxlength="${opts.maxLength}"` : '';
html += `
<div class="toast-input-wrapper">
<input
type="${opts.inputType}"
class="toast-input"
placeholder="${opts.placeholder}"
value="${opts.defaultValue}"
${maxLengthAttr}
autocomplete="off"
/>
</div>
`;
html += `
<div class="toast-dialog-buttons">
<button class="toast-dialog-button toast-dialog-cancel" style="color: ${opts.cancelColor}">
${opts.cancelText}
</button>
<button class="toast-dialog-button toast-dialog-confirm" style="color: ${opts.confirmColor}">
${opts.confirmText}
</button>
</div>
</div>`;
dialog.innerHTML = html;
// 添加到DOM
overlay.appendChild(dialog);
document.body.appendChild(overlay);
// 获取输入框
const input = dialog.querySelector('.toast-input');
const confirmBtn = dialog.querySelector('.toast-dialog-confirm');
const cancelBtn = dialog.querySelector('.toast-dialog-cancel');
// 触发动画
setTimeout(() => {
overlay.classList.add('toast-overlay-visible');
dialog.classList.add('toast-dialog-visible');
// 自动聚焦并选中文本
input.focus();
if (opts.defaultValue) {
input.select();
}
}, 10);
function close() {
overlay.classList.remove('toast-overlay-visible');
dialog.classList.remove('toast-dialog-visible');
setTimeout(() => {
overlay.remove();
}, 300);
}
function handleConfirm() {
const value = input.value;
close();
resolve(value);
}
function handleCancel() {
close();
resolve(null);
}
// 按钮事件
confirmBtn.addEventListener('click', handleConfirm);
cancelBtn.addEventListener('click', handleCancel);
// 回车确认
input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
handleConfirm();
}
});
// ESC取消
input.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
handleCancel();
}
});
// 点击遮罩关闭
if (opts.closeOnClickOverlay !== false) {
overlay.addEventListener('click', (e) => {
if (e.target === overlay) {
handleCancel();
}
});
}
});
}
// 导出API
return {
show,
success,
error,
warning,
info,
alert,
confirm,
prompt,
clear,
hide
};
})();
// 全局暴露
window.Toast = Toast;