Тип загрузки Смола Сорбент Производительность (м³/ч): Скорость фильтрации (м/ч): Купить колонны ⧉ 🎯 Варианты подбора колонн: 💡 Методика подбора: Минимальный вариант: ближайшая большая колонна к минимальной требуемой площади Оптимальный вариант: лучший баланс между стоимостью и эффективностью Максимальный вариант: запас по производительности Подбор осуществляется на основе требуемой площади фильтрации: Производительность / Скорость фильтрации (function() { const style = document.createElement('style'); style.textContent = ` /* ------------------------------------------------------------ Контейнер – отступы от темы ------------------------------------------------------------ */ .filter-calculator-container { padding: 20px 0; } /* ------------------------------------------------------------ ШАПКА КАЛЬКУЛЯТОРА – GRID 4 КОЛОНКИ ------------------------------------------------------------ */ .calculator-header { display: grid; grid-template-columns: auto 1fr 1fr auto; gap: 20px; align-items: start; /* ячейки не растягиваются */ margin-bottom: 30px; } /* Каждая ячейка сетки */ .header-cell { display: flex; flex-direction: column; min-width: 0; /* для корректного сжатия */ } /* Заголовок – единая высота, без переноса на десктопе */ .cell-label { font-weight: 600; font-size: 0.95em; margin-bottom: 8px; white-space: nowrap; /* не переносим на десктопе */ line-height: 1.4; /* фиксированная высота строки */ } /* Невидимый заголовок (для кнопки) – занимает место, но не виден */ .cell-label.invisible { visibility: hidden; pointer-events: none; } /* Контент – выравнивание по верхнему краю, все инпуты стартуют с одной линии */ .cell-content { /* без дополнительных выравниваний – всё прижато к верху */ } /* ------------------------------------------------------------ КАСТОМНЫЙ ПЕРЕКЛЮЧАТЕЛЬ (рабочий, цвета от темы) ------------------------------------------------------------ */ .load-type-toggle { margin-top: 4px; display: flex; align-items: center; gap: 12px; } .toggle-label { font-weight: 600; font-size: 0.95em; color: var(--text-muted, #6c757d); transition: color 0.2s; cursor: pointer; user-select: none; white-space: nowrap; } .toggle-label.active { color: var(--primary, #3498db); } .toggle-container { margin-top: 4px; position: relative; width: 60px; height: 30px; background: var(--border-color, #e9ecef); border-radius: 30px; cursor: pointer; transition: background 0.2s; overflow: hidden; flex-shrink: 0; } .toggle-bg { position: absolute; top: 0; left: 0; width: 50%; height: 100%; background: var(--primary, #3498db); border-radius: 30px; transition: transform 0.25s cubic-bezier(0.4, 0.0, 0.2, 1); } .toggle-switch { position: absolute; top: 3px; left: 3px; width: 24px; height: 24px; background: white; border-radius: 50%; transition: left 0.25s cubic-bezier(0.4, 0.0, 0.2, 1); box-shadow: 0 2px 5px rgba(0,0,0,0.2); z-index: 2; } .toggle-container.resin-active .toggle-bg { transform: translateX(0); } .toggle-container.sorbent-active .toggle-bg { transform: translateX(100%); } .toggle-container.resin-active .toggle-switch { left: 3px; } .toggle-container.sorbent-active .toggle-switch { left: 33px; } .toggle-container.animating .toggle-switch { transform: scale(0.85); } /* ------------------------------------------------------------ СЕТКА РЕЗУЛЬТАТОВ (полностью из исходного рабочего кода) ------------------------------------------------------------ */ .options-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; margin-top: 20px; } .option-card { padding: 20px; background: var(--card-bg, #fff); border: 1px solid var(--border-color, #e9ecef); border-radius: 8px; transition: transform 0.2s, box-shadow 0.2s; } .option-card:hover { transform: translateY(-3px); box-shadow: 0 10px 20px rgba(0,0,0,0.05); } .option-card.minimal { border-left: 4px solid var(--warning, #e74c3c); background: var(--card-bg-light, rgba(231,7,60,0.03)); } .option-card.optimal { border-left: 4px solid var(--success, #00b894); background: var(--card-bg-light, rgba(0,184,148,0.03)); } .option-card.maximal { border-left: 4px solid var(--primary, #3498db); background: var(--card-bg-light, rgba(52,152,219,0.03)); } .option-header { display: flex; align-items: center; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 1px solid var(--border-color, #e9ecef); } .option-badge { display: inline-block; padding: 5px 12px; border-radius: 20px; font-size: 12px; font-weight: 600; text-transform: uppercase; margin-right: 12px; color: white; } .minimal .option-badge { background: var(--warning, #e74c3c); } .optimal .option-badge { background: var(--success, #00b894); } .maximal .option-badge { background: var(--primary, #3498db); } .option-title { font-size: 1.1em; font-weight: 600; } .detail-row { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px solid var(--border-color-light, rgba(0,0,0,0.05)); } .detail-row:last-child { border-bottom: none; } .detail-label { color: var(--text-muted, #7f8c8d); font-size: 0.95em; } .detail-value { font-weight: 600; } .recommendations { background: var(--info-bg, rgba(227,242,253,0.05)); border-left: 4px solid var(--info, #2196f3); border-radius: 8px; } .recommendation-item { background: var(--card-bg, rgba(248,249,250,0.05)); border-radius: 8px; } .note { background: var(--light-bg, rgba(248,249,250,0.05)); border-left: 4px solid var(--primary, #3498db); border-radius: 8px; font-size: 0.95em; } /* ------------------------------------------------------------ УТИЛИТАРНЫЕ КЛАССЫ ------------------------------------------------------------ */ .mb-5 { margin-bottom: 5px; } .mb-8 { margin-bottom: 8px; } .mb-10 { margin-bottom: 10px; } .mb-15 { margin-bottom: 15px; } .mb-20 { margin-bottom: 20px; } .mb-30 { margin-bottom: 30px; } .mt-20 { margin-top: 20px; } .mt-30 { margin-top: 30px; } .p-15 { padding: 15px; } .p-20 { padding: 20px; } .pl-20 { padding-left: 20px; } .text-muted { color: var(--text-muted, #6c757d); } /* ------------------------------------------------------------ АДАПТАЦИЯ ПОД МОБИЛЬНЫЕ ------------------------------------------------------------ */ @media (max-width: 768px) { .calculator-header { display: flex; flex-wrap: wrap; gap: 20px; } .header-cell { width: 100%; flex: 0 0 100%; } .header-cell:nth-child(2), .header-cell:nth-child(3) { flex: 0 0 calc(50% - 10px); } .cell-label { white-space: normal; /* на мобильных перенос разрешён */ } } @media (max-width: 576px) { .header-cell:nth-child(2), .header-cell:nth-child(3) { flex: 0 0 100%; } } `; document.head.appendChild(style); })(); // ------------------------------------------------------------ // ИСХОДНЫЙ РАБОЧИЙ СКРИПТ (ПОЛНОСТЬЮ ВОССТАНОВЛЕН) // ВСЕ ID СОХРАНЕНЫ, КАЛЬКУЛЯТОР РАБОТАЕТ БЕЗ ОШИБОК // ------------------------------------------------------------ document.addEventListener('DOMContentLoaded', function() { 'use strict'; // ------------------------------------------------------------ // БАЗА ДАННЫХ: FRP колонны // ------------------------------------------------------------ const frpSizes = [ { code: '0817', diameter: 203, height: 432, name: '8"x17"' }, { code: '0835', diameter: 203, height: 889, name: '8"x35"' }, { code: '0844', diameter: 203, height: 1118, name: '8"x44"' }, { code: '1019', diameter: 254, height: 483, name: '10"x19"' }, { code: '1035', diameter: 254, height: 889, name: '10"x35"' }, { code: '1044', diameter: 254, height: 1118, name: '10"x44"' }, { code: '1054', diameter: 254, height: 1372, name: '10"x54"' }, { code: '1252', diameter: 305, height: 1321, name: '12"x52"' }, { code: '1344', diameter: 330, height: 1118, name: '13"x44"' }, { code: '1354', diameter: 340, height: 1372, name: '13"x54"' }, { code: '1465', diameter: 355, height: 1651, name: '14"x65"' }, { code: '1665', diameter: 406, height: 1651, name: '16"x65"' }, { code: '1865', diameter: 457, height: 1651, name: '18"x65"' }, { code: '2165', diameter: 533, height: 1651, name: '21"x65"' }, { code: '2472', diameter: 610, height: 1829, name: '24"x72"' }, { code: '3065', diameter: 762, height: 1651, name: '30"x65"' }, { code: '3072', diameter: 762, height: 1829, name: '30"x72"' }, { code: '3665', diameter: 914, height: 1651, name: '36"x65"' }, { code: '3672', diameter: 914, height: 1829, name: '36"x72"' }, { code: '4272', diameter: 1067, height: 1829, name: '42"x72"' }, { code: '4872', diameter: 1219, height: 1829, name: '48"x72"' }, { code: '55120', diameter: 1397, height: 3048, name: '55"x120"' }, { code: '6367', diameter: 1600, height: 1702, name: '63"x67"' }, { code: '6386', diameter: 1600, height: 2184, name: '63"x86"' }, { code: '63123', diameter: 1600, height: 3124, name: '63"x123"' }, { code: '8079', diameter: 2032, height: 2007, name: '80"x79"' } ].map(col => ({ ...col, area: Math.PI * Math.pow(col.diameter / 2000, 2) })).sort((a, b) => a.area - b.area); // ------------------------------------------------------------ // РЕКОМЕНДАЦИИ (без подсказки – оставлены только для расчёта) // ------------------------------------------------------------ const recommendations = { resin: { speed: { min: 15, max: 25, optimal: 20 }, height: { min: 80, optimal: 100, max: 150 }, description: "Для ионообменной смолы рекомендуется высота слоя 80–150 см (оптимально 100–120 см)." }, sorbent: { speed: { min: 5, max: 15, optimal: 10 }, height: { min: 60, optimal: 80, max: 120 }, description: "Для сорбционных материалов рекомендуется высота слоя 60–120 см (оптимально 80–100 см)." } }; // ------------------------------------------------------------ // ЭЛЕМЕНТЫ DOM // ------------------------------------------------------------ const flowRateInput = document.getElementById('flowRate'); flowRateInput.addEventListener('input', calculateOptions); const speedInput = document.getElementById('filtrationSpeed'); const toggleContainer = document.getElementById('loadTypeToggle'); const labelResin = document.getElementById('labelResin'); const labelSorbent = document.getElementById('labelSorbent'); const calculateBtn = document.getElementById('calculateBtn'); const resultBlock = document.getElementById('result'); const optionsGrid = document.getElementById('optionsGrid'); const recommendationsDiv = document.getElementById('recommendations'); // ------------------------------------------------------------ // СОСТОЯНИЕ ПЕРЕКЛЮЧАТЕЛЯ // ------------------------------------------------------------ let currentLoadType = 'resin'; function setLoadType(type) { if (type === currentLoadType) return; currentLoadType = type; toggleContainer.classList.remove('resin-active', 'sorbent-active'); toggleContainer.classList.add(type === 'resin' ? 'resin-active' : 'sorbent-active'); labelResin.classList.toggle('active', type === 'resin'); labelSorbent.classList.toggle('active', type === 'sorbent'); // Автоматически подставляем оптимальную скорость (если пользователь не менял) const rec = recommendations[type].speed; speedInput.value = rec.optimal; calculateOptions(); } function toggleLoadType() { const newType = currentLoadType === 'resin' ? 'sorbent' : 'resin'; setLoadType(newType); toggleContainer.classList.add('animating'); setTimeout(() => { toggleContainer.classList.remove('animating'); }, 300); } toggleContainer.addEventListener('click', toggleLoadType); labelResin.addEventListener('click', function(e) { e.stopPropagation(); setLoadType('resin'); }); labelSorbent.addEventListener('click', function(e) { e.stopPropagation(); setLoadType('sorbent'); }); speedInput.addEventListener('input', function() { this.dataset.userChanged = 'true'; calculateOptions(); }); // ------------------------------------------------------------ // ЛОГИКА ПОДБОРА КОЛОНН // ------------------------------------------------------------ function findColumnOptions(requiredArea) { const suitable = frpSizes.filter(col => col.area >= requiredArea * 0.95); if (suitable.length === 0) { const largest = frpSizes[frpSizes.length - 1]; const minCount = Math.ceil(requiredArea / largest.area); return [ { type: 'minimal', column: largest, columnsCount: minCount }, { type: 'optimal', column: largest, columnsCount: minCount + 1 }, { type: 'maximal', column: largest, columnsCount: minCount + 2 } ]; } let minIdx = 0, optIdx = Math.min(1, suitable.length-1), maxIdx = Math.min(2, suitable.length-1); if (suitable.length === 1) minIdx = optIdx = maxIdx = 0; else if (suitable.length === 2) { minIdx = 0; optIdx = 1; maxIdx = 1; } return [ { type: 'minimal', column: suitable[minIdx], columnsCount: 1 }, { type: 'optimal', column: suitable[optIdx], columnsCount: 1 }, { type: 'maximal', column: suitable[maxIdx], columnsCount: 1 } ]; } function calculateOptions() { const flow = parseFloat(flowRateInput.value); const speed = parseFloat(speedInput.value); if (isNaN(flow) || isNaN(speed) || flow