feat: Добавить мобильную адаптацию для POS-терминала
- Добавить фиксированную панель корзины внизу экрана на мобильных - Отображение количества товаров и суммы - Кнопки "Продать" и "Очистить" всегда доступны - Тап на панель открывает корзину как overlay - Фиксировать поиск и категории сверху на мобильных - Поиск всегда виден при скролле - Категории в collapsible-блоке (сворачиваются) - Категории в 3 колонки на мобильных - Улучшить поиск по токенам (разбивает фразу на слова) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -787,10 +787,12 @@ function renderProducts() {
|
||||
// Для витрины — клиентская фильтрация по поиску
|
||||
const searchTerm = document.getElementById('searchInput').value.toLowerCase().trim();
|
||||
if (searchTerm) {
|
||||
const tokens = searchTerm.split(/\s+/).filter(t => t.length > 0);
|
||||
filtered = filtered.filter(item => {
|
||||
const name = (item.name || '').toLowerCase();
|
||||
const sku = (item.sku || '').toLowerCase();
|
||||
return name.includes(searchTerm) || sku.includes(searchTerm);
|
||||
// Каждый токен должен совпадать хотя бы с одним словом в name или sku
|
||||
return tokens.every(token => name.includes(token) || sku.includes(token));
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -1261,6 +1263,7 @@ function renderCart() {
|
||||
list.innerHTML = '<p class="text-muted text-center py-4 small">Корзина пуста</p>';
|
||||
document.getElementById('cartTotal').textContent = '0.00';
|
||||
updateShowcaseButtonState(); // Обновляем состояние кнопки
|
||||
updateMobileCartBar(); // Обновляем мобильный бар даже когда корзина пуста
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1420,9 +1423,59 @@ function renderCart() {
|
||||
});
|
||||
|
||||
document.getElementById('cartTotal').textContent = formatMoney(total);
|
||||
|
||||
|
||||
// Обновляем состояние кнопки "НА ВИТРИНУ"
|
||||
updateShowcaseButtonState();
|
||||
|
||||
// Обновляем мобильный бар корзины
|
||||
updateMobileCartBar();
|
||||
}
|
||||
|
||||
/**
|
||||
* Склонение слов в зависимости от числа
|
||||
* @param {number} number - число
|
||||
* @param {string} one - форма для 1 (товар)
|
||||
* @param {string} two - форма для 2-4 (товара)
|
||||
* @param {string} five - форма для 5+ (товаров)
|
||||
*/
|
||||
function getNoun(number, one, two, five) {
|
||||
const n = Math.abs(number);
|
||||
const n10 = n % 10;
|
||||
const n100 = n % 100;
|
||||
|
||||
if (n100 >= 11 && n100 <= 19) {
|
||||
return five;
|
||||
}
|
||||
if (n10 === 1) {
|
||||
return one;
|
||||
}
|
||||
if (n10 >= 2 && n10 <= 4) {
|
||||
return two;
|
||||
}
|
||||
return five;
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновляет мобильный бар корзины
|
||||
*/
|
||||
function updateMobileCartBar() {
|
||||
const countEl = document.querySelector('.mobile-cart-count');
|
||||
const totalEl = document.querySelector('.mobile-cart-total');
|
||||
|
||||
if (!countEl || !totalEl) return;
|
||||
|
||||
let count = 0;
|
||||
let total = 0;
|
||||
|
||||
cart.forEach((item) => {
|
||||
count += item.qty;
|
||||
total += item.qty * item.price;
|
||||
});
|
||||
|
||||
// Округляем количество до целого для отображения
|
||||
const displayCount = Math.round(count);
|
||||
countEl.textContent = `${displayCount} ${getNoun(displayCount, 'товар', 'товара', 'товаров')}`;
|
||||
totalEl.textContent = formatMoney(total);
|
||||
}
|
||||
|
||||
async function removeFromCart(cartKey) {
|
||||
@@ -3260,6 +3313,85 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
document.getElementById('confirmAddUnitToCart').addEventListener('click', () => {
|
||||
addToCartFromModal();
|
||||
});
|
||||
|
||||
// ===== МОБИЛЬНАЯ КОРЗИНА =====
|
||||
|
||||
// Тап на бар — открываем корзину
|
||||
const mobileCartSummary = document.getElementById('mobileCartSummary');
|
||||
if (mobileCartSummary) {
|
||||
mobileCartSummary.addEventListener('click', () => {
|
||||
const overlay = document.getElementById('mobileCartOverlay');
|
||||
const body = document.getElementById('mobileCartBody');
|
||||
|
||||
// Копируем содержимое корзины
|
||||
if (body && overlay) {
|
||||
const cartList = document.getElementById('cartList');
|
||||
body.innerHTML = cartList ? cartList.innerHTML : '<p class="text-muted text-center py-4">Корзина пуста</p>';
|
||||
overlay.classList.add('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Кнопка закрытия мобильной корзины
|
||||
const mobileCartClose = document.getElementById('mobileCartClose');
|
||||
if (mobileCartClose) {
|
||||
mobileCartClose.addEventListener('click', () => {
|
||||
const overlay = document.getElementById('mobileCartOverlay');
|
||||
if (overlay) {
|
||||
overlay.classList.remove('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Закрытие по клику на фон
|
||||
const mobileCartOverlay = document.getElementById('mobileCartOverlay');
|
||||
if (mobileCartOverlay) {
|
||||
mobileCartOverlay.addEventListener('click', (e) => {
|
||||
if (e.target.id === 'mobileCartOverlay') {
|
||||
e.target.classList.remove('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Мобильная кнопка "Продать"
|
||||
const mobileCheckoutBtn = document.getElementById('mobileCheckoutBtn');
|
||||
if (mobileCheckoutBtn) {
|
||||
mobileCheckoutBtn.addEventListener('click', () => {
|
||||
const checkoutBtn = document.getElementById('checkoutNow');
|
||||
if (checkoutBtn) {
|
||||
checkoutBtn.click();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Мобильная кнопка "Очистить"
|
||||
const mobileClearCartBtn = document.getElementById('mobileClearCartBtn');
|
||||
if (mobileClearCartBtn) {
|
||||
mobileClearCartBtn.addEventListener('click', () => {
|
||||
const clearBtn = document.getElementById('clearCart');
|
||||
if (clearBtn) {
|
||||
clearBtn.click();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ===== СВОРАЧИВАНИЕ КАТЕГОРИЙ НА МОБИЛЬНЫХ =====
|
||||
|
||||
const categoriesToggle = document.getElementById('categoriesToggle');
|
||||
const categoriesContent = document.getElementById('categoriesContent');
|
||||
|
||||
if (categoriesToggle && categoriesContent) {
|
||||
categoriesToggle.addEventListener('click', () => {
|
||||
categoriesToggle.classList.toggle('collapsed');
|
||||
categoriesContent.classList.toggle('collapsed');
|
||||
});
|
||||
|
||||
// Автоматически сворачиваем категории на мобильных при загрузке
|
||||
if (window.innerWidth <= 767) {
|
||||
categoriesToggle.classList.add('collapsed');
|
||||
categoriesContent.classList.add('collapsed');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Смена склада
|
||||
|
||||
Reference in New Issue
Block a user