// @ts-nocheck
class SweetalertSystem {
constructor() {
this.fieldRegistry = {};
this.modalRegistry = {}; // 添加模态类型注册表
this.nestedModalStack = []; // 添加嵌套模态栈
this.initCoreComponents();
// 初始化事件处理器映射
this.buttonHandlers = new Map();
this.addEntityButtons = null;
}
initCoreComponents() {
// 注册核心字段类型
this.registerFieldType('text', {
render: (config) => {
return `<div class="mb-3">
<label class="form-label">${config.label}${config.required ? '*' : ''}</label>
<input type="text" class="form-control" name="${config.name}"
value="${config.value || ''}" ${config.required ? 'required' : ''}
placeholder="${config.placeholder || ''}">
</div>`;
}
});
this.registerFieldType('select', {
render: (config) => {
const options = (config.options || [])
.map(opt => `<option value="${opt.value}" ${config.value == opt.value ? 'selected' : ''}>${opt.label}</option>`)
.join('');
return `<div class="mb-3">
<label class="form-label">${config.label}${config.required ? '*' : ''}</label>
<div class="input-group">
<select class="form-select" name="${config.name}" ${config.required ? 'required' : ''}>
<option value="">${config.placeholder || '请选择'}</option>
${options}
</select>
${config.addButton ? `
<button class="btn btn-outline-secondary add-entity-btn" type="button"
data-target-field="${config.name}">
<i class="fa fa-plus"></i> ${config.addButton.label}
</button>
` : ''}
</div>
</div>`;
},
// 更新 afterRender 方法
afterRender: (element, config) => {
if (config.select2) {
const $select = $(element).find('select');
const swalContainer = Swal.getContainer();
this.safeInitSelect2($select, {
theme: 'bootstrap',
placeholder: config.placeholder || '请选择',
allowClear: true,
dropdownParent: swalContainer || document.body
});
}
}
});
this.registerFieldType('number', {
render: (config) => {
return `<div class="mb-3">
<label class="form-label">${config.label}${config.required ? '*' : ''}</label>
<input type="number" class="form-control" name="${config.name}"
value="${config.value || ''}" ${config.required ? 'required' : ''}
step="${config.step || 1}" min="${config.min || 0}">
</div>`;
}
});
this.registerFieldType('checkbox', {
render: (config) => {
return `<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" name="${config.name}"
id="${config.name}" ${config.value ? 'checked' : ''}>
<label class="form-check-label" for="${config.name}">${config.label}</label>
</div>`;
}
});
}
// 修改注册方法 - 接受对象而不是函数
registerFieldType(type, definition) {
if (!definition.render || typeof definition.render !== 'function') {
throw new Error(`Field type '${type}' must have a render function`);
}
this.fieldRegistry[type] = definition;
}
// === 字段渲染方法 ===
renderTextField(config) {
return `<div class="mb-3">
<label class="form-label">${config.label}${config.required ? '*' : ''}</label>
<input type="text" class="form-control" name="${config.name}"
value="${config.value || ''}" ${config.required ? 'required' : ''}
placeholder="${config.placeholder || ''}">
</div>`;
}
renderSelectField(config) {
const options = (config.options || [])
.map(opt => `<option value="${opt.value}" ${config.value == opt.value ? 'selected' : ''}>${opt.label}</option>`)
.join('');
return `<div class="mb-3">
<label class="form-label">${config.label}${config.required ? '*' : ''}</label>
<div class="input-group">
<select class="form-select" name="${config.name}" ${config.required ? 'required' : ''}>
<option value="">${config.placeholder || '请选择'}</option>
${options}
</select>
${config.addButton ? `
<button class="btn btn-outline-secondary add-entity-btn" type="button"
data-target-field="${config.name}">
<i class="fa fa-plus"></i> ${config.addButton.label}
</button>
` : ''}
</div>
</div>`;
}
renderNumberField(config) {
return `<div class="mb-3">
<label class="form-label">${config.label}${config.required ? '*' : ''}</label>
<input type="number" class="form-control" name="${config.name}"
value="${config.value || ''}" ${config.required ? 'required' : ''}
step="${config.step || 1}" min="${config.min || 0}">
</div>`;
}
renderCheckboxField(config) {
return `<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" name="${config.name}"
id="${config.name}" ${config.value ? 'checked' : ''}>
<label class="form-check-label" for="${config.name}">${config.label}</label>
</div>`;
}
// === 核心方法 ===// === 核心方法 ===
/**
* 打开表单弹窗(支持多个新建按钮)
* @param {Object} config - 配置对象
*/
async openForm(config) {
const { title, fields, onSubmit, onAddEntity } = config;
// 生成表单HTML
const formHTML = fields.map(fieldConfig => {
if (!fieldConfig.type) return '<div class="error">Missing field type</div>';
const fieldType = this.fieldRegistry[fieldConfig.type];
if (!fieldType) return `<div class="error">Field type '${fieldConfig.type}' not supported</div>`;
return fieldType.render(fieldConfig);
}).join('');
// 创建弹窗
return new Promise((resolve) => {
Swal.fire({
title,
html: `<form>${formHTML}</form>`,
focusConfirm: false,
showCancelButton: true,
confirmButtonText: config.confirmButtonText || '保存',
cancelButtonText: config.cancelButtonText || '取消',
preConfirm: () => this.collectFormData(),
// ... 配置 ...
didOpen: () => {
// 使用 MutationObserver 确保 DOM 完全渲染
const observer = new MutationObserver(() => {
if (Swal.getPopup().querySelectorAll('.add-entity-btn').length > 0) {
observer.disconnect();
this.setupMultiButtonHandlers(config, onAddEntity);
}
});
observer.observe(Swal.getPopup(), {
childList: true,
subtree: true
});
},
willClose: () => {
this.cleanupMultiButtonHandlers();
this.destroyAllSelect2(); // 确保清理所有 Select2 实例
}
}).then((result) => {
if (result.isConfirmed) {
onSubmit(result.value);
resolve(true);
} else {
resolve(false);
}
});
});
}
collectFormData() {
const data = {};
const popup = Swal.getPopup();
const form = popup.querySelector('form');
if (form) {
const formData = new FormData(form);
for (const [name, value] of formData.entries()) {
// 处理多值字段
if (data[name]) {
if (!Array.isArray(data[name])) {
data[name] = [data[name]];
}
data[name].push(value);
} else {
data[name] = value;
}
}
}
return data;
}
setupEventHandlers(config) {
const popup = Swal.getPopup();
// 绑定添加按钮事件
popup.querySelectorAll('.add-entity-btn').forEach(btn => {
btn.addEventListener('click', async () => {
const targetField = btn.dataset.targetField;
if (config.onAddEntity) {
const newItem = await config.onAddEntity(targetField);
if (newItem) {
this.updateSelectField(popup, targetField, newItem);
}
}
});
});
}
// === 模态框注册方法 ===
registerModalType(name, config) {
// 添加afterRender处理
const enhancedConfig = {
...config,
afterRender: (config, container) => {
// 初始化Select2
container.find('.select2-field').each(function() {
const $select = $(this);
$select.select2({
theme: 'bootstrap',
placeholder: $select.attr('placeholder') || '请选择',
allowClear: true,
dropdownParent: $select.closest('.modal')
});
});
// 调用原始afterRender(如果存在)
if (config.afterRender) {
config.afterRender(config, container);
}
}
};
this.modalRegistry[name] = enhancedConfig;
}
// 注册实体表单
registerEntityForm(entityType, fields) {
this.registerModalType(entityType, {
template: `<div class="modal fade" id="${entityType}Modal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="${entityType}Title"></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body" id="${entityType}Body"></div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="button" class="btn btn-primary" id="${entityType}Submit">保存</button>
</div>
</div>
</div>
</div>`,
render: (config, container) => {
container.find(`#${entityType}Title`).text(config.title);
let formHTML = '';
// 确保fields是数组
const fields = Array.isArray(config.fields) ? config.fields : [];
fields.forEach(fieldConfig => {
const fieldType = this.fieldRegistry[fieldConfig.type];
if (fieldType && fieldType.render) {
formHTML += fieldType.render(fieldConfig);
}
});
container.find(`#${entityType}Body`).html(formHTML);
},
getFormData: (container) => {
const formData = {};
container.find('input, select, textarea').each(function() {
const $el = $(this);
const name = $el.attr('name');
if (!name) return;
if ($el.attr('type') === 'checkbox') {
formData[name] = $el.is(':checked');
} else {
formData[name] = $el.val();
}
});
return formData;
},
bindEvents: (config, container, onSubmit) => {
container.find(`#${entityType}Submit`).off('click').on('click', () => {
const formData = this.getFormData(container);
onSubmit(formData);
});
}
});
}
// 打开模态框
open(modalType, config, callbacks = {}) {
// 如果当前有激活的弹窗,先隐藏它
if (this.nestedModalStack.length > 0) {
const currentModal = this.nestedModalStack[this.nestedModalStack.length - 1];
currentModal.hide();
}
// 检查模态类型是否注册
if (!this.modalRegistry[modalType]) {
console.error(`未注册的模态类型: ${modalType}`);
return null;
}
const modalConfig = this.modalRegistry[modalType];
const modalId = `${modalType}Modal`;
let $modal = $(`#${modalId}`);
// 如果模态框不存在则创建
if ($modal.length === 0) {
$modal = $(modalConfig.template);
$('body').append($modal);
}
// 清除之前的模态框实例
if ($modal.data('bs.modal')) {
$modal.data('bs.modal').dispose();
}
// 渲染模态内容
modalConfig.render(config, $modal);
// 绑定事件
if (modalConfig.bindEvents) {
modalConfig.bindEvents(config, $modal, callbacks.onSubmit || (() => {}));
}
// 创建模态实例
const modalInstance = new bootstrap.Modal($modal[0]);
// 执行afterRender回调
if (modalConfig.afterRender) {
modalConfig.afterRender(config, $modal);
}
// 初始化Select2
$modal.find('.select2-field').each(function() {
const $select = $(this);
$select.select2({
theme: 'bootstrap',
placeholder: $select.attr('placeholder') || '请选择',
allowClear: true,
dropdownParent: $select.closest('.modal')
});
});
// 显示弹窗
modalInstance.show();
// 将新弹窗加入栈
const modalObj = {
hide: () => modalInstance.hide(),
show: () => modalInstance.show(),
getElement: () => $modal[0],
config
};
this.nestedModalStack.push(modalObj);
// 使用延迟确保 DOM 完全渲染
setTimeout(() => {
// 安全初始化 Select2
$modal.find('.select2-field').each((index, element) => {
const $select = $(element);
this.safeInitSelect2($select, {
theme: 'bootstrap',
placeholder: $select.attr('placeholder') || '请选择',
allowClear: true,
dropdownParent: $modal
});
});
// 绑定新建按钮事件
$modal.find('.add-entity-btn').on('click', async () => {
const entityType = $(this).data('entityType');
const targetField = $(this).data('targetField');
if (callbacks.onAddEntity) {
const newItem = await callbacks.onAddEntity(entityType, targetField);
if (newItem) {
this.updateSelectField($modal[0], targetField, newItem);
}
}
});
}, 50);
// 返回包含关闭方法的实例对象
return modalObj;
}
// 关闭当前弹窗并显示上一个
closeCurrentModal() {
if (this.nestedModalStack.length > 0) {
const currentModal = this.nestedModalStack.pop();
currentModal.hide();
// 如果有上一个弹窗,显示它
if (this.nestedModalStack.length > 0) {
const prevModal = this.nestedModalStack[this.nestedModalStack.length - 1];
prevModal.show();
}
}
}
// 获取表单数据
getFormData(container) {
const formData = {};
container.find('input, select, textarea').each(function() {
const $el = $(this);
const name = $el.attr('name');
if (!name) return;
if ($el.attr('type') === 'checkbox') {
formData[name] = $el.is(':checked');
} else if ($el.attr('type') === 'radio') {
if ($el.is(':checked')) {
formData[name] = $el.val();
}
} else {
formData[name] = $el.val();
}
});
return formData;
}
/**
* 设置多个按钮的事件处理
* @param {Object} config - 配置对象
* @param {Function} onAddEntity - 添加实体的回调函数
*/
setupMultiButtonHandlers(config, onAddEntity) {
const popup = Swal.getPopup();
// 确保弹窗存在
if (!popup) {
console.warn('弹窗不存在,无法绑定事件');
return;
}
// 移除旧的事件监听器(防止重复绑定)
this.cleanupMultiButtonHandlers();
// 绑定所有添加按钮事件
this.addEntityButtons = Array.from(popup.querySelectorAll('.add-entity-btn'));
this.addEntityButtons.forEach(btn => {
const handler = async () => {
const targetField = btn.dataset.targetField;
const entityType = btn.dataset.entityType;
if (onAddEntity) {
const newItem = await onAddEntity(entityType, targetField);
if (newItem) {
this.updateSelectField(popup, targetField, newItem);
}
}
};
btn.addEventListener('click', handler);
// 保存处理器以便后续清理
this.buttonHandlers.set(btn, handler);
});
}
/**
* 清理按钮事件处理器
*/
cleanupMultiButtonHandlers() {
if (this.addEntityButtons) {
this.addEntityButtons.forEach(btn => {
const handler = this.buttonHandlers.get(btn);
if (handler) {
btn.removeEventListener('click', handler);
this.buttonHandlers.delete(btn);
}
});
this.addEntityButtons = null;
}
}
/**
* 更新下拉框选项
* @param {HTMLElement} popup - 弹窗元素
* @param {string} fieldName - 字段名
* @param {Object} newItem - 新选项 {value, label}
*/
updateSelectField(popup, fieldName, newItem) {
// 确保弹窗仍然存在
if (!popup || !popup.isConnected) {
console.warn('弹窗已关闭,无法更新字段');
return;
}
const select = popup.querySelector(`select[name="${fieldName}"]`);
if (select) {
// 添加新选项
const option = document.createElement('option');
option.value = newItem.value;
option.textContent = newItem.label;
option.selected = true;
select.appendChild(option);
// 如果使用Select2,刷新组件
if ($(select).hasClass('select2-hidden-accessible')) {
$(select).trigger('change');
}
}
}
// 安全初始化 Select2
safeInitSelect2($select, options) {
try {
// 检查是否已初始化
if ($select.data('select2')) {
this.safeDestroySelect2($select);
}
// 设置 dropdownParent 确保在模态框内正确显示
const dropdownParent = options.dropdownParent || Swal.getPopup() || document.body;
$select.select2({
...options,
dropdownParent
});
$select.addClass('select2-initialized');
return true;
} catch (error) {
console.error('Select2 initialization failed:', error);
return false;
}
}
// 安全销毁 Select2
safeDestroySelect2($select) {
try {
if ($select.data('select2')) {
$select.select2('destroy');
}
$select.removeClass('select2-initialized');
return true;
} catch (error) {
console.error('Select2 destruction failed:', error);
return false;
}
}
// 清理所有 Select2 实例
destroyAllSelect2() {
const popup = Swal.getPopup();
if (popup) {
$(popup).find('.select2-initialized').each((index, element) => {
this.safeDestroySelect2($(element));
});
}
}
}
export default SweetalertSystem;
SweetalertSystem2.js:472 Uncaught TypeError: Cannot read properties of null (reading ‘querySelectorAll’)
at SweetalertSystem.open (SweetalertSystem2.js:472:20)
at HTMLButtonElement.showDingdanModal (tianjia3.js:316:55)
at HTMLButtonElement.dispatch (jquery-3.2.1.min.js:
最新发布