Калькулятор стоимости партии оборудования с доставкой
Считает партию по прайсу: количество, скидка за объём по ступеням, способ доставки и пусконаладка. Показывает итог без НДС и с НДС, цену за единицу и срок.
<section class="vbkpsd">
<div class="vbkpsd-wrap">
<div class="vbkpsd-head">
<h2 class="vbkpsd-h2">Стоимость партии со скидкой за объём</h2>
<p class="vbkpsd-lead">Выберите позицию, укажите количество и способ доставки. Покажем сумму без НДС и с НДС, цену за единицу и срок отгрузки.</p>
</div>
<div class="vbkpsd-grid">
<div class="vbkpsd-panel">
<div class="vbkpsd-field">
<label class="vbkpsd-lbl" for="vbkpsd-item">Позиция из прайса</label>
<select id="vbkpsd-item" class="vbkpsd-sel"></select>
</div>
<div class="vbkpsd-field">
<div class="vbkpsd-lbl vbkpsd-lbl--row"><span>Количество в партии</span><b id="vbkpsd-qty">10</b></div>
<input type="range" id="vbkpsd-range" min="1" max="60" step="1" value="10" aria-label="Количество единиц в партии">
<div class="vbkpsd-ticks"><i>1</i><i>30</i><i>60</i></div>
</div>
<div class="vbkpsd-field">
<span class="vbkpsd-lbl">Доставка</span>
<div class="vbkpsd-seg" id="vbkpsd-del" role="group" aria-label="Способ доставки"></div>
</div>
<div class="vbkpsd-field">
<label class="vbkpsd-check" for="vbkpsd-mount">
<input type="checkbox" id="vbkpsd-mount">
<span class="vbkpsd-check__box" aria-hidden="true"></span>
<span class="vbkpsd-check__t"><b>Монтаж и пусконаладка</b>Выезд инженера, установка по месту, запуск и инструктаж персонала</span>
</label>
</div>
</div>
<div class="vbkpsd-out">
<ul class="vbkpsd-rows" id="vbkpsd-rows"></ul>
<div class="vbkpsd-sum">
<div><span>Итого без НДС</span><b id="vbkpsd-net"></b></div>
<div class="vbkpsd-sum--main"><span>Итого с НДС 20%</span><b id="vbkpsd-gross"></b></div>
</div>
<div class="vbkpsd-meta">
<div><span>Цена за единицу с НДС</span><b id="vbkpsd-unit"></b></div>
<div><span>Срок отгрузки</span><b id="vbkpsd-term"></b></div>
</div>
<p class="vbkpsd-note">Расчёт предварительный. Итоговая цена фиксируется в счёте, он действует 5 рабочих дней. Отсрочка платежа обсуждается отдельно.</p>
<a class="vbkpsd-cta" href="#zapros">Запросить счёт на эту партию</a>
</div>
</div>
</div>
</section>
<style>
.vbkpsd, .vbkpsd *, .vbkpsd *::before, .vbkpsd *::after { box-sizing: border-box; }
.vbkpsd {
--acc: #e0721f; --acc-dark: #bd5c13; --ink: #161c22; --muted: #5d6b78; --line: #dde3e9; --soft: #f3f6f9; --dark: #1a2128;
--f: var(--t-text-font, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif);
--fh: var(--t-headline-font, var(--f));
background: var(--soft); color: var(--ink); font-family: var(--f); padding: clamp(36px, 5vw, 80px) 20px;
}
.vbkpsd-wrap { max-width: 1160px; margin: 0 auto; padding: 0 clamp(0px, 2vw, 40px); }
.vbkpsd-head { max-width: 44em; margin-bottom: clamp(22px, 3vw, 34px); }
.vbkpsd-h2 { font-family: var(--fh); font-size: clamp(25px, 3.2vw, 38px); line-height: 1.12; font-weight: 700; letter-spacing: -.02em; margin: 0 0 12px; }
.vbkpsd-lead { font-size: 16px; line-height: 1.6; color: var(--muted); margin: 0; }
.vbkpsd-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 16px; align-items: start; }
.vbkpsd-panel, .vbkpsd-out { background: #fff; border: 1px solid var(--line); border-radius: 18px; padding: clamp(20px, 2.4vw, 28px); }
.vbkpsd-field { margin-bottom: 22px; }
.vbkpsd-field:last-child { margin-bottom: 0; }
.vbkpsd-lbl { display: block; font-size: 13.5px; color: var(--muted); margin-bottom: 9px; }
.vbkpsd-lbl--row { display: flex; justify-content: space-between; align-items: baseline; }
.vbkpsd-lbl--row b { font-family: var(--fh); font-size: 19px; color: var(--ink); }
.vbkpsd-sel { width: 100%; height: 52px; padding: 0 36px 0 14px; font: inherit; font-size: 16px; appearance: none; -webkit-appearance: none;
border: 1.5px solid var(--line); border-radius: 10px; background: #fff; color: var(--ink);
background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%);
background-position: calc(100% - 19px) 23px, calc(100% - 13px) 23px; background-size: 6px 6px, 6px 6px; background-repeat: no-repeat; }
.vbkpsd-sel:focus { outline: 2px solid var(--acc); outline-offset: -2px; }
.vbkpsd input[type=range] { -webkit-appearance: none; appearance: none; width: 100%; height: 4px; border-radius: 4px; background: var(--line); outline: none; }
.vbkpsd input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 24px; height: 24px; border-radius: 50%;
background: var(--acc); border: 5px solid #fff; box-shadow: 0 1px 5px rgba(0,0,0,.2); cursor: pointer; }
.vbkpsd input[type=range]::-moz-range-thumb { width: 18px; height: 18px; border-radius: 50%; background: var(--acc); border: 3px solid #fff; cursor: pointer; }
.vbkpsd-ticks { display: flex; justify-content: space-between; margin-top: 6px; font-size: 11px; color: #93a1ad; }
.vbkpsd-ticks i { font-style: normal; }
.vbkpsd-seg { display: grid; gap: 8px; }
.vbkpsd-seg button { display: flex; align-items: baseline; gap: 10px; width: 100%; padding: 13px 15px; border: 1.5px solid var(--line); margin: 0;
appearance: none; font: inherit; font-size: 15px; cursor: pointer; text-align: left; border-radius: 10px;
background: #fff !important; color: var(--ink) !important; transition: .18s ease; }
.vbkpsd-seg button span { margin-left: auto; font-size: 13px; color: var(--muted); white-space: nowrap; }
.vbkpsd-seg button:hover { border-color: var(--acc); }
.vbkpsd-seg .vbkpsd-on { border-color: var(--acc); background: #fdf3ea !important; }
.vbkpsd-check { display: flex; align-items: flex-start; gap: 11px; cursor: pointer; border: 1.5px solid var(--line); border-radius: 10px; padding: 14px 15px; }
.vbkpsd-check input { position: absolute; opacity: 0; width: 0; height: 0; }
.vbkpsd-check__box { flex: 0 0 20px; width: 20px; height: 20px; margin-top: 2px; border: 1.5px solid #b9c4ce; border-radius: 5px; background: #fff; position: relative; }
.vbkpsd-check input:checked + .vbkpsd-check__box { background: var(--acc); border-color: var(--acc); }
.vbkpsd-check input:checked + .vbkpsd-check__box::after { content: ''; position: absolute; left: 6px; top: 2px; width: 5px; height: 10px; border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(45deg); }
.vbkpsd-check__t { font-size: 12.5px; line-height: 1.5; color: var(--muted); }
.vbkpsd-check__t b { display: block; font-size: 15px; color: var(--ink); margin-bottom: 3px; }
.vbkpsd-rows { list-style: none; margin: 0 0 18px; padding: 0; }
.vbkpsd-rows li { display: flex; align-items: baseline; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--line); font-size: 14.5px; }
.vbkpsd-rows li b { margin-left: auto; font-weight: 600; white-space: nowrap; }
.vbkpsd-rows li em { font-style: normal; font-size: 12.5px; color: var(--muted); }
.vbkpsd-rows .vbkpsd-minus b { color: var(--acc); }
.vbkpsd-sum { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-bottom: 10px; }
.vbkpsd-sum div { background: var(--soft); border-radius: 12px; padding: 15px 17px; }
.vbkpsd-sum span { display: block; font-size: 12.5px; color: var(--muted); margin-bottom: 5px; }
.vbkpsd-sum b { font-family: var(--fh); font-size: 21px; font-weight: 700; }
.vbkpsd-sum--main { background: var(--dark) !important; }
.vbkpsd-sum--main span { color: #93a1ad; }
.vbkpsd-sum--main b { color: var(--acc); }
.vbkpsd-meta { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
.vbkpsd-meta div { border: 1px solid var(--line); border-radius: 12px; padding: 13px 16px; }
.vbkpsd-meta span { display: block; font-size: 12.5px; color: var(--muted); margin-bottom: 4px; }
.vbkpsd-meta b { font-family: var(--fh); font-size: 17px; font-weight: 700; }
.vbkpsd-note { margin: 16px 0 0; font-size: 12.5px; line-height: 1.55; color: var(--muted); }
.vbkpsd-cta, .vbkpsd-cta:link, .vbkpsd-cta:visited, .vbkpsd-cta:active, .vbkpsd-cta:focus { display: flex; align-items: center; justify-content: center;
min-height: 56px; margin-top: 16px; padding: 0 26px; border-radius: 10px; font-size: 16px; font-weight: 600;
background: var(--acc); color: #14181c !important; text-decoration: none !important; }
.vbkpsd-cta:hover { background: var(--acc-dark); color: #fff !important; }
@media (prefers-reduced-motion: reduce) { .vbkpsd * { transition: none !important; } }
</style>
<script>
(function () {
/* КОЭФФИЦИЕНТЫ РАСЧЁТА */
var CFG = {
vat: 0.2,
items: [
{ n: 'Компрессор винтовой 7,5 кВт', u: 'шт', price: 268000, days: 3, mount: 24000 },
{ n: 'Насос консольный 45 куб. м/ч', u: 'шт', price: 96000, days: 2, mount: 12000 },
{ n: 'Таль электрическая 3 т', u: 'шт', price: 112000, days: 5, mount: 18000 },
{ n: 'Полуавтомат сварочный 350 А', u: 'шт', price: 74500, days: 1, mount: 6500 },
{ n: 'Стеллаж паллетный, секция', u: 'секция', price: 18400, days: 2, mount: 2600 },
{ n: 'Тележка гидравлическая 2,5 т', u: 'шт', price: 21900, days: 1, mount: 1800 }
],
/* СТУПЕНИ СКИДКИ: от какого количества и какая доля */
steps: [ { from: 5, d: 0.03 }, { from: 10, d: 0.06 }, { from: 25, d: 0.09 }, { from: 40, d: 0.12 } ],
/* ДОСТАВКА: подача плюс ставка за единицу, плюс дни в пути */
delivery: [
{ k: 'self', n: 'Самовывоз со склада', base: 0, per: 0, days: 0, note: 'бесплатно' },
{ k: 'term', n: 'До терминала перевозчика', base: 2500, per: 120, days: 2, note: 'от 2 500 ₽' },
{ k: 'obj', n: 'До объекта заказчика', base: 6800, per: 340, days: 4, note: 'от 6 800 ₽' }
],
mountDays: 2
};
var roots = document.querySelectorAll('.vbkpsd');
for (var i = 0; i < roots.length; i++) init(roots[i]);
function init(root) {
if (root.__vb) return; root.__vb = 1;
var sel = root.querySelector('#vbkpsd-item'), range = root.querySelector('#vbkpsd-range');
var seg = root.querySelector('#vbkpsd-del'), mount = root.querySelector('#vbkpsd-mount');
var rows = root.querySelector('#vbkpsd-rows');
var st = { i: 0, q: 10, d: 'term', m: false };
function money(n) { return String(Math.round(n)).replace(/\B(?=(\d{3})+(?!\d))/g, ' ') + ' ₽'; }
function set(id, v) { root.querySelector(id).textContent = v; }
var h = '';
for (var a = 0; a < CFG.items.length; a++) h += '<option value="' + a + '">' + CFG.items[a].n + '<\/option>';
sel.innerHTML = h;
h = '';
for (var b = 0; b < CFG.delivery.length; b++) {
h += '<button type="button" data-k="' + CFG.delivery[b].k + '">' + CFG.delivery[b].n +
'<span>' + CFG.delivery[b].note + '<\/span><\/button>';
}
seg.innerHTML = h;
var segBtns = seg.querySelectorAll('button');
for (var c = 0; c < segBtns.length; c++) {
segBtns[c].addEventListener('click', function () { st.d = this.getAttribute('data-k'); render(); });
}
sel.addEventListener('change', function () { st.i = +this.value; render(); });
range.addEventListener('input', function () { st.q = +this.value; render(); });
mount.addEventListener('change', function () { st.m = this.checked; render(); });
function discount(q) {
var d = 0;
for (var k = 0; k < CFG.steps.length; k++) if (q >= CFG.steps[k].from) d = CFG.steps[k].d;
return d;
}
function render() {
var it = CFG.items[st.i], q = st.q;
var del = CFG.delivery[0];
for (var k = 0; k < CFG.delivery.length; k++) if (CFG.delivery[k].k === st.d) del = CFG.delivery[k];
for (var j = 0; j < segBtns.length; j++) {
segBtns[j].className = segBtns[j].getAttribute('data-k') === st.d ? 'vbkpsd-on' : '';
}
var goods = it.price * q;
var dsc = discount(q), cut = goods * dsc;
var ship = del.base ? del.base + del.per * q : 0;
var work = st.m ? it.mount * q : 0;
var net = goods - cut + ship + work;
var gross = net * (1 + CFG.vat);
var days = it.days + (q > 25 ? 4 : q > 10 ? 2 : 0) + del.days + (st.m ? CFG.mountDays : 0);
var html = '<li>Оборудование, ' + q + ' ' + it.u + '<b>' + money(goods) + '<\/b><\/li>';
html += '<li class="vbkpsd-minus">Скидка за объём <em>' + Math.round(dsc * 100) + '%<\/em><b>' +
(cut ? '-' + money(cut) : money(0)) + '<\/b><\/li>';
html += '<li>' + del.n + '<b>' + money(ship) + '<\/b><\/li>';
html += '<li>Монтаж и пусконаладка<b>' + (st.m ? money(work) : 'не входит') + '<\/b><\/li>';
rows.innerHTML = html;
set('#vbkpsd-qty', q + ' ' + it.u);
set('#vbkpsd-net', money(net));
set('#vbkpsd-gross', money(gross));
set('#vbkpsd-unit', money(gross / q));
set('#vbkpsd-term', days + ' раб. дн.');
}
render();
}
})();
</script> Вставляется целиком в блок T123 «HTML-код». Внутри уже есть стили и скрипт, подключать ничего не нужно.
Как вставить блок в Tilda
- Скопируйте кодКнопка «Скопировать код» выше кладёт всё содержимое в буфер обмена.
- Добавьте блок T123В редакторе страницы: «Библиотека блоков» → «Другое» → T123 «HTML-код».
- Вставьте и сохранитеНажмите «Контент», вставьте код в поле, сохраните и обновите страницу.
- ОпубликуйтеВ превью скрипты Tilda работают не всегда — проверяйте на опубликованной странице.
Подробная инструкция с граблями — как вставить вайб-блок в Tilda.
Что менять под себя
| Где в коде | За что отвечает |
|---|---|
массив items в CFG | позиции прайса, цены, сроки и стоимость монтажа |
массив steps | ступени скидки за объём и её размер |
массив delivery | способы доставки, подача, ставка за единицу и дни в пути |
поле vat | ставка налога в расчёте |
атрибуты min и max у ползунка | границы количества в партии |
текст vbkpsd-note | условия по сроку действия счёта |
Калькулятор партии с доставкой: зачем он нужен
Калькулятор закрывает первый вопрос закупщика: сколько выйдет партия целиком. Слева стоит панель с четырьмя полями: позиция из прайса, ползунок количества, три способа доставки и переключатель монтажа с пусконаладкой. Справа собирается расчёт: строка по оборудованию, скидка за объём, доставка и монтаж, ниже итог без НДС и итог с НДС в тёмной карточке.
Все коэффициенты лежат в объекте CFG в начале скрипта: перечень позиций с ценой, сроком и стоимостью монтажа, ступени скидки по количеству, три варианта доставки с подачей и ставкой за единицу, ставка НДС. Срок отгрузки складывается из срока по позиции, надбавки за крупную партию, дней в пути и двух дней на пусконаладку. Под расчётом стоит оговорка про срок действия счёта и ссылка на запрос счёта.
Вопросы по этому блоку
-
Правьте массив steps: поле from задаёт количество, с которого действует ступень, поле d долю скидки. Ступени перебираются по порядку, применяется последняя подходящая.
-
Поставьте vat в ноль и уберите вторую карточку из блока vbkpsd-sum в разметке. Цена за единицу тогда посчитается по сумме без налога.
-
Да, поставьте вместо него input типа number с тем же идентификатором и шрифтом от 16 пикселей, чтобы браузер на телефоне не увеличивал страницу. Обработчик input менять не нужно.
-
Да. Код на этой странице копируется целиком, без регистрации и подписки. Указывать авторство не нужно, использовать можно в коммерческих проектах.
-
Нет. Все классы внутри имеют собственный префикс vbkpsd, поэтому не пересекаются ни со стилями Tilda, ни с другими блоками. Скрипт привязан к своему элементу и запускается один раз, даже если блок вставлен дважды.
-
Раскладка перестраивается под узкие экраны, размеры заданы через clamp, поля ввода имеют шрифт 16 пикселей — iOS не будет зумить страницу при фокусе. Отдельной мобильной версии подключать не нужно.
Другие калькуляторы
Ещё блоки для поставок оборудования
Собрать сайт целиком, а не по блокам
Дровосеки делают сайты на Tilda под ключ: структура, тексты, дизайн, вёрстка и вайб-блоки под задачи бизнеса.