В одном из личных проектов, на сайте МебельДагестана пришлось делать калькулятор доставки. Так как почти всех клиентов которых интересует корпусная мебель на заказ, впервую очередь интересуют вопросы доставки и сборки. Готовых решений я не нашел. Были конечно модули от самых транспортных компаний но они мне показались сложными в настройке да еще и требовали согласования с самой транспортной компанией. Потому было решено делать свой калькулятор доствка на сайте.
Добавляем tabs с доставкой на странице товара
Дизайн предполагался простым, 2 колонки. 1 колонка с полями ввода значений, вторая колонка с результирующей стоимостью и кнопкой расчета. Логика не предполагала сложных расчетов. Расчет все таки должен давать ориентировочную цену доставки. Точную цену в любом случае дать не получится, так как логисты транспортной все будут рассчитывать по своим коэффициентам.
Вставить на сайт форму я решил в дополнительный вкладке доп информации woocommerce./
Так было до. А дальше скрин после добавления вкладки с рассчетом доставки.
Код добавления вкладки следующий
add_filter( 'woocommerce_product_tabs', 'woo_delivery_tab' ); function woo_delivery_tab( $tabs ) { // Только для категорий кухни и шкафы global $product; if ( has_term( array( 'Кухни', 'Шкафы-купе', 'Прихожие', 'Диваны' ), 'product_cat', $product->get_id() )) { $tabs['woo_delivery_tab'] = array( 'title' => __( 'Доставка', 'woocommerce' ), 'priority' => 20, 'callback' => 'woo_delivery_tab_content' ); return $tabs; } else { return $tabs; } } function woo_delivery_tab_content() { global $product; // проверяем, принадлежит ли товар категории "Кухни" if ( has_term( 'Кухни', 'product_cat', $product->get_id()) ) { // Подключаем файл с нужным HTML-кодом get_template_part( 'template-parts/tabs/delivery-kitchen', 25 ); } }
При этом тут есть функция get_template_part( ‘template-parts/tabs/delivery-kitchen’, 25 ); которая подразумевает что у нас в папке дочерней темы по пути template-parts/tabs/ находится файл delivery-kitchen.php в котором мы и будем писать логику.
Пишем код калькулятора на html + js
Никаких данных из самого товара я решил не подтягивать, а потому на php ничего писать не пришлось. Код файла delivery-kitchen.php это html код и js скрипт. Все очень просто. Кстати почему файл так называется, потому что я для каждой категории товаров скорее всего буду делать свой калькулятор, так как вариант просчета доставка для кухни и например дивана будет немного отличаться.
Итак код верстки самой формы такой.
<div class="calculator"> <div class="delivery-form"> <div> <h2 class="has-text-align-center wp-block-heading" access="false" id="control-1417866">Рассчитать стоимость доставки</h2> </div> <form> <div> <label for="size">Размер (пог. метр):</label> <input type="number" id="size" name="size" value="3" placeholder="Длина кухни/шкафа в погонных метрах" required> <div id="size-error" class="invalid-feedback"></div> </div> <div> <label for="delivery_city">Куда:</label> <select name="delivery_city" id="delivery_city" required> <option value="Москва">Москва</option> <option value="Санкт-Петербург">Санкт-Петербург</option> <option value="Новосибирск">Новосибирск</option> <option value="Екатеринбург">Екатеринбург</option> <option value="Нижний Новгород">Нижний Новгород</option> <option value="Казань">Казань</option> <option value="Челябинск">Челябинск</option> <option value="Омск">Омск</option> <option value="Самара">Самара</option> <option value="Ростов-на-Дону">Ростов-на-Дону</option> <option value="Уфа">Уфа</option> <option value="Красноярск">Красноярск</option> <option value="Пермь">Пермь</option> <option value="Воронеж">Воронеж</option> <option value="Волгоград">Волгоград</option> </select> </div> <div> <label for="weight">Вес груза (кг):</label> <input type="number" id="weight" name="weight" min="1" max="10000" placeholder="Укажите вес" value="150" required> <div id="weight-error" class="invalid-feedback"></div> </div> <div> <label for="volume">Объем груза (м³):</label> <input type="number" id="volume" name="volume" min="0.7" step="0.1" placeholder="Укажите объем" required value="2.1"> <div id="volume-error" class="invalid-feedback"></div> </div> </form> </div> <div class="result-column"> <div class="result-wrapper"> <h2 class="has-text-align-center wp-block-heading">Стоимость доставки</h2> <p id="total_cost" class="result-value">0 руб.</p> <div> <button class="calculate-btn" type="button" onclick="calculateDelivery()">Рассчитать</button> </div> </div> </div> </div>
Тут ничего особенного 2 колонки flex блоков. В первой элементы ввода во второй расчет стоимости. Стоит отметить что код самой формы почти на 80% написан с использованием chatGPT.
Далее привожу часть файла в которой располагается js код
<script> const citySelect = document.getElementById("delivery_city"); const weightInput = document.getElementById("weight"); const volumeInput = document.getElementById("volume"); const sizeInput = document.getElementById("size"); const totalCostDiv = document.getElementById("total_cost"); const sizeError = document.getElementById("size-error"); const weightError = document.getElementById("weight-error"); const volumeError = document.getElementById("volume-error"); function calculateDelivery() { document.getElementById("total_cost").innerHTML = ""; const city = citySelect.value; const weight = weightInput.value; const volume = volumeInput.value; const size = parseFloat(sizeInput.value);; let cost = 0; switch (city) { case "Москва": cost = 500; break; case "Санкт-Петербург": cost = 600; break; case "Новосибирск": cost = 1000; break; case "Екатеринбург": cost = 900; break; case "Нижний Новгород": cost = 700; break; case "Казань": cost = 800; break; case "Челябинск": cost = 1000; break; case "Омск": cost = 1200; break; case "Самара": cost = 900; break; case "Ростов-на-Дону": cost = 1100; break; case "Уфа": cost = 1100; break; case "Красноярск": cost = 1300; break; case "Пермь": cost = 1000; break; case "Воронеж": cost = 800; break; case "Волгоград": cost = 1200; break; default: break; } if (isNaN(weight) || weight < 50) { weightError.textContent = 'Введите корректное значение веса'; weightInput.classList.add('is-invalid'); } else { weightError.textContent = ''; weightInput.classList.remove('is-invalid'); // Округляем вес до ближайшего числа, оканчивающегося на 50 let roundedWeight = Math.ceil(weight/50) * 50; // Вычисляем коэффициент увеличения стоимости let coefficient = roundedWeight/50 + 1; // Умножаем стоимость доставки на коэффициент cost *= coefficient; } if (isNaN(size) || size < 1) { sizeError.textContent = 'Введите корректное значение размера'; sizeInput.classList.add('is-invalid'); } else { sizeError.textContent = ''; sizeInput.classList.remove('is-invalid'); cost = Math.ceil(cost *size); } if (isNaN(volume) || volume < 0.7) { volumeError.textContent = 'Введите корректное значение объема'; volumeInput.classList.add('is-invalid'); } else { volumeError.textContent = ''; volumeInput.classList.remove('is-invalid'); cost += (volume/0.7)*1036; } totalCostDiv.innerHTML = `${cost} руб.`; } sizeInput.addEventListener('input', function() { totalCostDiv.innerHTML = ''; }); sizeInput.addEventListener('change', () => { const sizeValue = parseFloat(sizeInput.value); if (!isNaN(sizeValue)) { //Изменяем вес с шагом 50 const weightValue = sizeValue * 50; weightInput.value = weightValue; //Изменяем объем с шагом 0.7 const volumeValue = (sizeValue * 0.7).toFixed(1); volumeInput.value = volumeValue; } }); </script>
В целом по коду думаю итак все понятно. По id элементов формы мы получаем значения полей и в функции calculateDelivery() делаем свой рассчет. Также есть простая валидация на заполнение элементов ввода. Размера, объема и веса. Отмечу что я свою форму заполняю начальными данным, например размер 3 метра, вест 150 кг. объем 2.1 куба.
И при изменении размера также изменяю параметры груза с определнным шагом, для веса 50 кг, для объема 0.7 кубов. Цифры для себя я взял эмпирические у вас они могут быть другими.
Ну и конечно же добавим стили css для адекватного отображения формы.
<style> .is-invalid { border-color: #dc3545; /* устанавливаем цвет рамки элемента */ padding-right: calc(1.5em + .75rem); /* увеличиваем правый отступ, чтобы было место для иконки ошибки */ background-image: url('path/to/error-icon.png'); /* устанавливаем изображение иконки ошибки */ background-repeat: no-repeat; /* запрещаем повторение изображения */ background-position: right calc(.375em + .1875rem) center; /* устанавливаем позицию иконки ошибки */ background-size: calc(.75em + .375rem) calc(.75em + .375rem); /* устанавливаем размер иконки ошибки */ } .is-invalid:focus { border-color: #dc3545; /* устанавливаем цвет рамки при фокусе */ box-shadow: 0 0 0 .2rem rgba(220,53,69,.25); /* добавляем тень при фокусе */ } .is-invalid ~ .invalid-feedback { display: block; /* показываем блок с сообщением об ошибке */ } .invalid-feedback { color: red; font-size: 14px; margin-top: 5px; } .calculator { display: flex; justify-content: space-between; align-items: center; } .result-column { flex-basis: 33.33%; /* одна треть ширины */ flex-grow: 1; } .result-wrapper { background-color: #f5f5f5; padding: 50px 10px; border-radius: 5px; text-align: center; } .result-text { margin-bottom: 10px; font-size: 16px; font-weight: bold; } .result-value { font-size: 30px; font-weight: bold; } .delivery-form { display: flex; flex-direction: column; flex-basis: 66.67%; /* две трети ширины */ flex-grow: 1; padding-right: 20px; gap: 10px; font-family: Arial, sans-serif; } .delivery-form label { display: block; margin-bottom: 5px; font-weight: bold; } .delivery-form select, .delivery-form input[type="number"], .delivery-form input[type="checkbox"] { width: 100%; padding: 10px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box; margin-bottom: 10px; } .delivery-form input[type="checkbox"] { display: inline-block; margin-right: 10px; } .calculate-btn { padding: 10px 20px; border-radius: 5px; border: none; background-color: rgb(26, 137, 23); /* зеленый цвет */ color: #fff; cursor: pointer; transition: all 0.3s ease; align-self: flex-start; } .calculate-btn:hover { background-color: #136f13; } .delivery-form #total_cost { margin-top: 10px; font-weight: bold; } </style>
Итог
Вот так просто, припомощи chatGpt за 1 вечер можно создать красивую функциональную и адаптивную форму рассчета примерной стоимости доставки. Несомненно такая форма должна увеличить конверсию на сайте и благотворно повлиять на поведенческие факторы.