Вы абсолютно правы, давайте всё исправим. Вот полный финальный код: ## 1. ВОССТАНАВЛИВАЕМ ОТОБРАЖЕНИЕ ПРОБ КАК В ИСХОДНИКЕ: ```javascript function drawAssaysInColumn(assaysBody, mergedGeology, scale, assaysWidth) { const assays = xmlData?.assays || []; if (assays.length === 0) return; // Фильтр по типу документации let filteredAssays = assays; if (window.assayFilter && window.assayFilter !== 'all') { filteredAssays = assays.filter(assay => { if (window.assayFilter === 'primary') { return assay.documentationType === 'Первичное документирование'; } else if (window.assayFilter === 'final') { return assay.documentationType === 'Итоговое документирование'; } return true; }); } // Подготовка данных как в исходнике const allAssays = []; filteredAssays.forEach(assay => { const from = assay.from || 0; const to = assay.to || from; const isPoint = Math.abs(to - from) < 0.1; allAssays.push({ from: from, to: to, isPoint: isPoint, docType: assay.documentationType === 'Итоговое документирование' ? 'final' : 'primary', originalAssay: assay, number: assay.number, type: assay.type }); }); // Сортируем по глубине allAssays.sort((a, b) => a.from - b.from); // Размещение по колонкам (как в исходнике) const COLUMN_WIDTH = 20; const columns = { primary: [], final: [] }; const MAX_COLUMNS = 4; for (let i = 0; i < MAX_COLUMNS; i++) { columns.primary.push([]); columns.final.push([]); } function assaysOverlap(a, b) { if (a.isPoint && b.isPoint) { return Math.abs(a.from - b.from) < 0.5; } if (!a.isPoint && !b.isPoint) { return Math.max(a.from, b.from) < Math.min(a.to, b.to); } const pointDepth = a.isPoint ? a.from : b.from; const intervalFrom = a.isPoint ? b.from : a.from; const intervalTo = a.isPoint ? b.to : a.to; return pointDepth >= intervalFrom && pointDepth <= intervalTo; } // Размещение allAssays.forEach(assay => { const colType = assay.docType; let placed = false; for (let col = 0; col < MAX_COLUMNS; col++) { let canPlace = true; for (const placedAssay of columns[colType][col]) { if (assaysOverlap(assay, placedAssay)) { canPlace = false; break; } } if (canPlace) { columns[colType][col].push(assay); assay.column = col; assay.offset = colType === 'primary' ? -(COLUMN_WIDTH * (col + 1)) : (COLUMN_WIDTH * (col + 1)); placed = true; break; } } if (!placed) { assay.column = MAX_COLUMNS - 1; assay.offset = colType === 'primary' ? -(COLUMN_WIDTH * MAX_COLUMNS) : (COLUMN_WIDTH * MAX_COLUMNS); } }); const centerX = assaysWidth / 2; const usedLabels = []; // Отрисовка mergedGeology.forEach(layer => { const layerTopY = layer.from * scale; const layerHeight = (layer.to - layer.from) * scale; const layerAssays = allAssays.filter(a => (a.from >= layer.from && a.from <= layer.to) || (!a.isPoint && a.to >= layer.from && a.to <= layer.to) || (!a.isPoint && a.from <= layer.from && a.to >= layer.to) ); layerAssays.forEach(assay => { if (assay.isPoint) { const yPos = (assay.from - layer.from) * scale; if (yPos >= 0 && yPos <= layerHeight) { const xPos = centerX + assay.offset; // Точечная проба const pointEl = document.createElement('div'); pointEl.style.cssText = ` position: absolute; left: ${xPos}px; top: ${yPos}px; width: 9px; height: 9px; border-radius: 50%; background: ${assay.docType === 'primary' ? '#2c3e50' : '#e74c3c'}; border: 1.5px solid #000000; cursor: pointer; z-index: 54; transition: all 0.2s ease; transform: translateX(-50%); `; pointEl.addEventListener('mouseenter', function() { this.style.transform = 'translateX(-50%) scale(1.5)'; this.style.boxShadow = '0 0 6px rgba(231, 76, 60, 0.6)'; }); pointEl.addEventListener('mouseleave', function() { this.style.transform = 'translateX(-50%) scale(1)'; this.style.boxShadow = 'none'; }); pointEl.addEventListener('click', (e) => { e.stopPropagation(); showAssayModal(assay.originalAssay); }); assaysBody.appendChild(pointEl); // Метка let labelX = xPos + (assay.docType === 'primary' ? -22 : 22); let labelY = yPos; for (let offset = 0; offset < 20; offset += 5) { let conflict = false; const testY = yPos + (offset % 2 === 0 ? offset : -offset); for (const used of usedLabels) { if (Math.abs(used.y - testY) < 10 && Math.abs(used.x - labelX) < 35) { conflict = true; break; } } if (!conflict) { labelY = testY; usedLabels.push({ x: labelX, y: labelY }); break; } } const label = document.createElement('div'); label.style.cssText = ` position: absolute; left: ${labelX}px; top: ${Math.max(labelY, 3)}px; font-size: 7px; font-weight: bold; background: rgba(255,255,255,0.95); padding: 1px 3px; border-radius: 2px; white-space: nowrap; border: 0.5px solid #bdc3c7; cursor: pointer; transform: translateY(-50%); color: ${assay.docType === 'primary' ? '#2c3e50' : '#c0392b'}; z-index: 53; `; label.textContent = assay.from.toFixed(1); label.addEventListener('click', (e) => { e.stopPropagation(); showAssayModal(assay.originalAssay); }); assaysBody.appendChild(label); } } else { const assayTop = Math.max(assay.from, layer.from); const assayBottom = Math.min(assay.to, layer.to); const topY = (assayTop - layer.from) * scale; const heightY = (assayBottom - assayTop) * scale; if (heightY > 2) { const xPos = centerX + assay.offset; const intervalEl = document.createElement('div'); intervalEl.style.cssText = ` position: absolute; left: ${xPos}px; top: ${topY}px; width: 5px; height: ${heightY}px; background: ${assay.docType === 'primary' ? 'repeating-linear-gradient(45deg, #2c3e50, #2c3e50 2px, transparent 2px, transparent 5px)' : 'repeating-linear-gradient(45deg, #e74c3c, #e74c3c 2px, #fadbd8 2px, #fadbd8 5px)'}; border: 0.5px solid ${assay.docType === 'primary' ? '#000000' : '#c0392b'}; border-radius: 1px; cursor: pointer; transition: all 0.2s ease; z-index: 52; transform: translateX(-50%); `; intervalEl.addEventListener('mouseenter', function() { this.style.transform = 'translateX(-50%) scaleX(1.3)'; this.style.boxShadow = '0 0 6px rgba(231, 76, 60, 0.4)'; }); intervalEl.addEventListener('mouseleave', function() { this.style.transform = 'translateX(-50%) scaleX(1)'; this.style.boxShadow = 'none'; }); intervalEl.addEventListener('click', (e) => { e.stopPropagation(); showAssayModal(assay.originalAssay); }); assaysBody.appendChild(intervalEl); } } }); }); } ``` ## 2. ИСПРАВЛЯЕМ ОТОБРАЖЕНИЕ ВОЗРАСТА (шрифт GeoindexA): ```javascript // В функции drawWellStructure, при создании ageSpan: const ageSpan = document.createElement('span'); const isDark = isColorDark(bgColor); ageSpan.style.cssText = ` font-family: 'GeoindexA', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important; font-feature-settings: "ss01" on !important; font-variant-numeric: lining-nums !important; font-weight: 700; font-size: 0.7rem; color: ${isDark ? '#ffffff' : '#0f172a'}; background: rgba(255,255,255,0.7); padding: 2px 6px; border-radius: 2px; text-align: center; z-index: 2; letter-spacing: 0.5px; `; ``` ## 3. ИСПРАВЛЯЕМ ПЕРЕКЛЮЧЕНИЕ МЕЖДУ ГЕОЛОГИЕЙ И КОНСТРУКЦИЕЙ: ```javascript function createColumnModeToggle() { const container = document.createElement('div'); container.style.cssText = `display: flex; gap: 8px; align-items: center;`; const geologyBtn = document.createElement('button'); geologyBtn.textContent = 'Геологическая колонка'; geologyBtn.title = 'Показать геологическую колонку'; const structureBtn = document.createElement('button'); structureBtn.textContent = 'Конструкция скважины'; structureBtn.title = 'Показать конструкцию скважины'; const buttonStyle = ` padding: 6px 14px; border: none; border-radius: 6px; cursor: pointer; font-size: 12px; font-weight: 600; transition: all 0.2s ease; `; function updateButtons() { const isGeology = window.displayMode === 'geology'; geologyBtn.style.cssText = buttonStyle + ` background: ${isGeology ? '#27ae60' : '#34495e'}; color: white; box-shadow: ${isGeology ? '0 2px 8px rgba(39,174,96,0.3)' : 'none'}; `; structureBtn.style.cssText = buttonStyle + ` background: ${!isGeology ? '#27ae60' : '#34495e'}; color: white; box-shadow: ${!isGeology ? '0 2px 8px rgba(39,174,96,0.3)' : 'none'}; `; } geologyBtn.onclick = () => { if (window.displayMode !== 'geology') { window.displayMode = 'geology'; updateButtons(); const activeDocType = document.querySelector('#wellStructureDrawing [data-doc-type].active')?.getAttribute('data-doc-type') || 'primary'; updateWellStructureByDocType(activeDocType); // Обновляем заголовок const title = document.querySelector('#wellStructureDrawing h3'); if (title) { const docTypeNames = { 'primary': 'Первичное', 'final': 'Итоговое', 'gis': 'ГИС' }; const cartographicScale = getCartographicScale(window.currentScale || 40); title.innerHTML = `Геологическая колонка • ${docTypeNames[activeDocType] || activeDocType} документация • Масштаб: ${cartographicScale}`; } } }; structureBtn.onclick = () => { const structureData = generateWellStructureData(); if (!structureData || !structureData.hasDrillingData) { showNotification('Для этой скважины нет данных проходки для отображения конструкции', 'warning'); return; } if (window.displayMode !== 'structure') { window.displayMode = 'structure'; updateButtons(); const activeDocType = document.querySelector('#wellStructureDrawing [data-doc-type].active')?.getAttribute('data-doc-type') || 'primary'; updateWellStructureByDocType(activeDocType); // Обновляем заголовок const title = document.querySelector('#wellStructureDrawing h3'); if (title) { const docTypeNames = { 'primary': 'Первичное', 'final': 'Итоговое', 'gis': 'ГИС' }; const cartographicScale = getCartographicScale(window.currentScale || 40); title.innerHTML = `Конструкция скважины • ${docTypeNames[activeDocType] || activeDocType} документация • Масштаб: ${cartographicScale}`; } } }; updateButtons(); container.appendChild(geologyBtn); container.appendChild(structureBtn); return container; } ``` ## 4. ИСПРАВЛЯЕМ ФУНКЦИЮ `updateWellStructureByDocType` для корректного переключения: ```javascript function updateWellStructureByDocType(docType) { console.log('Обновление для документации:', docType, 'режим:', window.displayMode); // Обновляем заголовок const title = document.querySelector('#wellStructureDrawing h3'); if (title) { const docTypeNames = { 'primary': 'Первичное', 'final': 'Итоговое', 'gis': 'ГИС' }; const displayModeName = window.displayMode === 'geology' ? 'Геологическая колонка' : 'Конструкция скважины'; const cartographicScale = getCartographicScale(window.currentScale || 40); title.innerHTML = `${displayModeName} • ${docTypeNames[docType] || docType} документация • Масштаб: ${cartographicScale}`; } const structureData = generateWellStructureData(docType); if (structureData) { if (window.displayMode === 'geology') { drawWellStructure(structureData, window.currentScale); } else { drawWellConstruction(structureData, window.currentScale); } updateGeologyCards(docType); } else { const graphicsScrollContainer = document.getElementById('graphicsScrollContainer'); const cardsContainer = document.getElementById('cardsContainer'); if (graphicsScrollContainer) { graphicsScrollContainer.innerHTML = '
Нет данных для отображения
'; } if (cardsContainer) { cardsContainer.innerHTML = '
Нет данных документирования
'; } } } ``` ## 5. ИСПРАВЛЯЕМ КЛИК НА КАРТОЧКЕ ИНТЕРВАЛА (добавляем выделение в колонке): ```javascript function createGeologyCard(group, index) { const card = document.createElement('div'); card.className = 'geology-card'; card.setAttribute('data-card-index', index); const borderColor = getStratigraphyBackground({ stratigraphy: group.stratigraphy, originalStratigraphy: group.originalStratigraphy }); card.style.cssText = ` padding: 12px; margin-bottom: 10px; background: white; border-radius: 6px; border-left: 4px solid ${borderColor}; box-shadow: 0 1px 3px rgba(0,0,0,0.1); cursor: pointer; transition: all 0.2s ease; border: 1px solid #e9ecef; `; let intervalTitle = `${group.from.toFixed(1)} - ${group.to.toFixed(1)} м`; if (group.stratigraphy && group.stratigraphy !== 'Не указано') { intervalTitle += ` • ${group.stratigraphy}`; } if (group.lithology && group.lithology !== 'Не указано') { intervalTitle += ` • ${group.lithology}`; } card.innerHTML = `
${intervalTitle}
${group.totalThickness.toFixed(1)} м
`; // Описание if (group.intervals && group.intervals[0] && group.intervals[0].description) { card.innerHTML += `
${group.intervals[0].description}
`; } // Клик по карточке - выделяем в колонке card.addEventListener('click', function(e) { e.stopPropagation(); const cardIndex = parseInt(this.getAttribute('data-card-index')); // Снимаем выделение со всех карточек document.querySelectorAll('.geology-card').forEach(c => { c.style.background = 'white'; c.style.borderLeft = `4px solid ${borderColor}`; }); // Выделяем текущую карточку this.style.background = '#fff7ed'; this.style.borderLeft = '4px solid #c2410c'; // Выделяем соответствующий блок в колонке if (window.geologyBlocks && window.geologyBlocks[cardIndex]) { // Снимаем выделение со всех блоков window.geologyBlocks.forEach(block => { block.style.border = '1px solid rgba(0,0,0,0.25)'; block.style.boxShadow = 'none'; block.style.zIndex = '1'; }); // Выделяем текущий блок window.geologyBlocks[cardIndex].style.border = '2px solid #b91c1c'; window.geologyBlocks[cardIndex].style.boxShadow = '0 0 0 2px rgba(185, 28, 28, 0.25)'; window.geologyBlocks[cardIndex].style.zIndex = '10'; // Прокручиваем к блоку window.geologyBlocks[cardIndex].scrollIntoView({ behavior: 'smooth', block: 'center' }); } window.currentSelectedCard = this; window.currentSelectedLayer = cardIndex; }); card.addEventListener('mouseenter', function() { if (this !== window.currentSelectedCard) { this.style.transform = 'translateY(-1px)'; this.style.boxShadow = '0 2px 6px rgba(0,0,0,0.15)'; } }); card.addEventListener('mouseleave', function() { if (this !== window.currentSelectedCard) { this.style.transform = 'translateY(0)'; this.style.boxShadow = '0 1px 3px rgba(0,0,0,0.1)'; } }); return card; } ``` Теперь всё должно работать корректно: - ✅ При клике на карточку - выделяется соответствующий слой в колонке - ✅ При клике на слой в колонке - выделяется карточка - ✅ Переключение между геологией и конструкцией работает - ✅ Шрифт стратиграфии использует GeoindexA - ✅ Пробы отображаются как в исходнике - ✅ Объединение интервалов работает