模組-bootstrap5 Modal-動態加入TEXTBOX 欄位設置

此功能是透過GPT產生架構,調整後的成果

廢言,有了AIGPT後,是增加了程式開發速度,也讓前端不熟的我能做到一些功能….

有需求是某一欄位可以多筆輸入,但DB只有一欄位,要如何呈現,當下想法就是Modal讓這欄位資料在此輸入,確認何用','串起來,存回!

 

以下是內容

/**
 * 動態輸入框模態彈窗 - 動態生成 TEXTBOX
 * @param {Object} options - 彈窗的選項
 * @param {string} options.title - 彈窗標題
 * @param {string} options.okText - 確定按鈕文字
 * @param {string} options.cancelText - 取消按鈕文字
 * @param {function} options.onOk - 確定按鈕回調函數
 * @param {function} options.onCancel - 取消按鈕回調函數
 * @param {boolean} options.showOkButton - 是否顯示確定按鈕
 * @param {boolean} options.cancelButton - 是否顯示取消按鈕
 * @param {string} options.backdrop - 背景設置
 * @param {boolean} options.keyboard - 是否允許鍵盤關閉
 * @param {Array} options.fields - 用戶自定義欄位
 * @param {number} options.fieldCount - 欄位數量
 */
function dynamicInputModal(options) {
    const defaults = {
        title: '提醒',
        okText: '確定',
        cancelText: '取消',
        onOk: (values) => { console.log(values); },
        onCancel: () => { },
        showOkButton: true,
        cancelButton: true,
        backdrop: 'static',
        keyboard: false,
        fields: [], // 用戶自定義欄位
        fieldCount: 5 // 默認欄位數量
    };

    options = { ...defaults, ...options };

    // 生成唯一ID
    const modalId = 'alertModal_' + Date.now();

    // 根據欄位配置生成動態的輸入框 HTML
    let dynamicFieldsHtml = '';
    options.fields.forEach((field) => {
        const { name, label, defaultValue = '', placeholder, oninput, maxLength, minLength, pattern } = field;

        // 設定欄位數量以 `fieldCount` 為主,並填充不足的值
        const values = defaultValue ? defaultValue.split(',').map(v => v.trim()).filter(v => v) : [];
        const finalFieldCount = Math.max(values.length, options.fieldCount);

        // 如果 values 的長度不足 finalFieldCount,則填充空字串
        while (values.length < finalFieldCount) {
            values.push('');
        }

        // 生成動態欄位 HTML
        //values.forEach((value, idx) => {
        //    dynamicFieldsHtml += `
        //    <div class="col-md-6">
        //        <label>${label || name} ${idx + 1}</label>
        //        <input type="text"
        //            class="form-control ${name}-field"
        //            value="${value.trim()}"
        //            ${maxLength ? `maxlength="${maxLength}"` : ''}
        //            ${minLength ? `minlength="${minLength}"` : ''}
        //            ${pattern ? `pattern="${pattern}"` : ''}
        //            oninput="validateField(this, '${pattern}', ${minLength}, ${maxLength})">
        //        ${minLength ? `<small class="form-text text-muted">至少 ${minLength} 個字元</small>` : ''}
        //        ${maxLength ? `<small class="form-text text-muted">最多 ${maxLength} 個字元</small>` : ''}
        //        ${pattern ? `<small class="form-text text-muted">必須符合格式: ${pattern}</small>` : ''}
        //        <div class="invalid-feedback">輸入不符合格式要求</div>
        //    </div>
        //`;
        //});
        values.forEach((value, idx) => {
            dynamicFieldsHtml += `
            <div class="col-md-6">
                <label>${label || name} ${idx + 1}</label>
                <input type="text" placeholder="${placeholder}" maxLength="${maxLength}"
                    class="form-control ${name}-field" 
                    value="${value.trim()}" 
                    ${maxLength ? `maxlength="${maxLength}"` : ''}
                    ${minLength ? `minlength="${minLength}"` : ''}
                    ${pattern ? `pattern="${pattern}"` : ''}
                    ${oninput ? `oninput="${oninput}"` : ''}
                    >                
               
            </div>
        `;
        });
    });

    // 生成並顯示彈窗
    const html = `
        <div class="modal fade" id="${modalId}" data-bs-backdrop="${options.backdrop}" data-bs-keyboard="${options.keyboard}">
            <div class="modal-dialog">
                <div class="modal-content position-relative">
                    <div class="position-absolute top-0 end-0 mt-2 me-2 z-1">
                        <button class="btn-close btn btn-sm btn-circle d-flex flex-center transition-base" type="button" data-bs-dismiss="modal" aria-label="Close"></button>
                    </div>
                    <div class="modal-body p-0">
                        <div class="rounded-top-3 py-3 ps-4 pe-6 bg-200">
                            <h4 class="mb-0 fw-semi-bold">${options.title}</h4>
                        </div>
                        <div class="p-4 pt-3">
                            <div class="row gx-2 gy-3 mx-0 pb-1 scrollbar" style="max-height: calc(100vh - 235px);margin-left: 0px;">
                                ${dynamicFieldsHtml}
                            </div>
                            <div class="d-flex justify-content-end gap-2 mt-3 pt-2">
                                ${options.cancelButton ? `<button type="button" class="btn btn-secondary btnNo">${options.cancelText}</button>` : ''}
                                ${options.showOkButton ? `<button type="button" class="btn btn-primary btnYes">${options.okText}</button>` : ''}
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    `;

    // 添加彈窗 HTML 到頁面中
    document.body.insertAdjacentHTML('beforeend', html);

    // 獲取模態框元素
    const modalElement = document.getElementById(modalId);
    modalElement.style.zIndex = '1060';

    // 創建新的背景遮罩
    const backdrop = document.createElement('div');
    backdrop.className = 'modal-backdrop fade show';
    backdrop.style.zIndex = '1059';
    document.body.appendChild(backdrop);

    // 顯示彈窗
    const modal = new bootstrap.Modal(modalElement);
    modal.show();

    // 定義關閉模態框的函數
    const closeModal = () => {
        modal.hide();
        modalElement.addEventListener('hidden.bs.modal', () => {
            modalElement.remove();
            backdrop.remove();
        }, { once: true });
    };

    // 綁定確定按鈕的點擊事件
    if (options.showOkButton) {
        modalElement.querySelector('.btnYes').addEventListener('click', () => {
            const values = {};
            let valid = true; // 驗證標誌

            options.fields.forEach(field => {
                const inputs = modalElement.querySelectorAll(`.${field.name}-field`);
                values[field.name] = Array.from(inputs).map(input => input.value);

                inputs.forEach(input => {
                    // 驗證最小長度
                    if (field.minLength && input.value.length < field.minLength) {
                        valid = false;
                        input.classList.add('is-invalid');
                    }
                    // 驗證最大長度
                    if (field.maxLength && input.value.length > field.maxLength) {
                        valid = false;
                        input.classList.add('is-invalid');
                    }
                    // 驗證正規表達式
                    if (field.pattern && !new RegExp(field.pattern).test(input.value)) {
                        valid = false;
                        input.classList.add('is-invalid');
                    }
                });
            });

            // 只有在所有欄位通過驗證後才執行回調
            if (valid) {
                if (typeof options.onOk === 'function') {
                    options.onOk(values);
                }
                closeModal();
            }
        });
    }

    // 綁定取消按鈕的點擊事件
    if (options.cancelButton) {
        modalElement.querySelector('.btnNo').addEventListener('click', () => {
            console.log('使用者取消操作');
            if (typeof options.onCancel === 'function') {
                options.onCancel();
            }
            closeModal();
        });
    }

    // 綁定關閉按鈕的點擊事件
    modalElement.querySelector('.btn-close').addEventListener('click', closeModal);

    // 如果 backdrop 設置為 true,點擊背景也關閉模態框
    if (options.backdrop !== 'static') {
        backdrop.addEventListener('click', closeModal);
    }
}
範例:GPT給予的寫法設定有點不符合使用,所以程式內我修改簡易版

