/
Coderdev
/
web-nodejs-labs
Обзор
Документация
Войти
/
Coderdev
/
web-nodejs-labs
Код
Запросы
0
Задачи
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
public/lab9/task3/index.html
207 строк
8 KB
Coderdev
1
12 май 2026, 12:34
12 май 2026, 12:34
f5e899f
Код
Авторство
О чём код?
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <title>Задание 3 — Магазин</title> <style> body { font-family: sans-serif; max-width: 800px; margin: 40px auto; padding: 0 20px; color: #222; } h1 { border-bottom: 2px solid #222; padding-bottom: 10px; } a { color: #2563eb; } .topbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; } .cart-link { background: #2563eb; color: #fff; padding: 8px 16px; border-radius: 20px; text-decoration: none; font-size: 0.95rem; } .cart-link:hover { background: #1d4ed8; } button { padding: 7px 14px; border: none; border-radius: 6px; cursor: pointer; font-size: 0.9rem; } .btn-blue { background: #2563eb; color: #fff; } .btn-blue:hover { background: #1d4ed8; } .btn-green { background: #16a34a; color: #fff; } .btn-green:hover { background: #15803d; } .btn-red { background: #dc2626; color: #fff; } .btn-red:hover { background: #b91c1c; } .btn-ghost { background: #fff; color: #555; border: 1px solid #ccc; } .btn-ghost:hover { background: #f4f4f4; } button:disabled { background: #ccc; cursor: not-allowed; } .product { display: flex; justify-content: space-between; align-items: center; padding: 12px 16px; border: 1px solid #e5e5e5; border-radius: 8px; margin: 6px 0; } .product:hover { background: #fafafa; } .price { color: #16a34a; font-weight: 600; } .muted { color: #888; font-size: 0.85rem; } .in-cart { color: #2563eb; font-size: 0.85rem; } hr { border: none; border-top: 1px solid #e5e5e5; margin: 30px 0; } .cart-item { display: flex; align-items: center; gap: 12px; padding: 10px 0; border-bottom: 1px solid #f0f0f0; } .cart-item-info { flex: 1; } .qty-ctrl { display: flex; align-items: center; gap: 6px; } .qty-btn { width: 28px; height: 28px; padding: 0; font-weight: bold; background: #f4f4f4; color: #222; border: 1px solid #ccc; border-radius: 6px; } .qty-btn:hover { background: #e5e5e5; } .qty-num { min-width: 24px; text-align: center; font-weight: 600; } .total { font-size: 1.1rem; font-weight: 700; margin: 16px 0; } .cart-actions { display: flex; gap: 10px; margin-top: 12px; } .status { padding: 10px 14px; border-radius: 8px; margin-top: 12px; font-size: 0.9rem; } .status.ok { background: #dcfce7; color: #15803d; } .status.err { background: #fee2e2; color: #b91c1c; } .status.warn { background: #fef9c3; color: #854d0e; } </style> </head> <body> <div class="topbar"> <div><a href="/lab9/">← Назад</a></div> <a href="#cart" class="cart-link">🛒 Моя корзина (<span id="cartCount">0</span>)</a> </div> <h1>Задание 3 — Магазин</h1> <h2>Каталог товаров</h2> <div id="productList">Загрузка...</div> <hr> <h2 id="cart">Корзина</h2> <div id="cartList">Корзина пуста</div> <div id="cartTotal"></div> <div class="cart-actions"> <button class="btn-ghost" onclick="checkAvailability()">Оформить товар</button> <button class="btn-green" onclick="buyCart()">Купить</button> </div> <div id="statusMsg"></div> <script> const API = '/api/lab9/products'; const fmt = cents => (cents / 100).toLocaleString('ru-RU', { style: 'currency', currency: 'RUB' }); let products = []; let cart = JSON.parse(localStorage.getItem('lab9_cart') || '{}'); function saveCart() { localStorage.setItem('lab9_cart', JSON.stringify(cart)); updateCartCount(); } function updateCartCount() { const count = Object.values(cart).reduce((a, b) => a + b, 0); document.getElementById('cartCount').textContent = count; } async function loadProducts() { const res = await fetch(API); products = await res.json(); renderProducts(); renderCart(); updateCartCount(); } function renderProducts() { document.getElementById('productList').innerHTML = products.map(p => ` <div class="product"> <div> <div>${p.title}</div> <div class="muted">Склад: ${p.amount} шт.</div> ${cart[p.id] ? `<div class="in-cart">В корзине: ${cart[p.id]} шт.</div>` : ''} </div> <div style="display:flex;align-items:center;gap:12px"> <span class="price">${fmt(p.price)}</span> <button class="btn-blue" onclick="addToCart(${p.id})" ${p.amount === 0 ? 'disabled' : ''}> ${p.amount === 0 ? 'Нет' : 'В корзину'} </button> </div> </div>`).join(''); } function addToCart(id) { const cur = cart[id] || 0; if (cur >= 5) { alert('Максимум 5 штук одного товара'); return; } cart[id] = cur + 1; saveCart(); renderProducts(); renderCart(); } function changeQty(id, delta) { const next = (cart[id] || 0) + delta; if (next < 1) { delete cart[id]; } else if (next > 5) { alert('Максимум 5 штук'); return; } else { cart[id] = next; } saveCart(); renderProducts(); renderCart(); } function removeFromCart(id) { delete cart[id]; saveCart(); renderProducts(); renderCart(); } function renderCart() { const ids = Object.keys(cart).filter(id => cart[id] > 0); if (!ids.length) { document.getElementById('cartList').innerHTML = '<p class="muted">Корзина пуста</p>'; document.getElementById('cartTotal').textContent = ''; return; } let total = 0; document.getElementById('cartList').innerHTML = ids.map(id => { const p = products.find(p => p.id == id); if (!p) return ''; const sub = p.price * cart[id]; total += sub; return ` <div class="cart-item"> <div class="cart-item-info"> <div>${p.title}</div> <div class="muted">${fmt(p.price)} × ${cart[id]} = ${fmt(sub)}</div> </div> <div class="qty-ctrl"> <button class="qty-btn" onclick="changeQty(${id}, -1)">−</button> <span class="qty-num">${cart[id]}</span> <button class="qty-btn" onclick="changeQty(${id}, +1)">+</button> </div> <button class="btn-red" onclick="removeFromCart(${id})">Удалить</button> </div>`; }).join(''); document.getElementById('cartTotal').innerHTML = `<div class="total">Итого: ${fmt(total)}</div>`; } async function checkAvailability() { const items = Object.entries(cart).map(([id, qty]) => ({ id: parseInt(id), qty })); if (!items.length) return; const res = await fetch(`${API}/cart/check`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ items }) }); const d = await res.json(); const el = document.getElementById('statusMsg'); if (d.ok) { el.className = 'status ok'; el.textContent = '✓ Все товары в наличии (код 200)'; } else { el.className = 'status warn'; el.textContent = '⚠ Нехватка: ' + d.failed.map(f => `${f.title} — есть ${f.available} шт.`).join(', ') + ' (код 400)'; } } async function buyCart() { const items = Object.entries(cart).map(([id, qty]) => ({ id: parseInt(id), qty })); if (!items.length) return; const res = await fetch(`${API}/cart/buy`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ items }) }); const d = await res.json(); const el = document.getElementById('statusMsg'); if (d.ok) { cart = {}; saveCart(); el.className = 'status ok'; el.textContent = '✓ Покупка оформлена!'; loadProducts(); } else { el.className = 'status err'; el.textContent = '✕ Ошибка: ' + d.failed.map(f => `${f.title} — есть только ${f.available} шт.`).join(', '); } } loadProducts(); </script> </body> </html>