/
erioxis
/
web-nodejs
Обзор
Документация
Войти
/
erioxis
/
web-nodejs
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
public/lab9/task3/index.html
334 строки
12 KB
erioxis
lab11fix
15 дек 2025, 23:21
15 дек 2025, 23:21
70dc7ba
Код
Авторство
О чём код?
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8" /> <title>ЛР9 — Задание 3</title> <style> * { margin:0; padding:0; box-sizing:border-box; } html, body { height:100%; font-family:'Courier New',monospace; background:#fff; color:#000; overflow-x:hidden; } body { display:flex; flex-direction:column; padding:20px; } .wrapper { flex:1 0 auto; } .header { text-align:center; margin-bottom:20px; padding:10px 0; border-bottom:1px solid #000; } .header h1 { font-family:'Georgia',serif; font-size:1.8rem; font-weight:normal; letter-spacing:0.1em; text-transform:uppercase; } .cart-link { position:fixed; top:20px; right:20px; padding:6px 12px; border:1px solid #000; background:#fff; color:#000; text-decoration:none; font-family:'Courier New',monospace; transition:background-color 0.2s; } .cart-link:hover { background:#000; color:#fff; } .container { max-width:1000px; margin:0 auto; padding:20px; background:#fff; border:2px solid #000; } .product-card { border:1px solid #000; padding:12px; margin-bottom:10px; display:flex; justify-content:space-between; align-items:center; } .product-info { font-size:1rem; } .price { font-weight:bold; } .cart-item { border:1px solid #000; padding:10px; margin-bottom:8px; display:flex; justify-content:space-between; align-items:center; } .item-quantity { display:flex; align-items:center; margin:0 10px; } .quantity-btn { width:24px; height:24px; padding:0; border:1px solid #000; background:#fff; cursor:pointer; } .quantity-btn:hover { background:#000; color:#fff; } .quantity-display { width:30px; text-align:center; margin:0 4px; } input[type="number"] { width:60px; padding:4px; border:1px solid #000; text-align:center; } .total { margin:15px 0; font-weight:bold; } button { padding:6px 12px; border:1px solid #000; background:#fff; color:#000; cursor:pointer; font-family:'Courier New',monospace; transition:background-color 0.2s; } button:hover { background:#000; color:#fff; } .action-buttons { margin:20px 0; } .result { margin-top:15px; padding:10px; border:1px solid #000; text-align:center; display:none; } .result.success { color:#4CAF50; } .result.error { color:#f44336; } .result.warning { color:#ff9800; } </style> </head> <body> <a href="#" class="cart-link" id="cartLink">Моя корзина (0)</a> <div class="wrapper"> <div class="header"> <h1>Задание 3 — Корзина</h1> </div> <div class="container" id="mainContainer"> <div id="products">Загрузка товаров...</div> </div> </div> <script> const API = '/api/lab9/products'; const API_CHECK_AVAILABILITY = '/api/lab9/cart/check-availability'; const API_PURCHASE = '/api/lab9/cart/purchase'; const formatPrice = (cents) => { if (isNaN(cents)) return 'N/A'; return (cents / 100).toLocaleString('ru-RU', { style: 'currency', currency: 'RUB', maximumFractionDigits: 2 }); }; function loadCart() { const cartData = localStorage.getItem('cart'); return cartData ? JSON.parse(cartData) : []; } function saveCart(cart) { localStorage.setItem('cart', JSON.stringify(cart)); } function updateCartCounter() { const cart = loadCart(); const totalItems = cart.reduce((sum, item) => sum + item.quantity, 0); const cartLink = document.getElementById('cartLink'); if (cartLink) { cartLink.textContent = `Моя корзина (${totalItems})`; } } function addToCart(productId) { let cart = loadCart(); const existingItem = cart.find(item => item.id === productId); if (existingItem) { existingItem.quantity = Math.min(existingItem.quantity + 1, 5); } else { cart.push({ id: productId, quantity: 1 }); } saveCart(cart); updateCartCounter(); alert('Товар добавлен в корзину!'); } async function loadProducts() { try { const res = await fetch(API); if (!res.ok) { const errorData = await res.json(); throw new Error(errorData.error || `HTTP error! status: ${res.status}`); } const products = await res.json(); const container = document.getElementById('products'); container.innerHTML = ''; if (products.length === 0) { container.innerHTML = '<div>Товары не найдены.</div>'; return; } products.forEach(product => { const card = document.createElement('div'); card.className = 'product-card'; card.innerHTML = ` <div class="product-info"> <strong>${product.title}</strong><br> <span class="price">${formatPrice(product.price)}</span> | Остаток: ${product.amount} шт. </div> <button onclick="addToCart(${product.id})">В корзину</button> `; container.appendChild(card); }); } catch (error) { console.error('Ошибка загрузки товаров:', error); document.getElementById('products').innerHTML = `<div class="error">Ошибка загрузки товаров: ${error.message}</div>`; } } async function getCartItemsDetails() { const cart = loadCart(); if (cart.length === 0) return []; try { const response = await fetch(API); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); const products = await response.json(); return cart.map(cartItem => { const product = products.find(p => p.id === cartItem.id); return { ...cartItem, product: product || null }; }); } catch (error) { console.error('Ошибка получения деталей товаров:', error); return []; } } async function renderCartPage() { const cartItemsContainer = document.getElementById('mainContainer'); const cartItems = await getCartItemsDetails(); if (cartItems.length === 0) { cartItemsContainer.innerHTML = ` <div style="text-align:center; padding:40px;"> <p>Корзина пуста</p> <button onclick="window.location.search=''">← Назад к товарам</button> </div> `; return; } let totalCents = 0; let html = '<h2>Моя корзина</h2>'; for (const item of cartItems) { if (!item.product) continue; const itemTotal = item.product.price * item.quantity; totalCents += itemTotal; html += ` <div class="cart-item"> <div class="product-info"> <strong>${item.product.title}</strong><br> <span class="price">${formatPrice(item.product.price)}</span> | Сумма: ${formatPrice(itemTotal)} </div> <div class="item-quantity"> <button type="button" class="quantity-btn" onclick="changeQuantity(${item.id}, -1)">-</button> <span class="quantity-display">${item.quantity}</span> <button type="button" class="quantity-btn" onclick="changeQuantity(${item.id}, 1)">+</button> </div> <button onclick="removeFromCart(${item.id})">Удалить</button> </div> `; } html += `<div class="total">Итого: ${formatPrice(totalCents)}</div>`; html += ` <div class="action-buttons"> <button onclick="checkAvailability()">Проверить доступность</button> <button onclick="purchaseItems()">Купить</button> <button onclick="clearCart()">Очистить корзину</button> <button onclick="window.location.search=''" style="margin-left:10px;">← Назад</button> </div> <div id="resultMessage" class="result"></div> `; cartItemsContainer.innerHTML = html; } function changeQuantity(productId, delta) { let cart = loadCart(); const item = cart.find(item => item.id === productId); if (item) { item.quantity = Math.max(1, Math.min(item.quantity + delta, 5)); saveCart(cart); renderCartPage(); } } function removeFromCart(productId) { let cart = loadCart(); cart = cart.filter(item => item.id !== productId); saveCart(cart); updateCartCounter(); renderCartPage(); } function clearCart() { localStorage.removeItem('cart'); updateCartCounter(); renderCartPage(); } async function checkAvailability() { const cartItems = loadCart(); if (cartItems.length === 0) { showResultMessage('Корзина пуста', 'error'); return; } try { const response = await fetch(API_CHECK_AVAILABILITY, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ items: cartItems }) }); const result = await response.json(); if (response.ok) { showResultMessage('Все товары доступны для покупки!', 'success'); } else { let errorMessage = 'Некоторые товары недоступны:<ul>'; result.unavailableItems.forEach(item => { if (item.reason) { errorMessage += `<li>Товар ID ${item.id}: ${item.reason}</li>`; } else { errorMessage += `<li>Товар ID ${item.id}: запрошено ${item.requested}, доступно ${item.available}</li>`; } }); errorMessage += '</ul>'; showResultMessage(errorMessage, 'error'); } } catch (error) { console.error('Ошибка проверки наличия:', error); showResultMessage(`Ошибка при проверке наличия: ${error.message}`, 'error'); } } async function purchaseItems() { const cartItems = loadCart(); if (cartItems.length === 0) { showResultMessage('Корзина пуста', 'error'); return; } try { const response = await fetch(API_PURCHASE, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ items: cartItems }) }); const result = await response.json(); if (response.ok) { showResultMessage('Покупка успешно завершена! Все товары списаны со склада.', 'success'); localStorage.removeItem('cart'); updateCartCounter(); renderCartPage(); } else { let resultMessage = 'Частичная покупка завершена:<ul>'; result.results.forEach(item => { if (item.success) { resultMessage += `<li>Товар ID ${item.id}: успешно куплено ${item.purchased} шт., осталось ${item.remaining} шт.</li>`; } else { resultMessage += `<li>Товар ID ${item.id}: ${item.error}</li>`; } }); resultMessage += '</ul>'; showResultMessage(resultMessage, 'warning'); } } catch (error) { console.error('Ошибка покупки:', error); showResultMessage(`Ошибка при покупке: ${error.message}`, 'error'); } } function showResultMessage(message, type) { const el = document.getElementById('resultMessage'); el.innerHTML = message; el.className = `result ${type}`; el.style.display = 'block'; setTimeout(() => { el.style.display = 'none'; }, 5000); } if (window.location.search === '?view=cart') { renderCartPage(); } else { document.getElementById('cartLink').onclick = (e) => { e.preventDefault(); window.location.search = '?view=cart'; }; loadProducts(); } updateCartCounter(); </script> </body> </html>