Хорошо, я внимательно изучил ваш код. Нужно добавить тонкую пунктирную линию по границам интервалов опробования (сверху и снизу каждого интервала пробы) в отрисовке геологической колонки.
Вот **полностью исправленные функции**, которые нужно заменить в вашем файле. Остальной код не меняется.
### 1. Замените функцию `drawWellStructure`
Эта функция теперь будет передавать данные об интервалах проб в функцию отрисовки меток глубин.
```javascript
function drawWellStructure(data, scale = 40) {
const graphicsScrollContainer = document.getElementById('graphicsScrollContainer');
if (!graphicsScrollContainer) return;
graphicsScrollContainer.innerHTML = '';
const activeDocType = data.documentationType || 'primary';
const mergedGeology = createMergedGeologyFromDocumentation(activeDocType);
const assays = xmlData?.assays || [];
const totalDepth = data.totalDepth;
const totalHeight = totalDepth * scale;
const headerHeight = 45;
// Ширина колонки
const columnWidth = 100;
const drillWidth = 85;
const assaysWidth = 150;
// Контейнер
const columnContainer = document.createElement('div');
columnContainer.style.cssText = `
position: relative;
width: 100%;
background: white;
border: 1px solid #cbd5e1;
border-radius: 4px;
`;
// ШАПКА
const header = document.createElement('div');
header.style.cssText = `
position: sticky;
top: 0;
z-index: 100;
display: flex;
height: ${headerHeight}px;
background: #e2e8f0;
border-bottom: 1px solid #94a3b8;
flex-shrink: 0;
`;
const depthHeader = document.createElement('div');
depthHeader.style.cssText = `
width: 60px;
min-width: 60px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 0.7rem;
color: #0f172a;
border-right: 1px solid #cbd5e1;
background: #e2e8f0;
`;
depthHeader.textContent = 'глубина, м';
const coreHeader = document.createElement('div');
coreHeader.style.cssText = `
width: ${columnWidth}px;
min-width: ${columnWidth}px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 0.7rem;
color: #0f172a;
border-right: 1px solid #cbd5e1;
background: #e2e8f0;
`;
coreHeader.textContent = 'Колонка';
const drillHeader = document.createElement('div');
drillHeader.style.cssText = `
width: ${drillWidth}px;
min-width: ${drillWidth}px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 0.7rem;
color: #0f172a;
border-right: 1px solid #cbd5e1;
background: #e2e8f0;
`;
drillHeader.textContent = 'Кат. бур.';
const assaysHeader = document.createElement('div');
assaysHeader.style.cssText = `
width: ${assaysWidth}px;
min-width: ${assaysWidth}px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 0.7rem;
color: #0f172a;
background: #e2e8f0;
`;
assaysHeader.textContent = 'Пробы';
header.appendChild(depthHeader);
header.appendChild(coreHeader);
header.appendChild(drillHeader);
header.appendChild(assaysHeader);
columnContainer.appendChild(header);
// ТЕЛО
const body = document.createElement('div');
body.style.cssText = `
display: flex;
position: relative;
overflow-y: visible;
`;
// Область глубины
const depthArea = document.createElement('div');
depthArea.style.cssText = `width: 60px; background: #f8fafc; border-right: 1px solid #cbd5e1; position: relative;`;
const depthBody = document.createElement('div');
depthBody.style.cssText = `position: relative; height: ${totalHeight}px;`;
depthArea.appendChild(depthBody);
// Область колонки
const coreArea = document.createElement('div');
coreArea.style.cssText = `width: ${columnWidth}px; background: #ffffff; border-right: 1px solid #cbd5e1; position: relative;`;
const coreBody = document.createElement('div');
coreBody.style.cssText = `position: relative; height: ${totalHeight}px;`;
coreArea.appendChild(coreBody);
// Область категорий бурения
const drillArea = document.createElement('div');
drillArea.style.cssText = `width: ${drillWidth}px; background: #f8fafc; border-right: 1px solid #cbd5e1; position: relative;`;
const drillBody = document.createElement('div');
drillBody.style.cssText = `position: relative; height: ${totalHeight}px;`;
drillArea.appendChild(drillBody);
// Область проб
const assaysArea = document.createElement('div');
assaysArea.style.cssText = `width: ${assaysWidth}px; background: #ffffff; position: relative;`;
const assaysBody = document.createElement('div');
assaysBody.style.cssText = `position: relative; height: ${totalHeight}px;`;
assaysArea.appendChild(assaysBody);
body.appendChild(depthArea);
body.appendChild(coreArea);
body.appendChild(drillArea);
body.appendChild(assaysArea);
columnContainer.appendChild(body);
graphicsScrollContainer.appendChild(columnContainer);
// === ШКАЛА ГЛУБИНЫ ===
for (let depth = 0; depth <= totalDepth; depth += 5) {
const yPos = depth * scale;
if (yPos >= 0 && yPos <= totalHeight) {
const line = document.createElement('div');
line.style.cssText = `position: absolute; top: ${yPos}px; left: 8px; right: 0; height: 1px; background: #cbd5e1;`;
const label = document.createElement('span');
label.style.cssText = `position: absolute; right: 6px; top: ${yPos}px; font-weight: 600; font-size: 0.65rem; color: #1e293b; background: #f8fafc; padding: 0 2px; transform: translateY(-50%);`;
label.textContent = depth.toString();
depthBody.appendChild(line);
depthBody.appendChild(label);
}
}
// Метки через 1 метр
for (let depth = 0; depth <= totalDepth; depth += 1) {
if (depth % 5 !== 0) {
const yPos = depth * scale;
if (yPos >= 0 && yPos <= totalHeight) {
const line = document.createElement('div');
line.style.cssText = `position: absolute; top: ${yPos}px; left: 8px; right: 0; height: 1px; background: #e2e8f0;`;
depthBody.appendChild(line);
}
}
}
// Границы интервалов документирования (только для геологии, убираем, чтобы не мешали)
mergedGeology.forEach((geo) => {
const topY = geo.from * scale;
const bottomY = geo.to * scale;
if (topY >= 0 && topY <= totalHeight) {
const label = document.createElement('div');
label.style.cssText = `position: absolute; right: 6px; top: ${topY}px; font-size: 0.6rem; color: #e74c3c; background: white; padding: 0 2px; transform: translateY(-50%); z-index: 5; font-weight: bold;`;
label.textContent = geo.from.toFixed(1);
depthBody.appendChild(label);
}
if (bottomY >= 0 && bottomY <= totalHeight) {
const label = document.createElement('div');
label.style.cssText = `position: absolute; right: 6px; top: ${bottomY}px; font-size: 0.6rem; color: #e74c3c; background: white; padding: 0 2px; transform: translateY(-50%); z-index: 5; font-weight: bold;`;
label.textContent = geo.to.toFixed(1);
depthBody.appendChild(label);
}
});
// === ОТРИСОВКА ГЕОЛОГИЧЕСКИХ ИНТЕРВАЛОВ ===
const geologyBlocks = [];
mergedGeology.forEach((geo, index) => {
const topY = geo.from * scale;
const heightY = (geo.to - geo.from) * scale;
if (heightY > 0) {
// Блок в колонке
const block = document.createElement('div');
const bgColor = getStratigraphyBackground({
stratigraphy: geo.stratigraphy,
originalStratigraphy: geo.originalStratigraphy
});
const isDark = isColorDark(bgColor);
block.style.cssText = `
position: absolute;
top: ${topY}px;
left: 0;
width: 100%;
height: ${heightY}px;
background: ${bgColor};
border: 1px solid rgba(0,0,0,0.25);
cursor: pointer;
transition: all 0.15s ease;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
`;
// Возраст
const ageSpan = document.createElement('span');
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;
`;
ageSpan.textContent = geo.originalStratigraphy || geo.stratigraphy || '';
block.appendChild(ageSpan);
// Крап литологии
if (geo.lcode && window.LITHOLOGY_PATTERNS && window.LITHOLOGY_PATTERNS[geo.lcode]) {
const patternOverlay = document.createElement('div');
const patternSize = getPatternSize(scale);
const fileName = window.LITHOLOGY_PATTERNS[geo.lcode];
const patternUrl = `litology/${fileName}`;
patternOverlay.style.cssText = `
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url("${patternUrl}");
background-repeat: repeat;
background-size: ${patternSize.width}px ${patternSize.height}px;
opacity: 0.5;
pointer-events: none;
z-index: 1;
`;
block.appendChild(patternOverlay);
}
block.dataset.layerId = index;
block.addEventListener('click', (e) => {
e.stopPropagation();
highlightGeologyBlock(index);
if (window.geologyCards && window.geologyCards[index]) {
window.geologyCards[index].scrollIntoView({ behavior: 'smooth', block: 'center' });
window.geologyCards[index].click();
}
});
coreBody.appendChild(block);
geologyBlocks.push(block);
// Категория бурения
const drillCell = document.createElement('div');
drillCell.style.cssText = `
position: absolute;
top: ${topY}px;
left: 0;
width: 100%;
height: ${heightY}px;
border-bottom: 1px solid #e2e8f0;
background: #f8fafc;
cursor: pointer;
`;
// Собираем все категории из интервалов
const categoryItems = [];
if (geo.intervals && geo.intervals.length > 0) {
geo.intervals.forEach(interval => {
if (interval.rockCategories && interval.rockCategories !== 'Не указано' && interval.rockCategories !== '') {
const cats = interval.rockCategories.split('; ');
cats.forEach(cat => {
const match = cat.match(/([\d.-]+)-([\d.-]+)\s*-\s*(.+)/);
if (match) {
categoryItems.push({
from: parseFloat(match[1]),
to: parseFloat(match[2]),
category: match[3].trim(),
intervalFrom: interval.from,
intervalTo: interval.to,
thickness: interval.thickness
});
} else {
const simpleMatch = cat.match(/\b([IVX]+)\b/);
if (simpleMatch) {
categoryItems.push({
category: simpleMatch[1],
intervalFrom: interval.from,
intervalTo: interval.to
});
}
}
});
}
});
}
if (categoryItems.length > 0) {
// Сортируем по глубине
categoryItems.sort((a, b) => (a.from || a.intervalFrom) - (b.from || b.intervalFrom));
// Создаём отдельные блоки для каждой категории
const categoriesContainer = document.createElement('div');
categoriesContainer.style.cssText = `
position: relative;
width: 100%;
height: 100%;
`;
categoryItems.forEach(catItem => {
const catFrom = catItem.from || catItem.intervalFrom;
const catTo = catItem.to || catItem.intervalTo;
const catTopY = (catFrom - geo.from) * scale;
const catHeightY = (catTo - catFrom) * scale;
if (catHeightY > 2) {
const catBlock = document.createElement('div');
catBlock.style.cssText = `
position: absolute;
top: ${catTopY}px;
left: 0;
width: 100%;
height: ${catHeightY}px;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.7rem;
line-height: 1.3;
text-align: center;
background: #f8fafc;
border-bottom: 1px dotted #e2e8f0;
`;
catBlock.innerHTML = `${catItem.category}`;
categoriesContainer.appendChild(catBlock);
} else {
// Для очень маленьких интервалов
const catBlock = document.createElement('div');
catBlock.style.cssText = `
position: absolute;
top: ${catTopY}px;
left: 0;
width: 100%;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.7rem;
background: #f8fafc;
`;
catBlock.innerHTML = `${catItem.category}`;
categoriesContainer.appendChild(catBlock);
}
});
drillCell.appendChild(categoriesContainer);
} else {
drillCell.innerHTML = '—';
}
drillCell.addEventListener('click', (e) => {
e.stopPropagation();
highlightGeologyBlock(index);
if (window.geologyCards && window.geologyCards[index]) {
window.geologyCards[index].scrollIntoView({ behavior: 'smooth', block: 'center' });
window.geologyCards[index].click();
}
});
drillBody.appendChild(drillCell);
}
});
// Функция подсветки блока
function highlightGeologyBlock(index) {
geologyBlocks.forEach((block, i) => {
if (i === index) {
block.style.border = '2px solid #b91c1c';
block.style.boxShadow = '0 0 0 2px rgba(185, 28, 28, 0.25)';
block.style.zIndex = '10';
} else {
block.style.border = '1px solid rgba(0,0,0,0.25)';
block.style.boxShadow = 'none';
block.style.zIndex = '1';
}
});
if (window.geologyCards) {
document.querySelectorAll('.geology-card').forEach((card, i) => {
if (i === index) {
card.style.background = '#fff7ed';
card.style.borderLeft = '4px solid #c2410c';
} else {
card.style.background = 'white';
card.style.borderLeft = '4px solid #e9ecef';
}
});
}
window.currentSelectedLayer = index;
}
// === ОТРИСОВКА ПРОБ С ПУНКТИРНЫМИ ЛИНИЯМИ ПО ГРАНИЦАМ ===
if (window.assaysVisible !== false && assays.length > 0) {
drawAssaysInColumnWithBoundaries(assaysBody, mergedGeology, scale, assaysWidth, depthBody);
}
window.geologyBlocks = geologyBlocks;
}
```
### 2. Добавьте **НОВУЮ** функцию `drawAssaysInColumnWithBoundaries`
Эта функция рисует пробы и добавляет тонкие пунктирные линии по верхней и нижней границе каждого интервала.
```javascript
function drawAssaysInColumnWithBoundaries(assaysBody, mergedGeology, scale, assaysWidth, depthBodyElement) {
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;
});
}
// Оставляем только ИНТЕРВАЛЬНЫЕ пробы (у которых from и to отличаются)
const intervalAssays = filteredAssays.filter(assay => {
const from = assay.from || 0;
const to = assay.to || from;
return (to - from) > 0.1;
});
if (intervalAssays.length === 0) return;
// Сортируем пробы по глубине
intervalAssays.sort((a, b) => (a.from || 0) - (b.from || 0));
// Проходим по всем интервальным пробам и рисуем пунктирные линии на шкале глубины
intervalAssays.forEach(assay => {
const topDepth = assay.from || 0;
const bottomDepth = assay.to || topDepth;
// Верхняя граница пробы
const topY = topDepth * scale;
if (topY >= 0 && topY <= 5000) { // 5000 - запас по высоте (~125м при scale=40)
const topLine = document.createElement('div');
topLine.style.cssText = `
position: absolute;
left: 60px;
right: 0;
top: ${topY}px;
height: 1px;
border-top: 1px dashed rgba(0, 0, 0, 0.25);
pointer-events: none;
z-index: 45;
`;
if (depthBodyElement) {
depthBodyElement.appendChild(topLine);
}
}
// Нижняя граница пробы
const bottomY = bottomDepth * scale;
if (bottomY >= 0 && bottomY <= 5000) {
const bottomLine = document.createElement('div');
bottomLine.style.cssText = `
position: absolute;
left: 60px;
right: 0;
top: ${bottomY}px;
height: 1px;
border-top: 1px dashed rgba(0, 0, 0, 0.25);
pointer-events: none;
z-index: 45;
`;
if (depthBodyElement) {
depthBodyElement.appendChild(bottomLine);
}
}
});
// Далее обычная отрисовка проб (код из оригинальной функции)
// Разделяем на точечные и интервальные для отрисовки
const pointAssays = [];
const intervalAssaysForDrawing = [];
filteredAssays.forEach(assay => {
const from = assay.from || 0;
const to = assay.to || from;
const isPoint = (to - from) < 0.1;
const docType = assay.documentationType === 'Итоговое документирование' ? 'final' : 'primary';
if (isPoint) {
pointAssays.push({
depth: from,
docType: docType,
number: assay.number,
type: assay.type,
description: assay.description,
value: assay.value,
unit: assay.unit,
barcode: assay.barcode,
author: assay.author,
samplingDate: assay.samplingDate,
originalAssay: assay
});
} else {
intervalAssaysForDrawing.push({
top: from,
bottom: to,
docType: docType,
number: assay.number,
type: assay.type,
description: assay.description,
value: assay.value,
unit: assay.unit,
barcode: assay.barcode,
author: assay.author,
samplingDate: assay.samplingDate,
originalAssay: assay
});
}
});
// Находим все информационные строки
const infoRows = assaysBody.querySelectorAll('div[style*="position: absolute"]');
if (infoRows.length === 0) {
// Если нет готовых строк, создаём свою область для проб
drawAssaysDirectly(assaysBody, mergedGeology, scale, pointAssays, intervalAssaysForDrawing);
return;
}
infoRows.forEach((row) => {
const assaysContainer = row.querySelector('div[style*="left: 0px"][style*="width: 100%"]') || row;
if (!assaysContainer) return;
const rowTop = parseFloat(row.style.top);
const rowHeight = parseFloat(row.style.height);
const layerFrom = rowTop / scale;
const layerTo = (rowTop + rowHeight) / scale;
// Пробы для этого слоя
const layerPointAssays = pointAssays.filter(p => p.depth >= layerFrom && p.depth <= layerTo);
const layerIntervalAssays = intervalAssaysForDrawing.filter(i =>
(i.top >= layerFrom && i.top <= layerTo) ||
(i.bottom >= layerFrom && i.bottom <= layerTo) ||
(i.top <= layerFrom && i.bottom >= layerTo)
);
// Контейнер для проб
const container = document.createElement('div');
container.style.cssText = `
position: relative;
width: 100%;
height: 100%;
overflow: visible;
`;
// Отрисовка точечных проб
layerPointAssays.forEach((assay) => {
const yPos = (assay.depth - layerFrom) * scale;
if (yPos >= 0 && yPos <= rowHeight) {
const offset = assay.docType === 'primary' ? -25 : 25;
const pointEl = document.createElement('div');
pointEl.style.cssText = `
position: absolute;
left: calc(50% + ${offset}px);
top: ${yPos}px;
width: 10px;
height: 10px;
border-radius: 50%;
background: ${assay.docType === 'primary' ? '#2c3e50' : '#e74c3c'};
border: 2px solid #000000;
cursor: pointer;
z-index: 54;
transition: all 0.3s ease;
transform: translateX(-50%);
`;
pointEl.addEventListener('mouseenter', function() {
this.style.transform = 'translateX(-50%) scale(1.8)';
this.style.boxShadow = '0 0 8px rgba(231, 76, 60, 0.6)';
this.style.zIndex = '55';
});
pointEl.addEventListener('mouseleave', function() {
this.style.transform = 'translateX(-50%) scale(1)';
this.style.boxShadow = 'none';
this.style.zIndex = '54';
});
pointEl.addEventListener('click', (e) => {
e.stopPropagation();
showAssayModal(assay.originalAssay);
});
container.appendChild(pointEl);
// Метка
const labelOffset = assay.docType === 'primary' ? -45 : 45;
const label = document.createElement('div');
label.style.cssText = `
position: absolute;
left: calc(50% + ${labelOffset}px);
top: ${Math.max(yPos, 10)}px;
font-size: 8px;
font-weight: bold;
background: rgba(255,255,255,0.95);
padding: 1px 4px;
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.depth.toFixed(1);
label.addEventListener('click', (e) => {
e.stopPropagation();
showAssayModal(assay.originalAssay);
});
container.appendChild(label);
}
});
// Отрисовка интервальных проб
layerIntervalAssays.forEach((assay) => {
const assayTop = Math.max(assay.top, layerFrom);
const assayBottom = Math.min(assay.bottom, layerTo);
const topY = (assayTop - layerFrom) * scale;
const heightY = (assayBottom - assayTop) * scale;
if (heightY > 2) {
const offset = assay.docType === 'primary' ? -30 : 30;
const intervalEl = document.createElement('div');
intervalEl.style.cssText = `
position: absolute;
left: calc(50% + ${offset}px);
top: ${topY}px;
width: 6px;
height: ${heightY}px;
background: ${assay.docType === 'primary' ?
'repeating-linear-gradient(45deg, #2c3e50, #2c3e50 3px, transparent 3px, transparent 6px)' :
'repeating-linear-gradient(45deg, #e74c3c, #e74c3c 3px, #fadbd8 3px, #fadbd8 6px)'};
border: 1px solid ${assay.docType === 'primary' ? '#000000' : '#c0392b'};
border-radius: 1px;
cursor: pointer;
transition: all 0.3s ease;
z-index: 52;
`;
intervalEl.addEventListener('mouseenter', function() {
this.style.transform = 'scale(1.05)';
this.style.boxShadow = '0 2px 8px rgba(231, 76, 60, 0.4)';
this.style.zIndex = '53';
});
intervalEl.addEventListener('mouseleave', function() {
this.style.transform = 'scale(1)';
this.style.boxShadow = 'none';
this.style.zIndex = '52';
});
intervalEl.addEventListener('click', (e) => {
e.stopPropagation();
showAssayModal(assay.originalAssay);
});
container.appendChild(intervalEl);
// Метка для интервала
if (assay.top >= layerFrom && heightY > 20) {
const labelOffset = assay.docType === 'primary' ? -45 : 45;
const label = document.createElement('div');
label.style.cssText = `
position: absolute;
left: calc(50% + ${labelOffset}px);
top: ${topY}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.top.toFixed(1);
label.addEventListener('click', (e) => {
e.stopPropagation();
showAssayModal(assay.originalAssay);
});
container.appendChild(label);
}
}
});
// Очищаем и добавляем контейнер
const existingContainer = assaysContainer.querySelector('.assays-drawing-container');
if (existingContainer) existingContainer.remove();
container.className = 'assays-drawing-container';
assaysContainer.appendChild(container);
});
}
// Вспомогательная функция для прямой отрисовки, если нет готовых строк
function drawAssaysDirectly(assaysBody, mergedGeology, scale, pointAssays, intervalAssays) {
if (!assaysBody) return;
// Создаём контейнер
const container = document.createElement('div');
container.style.cssText = `
position: relative;
width: 100%;
height: 100%;
overflow: visible;
`;
// Отрисовка точечных проб
pointAssays.forEach((assay) => {
const yPos = assay.depth * scale;
if (yPos >= 0 && yPos <= 5000) {
const offset = assay.docType === 'primary' ? -25 : 25;
const pointEl = document.createElement('div');
pointEl.style.cssText = `
position: absolute;
left: calc(50% + ${offset}px);
top: ${yPos}px;
width: 10px;
height: 10px;
border-radius: 50%;
background: ${assay.docType === 'primary' ? '#2c3e50' : '#e74c3c'};
border: 2px solid #000000;
cursor: pointer;
z-index: 54;
transition: all 0.3s ease;
transform: translateX(-50%);
`;
pointEl.addEventListener('mouseenter', function() {
this.style.transform = 'translateX(-50%) scale(1.8)';
this.style.boxShadow = '0 0 8px rgba(231, 76, 60, 0.6)';
this.style.zIndex = '55';
});
pointEl.addEventListener('mouseleave', function() {
this.style.transform = 'translateX(-50%) scale(1)';
this.style.boxShadow = 'none';
this.style.zIndex = '54';
});
pointEl.addEventListener('click', (e) => {
e.stopPropagation();
showAssayModal(assay.originalAssay);
});
container.appendChild(pointEl);
const labelOffset = assay.docType === 'primary' ? -45 : 45;
const label = document.createElement('div');
label.style.cssText = `
position: absolute;
left: calc(50% + ${labelOffset}px);
top: ${Math.max(yPos, 10)}px;
font-size: 8px;
font-weight: bold;
background: rgba(255,255,255,0.95);
padding: 1px 4px;
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.depth.toFixed(1);
label.addEventListener('click', (e) => {
e.stopPropagation();
showAssayModal(assay.originalAssay);
});
container.appendChild(label);
}
});
// Отрисовка интервальных проб
intervalAssays.forEach((assay) => {
const topY = assay.top * scale;
const heightY = (assay.bottom - assay.top) * scale;
if (heightY > 2 && topY >= 0 && topY <= 5000) {
const offset = assay.docType === 'primary' ? -30 : 30;
const intervalEl = document.createElement('div');
intervalEl.style.cssText = `
position: absolute;
left: calc(50% + ${offset}px);
top: ${topY}px;
width: 6px;
height: ${heightY}px;
background: ${assay.docType === 'primary' ?
'repeating-linear-gradient(45deg, #2c3e50, #2c3e50 3px, transparent 3px, transparent 6px)' :
'repeating-linear-gradient(45deg, #e74c3c, #e74c3c 3px, #fadbd8 3px, #fadbd8 6px)'};
border: 1px solid ${assay.docType === 'primary' ? '#000000' : '#c0392b'};
border-radius: 1px;
cursor: pointer;
transition: all 0.3s ease;
z-index: 52;
`;
intervalEl.addEventListener('mouseenter', function() {
this.style.transform = 'scale(1.05)';
this.style.boxShadow = '0 2px 8px rgba(231, 76, 60, 0.4)';
this.style.zIndex = '53';
});
intervalEl.addEventListener('mouseleave', function() {
this.style.transform = 'scale(1)';
this.style.boxShadow = 'none';\
this.style.zIndex = '52';
});
intervalEl.addEventListener('click', (e) => {
e.stopPropagation();
showAssayModal(assay.originalAssay);
});
container.appendChild(intervalEl);
if (heightY > 20) {
const labelOffset = assay.docType === 'primary' ? -45 : 45;
const label = document.createElement('div');
label.style.cssText = `
position: absolute;
left: calc(50% + ${labelOffset}px);
top: ${topY}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.top.toFixed(1);
label.addEventListener('click', (e) => {
e.stopPropagation();
showAssayModal(assay.originalAssay);
});
container.appendChild(label);
}
}
});
assaysBody.appendChild(container);
}
```
### Что изменилось:
1. **Функция `drawWellStructure`**:
- Теперь передаёт `depthBodyElement` (контейнер со шкалой глубины) в функцию отрисовки проб.
2. **Новая функция `drawAssaysInColumnWithBoundaries`**:
- Сначала фильтрует **только интервальные пробы** (у которых `to - from > 0.1`).
- Для каждой такой пробы рисует **тонкую пунктирную линию** (`border-top: 1px dashed rgba(0, 0, 0, 0.25)`) на уровне верхней и нижней границы.
- Линии рисуются в контейнере шкалы глубины (`depthBodyElement`), начиная с `left: 60px` (после колонки глубины) и до правого края.
- Прозрачность 0.25 делает линии слабенькими, как вы и просили.
- Далее выполняется обычная отрисовка самих проб (точечных и интервальных) без изменений.
Теперь при переключении на вкладку "Колонка" (геологическая колонка) все интервалы проб будут отмечены слабыми пунктирными линиями на шкале глубины.