/
Cooolprogrammers
/
web-nodejs-labs
Обзор
Документация
Войти
/
Cooolprogrammers
/
web-nodejs-labs
Код
Запросы
0
Задачи
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
public/lab9/task2/app.js
391 строка
16 KB
darkvoid6@mail.ru
ЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫЫ
18 дек 2025, 07:59
18 дек 2025, 07:59
f2c4f06
Код
Авторство
О чём код?
const API_BASE = '/api/lab9'; // Форматирование цены: Копейки -> Рубли const formatPrice = (cents) => { return (cents / 100).toLocaleString('ru-RU', { style: 'currency', currency: 'RUB' }); }; // Парсинг цены из рублей в копейки const parsePriceToCents = (priceStr) => { return Math.round(parseFloat(priceStr) * 100); }; // Функция для переключения отображения JSON function toggleJson(elementId) { const element = document.getElementById(elementId); element.classList.toggle('hidden'); } // Функция для красивого форматирования JSON function formatJSON(obj) { return JSON.stringify(obj, null, 2); } // ЗАДАНИЕ 1: Получение товара по ID async function getProductById() { const productId = document.getElementById('productIdInput').value.trim(); if (!productId) { alert('Введите ID товара'); return; } try { const res = await fetch(`${API_BASE}/${productId}`); if (res.status === 404) { const error = await res.json(); document.getElementById('singleProductContent').innerHTML = ` <div class="error-message"> <strong>Ошибка 404:</strong> ${error.error} </div> `; } else { const product = await res.json(); document.getElementById('singleProductContent').innerHTML = ` <div class="product-card"> <div class="product-header"> <div class="product-title">${product.title}</div> </div> <div class="product-details"> <div class="price">${formatPrice(product.price)}</div> <div>Остаток: ${product.amount} шт.</div> <div>ID: ${product.id}</div> </div> </div> `; // Показываем JSON document.getElementById('productJson').textContent = formatJSON(product); } document.getElementById('singleProductResult').classList.remove('hidden'); } catch (error) { alert('Ошибка при поиске товара'); console.error(error); } } // Загрузка статистики склада async function loadStockStats() { try { const res = await fetch(`${API_BASE}/task1/stock`); const stats = await res.json(); document.getElementById('totalProducts').textContent = stats.total_products; document.getElementById('totalItems').textContent = stats.total_items; document.getElementById('totalValue').textContent = formatPrice(stats.total_value); } catch (error) { console.error('Ошибка при загрузке статистики:', error); } } // Загрузка всех товаров async function loadAllProducts() { try { const res = await fetch(`${API_BASE}/`); const products = await res.json(); displayProducts(products); document.getElementById('searchInput').value = ''; } catch (error) { console.error('Ошибка при загрузке товаров:', error); document.getElementById('productsContainer').innerHTML = '<p class="error-message">Ошибка при загрузке товаров</p>'; } } // Поиск товаров async function searchProducts() { const searchTerm = document.getElementById('searchInput').value.trim(); if (searchTerm.length < 1) { alert('Введите минимум 1 символ для поиска'); return; } try { const res = await fetch(`${API_BASE}/task1/products?q=${encodeURIComponent(searchTerm)}`); const products = await res.json(); displayProducts(products); } catch (error) { console.error('Ошибка при поиске товаров:', error); document.getElementById('productsContainer').innerHTML = '<p class="error-message">Ошибка при поиске товаров</p>'; } } // Отображение списка товаров function displayProducts(products) { const container = document.getElementById('productsContainer'); if (products.length === 0) { container.innerHTML = '<p>Товары не найдены</p>'; return; } container.innerHTML = products.map(product => ` <div class="product-card" data-product-id="${product.id}"> <div class="product-header"> <div class="product-title"> <span class="title-view" onclick="startInlineEdit(this, 'title', ${product.id})">${product.title}</span> <input type="text" class="edit-input title-edit hidden" value="${product.title}" onkeypress="handleTitleEdit(event, ${product.id})" onchange="markAsChanged(${product.id})"> </div> <div class="product-actions"> <button class="btn btn-danger btn-sm" onclick="deleteProduct(${product.id})">🗑️ Удалить</button> </div> </div> <div class="product-details"> <div class="price"> <span class="price-view" onclick="startInlineEdit(this, 'price', ${product.id})">${formatPrice(product.price)}</span> <input type="number" class="edit-input edit-price price-edit hidden" value="${(product.price / 100).toFixed(2)}" step="0.01" min="0" onkeypress="handlePriceEdit(event, ${product.id})" onchange="markAsChanged(${product.id})"> </div> <div class="amount-controls"> <button class="btn btn-warning btn-sm" onclick="changeAmount(${product.id}, -1)">-</button> <span class="amount-view">${product.amount} шт.</span> <input type="number" class="edit-input amount-edit hidden" value="${product.amount}" style="width: 80px;" min="0"> <button class="btn btn-success btn-sm" onclick="changeAmount(${product.id}, 1)">+</button> </div> <div>ID: ${product.id}</div> </div> </div> `).join(''); } // Массив для хранения измененных товаров let changedProducts = []; // Отметить товар как измененный function markAsChanged(productId) { if (!changedProducts.includes(productId)) { changedProducts.push(productId); } } // Начало инлайн-редактирования function startInlineEdit(element, type, productId) { const productCard = document.querySelector(`[data-product-id="${productId}"]`); if (type === 'title') { const titleView = productCard.querySelector('.title-view'); const titleEdit = productCard.querySelector('.title-edit'); titleView.classList.add('hidden'); titleEdit.classList.remove('hidden'); titleEdit.focus(); titleEdit.select(); } else if (type === 'price') { const priceView = productCard.querySelector('.price-view'); const priceEdit = productCard.querySelector('.price-edit'); priceView.classList.add('hidden'); priceEdit.classList.remove('hidden'); priceEdit.focus(); priceEdit.select(); } } // Завершение инлайн-редактирования function finishInlineEdit(element, type, productId) { const productCard = document.querySelector(`[data-product-id="${productId}"]`); if (type === 'title') { const titleView = productCard.querySelector('.title-view'); const titleEdit = productCard.querySelector('.title-edit'); titleView.textContent = titleEdit.value; titleView.classList.remove('hidden'); titleEdit.classList.add('hidden'); markAsChanged(productId); } else if (type === 'price') { const priceView = productCard.querySelector('.price-view'); const priceEdit = productCard.querySelector('.price-edit'); priceView.textContent = formatPrice(parsePriceToCents(priceEdit.value)); priceView.classList.remove('hidden'); priceEdit.classList.add('hidden'); markAsChanged(productId); } } // Обработка редактирования названия (Enter для завершения редактирования) function handleTitleEdit(event, productId) { if (event.key === 'Enter') { const productCard = document.querySelector(`[data-product-id="${productId}"]`); const titleEdit = productCard.querySelector('.title-edit'); finishInlineEdit(titleEdit, 'title', productId); } else if (event.key === 'Escape') { cancelInlineEdit('title', productId); } } // Обработка редактирования цены (Enter для завершения редактирования) function handlePriceEdit(event, productId) { if (event.key === 'Enter') { const productCard = document.querySelector(`[data-product-id="${productId}"]`); const priceEdit = productCard.querySelector('.price-edit'); finishInlineEdit(priceEdit, 'price', productId); } else if (event.key === 'Escape') { cancelInlineEdit('price', productId); } } // Отмена инлайн-редактирования (Esc) function cancelInlineEdit(type, productId) { const productCard = document.querySelector(`[data-product-id="${productId}"]`); if (type === 'title') { const titleView = productCard.querySelector('.title-view'); const titleEdit = productCard.querySelector('.title-edit'); titleEdit.value = titleView.textContent; // Восстанавливаем исходное значение titleView.classList.remove('hidden'); titleEdit.classList.add('hidden'); } else if (type === 'price') { const priceView = productCard.querySelector('.price-view'); const priceEdit = productCard.querySelector('.price-edit'); priceEdit.value = parseFloat(priceView.textContent.replace(/\s/g, '').replace('₽', '')); // Восстанавливаем исходное значение priceView.classList.remove('hidden'); priceEdit.classList.add('hidden'); } } // Изменение количества function changeAmount(productId, delta) { const productCard = document.querySelector(`[data-product-id="${productId}"]`); const amountView = productCard.querySelector('.amount-view'); const amountEdit = productCard.querySelector('.amount-edit'); let currentAmount = parseInt(amountView.textContent); let newAmount = currentAmount + delta; // Контролируем минимальное значение 0 if (newAmount < 0) { newAmount = 0; } amountView.textContent = newAmount + ' шт.'; amountEdit.value = newAmount; markAsChanged(productId); } // Сохранение всех изменений async function saveAllChanges() { if (changedProducts.length === 0) { showMessage('Нет изменений для сохранения', 'info'); return; } // Для каждого измененного продукта нужно получить текущие данные с формы for (const productId of changedProducts) { const productCard = document.querySelector(`[data-product-id="${productId}"]`); // Если поля редактирования видны, сначала завершаем редактирование const titleEdit = productCard.querySelector('.title-edit'); const priceEdit = productCard.querySelector('.price-edit'); const amountEdit = productCard.querySelector('.amount-edit'); if (!titleEdit.classList.contains('hidden')) { finishInlineEdit(null, 'title', productId); } if (!priceEdit.classList.contains('hidden')) { finishInlineEdit(null, 'price', productId); } // Получаем обновленные значения const titleView = productCard.querySelector('.title-view'); const priceView = productCard.querySelector('.price-view'); const amountView = productCard.querySelector('.amount-view'); const updatedData = { title: titleView.textContent, price: parsePriceToCents(parseFloat(priceView.textContent.replace(/\s/g, '').replace('₽', ''))), amount: parseInt(amountView.textContent) }; // Валидация if (!updatedData.title) { showMessage('Название товара не может быть пустым', 'error'); continue; } if (updatedData.amount < 0) { showMessage('Количество не может быть отрицательным', 'error'); continue; } try { const res = await fetch(`${API_BASE}/${productId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(updatedData) }); if (!res.ok) { throw new Error(`HTTP error! status: ${res.status}`); } } catch (error) { showMessage(`Ошибка при обновлении товара ID ${productId}: ${error.message}`, 'error'); } } // После успешного сохранения очищаем список измененных продуктов changedProducts = []; showMessage('Все изменения сохранены!', 'success'); // Обновляем статистику loadStockStats(); } // Удаление товара async function deleteProduct(productId) { if (!confirm('Вы уверены, что хотите удалить этот товар?')) { return; } try { const res = await fetch(`${API_BASE}/${productId}`, { method: 'DELETE' }); if (res.ok) { // Удаляем карточку товара из DOM const productCard = document.querySelector(`[data-product-id="${productId}"]`); productCard.remove(); // Обновляем статистику loadStockStats(); showMessage('Товар успешно удален!', 'success'); } else { showMessage('Ошибка при удалении товара', 'error'); } } catch (error) { showMessage('Ошибка при удалении товара', 'error'); console.error(error); } } // Показать сообщение function showMessage(message, type) { // Удаляем предыдущие сообщения const existingMessages = document.querySelectorAll('.message'); existingMessages.forEach(msg => msg.remove()); const messageDiv = document.createElement('div'); messageDiv.className = `message ${type}-message`; messageDiv.textContent = message; document.querySelector('.main-content').insertBefore(messageDiv, document.querySelector('.main-content').firstChild); // Автоматически удалить сообщение через 3 секунды setTimeout(() => { messageDiv.remove(); }, 3000); } // Инициализация document.addEventListener('DOMContentLoaded', function () { loadAllProducts(); loadStockStats(); });