Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<script src="https://unpkg.com/@popperjs/core@2"></script> | |
<!-- Основные стили PNotify --> | |
<link href="https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.css" rel="stylesheet"> | |
<script src="https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.js"></script> | |
<!-- SweetAlert2 CSS --> | |
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11.21.0/dist/sweetalert2.min.css"> | |
<!-- SweetAlert2 JS --> | |
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.21.0/dist/sweetalert2.all.min.js"></script> | |
<script src="https://unpkg.com/@vkontakte/vk-bridge/dist/browser.min.js"></script> | |
<meta charset="UTF-8"> | |
<title>Калькулятор удобрений</title> | |
<style> | |
/* Общие стили */ | |
body { | |
margin: 0 auto; | |
width: 1000px; | |
padding: 1em; | |
background-color: #f0f0f0; | |
font-family: Arial, sans-serif; | |
} | |
/* Стили для заголовка */ | |
.header-box { | |
border: 2px solid #2e8b57; | |
background-color: #2e8b57; | |
color: white; | |
text-align: center; | |
padding: 1em; | |
border-radius: 10px; | |
margin-bottom: 20px; | |
width: 1000px; | |
box-sizing: border-box; | |
} | |
/* Общие стили для всех рамок */ | |
fieldset, .calculation-box { | |
border: 2px solid #2e8b57; | |
background-color: #eaffea; | |
padding: 1em; | |
margin-bottom: 20px; | |
border-radius: 8px; | |
width: 1000px; | |
box-sizing: border-box; | |
} | |
legend { | |
font-weight: bold; | |
color: #2e8b57; | |
padding: 0 10px; | |
} | |
/* Стили для блока профиля */ | |
.main-container { | |
display: grid; | |
grid-template-columns: 38px repeat(7, 110px); | |
gap: 10px; | |
padding: 5px; | |
margin-left: -50px; /* Сдвигаем блок левее */ | |
} | |
.profile-container { | |
display: contents; | |
} | |
.profile-element { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
gap: 5px; | |
} | |
.profile-label { | |
font-weight: bold; | |
font-size: 0.9em; | |
} | |
.profile-element input { | |
width: 80px; | |
padding: 5px; | |
border: 1px solid #ccc; | |
border-radius: 4px; | |
text-align: center; | |
} | |
.nitrogen-container { | |
grid-column: 1 / -1; | |
display: flex; | |
gap: 20px; | |
padding-left: 40px; | |
margin-top: 5px; | |
} | |
.nitrogen-group { | |
display: flex; | |
align-items: center; | |
gap: 5px; | |
} | |
.nitrogen-group label { | |
font-weight: bold; | |
font-size: 0.9em; | |
} | |
.nitrogen-group input { | |
width: 60px; | |
padding: 5px; | |
border: 1px solid #ccc; | |
border-radius: 4px; | |
} | |
/* Стили для nitrogen */ | |
.nitrogen-container { | |
display: flex; | |
gap: 20px; | |
align-items: flex-start; | |
margin-left: 20px; /* Сдвигаем блок правее */ | |
margin-top: 10px; /* Отступ сверху */ | |
} | |
.nitrogen-column { | |
display: flex; | |
flex-direction: column; | |
gap: 10px; | |
align-items: center; /* Центрируем содержимое колонки */ | |
} | |
.column-header { | |
font-weight: bold; | |
font-size: 0.8em; | |
margin-bottom: 5px; | |
text-align: center; | |
width: 100%; | |
} | |
.nitrogen-group { | |
display: flex; | |
justify-content: center; /* Центрируем поля ввода */ | |
} | |
.nitrogen-group input { | |
width: 80px; | |
padding: 5px; | |
border: 1px solid #ccc; | |
border-radius: 4px; | |
} | |
.square-button { | |
width: 60px; | |
height: 30px; | |
font-size: 18px; /* Увеличенный размер текста */ | |
font-weight: bold; /* Жирный шрифт */ | |
border: none; /* Убираем границы */ | |
border-radius: 5px; /* Легкое скругление углов */ | |
background-color: #28a745; /* Зеленый цвет фона */ | |
color: white; /* Белый текст */ | |
cursor: pointer; /* Курсор при наведении */ | |
align-items: center; /* Вертикальное центрирование */ | |
justify-content: center; /* Горизонтальное центрирование */ | |
margin-bottom: 10px; /* Отступ снизу */ | |
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* Тень для объема */ | |
} | |
/* Стили для контейнера первого блока (NPK) */ | |
.npk-container { | |
display: flex; | |
flex-direction: column; | |
align-items: flex-start; | |
gap: 10px; /* Отступ между заголовком и основным блоком */ | |
margin-left: 15px; /* Общий отступ слева */ | |
margin-top: 10px; /* Отступ сверху */ | |
} | |
/* Стили для заголовка NPK */ | |
.header-npk { | |
font-weight: bold; | |
font-size: 0.9em; | |
text-align: center; | |
width: 65px; /* Ширина заголовка совпадает с шириной блока */ | |
margin-bottom: 5px; /* Отступ между заголовком и основным блоком */ | |
margin-left: -5px; /* Сдвигаем строки влево */ | |
margin-bottom: 5px; /* Отступ между заголовком и основным блоком */ | |
} | |
/* Стили для основного блока NPK */ | |
.block-npk { | |
display: flex; | |
flex-direction: column; | |
align-items: flex-start; | |
gap: 5px; | |
padding: 10px; | |
background-color: #eaffea; | |
border: 1px solid #2e8b57; | |
border-radius: 8px; | |
width: 65px; /* Ширина блока */ | |
} | |
/* Стили для внутреннего контейнера строк (NPK) */ | |
.inner-block-npk { | |
display: flex; | |
flex-direction: column; | |
align-items: flex-start; | |
gap: 5px; | |
margin-left: -33px; /* Сдвигаем строки влево */ | |
margin-right: 0px; /* Добавляем внешний отступ справа */ | |
padding-bottom: 26px; /* Добавляем внешний отступ справа */ | |
} | |
.row-npk { | |
display: flex; | |
align-items: center; | |
gap: 5px; | |
} | |
.label-npk { | |
font-weight: bold; | |
min-width: 50px; /* Минимальная ширина меток */ | |
text-align: right; | |
} | |
.row-npk span { | |
font-size: 0.9em; | |
} | |
/* Стили для контейнера второго блока (Ca-Mg-S) */ | |
.camgs-container { | |
display: flex; | |
flex-direction: column; | |
align-items: flex-start; | |
gap: 10px; /* Отступ между заголовком и основным блоком */ | |
margin-left: 8px; /* Общий отступ слева */ | |
margin-top: 10px; /* Отступ сверху */ | |
} | |
/* Стили для заголовка Ca-Mg-S */ | |
.header-camgs { | |
font-weight: bold; | |
font-size: 0.9em; | |
text-align: center; | |
width: 75px; /* Ширина заголовка совпадает с шириной блока */ | |
margin-bottom: 5px; /* Отступ между заголовком и основным блоком */ | |
} | |
/* Стили для основного блока Ca-Mg-S */ | |
.block-camgs { | |
display: flex; | |
flex-direction: column; | |
align-items: flex-start; | |
gap: 5px; | |
padding: 10px; | |
background-color: #eaffea; | |
border: 1px solid #2e8b57; | |
border-radius: 8px; | |
width: 85px; /* Ширина блока */ | |
} | |
/* Стили для внутреннего контейнера строк (Ca-Mg-S) */ | |
.inner-block-camgs { | |
display: flex; | |
flex-direction: column; | |
align-items: flex-start; | |
gap: 5px; | |
margin-left: -31px; /* Сдвигаем строки влево */ | |
margin-right: 0px; /* Добавляем внешний отступ справа */ | |
padding-bottom: 26px; /* Добавляем внешний отступ*/ | |
} | |
.row-camgs { | |
display: flex; | |
align-items: center; | |
gap: 5px; | |
} | |
.label-camgs { | |
font-weight: bold; | |
min-width: 65px; /* Минимальная ширина меток */ | |
text-align: right; | |
} | |
.row-camgs span { | |
font-size: 0.9em; | |
} | |
/* Стили для контейнера блока N1 */ | |
.n1-container { | |
display: flex; | |
flex-direction: column; | |
align-items: flex-start; | |
gap: 10px; /* Отступ между заголовком и основным блоком */ | |
margin-left: 8px; /* Общий отступ слева */ | |
margin-top: 10px; /* Отступ сверху */ | |
} | |
/* Стили для заголовка N1 */ | |
.header-n1 { | |
font-weight: bold; | |
font-size: 0.9em; | |
text-align: left; | |
width: 100%; /* Ширина заголовка */ | |
margin-bottom: 5px; /* Отступ между заголовком и основным блоком */ | |
} | |
.header-n1 span { | |
margin-right: 5px; /* Отступ между "N1=" и значением */ | |
} | |
#n1-value { | |
font-weight: normal; | |
color: #333; | |
min-width: 100px; /* Место для значения */ | |
} | |
/* Стили для основного блока N1 */ | |
.block-n1 { | |
display: flex; | |
flex-direction: column; | |
align-items: flex-start; | |
gap: 5px; | |
padding: 10px; | |
background-color: #eaffea; | |
border: 1px solid #2e8b57; | |
border-radius: 8px; | |
width: 200px; /* Ширина блока */ | |
} | |
/* Стили для строки с меткой и индикатором */ | |
.n1-row { | |
display: flex; | |
flex-direction: column; | |
align-items: flex-start; | |
gap: 5px; | |
width: 100%; | |
} | |
.label-n1 { | |
font-weight: bold; | |
font-size: 0.9em; | |
text-align: left; | |
width: 100%; | |
} | |
/* Стили для контейнера индикатора */ | |
.indicator-container { | |
width: 150px; /* Ширина индикатора */ | |
height: 10px; /* Высота индикатора */ | |
background-color: #f0f0f0; /* Фон индикатора */ | |
border-radius: 5px; | |
overflow: hidden; | |
position: relative; | |
} | |
/* Стили для индикатора */ | |
.indicator { | |
height: 100%; | |
position: absolute; | |
top: 0; | |
left: 0; | |
transition: width 0.3s ease; /* Плавное изменение ширины */ | |
} | |
.red-indicator { | |
background-color: red; | |
} | |
.blue-indicator { | |
background-color: blue; | |
} | |
/* Стили для таблицы удобрений */ | |
.fertilisers-container { | |
display: flex; | |
flex-direction: column; | |
margin-left: 20px; /* Сдвигаем блок */ | |
} | |
.fert-row { | |
display: flex; | |
align-items: center; | |
margin-bottom: 8px; | |
} | |
.fert-header { | |
font-weight: bold; | |
text-align: center; | |
width: 80px; | |
padding: 5px; | |
font-size: 0.9em; | |
} | |
.fert-name { | |
font-weight: bold; | |
width: 120px; | |
text-align: left; | |
font-size: 0.9em; | |
} | |
.fert-cell { | |
text-align: center; | |
width: 80px; | |
padding: 5px; | |
font-size: 0.9em; | |
} | |
.fert-input { | |
width: 70px; | |
padding: 5px; | |
border: 1px solid #ccc; | |
border-radius: 4px; | |
text-align: center; | |
margin: 0 5px; | |
font-size: 0.9em; | |
} | |
/* Стили для блока расчета */ | |
.calculation-container { | |
display: flex; | |
gap: 40px; /* Расстояние между левым и правым блоками */ | |
align-items: flex-start; | |
} | |
/* Левый блок */ | |
.left-section { | |
display: flex; | |
flex-direction: column; | |
gap: 20px; /* Расстояние между элементами */ | |
width: 100%; /* Занимает всю ширину */ | |
} | |
.left-section label { | |
font-weight: bold; | |
font-size: 0.9em; | |
} | |
.left-section select { | |
width: 100%; /* Выпадающий список занимает всю ширину */ | |
padding: 5px; | |
border: 1px solid #ccc; | |
border-radius: 4px; | |
} | |
.button-group { | |
display: flex; | |
gap: 10px; /* Расстояние между кнопками */ | |
} | |
.button-group button { | |
flex: 1; /* Кнопки занимают одинаковое пространство */ | |
padding: 8px; | |
border: none; | |
border-radius: 5px; | |
cursor: pointer; | |
font-weight: bold; | |
transition: background-color 0.3s; | |
} | |
#save-profile { | |
background-color: #28a745; /* Зелёная кнопка */ | |
color: white; | |
} | |
#delete-profile { | |
background-color: #dc3545; /* Красная кнопка */ | |
color: white; | |
} | |
/* Правый блок */ | |
.right-section { | |
display: flex; | |
flex-direction: column; | |
gap: 10px; | |
} | |
.input-group { | |
display: flex; | |
align-items: center; | |
gap: 10px; | |
} | |
.input-group label { | |
width: 120px; /* Ширина меток */ | |
text-align: right; | |
font-weight: bold; | |
font-size: 0.9em; | |
} | |
.input-group input { | |
width: 70px; | |
padding: 5px; | |
border: 1px solid #ccc; | |
border-radius: 4px; | |
} | |
#calculate-btn { | |
background-color: #2e8b57; | |
color: white; | |
border: none; | |
padding: 8px 16px; | |
font-size: 16px; | |
border-radius: 5px; | |
cursor: pointer; | |
transition: background-color 0.3s; | |
margin-bottom: 5px; | |
font-weight: bold; | |
} | |
#calculate-btn:hover { | |
background-color: #3cb371; | |
} | |
/* micro контейнер */ | |
.micro-container { | |
padding: 10px; | |
} | |
.micro-row { | |
display: flex; | |
gap: 20px; | |
} | |
.micro-group { | |
display: flex; | |
align-items: center; | |
gap: 5px; | |
} | |
.micro-group label { | |
min-width: 120px; | |
} | |
.micro-group input { | |
width: 80px; | |
text-align: right; | |
} | |
</style> | |
<script> | |
vkBridge.send('VKWebAppInit'); | |
</script> | |
<body> | |
<div class="header-box"> | |
<h1>Калькулятор удобрений</h1> | |
</div> | |
<fieldset> | |
<legend>Макропрофиль в мг/л (ppm)</legend> | |
<div class="main-container"> | |
<!-- Основные элементы --> | |
<div class="profile-container"> | |
<div class="profile-element" style="grid-column: 2"> | |
<span class="profile-label">N</span> | |
<input id="profile_n" type="number" value="125.000" step="0.001"/> | |
</div> | |
<div class="profile-element" style="grid-column: 3"> | |
<span class="profile-label">P</span> | |
<input id="profile_p" type="number" value="31.000" step="0.001"/> | |
</div> | |
<div class="profile-element" style="grid-column: 4"> | |
<span class="profile-label">K</span> | |
<input id="profile_k" type="number" value="210.000" step="0.001"/> | |
</div> | |
<div class="profile-element" style="grid-column: 5"> | |
<span class="profile-label">Ca</span> | |
<input id="profile_ca" type="number" value="82.000" step="0.001"/> | |
</div> | |
<div class="profile-element" style="grid-column: 6"> | |
<span class="profile-label">Mg</span> | |
<input id="profile_mg" type="number" value="24.000" step="0.001"/> | |
</div> | |
<div class="profile-element" style="grid-column: 7"> | |
<span class="profile-label">S</span> | |
<input id="profile_s" type="number" value="57.5" step="0.001"/> | |
</div> | |
<div class="profile-element" style="grid-column: 8"> | |
<span class="profile-label">EC</span> | |
<input id="profile_ec" type="number" value="0.0" step="0.001"/> | |
</div> | |
<div class="profile-element" style="grid-column: 9"> | |
<span class="profile-label">t°C</span> | |
<input id="profile_temp" type="number" value="25.0" step="0.01"/> | |
</div> | |
</div> | |
<!-- NH4 -- NO3 --> | |
<!-- Азотные элементы и блок NPK --> | |
<div style="display: flex; align-items: flex-start; gap: 20px;"> | |
<!-- Колонки азотных элементов --> | |
<div class="nitrogen-container" style="display: flex; gap: 20px;"> | |
<!-- Колонка 1: NH4 --> | |
<div class="nitrogen-column"> | |
<button class="square-button" id="minus-button" style="font-weight: bold;">-</button> | |
<div class="column-header">NH4</div> | |
<div class="nitrogen-group"> | |
<input id="profile_nh4" type="number" value="1.0" step="1.0" readonly style="background-color: #f0f0f0; color: #666;"> | |
</div> | |
<div class="nitrogen-group"> | |
<input id="calculated_nh4" type="number" value="0.000" step="0.001" readonly style="background-color: #f0f0f0; color: #666;"> | |
</div> | |
</div> | |
<!-- Колонка 2: NO3 --> | |
<div class="nitrogen-column"> | |
<button class="square-button" id="plus-button" style="font-weight: bold;">+</button> | |
<div class="column-header">NO3</div> | |
<div class="nitrogen-group"> | |
<input id="profile_no3" type="number" value="8.25" step="0.01" min="0" max="100.001"/> | |
</div> | |
<div class="nitrogen-group"> | |
<input id="calculated_no3" type="number" value="0.000" step="0.001" readonly style="background-color: #f0f0f0; color: #666;"> | |
</div> | |
</div> | |
</div> | |
<!-- Первый блок NPK --> | |
<div class="npk-container"> | |
<!-- Заголовок NPK --> | |
<div class="header-npk">N-P-K</div> | |
<!-- Основной блок с содержимым --> | |
<div class="block-npk"> | |
<!-- Дополнительный контейнер для строк --> | |
<div class="inner-block-npk"> | |
<div class="row-npk"> | |
<span class="label-npk">N:</span> | |
<span id="npk-n-value">0</span> | |
</div> | |
<div class="row-npk"> | |
<span class="label-npk">P:</span> | |
<span id="npk-p-value">0</span> | |
</div> | |
<div class="row-npk"> | |
<span class="label-npk">K:</span> | |
<span id="npk-k-value">0</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Второй блок Ca-Mg-S --> | |
<div class="camgs-container"> | |
<!-- Заголовок Ca-Mg-S --> | |
<div class="header-camgs">Ca-Mg-S</div> | |
<!-- Основной блок с содержимым --> | |
<div class="block-camgs"> | |
<!-- Дополнительный контейнер для строк --> | |
<div class="inner-block-camgs"> | |
<div class="row-camgs"> | |
<span class="label-camgs">CaO:</span> | |
<span id="caMaS-ca-value">0</span> | |
</div> | |
<div class="row-camgs"> | |
<span class="label-camgs">MgO:</span> | |
<span id="caMaS-mg-value">0</span> | |
</div> | |
<div class="row-camgs"> | |
<span class="label-camgs">SO:</span> | |
<span id="caMaS-so-value">0</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Блок N1 --> | |
<div class="n1-container"> | |
<!-- Заголовок N1 --> | |
<div class="header-n1"> | |
<span>N1:</span> | |
<span id="n1-value">0.00:0.00:0.00:0.00:0.00 PPM=0.00</span> | |
</div> | |
<!-- Основной блок с содержимым --> | |
<div class="block-n1"> | |
<!-- Катионы --> | |
<div class="n1-row"> | |
<span class="label-n1">Катионы:</span> | |
<div class="indicator-container"> | |
<div class="indicator red-indicator" id="cation-indicator"></div> | |
</div> | |
</div> | |
<!-- Анионы --> | |
<div class="n1-row"> | |
<span class="label-n1">Анионы:</span> | |
<div class="indicator-container"> | |
<div class="indicator blue-indicator" id="anion-indicator"></div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</fieldset> | |
<fieldset> | |
<legend>Составы солей</legend> | |
<div class="fertilisers-container"> | |
<!-- Заголовки --> | |
<div class="fert-row"> | |
<span class="fert-name">Удобрение</span> | |
<span class="fert-header">NH4</span> | |
<span class="fert-header">NO3</span> | |
<span class="fert-header">P</span> | |
<span class="fert-header">K</span> | |
<span class="fert-header">Ca</span> | |
<span class="fert-header">Mg</span> | |
<span class="fert-header">S</span> | |
<span class="fert-header">Cl</span> | |
<span class="fert-header">Грамм</span> | |
</div> | |
<!-- Строки с удобрениями --> | |
<div class="fert-row"> | |
<span id="ca_no" class="fert-name" style="cursor: pointer;">Ca(NO₃)₂</span> | |
<span class="fert-cell">-</span> | |
<input class="fert-input" type="number" value="11.863" step="0.001" id="fert_ca_no3"/> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<input class="fert-input" type="number" value="16.972" step="0.001" id="fert_ca_ca"/> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<input class="fert-input" type="number" step="0.001" id="calcium_nitrate" readonly style="background-color: #d1e7fd;" /> | |
</div> | |
<div class="fert-row"> | |
<span id="kno" class="fert-name" style="cursor: pointer;">KNO₃</span> | |
<span class="fert-cell">-</span> | |
<input class="fert-input" type="number" value="13.854" step="0.001" id="fert_kno3_no3"/> | |
<span class="fert-cell">-</span> | |
<input class="fert-input" type="number" value="36.672" step="0.001" id="fert_kno3_k"/> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<input class="fert-input" type="number" step="0.001" id="potassium_nitrate" readonly style="background-color: #d1e7fd;" /> | |
</div> | |
<div class="fert-row"> | |
<span class="fert-name">NH₄NO₃</span> | |
<input class="fert-input" type="number" value="17.499" step="0.001" id="fert_nh4no3_nh4"/> | |
<input class="fert-input" type="number" value="17.499" step="0.001" id="fert_nh4no3_no3"/> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<input class="fert-input" type="number" step="0.001" id="ammonium_nitrate" readonly style="background-color: #d1e7fd;" /> | |
</div> | |
<div class="fert-row"> | |
<span class="fert-name">MgSO₄</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<input class="fert-input" type="number" value="10.22" step="0.001" id="fert_mgso4_mg" /> | |
<input class="fert-input" type="number" value="13.483" step="0.001" id="fert_mgso4_s" /> | |
<span class="fert-cell">-</span> | |
<input class="fert-input" type="number" step="0.001" id="magnesium_sulfate" readonly style="background-color: #d4f5d4;" /> | |
</div> | |
<div class="fert-row"> | |
<span class="fert-name">KH₂PO₄</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<input class="fert-input" type="number" value="22.761" step="0.001" id="fert_kh2po4_p" /> | |
<input class="fert-input" type="number" value="28.731" step="0.001" id="fert_kh2po4_k"/> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<input class="fert-input" type="number" step="0.001" id="monopotassium_phosphate" readonly style="background-color: #d4f5d4;" /> | |
</div> | |
<div class="fert-row"> | |
<span class="fert-name">K₂SO₄</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<input class="fert-input" type="number" value="44.874" step="0.001" id="fert_k2so4_k"/> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<input class="fert-input" type="number" value="18.401" step="0.001" id="fert_k2so4_s" /> | |
<span class="fert-cell">-</span> | |
<input class="fert-input" type="number" step="0.001" id="potassium_sulfate" readonly style="background-color: #d4f5d4;" /> | |
</div> | |
</div> | |
</fieldset> | |
<fieldset> | |
<legend>Микроэлементы в мг/л (ppm)</legend> | |
<div class="fertilisers-container"> | |
<!-- Заголовки --> | |
<div class="fert-row"> | |
<span class="fert-name">Элемент</span> | |
<span class="fert-header">Fe</span> | |
<span class="fert-header">Zn</span> | |
<span class="fert-header">Cu</span> | |
<span class="fert-header">Mn</span> | |
<span class="fert-header">B</span> | |
<span class="fert-header">Mo</span> | |
<span class="fert-header">Fe (11%)</span> | |
<span class="fert-header">Аквамикс<sup>®</sup></span> | |
</div> | |
<!-- Строки с микроэлементами --> | |
<div class="fert-row"> | |
<span class="fert-name">Железо</span> | |
<input class="fert-input ppm-output" type="number" step="0.001" id="ppm_fe" readonly /> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<input class="fert-input" type="number" value="2.00" step="0.01" id="fert_fe_chelate_mass"/> | |
<input class="fert-input" type="number" value="1.00" step="0.01" id="fert_fe_complex_mass" /> | |
</div> | |
<div class="fert-row"> | |
<span class="fert-name">Цинк</span> | |
<span class="fert-cell">-</span> | |
<input class="fert-input ppm-output" type="number" step="0.001" id="ppm_zn" readonly /> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
</div> | |
<div class="fert-row"> | |
<span class="fert-name">Медь</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<input class="fert-input ppm-output" type="number" step="0.001" id="ppm_cu" readonly /> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
</div> | |
<div class="fert-row"> | |
<span class="fert-name">Марганец</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<input class="fert-input ppm-output" type="number" step="0.001" id="ppm_mn" readonly /> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
</div> | |
<div class="fert-row"> | |
<span class="fert-name">Бор</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<input class="fert-input ppm-output" type="number" step="0.001" id="ppm_b" readonly /> | |
<span class="fert-cell">-</span> | |
</div> | |
<div class="fert-row"> | |
<span class="fert-name">Молибден</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<span class="fert-cell">-</span> | |
<input class="fert-input ppm-output" type="number" step="0.001" id="ppm_mo" readonly /> | |
<span class="fert-cell">-</span> | |
</div> | |
</div> | |
</fieldset> | |
<fieldset class="calculation-box"> | |
<legend>Расчёт удобрений</legend> | |
<div class="calculation-container"> | |
<!-- Левый блок --> | |
<div class="left-section"> | |
<!-- Выпадающий список --> | |
<label for="profile-selector">Выберите профиль:</label> | |
<select id="profile-selector"></select> | |
<!-- Кнопки сохранения и удаления --> | |
<div class="button-group"> | |
<button id="save-profile">Сохранить текущий профиль</button> | |
<button id="delete-profile">Удалить выбранный профиль</button> | |
</div> | |
</div> | |
<!-- Правый блок --> | |
<div class="right-section"> | |
<div class="input-column"> | |
<div class="input-group"> | |
<label for="liters-input">Литры:</label> | |
<input type="number" id="liters-input" value="100" min="1" step="1"> | |
</div> | |
<div class="input-group"> | |
<label for="rounding-precision">Точность:</label> | |
<input type="number" id="rounding-precision" value="3" min="0" max="3" step="1"> | |
</div> | |
<button id="calculate-btn">Рассчитать</button> | |
</div> | |
</div> | |
</div> | |
</fieldset> | |
<!-- Кастомные стили --> | |
<style> | |
.pnotify-success { | |
background: linear-gradient(135deg, #4CAF50, #2E7D32) ; | |
border-left: 5px solid #1B5E20 ; | |
} | |
.pnotify-error { | |
background: linear-gradient(135deg, #F44336, #C62828) ; | |
border-left: 5px solid #B71C1C ; | |
} | |
.deficit-item { | |
margin: 8px 0; | |
display: flex; | |
align-items: center; | |
} | |
.deficit-item i { | |
margin-right: 10px; | |
font-size: 1.2em; | |
} | |
</style> | |
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/toastify-js"></script> | |
<script> | |
let call_data; | |
const ecConstants = { | |
'P': 0.0012, 'K': 0.0018, 'Mg': 0.0015, | |
'Ca': 0.0016, 'S': 0.0014, | |
'N (NO3-)': 0.0017, 'N (NH4+)': 0.0019 | |
}; | |
document.getElementById('calculate-btn').addEventListener('click', function () { | |
console.log("=== НАЧАЛО ОБРАБОТКИ ==="); | |
// 1. Получаем значение точности округления | |
const roundingInput = document.getElementById('rounding-precision'); | |
const initialRounding = parseInt(roundingInput.value); | |
const roundingPrecision = Math.min(Math.max(initialRounding || 3, 0), 6); | |
// 2. Функция для безопасного получения числового значения | |
const getValue = (id) => { | |
const element = document.getElementById(id); | |
if (!element) { | |
console.error(`Элемент с ID ${id} не найден!`); | |
return 0; | |
} | |
const value = parseFloat(element.value); | |
return isNaN(value) ? 0 : value; | |
}; | |
// 3. Формируем данные для сервера | |
const fertilizerConstants = { | |
"Кальциевая селитра": { | |
"N (NO3-)": getValue('fert_ca_no3') / 100, | |
"Ca": getValue('fert_ca_ca') / 100 | |
}, | |
"Калий азотнокислый": { | |
"N (NO3-)": getValue('fert_kno3_no3') / 100, | |
"K": getValue('fert_kno3_k') / 100 | |
}, | |
"Аммоний азотнокислый": { | |
"N (NO3-)": getValue('fert_nh4no3_no3') / 100, | |
"N (NH4+)": getValue('fert_nh4no3_nh4') / 100 | |
}, | |
"Сульфат магния": { | |
"Mg": getValue('fert_mgso4_mg') / 100, | |
"S": getValue('fert_mgso4_s') / 100 | |
}, | |
"Монофосфат калия": { | |
"P": getValue('fert_kh2po4_p') / 100, | |
"K": getValue('fert_kh2po4_k') / 100 | |
}, | |
"Калий сернокислый": { | |
"K": getValue('fert_k2so4_k') / 100, | |
"S": getValue('fert_k2so4_s') / 100 | |
} | |
}; | |
const profileSettings = { | |
'P': getValue('profile_p'), | |
'K': getValue('profile_k'), | |
'Mg': getValue('profile_mg'), | |
'Ca': getValue('profile_ca'), | |
'S': getValue('profile_s'), | |
'NO3_RAT': getValue('profile_no3'), | |
'TOTAL_NITROG': getValue('profile_n'), | |
'liters': parseInt(document.getElementById('liters-input').value) || 1, | |
'rounding_precision': roundingPrecision | |
}; | |
const requestData = { | |
fertilizerConstants: fertilizerConstants, | |
profileSettings: profileSettings | |
}; | |
console.log("=== ПОЛНЫЙ ОБЪЕКТ ДЛЯ ОТПРАВКИ ===", JSON.stringify(requestData, null, 2)); | |
// 4. Отправка данных на сервер | |
fetch('/calculation', { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
}, | |
body: JSON.stringify(requestData), | |
}) | |
.then(response => { | |
console.log("Получен ответ от сервера. Статус:", response.status); | |
if (!response.ok) { | |
throw new Error(`HTTP error! status: ${response.status}`); | |
} | |
return response.json(); | |
}) | |
.then(data => { | |
call_data = data; | |
console.log("=== УСПЕШНЫЙ ОТВЕТ ОТ СЕРВЕРА ===", call_data); | |
// Обновляем только поля вывода | |
data_out(call_data); | |
// Обновляем поля NH4 и NO3 | |
updateNitrogenFields(call_data); | |
// Показываем статус расчёта | |
showCalculationStatus(call_data); | |
// Вызываем функцию для расчета катионов и анионов | |
calculateCationsAndAnions(data); | |
calculateAndUpdate(call_data); | |
calculateMicroElements(); | |
calculateN1Ratio(call_data); | |
// Рассчитываем EC | |
const temperature = parseFloat(document.getElementById('profile_temp').value) || 25; | |
const ecValue = calculateEC(call_data, temperature); | |
const ecInput = document.getElementById('profile_ec'); | |
if (ecInput) { | |
ecInput.value = ecValue.toFixed(2); | |
console.log(`Установлено значение EC: ${ecValue.toFixed(2)}`); | |
} | |
}) | |
.catch(error => { | |
console.error("=== ОШИБКА ПРИ ОБРАБОТКЕ ===", error); | |
alert("Ошибка при расчете: " + error.message); | |
}); | |
}); | |
function data_out(data) { | |
console.log("=== ЗАПИСЬ ДАННЫХ В ФОРМУ ==="); | |
// Сопоставление названий удобрений с их идентификаторами | |
const fertilizerIdMap = { | |
"Кальциевая селитра": "calcium_nitrate", | |
"Калий азотнокислый": "potassium_nitrate", | |
"Аммоний азотнокислый": "ammonium_nitrate", | |
"Сульфат магния": "magnesium_sulfate", | |
"Монофосфат калия": "monopotassium_phosphate", | |
"Калий сернокислый": "potassium_sulfate" | |
}; | |
// Записываем массы удобрений | |
for (const [fertilizer, grams] of Object.entries(data.fertilizers)) { | |
const inputId = fertilizerIdMap[fertilizer]; | |
const inputElement = document.getElementById(inputId); | |
if (inputElement) { | |
inputElement.value = grams.toFixed(3); // Записываем значение в поле вывода | |
console.log(`Записано значение для ${fertilizer}: ${grams.toFixed(3)} г`); | |
} else { | |
console.warn(`Поле для удобрения ${fertilizer} не найдено`); | |
} | |
} | |
// Логика для CaCl больше не нужна | |
console.log("Логика для Кальция хлористого удалена."); | |
} | |
function updateNitrogenFields(data) { | |
console.log("=== ОБНОВЛЕНИЕ ЗНАЧЕНИЙ NH4 И NO3 ==="); | |
// Извлекаем значения NH4 и NO3 из actual_profile | |
const nh4Value = data.actual_profile["N (NH4+)"] || 0; | |
const no3Value = data.actual_profile["N (NO3-)"] || 0; | |
// Обновляем поля NH4 | |
document.getElementById("calculated_nh4").value = nh4Value.toFixed(3); // Расчетное значение NH4 | |
// Обновляем поля NO3 | |
document.getElementById("calculated_no3").value = no3Value.toFixed(3); // Расчетное значение NO3 | |
console.log(`Значения NH4 и NO3 обновлены: NH4=${nh4Value}, NO3=${no3Value}`); | |
} | |
function calculateAndUpdate(data) { | |
console.log("=== РАСЧЕТ И ОБНОВЛЕНИЕ ДАННЫХ ==="); | |
// Извлекаем значения из actual_profile | |
const nValue = (data.actual_profile["N (NH4+)"] || 0) + (data.actual_profile["N (NO3-)"] || 0); | |
const pValue = data.actual_profile["P"] || 0; | |
const kValue = data.actual_profile["K"] || 0; | |
const caValue = data.actual_profile["Ca"] || 0; | |
const mgValue = data.actual_profile["Mg"] || 0; | |
const sValue = data.actual_profile["S"] || 0; | |
// Динамический расчет процента азота | |
const totalNitrogen = data.nitrogen_ratios.TOTAL_NITROGEN || 0; // Берем TOTAL_NITROGEN из ответа | |
const nPercent = totalNitrogen / 10; // Делим на 10, как предложено | |
// Рассчитываем общую массу раствора | |
const totalMass = nValue / (nPercent / 100); // Общая масса = N / (nPercent / 100) | |
// Переводим элементы в оксидную форму | |
const pOxide = pValue * 2.29; // P → P2O5 | |
const kOxide = kValue * 1.2; // K → K2O | |
const caOxide = caValue * 1.4; // Ca → CaO | |
const mgOxide = mgValue * 1.67; // Mg → MgO | |
const sOxide = sValue * 2.5; // S → SO3 | |
// Рассчитываем проценты от общей массы | |
const pPercent = (pOxide / totalMass) * 100; | |
const kPercent = (kOxide / totalMass) * 100; | |
const caPercent = (caOxide / totalMass) * 100; | |
const mgPercent = (mgOxide / totalMass) * 100; | |
const sPercent = (sOxide / totalMass) * 100; | |
console.log(`Результаты расчета: | |
N: ${nPercent.toFixed(2)}%, | |
P2O5: ${pPercent.toFixed(2)}%, | |
K2O: ${kPercent.toFixed(2)}%, | |
CaO: ${caPercent.toFixed(2)}%, | |
MgO: ${mgPercent.toFixed(2)}%, | |
SO3: ${sPercent.toFixed(2)}%`); | |
// Обновляем HTML-элементы | |
document.getElementById("npk-n-value").textContent = nPercent.toFixed(2); | |
document.getElementById("npk-p-value").textContent = pPercent.toFixed(2); | |
document.getElementById("npk-k-value").textContent = kPercent.toFixed(2); | |
document.getElementById("caMaS-ca-value").textContent = caPercent.toFixed(2); | |
document.getElementById("caMaS-mg-value").textContent = mgPercent.toFixed(2); | |
document.getElementById("caMaS-so-value").textContent = sPercent.toFixed(2); | |
} | |
function calculateCationsAndAnions() { | |
console.log("=== ДИНАМИЧЕСКИЙ РАСЧЕТ КАТИОНОВ И АНИОНОВ ==="); | |
// 1. Получаем текущие значения из формы | |
const getValue = (id) => parseFloat(document.getElementById(id).value) || 0; | |
const profile = { | |
'N (NO3-)': getValue('calculated_no3'), | |
'N (NH4+)': getValue('calculated_nh4'), | |
'P': getValue('profile_p'), | |
'K': getValue('profile_k'), | |
'Ca': getValue('profile_ca'), | |
'Mg': getValue('profile_mg'), | |
'S': getValue('profile_s') | |
// Убрали 'Cl', так как больше не учитываем кальций хлористый | |
}; | |
// 2. Константы из вашей формы (упрощенные) | |
const FERTILIZER_CONSTANTS = { | |
'Кальциевая селитра': { 'N (NO3-)': 0.11863, 'Ca': 0.16972 }, | |
'Калий азотнокислый': { 'N (NO3-)': 0.13854, 'K': 0.36672 }, | |
'Аммоний азотнокислый': { 'N (NO3-)': 0.17499, 'N (NH4+)': 0.17499 }, | |
'Сульфат магния': { 'Mg': 0.10220, 'S': 0.13483 }, | |
'Монофосфат калия': { 'P': 0.22761, 'K': 0.28731 }, | |
'Калий сернокислый': { 'K': 0.44874, 'S': 0.18401 } | |
// Убрали 'Кальций хлорид' | |
}; | |
// 3. Молярные массы и валентности | |
const ION_DATA = { | |
'Ca': { mass: 40.08, charge: 2 }, | |
'Mg': { mass: 24.305, charge: 2 }, | |
'K': { mass: 39.098, charge: 1 }, | |
'NH4': { mass: 18.038, charge: 1 }, | |
'NO3': { mass: 62.004, charge: 1 }, | |
'SO4': { mass: 96.06, charge: 2 }, | |
'H2PO4': { mass: 96.99, charge: 1 } | |
// Убрали 'Cl', так как больше не учитываем хлор | |
}; | |
// 4. Пересчет серы (S → SO4²⁻) | |
const so4 = profile['S'] * (96.06 / 32.06); | |
// 5. Расчет mEq/L для каждого иона | |
const ions = { | |
'Ca': profile['Ca'] * ION_DATA['Ca'].charge / ION_DATA['Ca'].mass, | |
'Mg': profile['Mg'] * ION_DATA['Mg'].charge / ION_DATA['Mg'].mass, | |
'K': profile['K'] * ION_DATA['K'].charge / ION_DATA['K'].mass, | |
'NH4': profile['N (NH4+)'] * ION_DATA['NH4'].charge / ION_DATA['NH4'].mass, | |
'NO3': profile['N (NO3-)'] * ION_DATA['NO3'].charge / ION_DATA['NO3'].mass, | |
'SO4': so4 * ION_DATA['SO4'].charge / ION_DATA['SO4'].mass, | |
'H2PO4': profile['P'] * ION_DATA['H2PO4'].charge / ION_DATA['H2PO4'].mass | |
// Убрали 'Cl', так как больше не учитываем хлор | |
}; | |
// 6. Суммирование | |
const totalCations = ions['Ca'] + ions['Mg'] + ions['K'] + ions['NH4']; | |
const totalAnions = ions['NO3'] + ions['SO4'] + ions['H2PO4']; | |
// Убрали 'Cl' из суммы анионов | |
// 7. Расчет процентов | |
const total = totalCations + totalAnions; | |
const cationPercent = total > 0 ? (totalCations / total * 100).toFixed(1) : 0; | |
const anionPercent = total > 0 ? (totalAnions / total * 100).toFixed(1) : 0; | |
// 8. Вывод результатов | |
console.log(`Катионы: ${totalCations.toFixed(2)} mEq/L (${cationPercent}%)`); | |
console.log(`Анионы: ${totalAnions.toFixed(2)} mEq/L (${anionPercent}%)`); | |
console.log(`Дисбаланс: ${(totalCations - totalAnions).toFixed(2)} mEq/L`); | |
// 9. Обновление UI | |
document.getElementById("n1-value").textContent = | |
`Катионы: ${totalCations.toFixed(2)} mEq/L | Анионы: ${totalAnions.toFixed(2)} mEq/L`; | |
// Обновление ширины индикаторов | |
document.getElementById("cation-indicator").style.width = `${cationPercent}%`; | |
document.getElementById("anion-indicator").style.width = `${anionPercent}%`; | |
} | |
function calculateEC(data, temperature, alpha = 0.019) { | |
console.log("=== РАСЧЕТ ЭЛЕКТРОПРОВОДНОСТИ (EC) ==="); | |
const profile = data.actual_profile; | |
let totalEC = 0; | |
const ecConstants = { | |
'P': 0.0012, 'K': 0.0018, 'Mg': 0.0015, | |
'Ca': 0.0016, 'S': 0.0014, | |
'N (NO3-)': 0.0017, 'N (NH4+)': 0.0019 | |
}; | |
const significantElements = Object.keys(ecConstants); | |
for (const element of significantElements) { | |
const ppm = profile[element] || 0; | |
const ecFactor = ecConstants[element]; | |
const elementEC = ppm * ecFactor; | |
totalEC += elementEC; | |
console.log(`EC для ${element}: ${elementEC.toFixed(5)} (ppm=${ppm}, const=${ecFactor})`); | |
} | |
console.log(`Общая EC без компенсации: ${totalEC.toFixed(5)}`); | |
const compensatedEC = totalEC * (1 + alpha * (temperature - 25)); | |
console.log(`Компенсированная EC: ${compensatedEC.toFixed(5)} (T=${temperature}°C, α=${alpha})`); | |
return compensatedEC; | |
} | |
function calculateMicroElements() { | |
// 1. Получаем объем раствора (в литрах) | |
const litersInput = document.getElementById('liters-input'); | |
if (!litersInput) { | |
console.error("Элемент с id='liters-input' не найден!"); | |
return; | |
} | |
const solutionVolume = parseFloat(litersInput.value); | |
if (!solutionVolume || solutionVolume <= 0) { | |
alert("Введите корректный объем раствора!"); | |
return; | |
} | |
// 2. Получаем массы удобрений (в граммах) | |
const feChelateInput = document.getElementById('fert_fe_chelate_mass'); | |
const complexPowderInput = document.getElementById('fert_fe_complex_mass'); | |
if (!feChelateInput || !complexPowderInput) { | |
console.error("Один из элементов для ввода масс удобрений не найден!"); | |
return; | |
} | |
const feChelateMass = parseFloat(feChelateInput.value) || 0; | |
const complexPowderMass = parseFloat(complexPowderInput.value) || 0; | |
// 3. Содержание микроэлементов в удобрениях (%) | |
const feChelateContent = { | |
Fe: 0.11 // Хелат железа (11%) | |
}; | |
const complexPowderContent = { | |
Fe: 0.0384, // Fe (ДТПА + ЭДТА) = 3.84% | |
Zn: 0.0053, // Zn (ЭДТА) = 0.53% | |
Cu: 0.0053, // Cu (ЭДТА) = 0.53% | |
Mn: 0.0257, // Mn (ЭДТА) = 2.57% | |
B: 0.0052, // B = 0.52% | |
Mo: 0.0013 // Mo = 0.13% | |
}; | |
// 4. Рассчитываем чистые количества микроэлементов (в граммах) | |
const pureElements = { | |
Fe: (feChelateMass * feChelateContent.Fe) + (complexPowderMass * complexPowderContent.Fe), | |
Zn: complexPowderMass * complexPowderContent.Zn, | |
Cu: complexPowderMass * complexPowderContent.Cu, | |
Mn: complexPowderMass * complexPowderContent.Mn, | |
B: complexPowderMass * complexPowderContent.B, | |
Mo: complexPowderMass * complexPowderContent.Mo | |
}; | |
// 5. Рассчитываем концентрации в PPM (мг/л) | |
const ppm = {}; | |
for (const element in pureElements) { | |
ppm[element] = (pureElements[element] * 1000) / solutionVolume; // Переводим граммы в мг/л | |
} | |
// 6. Выводим результаты в соответствующие поля матрицы | |
const outputFields = ['ppm_fe', 'ppm_zn', 'ppm_cu', 'ppm_mn', 'ppm_b', 'ppm_mo']; | |
outputFields.forEach((field, index) => { | |
const element = document.getElementById(field); | |
if (element) { | |
element.value = ppm[Object.keys(ppm)[index]]?.toFixed(3) || 0; | |
} else { | |
console.error(`Элемент с id='${field}' не найден!`); | |
} | |
}); | |
console.log("=== РАСЧЕТ PPM ===", ppm); | |
} | |
// Функция для расчета соотношений и формирования строки | |
function calculateN1Ratio(data) { | |
// Получаем значения элементов из ответа сервера | |
const { actual_profile, total_ppm } = data; | |
const { P, K, Ca, Mg, S } = actual_profile; | |
// Общий азот (N_total) = N (NH4+) + N (NO3-) | |
const N_total = actual_profile["N (NH4+)"] + actual_profile["N (NO3-)"]; | |
// Вычисляем соотношения относительно общего азота (принимаем его за единицу) | |
const P_ratio = (P / N_total).toFixed(2); | |
const K_ratio = (K / N_total).toFixed(2); | |
const CaO_ratio = (Ca / N_total).toFixed(2); // Предполагается, что Ca - это оксид кальция | |
const MgO_ratio = (Mg / N_total).toFixed(2); // Предполагается, что Mg - это оксид магния | |
const SO_ratio = (S / N_total).toFixed(2); // Предполагается, что S - это оксид серы | |
// Формируем строку в требуемом формате | |
const n1String = `${P_ratio}:${K_ratio}:${CaO_ratio}:${MgO_ratio}:${SO_ratio} PPM=${total_ppm.toFixed(2)}`; | |
// Вставляем строку в HTML | |
document.getElementById("n1-value").textContent = n1String; | |
} | |
</script> | |
<script> | |
// Инициализация | |
const notyf = new Notyf({ | |
duration: 5000, | |
position: {x: 'right', y: 'top'}, | |
types: [ | |
{type: 'success', background: '#4CAF50'}, | |
{type: 'error', background: '#F44336'} | |
] | |
}); | |
// Модифицированная функция | |
function showCalculationStatus(response) { | |
if (Object.keys(response.deficits || {}).length === 0) { | |
notyf.success('Расчёт успешен! Все элементы сбалансированы'); | |
} else { | |
notyf.error('Дефициты: ' + | |
Object.entries(response.deficits) | |
.map(([el, val]) => `${el}: ${val.toFixed(2)} ppm`) | |
.join(', ')); | |
} | |
} | |
</script> | |
<script> | |
// Функция для увеличения значений на 10% | |
function increaseValues() { | |
const fields = [ | |
"profile_n", "profile_p", "profile_k", | |
"profile_ca", "profile_mg", "profile_s" | |
]; | |
fields.forEach(id => { | |
const input = document.getElementById(id); | |
let value = parseFloat(input.value); // Берём текущее значение | |
if (!isNaN(value)) { // Проверяем, что значение является числом | |
value *= 1.1; // Увеличиваем на 10% | |
input.value = value.toFixed(3); // Округляем до 3 знаков после запятой | |
} | |
}); | |
} | |
// Функция для уменьшения значений на 10% | |
function decreaseValues() { | |
const fields = [ | |
"profile_n", "profile_p", "profile_k", | |
"profile_ca", "profile_mg", "profile_s" | |
]; | |
fields.forEach(id => { | |
const input = document.getElementById(id); | |
let value = parseFloat(input.value); // Берём текущее значение | |
if (!isNaN(value)) { // Проверяем, что значение является числом | |
value *= 0.9; // Уменьшаем на 10% | |
value = Math.max(value, 0); // Не позволяем значению стать отрицательным | |
input.value = value.toFixed(3); // Округляем до 3 знаков после запятой | |
} | |
}); | |
} | |
// Привязываем функции к кнопкам | |
document.getElementById("plus-button").addEventListener("click", increaseValues); | |
document.getElementById("minus-button").addEventListener("click", decreaseValues); | |
</script> | |
<script src="https://unpkg.com/tippy.js@6"></script> | |
<script> | |
tippy('#ca_no', { | |
content: 'Нитрат Кальция (кальциевая селитра)', | |
placement: 'top', // Расположение: top, bottom, left, right | |
arrow: true, // Добавить стрелку | |
}); | |
</script> | |
<script> | |
tippy('#kno', { | |
content: 'Нитрат калия (калиевая селитра)', | |
placement: 'top', // Расположение: top, bottom, left, right | |
arrow: true, // Добавить стрелку | |
}); | |
</script> | |
<script> | |
// Предустановленные профили (JSON) | |
// Предустановленные профили (JSON) | |
const predefinedProfiles = { | |
profiles: [ | |
{ | |
name: "Профиль 1", | |
values: { | |
profile_p: 31, | |
profile_k: 210, | |
profile_mg: 24, | |
profile_ca: 82, | |
profile_s: 57.5, | |
profile_no3: 8.25, // Соотношение азотов | |
profile_n: 125, | |
liters: 100, // Литры | |
rounding_precision: 3, // Точность | |
fert_fe_chelate_mass: 2.0, | |
fert_fe_complex_mass: 1.0 | |
} | |
}, | |
{ | |
name: "Профиль 2", | |
values: { | |
profile_p: 40, | |
profile_k: 250, | |
profile_mg: 30, | |
profile_ca: 90, | |
profile_s: 60, | |
profile_no3: 10, // Соотношение азотов | |
profile_n: 150, | |
liters: 150, // Литры | |
rounding_precision: 2, // Точность | |
fert_fe_chelate_mass: 3.0, | |
fert_fe_complex_mass: 2.0 | |
} | |
} | |
] | |
}; | |
// Загрузка всех профилей (предустановленные + пользовательские) | |
function loadAllProfiles() { | |
const userProfiles = JSON.parse(localStorage.getItem("userProfiles")) || []; | |
return [...predefinedProfiles.profiles, ...userProfiles]; | |
} | |
// Создание выпадающего списка | |
function populateProfileSelector() { | |
const profiles = loadAllProfiles(); | |
const selector = document.getElementById("profile-selector"); | |
// Очищаем предыдущие опции | |
selector.innerHTML = ""; | |
// Добавляем предустановленные профили | |
const predefinedGroup = document.createElement("optgroup"); | |
predefinedGroup.label = "Предустановленные профили"; | |
predefinedProfiles.profiles.forEach(profile => { | |
const option = document.createElement("option"); | |
option.value = profile.name; | |
option.textContent = profile.name; | |
predefinedGroup.appendChild(option); | |
}); | |
selector.appendChild(predefinedGroup); | |
// Добавляем пользовательские профили | |
const userProfiles = JSON.parse(localStorage.getItem("userProfiles")) || []; | |
if (userProfiles.length > 0) { | |
const userGroup = document.createElement("optgroup"); | |
userGroup.label = "Пользовательские профили"; | |
userProfiles.forEach(profile => { | |
const option = document.createElement("option"); | |
option.value = profile.name; | |
option.textContent = profile.name; | |
userGroup.appendChild(option); | |
}); | |
selector.appendChild(userGroup); | |
} | |
// Выбираем первый профиль по умолчанию | |
if (profiles.length > 0) { | |
selector.value = profiles[0].name; | |
updateProfileFields(profiles[0]); | |
} | |
} | |
// Обновление полей при выборе профиля | |
function updateProfileFields(selectedProfile) { | |
document.getElementById("profile_p").value = selectedProfile.values.profile_p || 0; | |
document.getElementById("profile_k").value = selectedProfile.values.profile_k || 0; | |
document.getElementById("profile_mg").value = selectedProfile.values.profile_mg || 0; | |
document.getElementById("profile_ca").value = selectedProfile.values.profile_ca || 0; | |
document.getElementById("profile_s").value = selectedProfile.values.profile_s || 0; | |
document.getElementById("profile_no3").value = selectedProfile.values.profile_no3 || 0; // Соотношение азотов | |
document.getElementById("profile_n").value = selectedProfile.values.profile_n || 0; | |
document.getElementById("liters-input").value = selectedProfile.values.liters || 100; // Литры | |
document.getElementById("rounding-precision").value = selectedProfile.values.rounding_precision || 3; // Точность | |
document.getElementById("fert_fe_chelate_mass").value = selectedProfile.values.fert_fe_chelate_mass || 0; | |
document.getElementById("fert_fe_complex_mass").value = selectedProfile.values.fert_fe_complex_mass || 0; | |
} | |
// Слушатель события изменения выбранного профиля | |
document.getElementById("profile-selector").addEventListener("change", function () { | |
const selectedProfileName = this.value; | |
const allProfiles = loadAllProfiles(); | |
const selectedProfile = allProfiles.find(profile => profile.name === selectedProfileName); | |
if (selectedProfile) { | |
updateProfileFields(selectedProfile); | |
} | |
}); | |
// Сохранение нового профиля | |
document.getElementById("save-profile").addEventListener("click", async function () { | |
// Используем SweetAlert2 для ввода имени профиля | |
const { value: profileName } = await Swal.fire({ | |
title: "Введите название профиля", | |
input: "text", | |
inputPlaceholder: "Название профиля", | |
showCancelButton: true, | |
confirmButtonText: "Сохранить", | |
cancelButtonText: "Отмена", | |
confirmButtonColor: "#28a745", // Зелёная кнопка | |
cancelButtonColor: "#dc3545", // Красная кнопка | |
inputValidator: (value) => { | |
if (!value) { | |
return "Название профиля не может быть пустым!"; | |
} | |
} | |
}); | |
if (!profileName) return; | |
// Создаём новый профиль | |
const newProfile = { | |
name: profileName, | |
values: { | |
profile_p: parseFloat(document.getElementById("profile_p").value) || 0, | |
profile_k: parseFloat(document.getElementById("profile_k").value) || 0, | |
profile_mg: parseFloat(document.getElementById("profile_mg").value) || 0, | |
profile_ca: parseFloat(document.getElementById("profile_ca").value) || 0, | |
profile_s: parseFloat(document.getElementById("profile_s").value) || 0, | |
profile_no3: parseFloat(document.getElementById("profile_no3").value) || 0, // Соотношение азотов | |
profile_n: parseFloat(document.getElementById("profile_n").value) || 0, | |
liters: parseFloat(document.getElementById("liters-input").value) || 100, // Литры | |
rounding_precision: parseInt(document.getElementById("rounding-precision").value) || 3, // Точность | |
fert_fe_chelate_mass: parseFloat(document.getElementById("fert_fe_chelate_mass").value) || 0, | |
fert_fe_complex_mass: parseFloat(document.getElementById("fert_fe_complex_mass").value) || 0 | |
} | |
}; | |
// Добавляем профиль в localStorage | |
let userProfiles = JSON.parse(localStorage.getItem("userProfiles")) || []; | |
userProfiles.push(newProfile); | |
localStorage.setItem("userProfiles", JSON.stringify(userProfiles)); | |
// Обновляем выпадающий список | |
populateProfileSelector(); | |
}); | |
// Удаление выбранного профиля | |
document.getElementById("delete-profile").addEventListener("click", async function () { | |
const selectedProfileName = document.getElementById("profile-selector").value; | |
if (!selectedProfileName) { | |
Swal.fire({ | |
icon: "error", | |
title: "Ошибка", | |
text: "Выберите профиль для удаления!" | |
}); | |
return; | |
} | |
// Находим индекс профиля в localStorage | |
let userProfiles = JSON.parse(localStorage.getItem("userProfiles")) || []; | |
const profileIndex = userProfiles.findIndex(profile => profile.name === selectedProfileName); | |
if (profileIndex === -1) { | |
Swal.fire({ | |
icon: "error", | |
title: "Ошибка", | |
text: "Невозможно удалить предустановленный профиль!" | |
}); | |
return; | |
} | |
// Показываем SweetAlert2 для подтверждения удаления | |
const result = await Swal.fire({ | |
title: "Вы уверены?", | |
text: "Это действие нельзя отменить!", | |
icon: "warning", | |
showCancelButton: true, | |
confirmButtonText: "Да, удалить!", | |
cancelButtonText: "Нет, отмена!", | |
confirmButtonColor: "#dc3545", // Красная кнопка | |
cancelButtonColor: "#28a745", // Зелёная кнопка | |
reverseButtons: true | |
}); | |
if (result.isConfirmed) { | |
// Удаляем профиль | |
userProfiles.splice(profileIndex, 1); | |
localStorage.setItem("userProfiles", JSON.stringify(userProfiles)); | |
// Обновляем выпадающий список | |
populateProfileSelector(); | |
// Выбираем первый профиль по умолчанию | |
const allProfiles = loadAllProfiles(); | |
if (allProfiles.length > 0) { | |
document.getElementById("profile-selector").value = allProfiles[0].name; | |
updateProfileFields(allProfiles[0]); | |
} | |
// Показываем сообщение об успешном удалении | |
Swal.fire({ | |
title: "Удалено!", | |
text: "Профиль успешно удалён.", | |
icon: "success" | |
}); | |
} else if (result.dismiss === Swal.DismissReason.cancel) { | |
// Показываем сообщение об отмене | |
Swal.fire({ | |
title: "Отменено", | |
text: "Профиль не был удалён.", | |
icon: "error" | |
}); | |
} | |
}); | |
// Инициализация выпадающего списка при загрузке страницы | |
populateProfileSelector(); | |
</script> | |
</body> | |
</html> |