/
Cooolprogrammers
/
web-nodejs-labs
Обзор
Документация
Войти
/
Cooolprogrammers
/
web-nodejs-labs
Код
Запросы
0
Задачи
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
public/lab10/task1/index.html
702 строки
23 KB
darkvoid6@mail.ru
ЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫ
18 дек 2025, 07:59
18 дек 2025, 07:59
f2c4f06
Код
Авторство
О чём код?
<!DOCTYPE html> <html lang="ru"> <head> <meta charset="UTF-8"> <title>Lab10 Task1 – Интернет-магазин</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> :root { --dark-bg: #0a0e14; --dark-card: #161b22; --dark-border: #30363d; --green-primary: #3fb950; --green-hover: #2ea043; --green-light: #7ee787; --text-main: #b0bec5; --text-muted: #8b949e; --danger-red: #f85149; --warning-yellow: #e3b341; --main-gradient: linear-gradient(135deg, #0a0e14, #161b22, #1e272e); --card-gradient: linear-gradient(145deg, #161b22, #0d1117); --accent-gradient: linear-gradient(135deg, #238636, #3fb950); --success-gradient: linear-gradient(135deg, #238636, #2ea043); --danger-gradient: linear-gradient(135deg, #da3633, #f85149); --glass-bg: rgba(22, 27, 34, 0.85); --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4); --shadow-md: 0 8px 32px rgba(0, 0, 0, 0.5); --shadow-lg: 0 20px 60px rgba(0, 0, 0, 0.6); } body { font-family: 'Inter', 'Segoe UI', system-ui, sans-serif; margin: 0; min-height: 100vh; background: var(--main-gradient); color: var(--text-main); } .container { max-width: 1300px; margin: 40px auto; padding: 25px; background: var(--glass-bg); border-radius: 28px; border: 1px solid var(--dark-border); box-shadow: var(--shadow-md); backdrop-filter: blur(10px); } /* ===== Шапка ===== */ header { display: flex; justify-content: space-between; align-items: center; padding: 20px 0 25px 0; border-bottom: 3px solid transparent; border-image: var(--accent-gradient) 1; margin-bottom: 30px; } header h1 { color: var(--green-light); font-size: 2.2em; margin: 0; text-shadow: 0 2px 8px rgba(126, 231, 135, 0.3); } header a { background: linear-gradient(135deg, #238636, #3fb950); color: #f0f6fc !important; padding: 12px 24px; border-radius: 12px; text-decoration: none; font-weight: 600; font-size: 1em; transition: all 0.3s ease; border: 1px solid #3fb950; box-shadow: 0 8px 20px rgba(46, 160, 67, 0.3); } header a:hover { background: linear-gradient(135deg, #2ea043, #4ed964); transform: translateY(-2px); box-shadow: 0 12px 25px rgba(46, 160, 67, 0.4); } /* ===== Кнопка корзины ===== */ .cart-btn { background: var(--accent-gradient); color: #f0f6fc; padding: 14px 28px; border-radius: 999px; cursor: pointer; font-weight: 600; font-size: 1.1em; border: 1px solid #3fb950; box-shadow: 0 8px 20px rgba(46, 160, 67, 0.3); transition: all 0.25s ease; } .cart-btn:hover { background: linear-gradient(135deg, #2ea043, #4ed964); transform: translateY(-3px); box-shadow: 0 12px 25px rgba(46, 160, 67, 0.4); } .cart-btn span { background: #f0f6fc; color: #238636; padding: 4px 12px; border-radius: 50px; margin-left: 10px; font-weight: bold; border: 1px solid #3fb950; } /* ===== Панель кнопок ===== */ .controls { margin: 30px 0; display: flex; gap: 14px; flex-wrap: wrap; align-items: center; } /* ===== Кнопки ===== */ button { padding: 12px 22px; background: var(--success-gradient); color: #f0f6fc; border: 1px solid #3fb950; border-radius: 14px; cursor: pointer; font-size: 1em; font-weight: 600; box-shadow: 0 8px 20px rgba(46, 160, 67, 0.3); transition: all 0.25s ease; } button:hover { background: linear-gradient(135deg, #2ea043, #4ed964); transform: translateY(-3px); box-shadow: 0 12px 25px rgba(46, 160, 67, 0.4); } button:active { transform: scale(0.98); } button:disabled { background: #21262d; color: var(--text-muted); border-color: var(--dark-border); cursor: not-allowed; box-shadow: none; } select, input, textarea { padding: 12px 16px; border-radius: 14px; border: 1px solid var(--dark-border); background: linear-gradient(145deg, #161b22, #0d1117); color: var(--text-main); font-size: 1em; box-shadow: inset 2px 2px 6px rgba(0,0,0,0.3); } select:focus, input:focus, textarea:focus { border-color: var(--green-primary); outline: none; box-shadow: 0 0 0 3px rgba(63, 185, 80, 0.2); } /* ===== Карточки товаров ===== */ .products { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 24px; margin-top: 30px; } .card { border-radius: 26px; background: var(--card-gradient); box-shadow: var(--shadow-md); text-align: center; padding: 22px 18px 26px; transition: all 0.3s ease; cursor: pointer; position: relative; overflow: hidden; border: 1px solid var(--dark-border); } .card::before { content: ""; position: absolute; inset: 0; padding: 2px; border-radius: 24px; background: var(--accent-gradient); mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); mask-composite: exclude; -webkit-mask-composite: xor; } .card:hover { transform: translateY(-8px); border-color: var(--green-primary); box-shadow: var(--shadow-lg); } .card h3 { margin: 10px 0; font-size: 1.3em; font-weight: 700; color: var(--green-light); } .card p { color: var(--text-muted); margin: 8px 0; } .price { font-size: 1.6em; font-weight: bold; color: var(--green-primary); text-shadow: 0 2px 8px rgba(63, 185, 80, 0.3); } .card button { margin-top: 14px; width: 100%; padding: 14px; border-radius: 16px; background: var(--accent-gradient); color: #f0f6fc; border: 1px solid #3fb950; } .card button:hover { background: linear-gradient(135deg, #2ea043, #4ed964); } /* ===== Пагинация ===== */ .pagination { text-align: center; margin: 60px 0 20px; font-size: 1.2em; } .pagination button { background: var(--accent-gradient); min-width: 180px; padding: 14px 30px; border: 1px solid #3fb950; } .pagination button:hover { background: linear-gradient(135deg, #2ea043, #4ed964); } .pagination span { margin: 0 30px; font-weight: 700; color: var(--green-light); } /* ===== Модальные окна ===== */ .modal { display: none; position: fixed; inset: 0; background: rgba(10, 14, 20, 0.85); justify-content: center; align-items: center; z-index: 1000; backdrop-filter: blur(10px); } .modal-content { background: var(--card-gradient); padding: 35px; border-radius: 30px; width: 95%; max-width: 520px; max-height: 90vh; overflow-y: auto; border: 1px solid var(--dark-border); box-shadow: var(--shadow-lg); position: relative; animation: modalShow 0.35s ease; color: var(--text-main); } @keyframes modalShow { from { transform: scale(0.9) translateY(20px); opacity: 0; } to { transform: scale(1) translateY(0); opacity: 1; } } .modal-content h2 { color: var(--green-light); margin-top: 0; } .close { position: absolute; top: 14px; right: 22px; font-size: 36px; cursor: pointer; color: var(--text-muted); transition: 0.2s; } .close:hover { color: var(--green-light); transform: rotate(90deg); } /* ===== Корзина ===== */ #cartItems table { width: 100%; border-collapse: collapse; } #cartItems th { background: rgba(46, 160, 67, 0.2); color: var(--green-light); padding: 12px; border: 1px solid var(--dark-border); } #cartItems td { padding: 12px 10px; text-align: center; border: 1px solid var(--dark-border); } #cartItems tr { background: rgba(46, 160, 67, 0.05); } #cartItems tr:nth-child(even) { background: rgba(46, 160, 67, 0.1); } hr { margin: 40px 0; border: none; height: 2px; background: var(--accent-gradient); border-radius: 10px; } /* Дополнительные стили */ small { color: var(--text-muted); } strong { color: var(--green-light); } /* Адаптивность */ @media (max-width: 768px) { .container { margin: 20px auto; padding: 15px; } header { flex-direction: column; gap: 15px; text-align: center; } .products { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 15px; } .modal-content { padding: 20px; margin: 15px; } } </style> </head> <body> <div class="container"> <header> <a href="/" style="margin-right: 20px;">Главная</a> <h1>🛒 Интернет-магазин</h1> <div class="cart-btn" onclick="openCart()">Корзина <span id="cartCount">0</span></div> </header> <div class="controls"> <button onclick="openAddCategory()">+ Добавить категорию</button> <button onclick="openAddProduct()">+ Добавить товар</button> <select id="categoryFilter" onchange="loadProducts(1)"> <option value="">Все категории</option> </select> </div> <div id="products" class="products"></div> <div id="pagination" class="pagination" style="display:none;"></div> </div> <!-- Модальные окна --> <div id="modalCategory" class="modal"> <div class="modal-content"> <span class="close" onclick="closeModal('modalCategory')">×</span> <h2>Новая категория</h2> <input type="text" id="newCategoryName" placeholder="Название категории"> <br><br> <button onclick="addCategory()">Создать</button> </div> </div> <div id="modalProduct" class="modal"> <div class="modal-content"> <span class="close" onclick="closeModal('modalProduct')">×</span> <h2>Новый товар</h2> <input type="text" id="newProductTitle" placeholder="Название товара" style="width:100%;padding:12px;margin:10px 0;"><br> <input type="number" id="newProductPrice" placeholder="Цена (₽)" style="width:100%;padding:12px;margin:10px 0;"><br> <input type="number" id="newProductAmount" placeholder="Количество" value="10" style="width:100%;padding:12px;margin:10px 0;"><br> <select id="newProductCategory" style="width:100%;padding:12px;margin:10px 0;"></select><br> <textarea id="newProductDescription" placeholder="Описание (необязательно)" style="width:100%;height:100px;padding:12px;margin:10px 0;resize:vertical;"></textarea><br> <button onclick="addProduct()" style="padding:14px 30px;font-size:1.1em;">Создать товар</button> </div> </div> <!-- Корзина + Заказы --> <div id="cartModal" class="modal"> <div class="modal-content" style="max-width:900px;"> <span class="close" onclick="closeModal('cartModal')">×</span> <h2>🛍️ Корзина</h2> <div id="cartItems"></div> <h3 id="totalSum" style="text-align:center;margin:20px 0;color:var(--green-light);">Итого: 0 ₽</h3> <div style="text-align:center;margin:20px 0;"> <button onclick="checkout()" style="padding:14px 40px;font-size:1.2em;background:var(--accent-gradient);border:1px solid #3fb950;color:#f0f6fc;">Оформить заказ</button> </div> <hr> <h2 style="color:var(--green-light);">📋 Мои заказы</h2> <div style="text-align:center;margin:20px 0;"> <input type="number" id="searchOrderId" placeholder="Номер заказа" style="padding:12px;width:250px;background:var(--card-gradient);color:var(--text-main);border:1px solid var(--dark-border);"> <button onclick="loadOrderById()" style="background:var(--accent-gradient);border:1px solid #3fb950;color:#f0f6fc;">Найти</button> </div> <div id="singleOrderResult" style="margin:20px 0;padding:20px;background:rgba(46,160,67,0.1);border-radius:10px;border:1px solid #238636;display:none;color:var(--text-main);"></div> <div id="ordersList"><p style="text-align:center;color:var(--text-muted);">Загрузка...</p></div> </div> </div> <script> // JavaScript остается без изменений, только стили меняем let categories = []; let products = []; let cart = JSON.parse(localStorage.getItem('lab10_cart') || '{}'); let currentPage = 1; let totalPages = 1; async function loadProducts(page = 1) { currentPage = page; const catId = document.getElementById('categoryFilter').value; let url = `/api/lab10/products?page=${page}&limit=12`; if (catId) url += `&category=${catId}`; try { const res = await fetch(url); const result = await res.json(); products = result.data || []; totalPages = Math.ceil(result.total / 12); document.getElementById('products').innerHTML = products.length === 0 ? '<p style="grid-column:1/-1;text-align:center;color:var(--text-muted);font-size:1.3em;">Товаров нет</p>' : products.map(p => ` <div class="card" onclick="location.href='product.html?id=${p.id}'"> <h3>${p.title}</h3> <p><strong>Категория:</strong> ${p.category_title || '—'}</p> <p class="price">${p.price.toLocaleString()} ₽</p> <p><small>Остаток: ${p.amount} шт.</small></p> <button onclick="event.stopPropagation(); addToCart(${p.id})" ${p.amount === 0 ? 'disabled' : ''}> ${p.amount === 0 ? 'Нет в наличии' : 'В корзину'} </button> </div> `).join(''); renderPagination(); window.scrollTo({ top: 0, behavior: 'smooth' }); } catch (e) { document.getElementById('products').innerHTML = '<p style="color:var(--danger-red);">Ошибка загрузки</p>'; } } function renderPagination() { const el = document.getElementById('pagination'); if (totalPages <= 1) { el.style.display = 'none'; return; } el.style.display = 'block'; el.innerHTML = ` <button onclick="loadProducts(${currentPage - 1})" ${currentPage <= 1 ? 'disabled' : ''}>← Предыдущая</button> <span>Страница ${currentPage} из ${totalPages}</span> <button onclick="loadProducts(${currentPage + 1})" ${currentPage >= totalPages ? 'disabled' : ''}>Следующая →</button> `; } function updateCartCount() { const count = Object.values(cart).reduce((s, q) => s + q, 0); document.getElementById('cartCount').textContent = count; } function addToCart(id, event) { if (event) event.stopPropagation(); const p = products.find(x => x.id === id); if (!p || p.amount === 0) return; cart[id] = (cart[id] || 0) + 1; localStorage.setItem('lab10_cart', JSON.stringify(cart)); updateCartCount(); alert('Добавлено в корзину!'); } function openCart() { renderCart(); loadAllOrders(); document.getElementById('cartModal').style.display = 'flex'; } function renderCart() { const items = Object.entries(cart); const container = document.getElementById('cartItems'); const totalEl = document.getElementById('totalSum'); if (items.length === 0) { container.innerHTML = '<p style="text-align:center;color:var(--text-muted);">Корзина пуста</p>'; totalEl.textContent = 'Итого: 0 ₽'; return; } let html = '<table width="100%"><tr><th>Товар</th><th>Кол-во</th><th>Сумма</th><th></th></tr>'; let total = 0; for (const [id, qty] of items) { const p = products.find(x => x.id == id); if (!p) continue; const sum = p.price * qty; total += sum; html += `<tr> <td>${p.title}</td> <td><button onclick="changeQty(${id},-1)" style="background:rgba(46,160,67,0.2);border:1px solid #238636;color:var(--green-light);">−</button> <input type="number" min="1" max="5" value="${qty}" onchange="changeQty(${id},this.value-${qty})" style="width:50px;background:var(--card-gradient);color:var(--text-main);border:1px solid var(--dark-border);"> <button onclick="changeQty(${id},1)" style="background:rgba(46,160,67,0.2);border:1px solid #238636;color:var(--green-light);">+</button></td> <td>${sum.toLocaleString()} ₽</td> <td><button onclick="removeFromCart(${id})" style="background:linear-gradient(135deg, #da3633, #f85149);border:1px solid #f85149;color:#f0f6fc;">×</button></td> </tr>`; } html += '</table>'; container.innerHTML = html; totalEl.textContent = `Итого: ${total.toLocaleString()} ₽`; } window.changeQty = (id, delta) => { let qty = (cart[id] || 0) + delta; if (qty < 1) qty = 1; if (qty > 5) qty = 5; cart[id] = qty; if (qty === 0) delete cart[id]; localStorage.setItem('lab10_cart', JSON.stringify(cart)); updateCartCount(); renderCart(); }; window.removeFromCart = (id) => { delete cart[id]; localStorage.setItem('lab10_cart', JSON.stringify(cart)); updateCartCount(); renderCart(); }; async function checkout() { const items = Object.entries(cart).map(([id, qty]) => ({ productId: +id, quantity: qty })); const res = await fetch('/api/lab10/task3/checkout', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ items }) }); if (res.ok) { alert('Заказ успешно оформлен!'); cart = {}; localStorage.removeItem('lab10_cart'); updateCartCount(); renderCart(); loadAllOrders(); } else { const err = await res.json(); alert('Ошибка: ' + (err.error || 'Нехватка товара')); } } async function loadAllOrders() { try { const res = await fetch('/api/lab10/task1/orders'); const orders = await res.json(); const el = document.getElementById('ordersList'); if (orders.length === 0) { el.innerHTML = '<p style="text-align:center;color:var(--text-muted);">Заказов нет</p>'; return; } el.innerHTML = orders.map(o => ` <div style="border:1px solid var(--dark-border);padding:15px;margin:10px 0;border-radius:8px;background:rgba(46,160,67,0.05);"> <strong style="color:var(--green-light);">Заказ #${o.id}</strong> • ${new Date(o.created_at).toLocaleString()}<br> <strong style="color:var(--green-primary);">${o.total_amount.toLocaleString()} ₽</strong> </div> `).join(''); } catch (e) { document.getElementById('ordersList').innerHTML = '<p style="color:var(--danger-red);">Ошибка загрузки заказов</p>'; } } window.loadOrderById = async () => { const id = document.getElementById('searchOrderId').value; if (!id) return alert('Введите ID'); const res = await fetch(`/api/lab10/task1/orders/${id}`); const div = document.getElementById('singleOrderResult'); if (!res.ok) { div.innerHTML = '<p style="color:var(--danger-red);">Заказ не найден</p>'; div.style.display = 'block'; return; } const order = await res.json(); const date = new Date(order.created_at).toLocaleString(); div.innerHTML = `<h3 style="color:var(--green-light);">Заказ #${order.id} от ${date}</h3> <ul>${order.items.map(i => `<li><strong style="color:var(--green-light);">${i.title}</strong> × ${i.quantity} = ${i.total_price.toLocaleString()} ₽</li>`).join('')}</ul> <h3 style="color:var(--green-primary);">Итого: ${order.total_amount.toLocaleString()} ₽</h3>`; div.style.display = 'block'; }; async function loadCategories() { const res = await fetch('/api/lab10/categories'); categories = await res.json(); const filter = document.getElementById('categoryFilter'); const select = document.getElementById('newProductCategory'); filter.innerHTML = '<option value="">Все категории</option>'; select.innerHTML = ''; categories.forEach(c => { filter.innerHTML += `<option value="${c.id}">${c.name}</option>`; select.innerHTML += `<option value="${c.id}">${c.name}</option>`; }); } window.addCategory = async () => { const name = document.getElementById('newCategoryName').value.trim(); if (name.length < 2) return alert('Введите название'); const res = await fetch('/api/lab10/task1/categories', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name }) }); if (res.ok) { alert('Готово!'); document.getElementById('newCategoryName').value = ''; closeModal('modalCategory'); loadCategories(); } else alert('Ошибка'); }; window.addProduct = async () => { const title = document.getElementById('newProductTitle').value.trim(); const price = +document.getElementById('newProductPrice').value; const amount = +document.getElementById('newProductAmount').value; const category_id = +document.getElementById('newProductCategory').value; const description = document.getElementById('newProductDescription').value.trim(); if (!title || !price || amount < 0 || !category_id) return alert('Заполните все поля!'); const res = await fetch('/api/lab10/task1/products', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title, price, amount, category_id, description: description || null }) }); if (res.ok) { alert('Товар добавлен!'); document.getElementById('newProductTitle').value = ''; document.getElementById('newProductPrice').value = ''; document.getElementById('newProductAmount').value = '10'; document.getElementById('newProductDescription').value = ''; closeModal('modalProduct'); loadProducts(currentPage); } else alert('Ошибка добавления'); }; function openAddCategory() { document.getElementById('modalCategory').style.display = 'flex'; } function openAddProduct() { loadCategories(); document.getElementById('modalProduct').style.display = 'flex'; } function closeModal(id) { document.getElementById(id).style.display = 'none'; } loadCategories(); loadProducts(1); updateCartCount(); </script> </body> </html>