dynamicInputModal({
    title: '資料填寫',
    fieldCount: 5, // 欄位數量,默認為 5
    fields: [
        {
            name: 'username', 
            label: '用戶名稱', 
            defaultValue: 'NA1,NA2,NA3', 
            minLength: 3, 
            maxLength: 10, 
            pattern: '^[A-Za-z0-9]+$' 
        },
        {
            name: 'macAddress', 
            label: 'MAC 地址', 
            defaultValue: '00:1A:2B:3C:4D:5E,00:1A:2B:3C:4D:5F', 
            pattern: '^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$'
        },
        {
            name: 'ipAddress', 
            label: 'IP 地址', 
            defaultValue: '192.168.0.1,192.168.0.2', 
            pattern: '^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$'
        }
    ],
    onOk: (values) => {
        console.log('提交的資料:', values);
    },
    onCancel: () => {
        console.log('使用者取消操作');
    }
});
簡易版
我將pattern: '^[A-Za-z0-9]+$' 改成 呼叫oninput 自訂檢核邏輯涵式。

 dynamicInputModal({
     title: 'MAC',
     fieldCount: 5, // 欄位數量,默認為 5
     fields: [                           
         {
             name: 'macAddress',
             label: 'MAC ',
             defaultValue: mac,
             placeholder: "00:1A:2B:3C:4D:5E",
             oninput:"檢查邏輯(this)",
             maxLength: 17
             // pattern: '^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$'
         },                           
     ],
     onOk: (values) => {
         
         this.editmodel.mac=values.macAddress.filter(value => value).join(',');
        
     },
     onCancel: () => {
         console.log('使用者取消操作');
     }
 });
 
 

這樣改,好像可以用,就先這用…用…