Files
wb-java-nc-shop/target/classes/static/js/ui-enhancements.js
2026-01-11 18:14:42 +08:00

290 lines
9.1 KiB
JavaScript
Raw Permalink 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容器
function ensureToastContainer() {
let container = document.getElementById('toastContainer');
if (!container) {
container = document.createElement('div');
container.id = 'toastContainer';
container.className = 'toast-container position-fixed top-0 end-0 p-3';
container.style.zIndex = '1060';
document.body.appendChild(container);
}
return container;
}
// 显示成功提示
function showSuccess(message, duration = 3000) {
const container = ensureToastContainer();
const toast = document.createElement('div');
toast.className = 'toast align-items-center text-white bg-success border-0';
toast.setAttribute('role', 'alert');
toast.setAttribute('aria-live', 'assertive');
toast.setAttribute('aria-atomic', 'true');
toast.innerHTML = `
<div class="d-flex">
<div class="toast-body">
<i class="bi bi-check-circle me-2"></i>${message}
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
`;
container.appendChild(toast);
const bsToast = new bootstrap.Toast(toast, { delay: duration });
bsToast.show();
toast.addEventListener('hidden.bs.toast', () => toast.remove());
}
// 显示错误提示
function showError(message, duration = 4000) {
const container = ensureToastContainer();
const toast = document.createElement('div');
toast.className = 'toast align-items-center text-white bg-danger border-0';
toast.setAttribute('role', 'alert');
toast.setAttribute('aria-live', 'assertive');
toast.setAttribute('aria-atomic', 'true');
toast.innerHTML = `
<div class="d-flex">
<div class="toast-body">
<i class="bi bi-exclamation-circle me-2"></i>${message}
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
`;
container.appendChild(toast);
const bsToast = new bootstrap.Toast(toast, { delay: duration });
bsToast.show();
toast.addEventListener('hidden.bs.toast', () => toast.remove());
}
// 显示警告提示
function showWarning(message, duration = 3000) {
const container = ensureToastContainer();
const toast = document.createElement('div');
toast.className = 'toast align-items-center text-white bg-warning border-0';
toast.setAttribute('role', 'alert');
toast.setAttribute('aria-live', 'assertive');
toast.setAttribute('aria-atomic', 'true');
toast.innerHTML = `
<div class="d-flex">
<div class="toast-body">
<i class="bi bi-exclamation-triangle me-2"></i>${message}
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
`;
container.appendChild(toast);
const bsToast = new bootstrap.Toast(toast, { delay: duration });
bsToast.show();
toast.addEventListener('hidden.bs.toast', () => toast.remove());
}
// 显示信息提示
function showInfo(message, duration = 3000) {
const container = ensureToastContainer();
const toast = document.createElement('div');
toast.className = 'toast align-items-center text-white bg-info border-0';
toast.setAttribute('role', 'alert');
toast.setAttribute('aria-live', 'assertive');
toast.setAttribute('aria-atomic', 'true');
toast.innerHTML = `
<div class="d-flex">
<div class="toast-body">
<i class="bi bi-info-circle me-2"></i>${message}
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
`;
container.appendChild(toast);
const bsToast = new bootstrap.Toast(toast, { delay: duration });
bsToast.show();
toast.addEventListener('hidden.bs.toast', () => toast.remove());
}
// 显示加载状态
function showLoading(elementId, message = '加载中...') {
const element = document.getElementById(elementId);
if (element) {
element.innerHTML = `
<div class="text-center py-5">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">${message}</span>
</div>
<p class="text-muted mt-3">${message}</p>
</div>
`;
}
}
// 按钮加载状态
function setButtonLoading(button, loading = true, originalText = null) {
if (loading) {
if (!button.dataset.originalText) {
button.dataset.originalText = button.innerHTML;
}
button.disabled = true;
button.innerHTML = `
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
处理中...
`;
} else {
button.disabled = false;
button.innerHTML = button.dataset.originalText || originalText || '提交';
delete button.dataset.originalText;
}
}
// 增强的fetch函数带加载状态和错误处理
async function enhancedFetch(url, options = {}) {
const { showLoading: showLoadingId, button: loadingButton, ...fetchOptions } = options;
// 显示加载状态
if (showLoadingId) {
showLoading(showLoadingId);
}
// 按钮加载状态
if (loadingButton) {
setButtonLoading(loadingButton, true);
}
try {
const response = await fetch(url, fetchOptions);
const data = await response.json();
// 恢复按钮状态
if (loadingButton) {
setButtonLoading(loadingButton, false);
}
return { response, data };
} catch (error) {
// 恢复按钮状态
if (loadingButton) {
setButtonLoading(loadingButton, false);
}
showError('网络请求失败,请检查网络连接');
throw error;
}
}
// 确认对话框(使用自定义模态框)
function confirmAction(message, title = '确认操作', confirmText = '确定', cancelText = '取消') {
return new Promise((resolve) => {
// 确保自定义模态框已加载
if (typeof CustomModal === 'undefined') {
console.error('CustomModal未加载请先引入custom-modal.js');
// 降级到原生confirm
resolve(confirm(message));
return;
}
const modal = new CustomModal({
title: title,
content: `<p style="margin: 0;">${message}</p>`,
confirmText: confirmText,
cancelText: cancelText,
onConfirm: () => {
resolve(true);
return true;
},
onClose: () => {
resolve(false);
}
});
modal.show();
});
}
// 表单验证增强
function validateForm(formId) {
const form = document.getElementById(formId);
if (!form) return false;
const requiredFields = form.querySelectorAll('[required]');
let isValid = true;
requiredFields.forEach(field => {
if (!field.value.trim()) {
field.classList.add('is-invalid');
isValid = false;
} else {
field.classList.remove('is-invalid');
field.classList.add('is-valid');
}
});
return isValid;
}
// 数字格式化
function formatNumber(num, decimals = 2) {
return parseFloat(num || 0).toFixed(decimals);
}
// 日期格式化
function formatDate(date, format = 'YYYY-MM-DD HH:mm:ss') {
if (!date) return '-';
const d = new Date(date);
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
const hours = String(d.getHours()).padStart(2, '0');
const minutes = String(d.getMinutes()).padStart(2, '0');
const seconds = String(d.getSeconds()).padStart(2, '0');
return format
.replace('YYYY', year)
.replace('MM', month)
.replace('DD', day)
.replace('HH', hours)
.replace('mm', minutes)
.replace('ss', seconds);
}
// 防抖函数
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// 节流函数
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// 导出到全局
window.UIEnhancements = {
showSuccess,
showError,
showWarning,
showInfo,
showLoading,
setButtonLoading,
enhancedFetch,
confirmAction,
validateForm,
formatNumber,
formatDate,
debounce,
throttle
};