// Основной JavaScript файл для главной страницы class MainApp { constructor() { this.warehouse = new WarehouseManager(); this.init(); } init() { // Инициализация после загрузки DOM $(document).ready(() => { this.loadDashboardData(); this.bindEvents(); this.initializeModals(); }); } bindEvents() { // Обработчики для модальных окон $('#saveItemBtn').on('click', () => this.addNewItem()); $('#saveIncomingBtn').on('click', () => this.processIncoming()); $('#saveOutgoingBtn').on('click', () => this.processOutgoing()); // Обработчики для фильтров и поиска $('#searchInput').on('input', (e) => this.handleSearch(e.target.value)); // Обработчики для сортировки $('.sortable').on('click', (e) => this.handleSort(e)); // Обработчики для экспорта $('#exportBtn').on('click', () => this.exportData()); } // Загрузка данных для дашборда async loadDashboardData() { try { // Показываем индикатор загрузки this.showLoading(); // Загружаем статистику const stats = await this.warehouse.getStatistics(); this.updateStatistics(stats); // Загружаем последние операции const operations = await this.warehouse.getRecentOperations(10); this.updateOperationsTable(operations); // Загружаем товары для модальных окон const items = await this.warehouse.getAllItems(); this.populateItemSelects(items); // Скрываем индикатор загрузки this.hideLoading(); } catch (error) { console.error('Ошибка при загрузке данных:', error); auth.showNotification('Ошибка при загрузке данных', 'danger'); this.hideLoading(); } } // Обновление статистики updateStatistics(stats) { $('#totalItems').text(stats.totalItems.toLocaleString()); $('#totalValue').text(stats.totalValue.toLocaleString() + ' ₽'); $('#lowStock').text(stats.lowStockItems); $('#todayOperations').text(stats.todayOperations); } // Обновление таблицы операций updateOperationsTable(operations) { const tbody = $('#recentOperationsTable tbody'); tbody.empty(); if (operations.length === 0) { tbody.append(`
Нет операций для отображения `); return; } operations.forEach(operation => { const row = this.createOperationRow(operation); tbody.append(row); }); } // Создание строки операции createOperationRow(operation) { const typeIcon = operation.type === 'incoming' ? 'fa-box-open text-success' : 'fa-truck text-warning'; const typeText = operation.type === 'incoming' ? 'Приход' : 'Расход'; const statusClass = this.getStatusClass(operation.status); const statusText = this.getStatusText(operation.status); return ` ${this.formatDate(operation.date)} ${typeText} ${operation.itemName} ${operation.quantity} ${operation.employeeName} ${statusText} `; } // Получение класса статуса getStatusClass(status) { switch (status) { case 'completed': return 'status-completed'; case 'pending': return 'status-pending'; case 'cancelled': return 'status-cancelled'; default: return 'status-pending'; } } // Получение текста статуса getStatusText(status) { switch (status) { case 'completed': return 'Завершено'; case 'pending': return 'В обработке'; case 'cancelled': return 'Отменено'; default: return 'Неизвестно'; } } // Форматирование даты formatDate(dateString) { const date = new Date(dateString); return date.toLocaleDateString('ru-RU', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); } // Заполнение селектов товаров populateItemSelects(items) { const selects = ['#incomingItem', '#outgoingItem']; selects.forEach(selectId => { const select = $(selectId); select.find('option:not(:first)').remove(); items.forEach(item => { select.append(``); }); }); } // Добавление нового товара async addNewItem() { const formData = this.getFormData('#addItemForm'); if (!this.validateForm(formData)) { return; } try { const newItem = await this.warehouse.addItem(formData); auth.showNotification('Товар успешно добавлен', 'success'); // Закрываем модальное окно $('#addItemModal').modal('hide'); // Обновляем данные this.loadDashboardData(); // Очищаем форму $('#addItemForm')[0].reset(); } catch (error) { console.error('Ошибка при добавлении товара:', error); auth.showNotification('Ошибка при добавлении товара', 'danger'); } } // Обработка прихода async processIncoming() { const formData = this.getFormData('#incomingForm'); if (!this.validateForm(formData)) { return; } try { const operation = await this.warehouse.processIncoming(formData); auth.showNotification('Приход успешно оформлен', 'success'); // Закрываем модальное окно $('#incomingModal').modal('hide'); // Обновляем данные this.loadDashboardData(); // Очищаем форму $('#incomingForm')[0].reset(); } catch (error) { console.error('Ошибка при оформлении прихода:', error); auth.showNotification('Ошибка при оформлении прихода', 'danger'); } } // Обработка расхода async processOutgoing() { const formData = this.getFormData('#outgoingForm'); if (!this.validateForm(formData)) { return; } try { const operation = await this.warehouse.processOutgoing(formData); auth.showNotification('Расход успешно оформлен', 'success'); // Закрываем модальное окно $('#outgoingModal').modal('hide'); // Обновляем данные this.loadDashboardData(); // Очищаем форму $('#outgoingForm')[0].reset(); } catch (error) { console.error('Ошибка при оформлении расхода:', error); auth.showNotification('Ошибка при оформлении расхода', 'danger'); } } // Получение данных формы getFormData(formSelector) { const form = $(formSelector); const formData = {}; form.find('input, select, textarea').each(function() { const field = $(this); const name = field.attr('id'); const value = field.val(); if (name) { formData[name] = value; } }); return formData; } // Валидация формы validateForm(formData) { const requiredFields = ['itemName', 'itemCategory', 'itemPrice', 'itemQuantity']; for (const field of requiredFields) { if (!formData[field] || formData[field].trim() === '') { auth.showNotification(`Поле "${this.getFieldLabel(field)}" обязательно для заполнения`, 'warning'); return false; } } // Проверка числовых значений if (formData.itemPrice && parseFloat(formData.itemPrice) < 0) { auth.showNotification('Цена не может быть отрицательной', 'warning'); return false; } if (formData.itemQuantity && parseInt(formData.itemQuantity) < 0) { auth.showNotification('Количество не может быть отрицательным', 'warning'); return false; } return true; } // Получение метки поля getFieldLabel(fieldName) { const labels = { 'itemName': 'Название товара', 'itemCategory': 'Категория', 'itemPrice': 'Цена', 'itemQuantity': 'Количество', 'incomingItem': 'Товар', 'incomingQuantity': 'Количество', 'outgoingItem': 'Товар', 'outgoingQuantity': 'Количество' }; return labels[fieldName] || fieldName; } // Инициализация модальных окон initializeModals() { // Очистка форм при закрытии модальных окон $('.modal').on('hidden.bs.modal', function() { $(this).find('form')[0].reset(); }); // Валидация в реальном времени $('.form-control, .form-select').on('input change', function() { const field = $(this); const value = field.val(); if (field.prop('required') && (!value || value.trim() === '')) { field.addClass('is-invalid'); } else { field.removeClass('is-invalid'); } }); } // Обработка поиска handleSearch(query) { // Реализация поиска по таблице const table = $('#recentOperationsTable'); const rows = table.find('tbody tr'); rows.each(function() { const row = $(this); const text = row.text().toLowerCase(); const matches = text.includes(query.toLowerCase()); if (matches || query === '') { row.show(); } else { row.hide(); } }); } // Обработка сортировки handleSort(event) { const column = $(event.target); const table = column.closest('table'); const tbody = table.find('tbody'); const rows = tbody.find('tr').toArray(); // Определяем направление сортировки const isAscending = !column.hasClass('sort-asc'); // Убираем классы сортировки со всех заголовков table.find('th').removeClass('sort-asc sort-desc'); // Добавляем класс сортировки к текущему заголовку column.addClass(isAscending ? 'sort-asc' : 'sort-desc'); // Сортируем строки rows.sort((a, b) => { const aText = $(a).find('td').eq(column.index()).text(); const bText = $(b).find('td').eq(column.index()).text(); if (isAscending) { return aText.localeCompare(bText, 'ru'); } else { return bText.localeCompare(aText, 'ru'); } }); // Перестраиваем таблицу tbody.empty().append(rows); } // Экспорт данных exportData() { try { const table = $('#recentOperationsTable'); const rows = table.find('tbody tr'); let csv = 'Дата,Тип,Товар,Количество,Сотрудник,Статус\n'; rows.each(function() { const cells = $(this).find('td'); if (cells.length > 1) { // Пропускаем пустые строки const row = []; cells.each(function() { row.push(`"${$(this).text().trim()}"`); }); csv += row.join(',') + '\n'; } }); // Создаем и скачиваем файл const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }); const link = document.createElement('a'); const url = URL.createObjectURL(blob); link.setAttribute('href', url); link.setAttribute('download', `операции_${new Date().toISOString().split('T')[0]}.csv`); link.style.visibility = 'hidden'; document.body.appendChild(link); link.click(); document.body.removeChild(link); auth.showNotification('Данные успешно экспортированы', 'success'); } catch (error) { console.error('Ошибка при экспорте:', error); auth.showNotification('Ошибка при экспорте данных', 'danger'); } } // Показать индикатор загрузки showLoading() { $('main').append(`

Загрузка данных...

`); } // Скрыть индикатор загрузки hideLoading() { $('#loadingOverlay').remove(); } } // Инициализация приложения const mainApp = new MainApp();