diff --git a/excel-editor.html b/excel-editor.html index 02f5fa5..bdf6869 100644 --- a/excel-editor.html +++ b/excel-editor.html @@ -218,21 +218,33 @@
-
+

1. Ключевое поле для поиска

+

По какому полю искать совпадения (номер телефона или лицевой счет)

+
- +
+
+ +
- +
-
- -
+

2. Какие столбцы заполнять

+

Укажите, какие столбцы из какого поля брать

+ +
+
+ +
diff --git a/excel-editor.js b/excel-editor.js index ce47050..1e6c13f 100644 --- a/excel-editor.js +++ b/excel-editor.js @@ -153,45 +153,84 @@ function populateFieldSelectors() { } }); - // Заполняем чекбоксы полей для заполнения - const fieldsContainer = document.getElementById('fields-to-fill'); - fieldsContainer.innerHTML = ''; + // Создаём первую строку сопоставления + addMappingRow(); +} + +function addMappingRow() { + const container = document.getElementById('column-mappings'); + const mainFields = Object.keys(mainData[0] || {}); + const templateFields = Object.keys(templateData[0] || {}); + const rowId = 'mapping-' + Date.now(); + const row = document.createElement('div'); + row.id = rowId; + row.className = 'mapping-row'; + row.style.cssText = 'display: grid; grid-template-columns: 1fr auto 1fr auto; gap: var(--kt-ai-space-3); align-items: center; margin-bottom: var(--kt-ai-space-2);'; + + // Заполняем столбец шаблона + let templateOptions = ''; templateFields.forEach(field => { - const div = document.createElement('div'); - div.style.cssText = 'display: inline-flex; align-items: center; gap: var(--kt-ai-space-2); margin-right: var(--kt-ai-space-3); margin-bottom: var(--kt-ai-space-2);'; - - const checkbox = document.createElement('input'); - checkbox.type = 'checkbox'; - checkbox.id = 'field-' + field; - checkbox.value = field; - checkbox.checked = true; // По умолчанию все поля выбраны - checkbox.onchange = processMapping; - - const label = document.createElement('label'); - label.htmlFor = 'field-' + field; - label.textContent = field; - label.style.fontSize = 'var(--kt-ai-text-sm)'; - - div.appendChild(checkbox); - div.appendChild(label); - fieldsContainer.appendChild(div); + templateOptions += ''; }); + // Заполняем столбец основного файла + let mainOptions = ''; + mainFields.forEach(field => { + mainOptions += ''; + }); + + row.innerHTML = ` +
+ + +
+
+ +
+
+ + +
+ + `; + + container.appendChild(row); processMapping(); } +function removeMappingRow(rowId) { + const row = document.getElementById(rowId); + if (row) { + row.remove(); + processMapping(); + } +} + +function getColumnMappings() { + const mappings = []; + document.querySelectorAll('.template-column').forEach((select, index) => { + const mainSelect = document.querySelectorAll('.main-column')[index]; + if (select && mainSelect) { + mappings.push({ + templateColumn: select.value, + mainColumn: mainSelect.value + }); + } + }); + return mappings; +} + function processMapping() { const mainKeyField = document.getElementById('main-key-field').value; const templateKeyField = document.getElementById('template-key-field').value; if (!mainKeyField || !templateKeyField) return; - // Собираем поля для заполнения - const fieldsToFill = []; - document.querySelectorAll('#fields-to-fill input[type="checkbox"]:checked').forEach(cb => { - fieldsToFill.push(cb.value); - }); + // Получаем ручные сопоставления столбцов + const columnMappings = getColumnMappings(); // Создаём карту соответствий const mainMap = new Map(); @@ -215,12 +254,16 @@ function processMapping() { if (mainRow) { matched++; - // Заполняем пустые поля в копии - fieldsToFill.forEach(field => { - const isEmpty = rowCopy[field] === null || rowCopy[field] === undefined || rowCopy[field] === ''; - if (isEmpty && mainRow[field] !== null && mainRow[field] !== undefined) { + // Заполняем пустые поля согласно сопоставлениям + columnMappings.forEach(mapping => { + const templateCol = mapping.templateColumn; + const mainCol = mapping.mainColumn; + const isEmpty = rowCopy[templateCol] === null || rowCopy[templateCol] === undefined || String(rowCopy[templateCol]).trim() === ''; + const hasValueInMain = mainRow[mainCol] !== null && mainRow[mainCol] !== undefined; + + if (isEmpty && hasValueInMain) { filled++; - rowCopy[field] = mainRow[field]; + rowCopy[templateCol] = mainRow[mainCol]; } }); } else { @@ -274,9 +317,18 @@ function processMapping() { allFields.forEach(field => { const originalValue = row.originalRow[field]; const newValue = row.templateRow[field]; - const wasEmpty = originalValue === null || originalValue === undefined || String(originalValue).trim() === ''; - const filled = wasEmpty && newValue !== null && newValue !== undefined; - const cellStyle = filled ? 'background: var(--kt-ai-success-bg); font-weight: 600;' : ''; + + // Проверяем, было ли это поле заполнено из сопоставления + let wasFilled = false; + columnMappings.forEach(mapping => { + if (mapping.templateColumn === field) { + const wasEmpty = originalValue === null || originalValue === undefined || String(originalValue).trim() === ''; + const hasValueInMain = row.mainRow && row.mainRow[mapping.mainColumn] !== null && row.mainRow[mapping.mainColumn] !== undefined; + wasFilled = wasEmpty && hasValueInMain; + } + }); + + const cellStyle = wasFilled ? 'background: var(--kt-ai-success-bg); font-weight: 600;' : ''; bodyHtml += '' + escapeHtml(newValue !== null && newValue !== undefined ? newValue : '') + ''; }); bodyHtml += '' + (row.matched ? '✓' : '✗') + ''; @@ -286,7 +338,7 @@ function processMapping() { table.innerHTML = headerHtml + bodyHtml; // Сохраняем результат для дальнейшей обработки - mappedResult = { templateData, fieldsToFill, mainMap, templateKeyField }; + mappedResult = { templateData, columnMappings, mainMap, templateKeyField }; document.getElementById('btn-process').disabled = matched === 0; } @@ -294,7 +346,7 @@ function processMapping() { function processFiles() { if (!mappedResult) return; - const { templateData, fieldsToFill, mainMap, templateKeyField } = mappedResult; + const { templateData, columnMappings, mainMap, templateKeyField } = mappedResult; // Полная обработка всех данных (без мутации!) let totalFilled = 0; @@ -305,13 +357,16 @@ function processFiles() { const mainRow = mainMap.get(keyValue); if (mainRow) { - fieldsToFill.forEach(field => { + columnMappings.forEach(mapping => { + const templateCol = mapping.templateColumn; + const mainCol = mapping.mainColumn; + // Проверяем на пустое значение (null, undefined, пустая строка) - const isEmpty = newRow[field] === null || newRow[field] === undefined || String(newRow[field]).trim() === ''; - const hasValueInMain = mainRow[field] !== null && mainRow[field] !== undefined; + const isEmpty = newRow[templateCol] === null || newRow[templateCol] === undefined || String(newRow[templateCol]).trim() === ''; + const hasValueInMain = mainRow[mainCol] !== null && mainRow[mainCol] !== undefined; if (isEmpty && hasValueInMain) { - newRow[field] = mainRow[field]; + newRow[templateCol] = mainRow[mainCol]; totalFilled++; } });