/
Centermant
/
web-nodejs
Обзор
Документация
Войти
/
Centermant
/
web-nodejs
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
public/lab9/task2/script.js
322 строки
12 KB
Centermant
feat(lab9:final)
25 ноя 2025, 22:02
25 ноя 2025, 22:02
6322796
Код
Авторство
О чём код?
const API = '/api/lab9/products'; const API_TASK1_STOCK = '/api/lab9/task1/stock'; const formatPrice = (cents) => { if (isNaN(cents)) return 'N/A'; return (cents / 100).toLocaleString('ru-RU', { style: 'currency', currency: 'RUB', maximumFractionDigits: 2 }); }; const pendingChanges = {}; // Загрузка и отображение статистики async function loadStockStats() { try { const res = await fetch(API_TASK1_STOCK); if (!res.ok) { const errorData = await res.json(); throw new Error(errorData.error || `HTTP error! status: ${res.status}`); } const stats = await res.json(); document.getElementById('statsContainer').innerHTML = ` <p><strong>Всего наименований:</strong> ${stats.total_products}</p> <p><strong>Общее количество единиц на складе:</strong> ${stats.total_items}</p> <p><strong>Общая стоимость:</strong> ${formatPrice(stats.total_value)}</p> `; } catch (error) { console.error('Ошибка загрузки статистики:', error); document.getElementById('statsContainer').innerHTML = `<p class="error">Ошибка загрузки статистики: ${error.message}</p>`; } } // Загрузка и отображение товаров 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 tableBody = document.getElementById('productsTableBody'); tableBody.innerHTML = ''; if (products.length === 0) { tableBody.innerHTML = '<tr><td colspan="5">Товары не найдены.</td></tr>'; return; } Object.keys(pendingChanges).forEach(key => delete pendingChanges[key]); products.forEach(product => { const row = document.createElement('tr'); row.setAttribute('data-id', product.id); pendingChanges[product.id] = { title: product.title, price: product.price, amount: product.amount }; row.innerHTML = ` <td>${product.id}</td> <td> <div class="editable-field" data-field="title" data-id="${product.id}"> ${product.title} </div> </td> <td> <div class="editable-field" data-field="price" data-id="${product.id}"> ${formatPrice(product.price)} </div> </td> <td> <div class="amount-control" data-id="${product.id}"> <button type="button" class="btn-secondary" onclick="changeAmount(${product.id}, -1)">-</button> <span class="amount-display" style="margin: 0 10px; min-width: 40px; display: inline-block; text-align: center;">${product.amount}</span> <button type="button" class="btn-secondary" onclick="changeAmount(${product.id}, 1)">+</button> </div> </td> <td> <button type="button" class="delete-btn" onclick="deleteProduct(${product.id})">×</button> </td> `; tableBody.appendChild(row); setupInlineEditing(); }); } catch (error) { console.error('Ошибка загрузки товаров:', error); document.getElementById('productsTableBody').innerHTML = `<tr><td colspan="5" class="error">Ошибка загрузки товаров: ${error.message}</td></tr>`; } } // Поиск товара по ID async function searchProductById() { const id = document.getElementById('productId').value.trim(); if (!id || isNaN(id)) { alert('Пожалуйста, введите корректный ID товара'); return; } try { const res = await fetch(`${API}/${id}`); if (!res.ok) { const errorData = await res.json(); throw new Error(errorData.error || `HTTP error! status: ${res.status}`); } const product = await res.json(); document.getElementById('productDetails').innerHTML = ` <div style="margin-top: 15px; padding: 10px; background: rgba(0, 0, 0, 0.2); border-radius: 8px;"> <p><strong>ID:</strong> ${product.id}</p> <p><strong>Название:</strong> ${product.title}</p> <p><strong>Цена:</strong> ${formatPrice(product.price)}</p> <p><strong>Остаток:</strong> ${product.amount} шт.</p> </div> `; } catch (error) { console.error('Ошибка поиска товара:', error); document.getElementById('productDetails').innerHTML = `<p class="error">Ошибка поиска товара: ${error.message}</p>`; } } // Удаление товара async function deleteProduct(id) { if (!confirm('Удалить товар?')) return; try { const res = await fetch(`${API}/${id}`, { method: 'DELETE' }); if (!res.ok) { const errorData = await res.json(); throw new Error(errorData.error || `HTTP error! status: ${res.status}`); } loadProducts(); loadStockStats(); } catch (error) { console.error('Ошибка удаления товара:', error); alert(`Ошибка удаления: ${error.message}`); } } // Изменение количества товара function changeAmount(id, delta) { const amountElement = document.querySelector(`.amount-control[data-id="${id}"] .amount-display`); const currentAmount = parseInt(amountElement.textContent); const newAmount = Math.max(0, currentAmount + delta); amountElement.textContent = newAmount; if (!pendingChanges[id]) { pendingChanges[id] = {}; } pendingChanges[id].amount = newAmount; } // Добавление товара document.getElementById('addForm').addEventListener('submit', async (e) => { e.preventDefault(); const formData = new FormData(e.target); const priceRub = parseFloat(formData.get('price')); const amount = parseInt(formData.get('amount')); if (priceRub <= 0) { alert('Цена должна быть больше 0'); return; } if (amount < 0) { alert('Количество не может быть отрицательным'); return; } const priceCents = Math.round(priceRub * 100); const data = { title: formData.get('title'), price: priceCents, amount: amount }; try { const res = await fetch(API, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); const result = await res.json(); if (!res.ok) { throw new Error(result.error || `HTTP error! status: ${res.status}`); } e.target.reset(); loadProducts(); loadStockStats(); } catch (error) { console.error('Ошибка сохранения товара:', error); alert(`Ошибка при сохранении: ${error.message}`); } }); // Инлайн-редактирование function setupInlineEditing() { document.querySelectorAll('.editable-field').forEach(element => { element.addEventListener('click', function() { const field = this.getAttribute('data-field'); const id = this.getAttribute('data-id'); const currentValue = pendingChanges[id][field]; const input = document.createElement('input'); input.type = field === 'price' ? 'number' : 'text'; input.step = field === 'price' ? '0.01' : undefined; input.value = field === 'price' ? (currentValue / 100).toFixed(2) : currentValue; input.className = 'inline-edit-input'; this.innerHTML = ''; this.appendChild(input); input.focus(); const finishEditing = () => { let newValue = input.value.trim(); if (field === 'price') { newValue = parseFloat(newValue); if (!isNaN(newValue) && newValue > 0) { pendingChanges[id].price = Math.round(newValue * 100); this.textContent = formatPrice(pendingChanges[id].price); } else { this.textContent = formatPrice(pendingChanges[id].price); } } else { if (newValue) { pendingChanges[id].title = newValue; this.textContent = newValue; } else { this.textContent = pendingChanges[id].title; } } }; input.addEventListener('blur', finishEditing); input.addEventListener('keypress', (e) => { if (e.key === 'Enter') finishEditing(); }); }); }); } // Сохранение всех изменений async function saveAllProducts() { const productIds = Object.keys(pendingChanges); let hasChanges = false; for (const id of productIds) { const original = await fetch(`${API}/${id}`).then(res => res.json()); const changes = pendingChanges[id]; const updates = {}; if (original.title !== changes.title) { updates.title = changes.title; hasChanges = true; } if (original.price !== changes.price) { updates.price = changes.price; hasChanges = true; } // Сохраняем изменения количества if (original.amount !== changes.amount) { try { const res = await fetch(`${API}/${id}/amount`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ amount: changes.amount }) }); if (!res.ok) { const errorData = await res.json(); console.error(`Ошибка обновления количества для товара ${id}:`, errorData.error); } } catch (error) { console.error(`Ошибка обновления количества для товара ${id}:`, error); } } // Сохраняем изменения названия и цены if (Object.keys(updates).length > 0) { try { const res = await fetch(`${API}/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(updates) }); if (!res.ok) { const errorData = await res.json(); console.error(`Ошибка обновления товара ${id}:`, errorData.error); } } catch (error) { console.error(`Ошибка обновления товара ${id}:`, error); } } } if (hasChanges || Object.keys(pendingChanges).length > 0) { loadProducts(); loadStockStats(); alert('Все изменения успешно сохранены!'); } else { alert('Нет изменений для сохранения'); } } // Загрузка начальных данных при загрузке страницы document.addEventListener('DOMContentLoaded', () => { loadProducts(); loadStockStats(); });