初始化v1
This commit is contained in:
289
target/classes/static/js/ui-enhancements.js
Normal file
289
target/classes/static/js/ui-enhancements.js
Normal file
@@ -0,0 +1,289 @@
|
||||
/**
|
||||
* 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
|
||||
};
|
||||
Reference in New Issue
Block a user