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

357 lines
9.0 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设计规范具有平滑过渡动画和响应式显示
*/
class CustomModal {
constructor(options = {}) {
this.id = options.id || 'customModal_' + Date.now();
this.title = options.title || '';
this.content = options.content || '';
this.size = options.size || 'medium'; // small, medium, large
this.showClose = options.showClose !== false;
this.onClose = options.onClose || null;
this.onConfirm = options.onConfirm || null;
this.confirmText = options.confirmText || '确定';
this.cancelText = options.cancelText || '取消';
this.showFooter = options.showFooter !== false;
this.backdrop = options.backdrop !== false;
this.modal = null;
}
show() {
// 创建模态框HTML
const modalHtml = this._createModalHtml();
// 移除已存在的模态框
const existing = document.getElementById(this.id);
if (existing) {
existing.remove();
}
// 添加到body
document.body.insertAdjacentHTML('beforeend', modalHtml);
this.modal = document.getElementById(this.id);
this.backdropEl = document.getElementById(this.id + '_backdrop');
// 添加显示动画
requestAnimationFrame(() => {
this.modal.classList.add('show');
if (this.backdropEl) {
this.backdropEl.classList.add('show');
}
document.body.style.overflow = 'hidden';
});
// 绑定事件
this._bindEvents();
}
hide() {
if (!this.modal) return;
this.modal.classList.remove('show');
if (this.backdropEl) {
this.backdropEl.classList.remove('show');
}
document.body.style.overflow = '';
// 动画结束后移除
setTimeout(() => {
if (this.modal && this.modal.parentNode) {
this.modal.remove();
}
if (this.backdropEl && this.backdropEl.parentNode) {
this.backdropEl.remove();
}
if (this.onClose) {
this.onClose();
}
}, 300);
}
_createModalHtml() {
const sizeClass = {
small: 'modal-sm',
medium: '',
large: 'modal-lg'
}[this.size];
const backdropHtml = this.backdrop ?
`<div class="custom-modal-backdrop" id="${this.id}_backdrop"></div>` : '';
return backdropHtml + `
<div class="custom-modal" id="${this.id}">
<div class="custom-modal-dialog ${sizeClass}">
<div class="custom-modal-content">
${this.title || this.showClose ? `
<div class="custom-modal-header">
${this.title ? `<h5 class="custom-modal-title">${this.title}</h5>` : ''}
${this.showClose ? `<button type="button" class="custom-modal-close" data-dismiss="modal">
<i class="bi bi-x-lg"></i>
</button>` : ''}
</div>
` : ''}
<div class="custom-modal-body">
${this.content}
</div>
${this.showFooter ? `
<div class="custom-modal-footer">
<button type="button" class="custom-modal-btn custom-modal-btn-cancel" data-dismiss="modal">
${this.cancelText}
</button>
${this.onConfirm ? `
<button type="button" class="custom-modal-btn custom-modal-btn-confirm">
${this.confirmText}
</button>
` : ''}
</div>
` : ''}
</div>
</div>
</div>
`;
}
_bindEvents() {
// 关闭按钮
const closeBtn = this.modal.querySelector('[data-dismiss="modal"]');
if (closeBtn) {
closeBtn.addEventListener('click', () => this.hide());
}
// 确认按钮
const confirmBtn = this.modal.querySelector('.custom-modal-btn-confirm');
if (confirmBtn && this.onConfirm) {
confirmBtn.addEventListener('click', () => {
if (this.onConfirm() !== false) {
this.hide();
}
});
}
// 点击背景关闭
if (this.backdropEl) {
this.backdropEl.addEventListener('click', () => this.hide());
}
// 点击内容区域不关闭
const content = this.modal.querySelector('.custom-modal-content');
if (content) {
content.addEventListener('click', (e) => e.stopPropagation());
}
}
updateContent(content) {
const body = this.modal?.querySelector('.custom-modal-body');
if (body) {
body.innerHTML = content;
}
}
}
// CSS样式通过JavaScript注入或单独引入CSS文件
const customModalStyles = `
<style id="custom-modal-styles">
.custom-modal-backdrop {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 1040;
opacity: 0;
transition: opacity 0.3s ease;
backdrop-filter: blur(4px);
}
.custom-modal-backdrop.show {
opacity: 1;
}
.custom-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1050;
display: flex;
align-items: center;
justify-content: center;
padding: 16px;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
}
.custom-modal.show {
opacity: 1;
pointer-events: auto;
}
.custom-modal-dialog {
width: 100%;
max-width: 500px;
margin: auto;
transform: scale(0.9) translateY(-20px);
transition: transform 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
}
.custom-modal.show .custom-modal-dialog {
transform: scale(1) translateY(0);
}
.custom-modal-dialog.modal-sm {
max-width: 400px;
}
.custom-modal-dialog.modal-lg {
max-width: 800px;
}
.custom-modal-content {
background: #fff;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
overflow: hidden;
max-height: 90vh;
display: flex;
flex-direction: column;
}
.custom-modal-header {
padding: 20px 24px;
border-bottom: 1px solid #f1f5f9;
display: flex;
align-items: center;
justify-content: space-between;
flex-shrink: 0;
}
.custom-modal-title {
font-size: 18px;
font-weight: 700;
color: #0f172a;
margin: 0;
}
.custom-modal-close {
background: none;
border: none;
font-size: 20px;
color: #94a3b8;
cursor: pointer;
padding: 4px;
line-height: 1;
transition: color 0.2s;
}
.custom-modal-close:hover {
color: #64748b;
}
.custom-modal-body {
padding: 24px;
overflow-y: auto;
flex: 1;
color: #334155;
font-size: 14px;
line-height: 1.6;
}
.custom-modal-footer {
padding: 16px 24px;
border-top: 1px solid #f1f5f9;
display: flex;
justify-content: flex-end;
gap: 12px;
flex-shrink: 0;
}
.custom-modal-btn {
padding: 10px 24px;
border-radius: 12px;
font-size: 14px;
font-weight: 600;
border: none;
cursor: pointer;
transition: all 0.2s;
}
.custom-modal-btn-cancel {
background: #f8fafc;
color: #64748b;
}
.custom-modal-btn-cancel:hover {
background: #f1f5f9;
}
.custom-modal-btn-confirm {
background: #0f172a;
color: #fff;
}
.custom-modal-btn-confirm:hover {
background: #1e293b;
transform: translateY(-1px);
}
.custom-modal-btn-confirm:active {
transform: translateY(0);
}
/* 响应式 */
@media (max-width: 576px) {
.custom-modal-dialog {
max-width: 100%;
margin: 0;
}
.custom-modal {
padding: 0;
align-items: flex-end;
}
.custom-modal-dialog {
transform: translateY(100%);
}
.custom-modal.show .custom-modal-dialog {
transform: translateY(0);
}
.custom-modal-content {
border-radius: 20px 20px 0 0;
max-height: 80vh;
}
}
</style>
`;
// 注入样式
if (!document.getElementById('custom-modal-styles')) {
document.head.insertAdjacentHTML('beforeend', customModalStyles);
}
// 导出到全局
window.CustomModal = CustomModal;
// 便捷方法
window.showCustomModal = function(options) {
const modal = new CustomModal(options);
modal.show();
return modal;
};
window.showCustomConfirm = function(message, title = '确认操作', onConfirm = null) {
return new CustomModal({
title: title,
content: `<p style="margin: 0;">${message}</p>`,
onConfirm: onConfirm,
showFooter: true
});
};