// JavaScript для страницы поиска
class SearchManager {
constructor() {
this.warehouse = new WarehouseManager();
this.searchResults = {
items: [],
operations: []
};
this.currentPage = 1;
this.resultsPerPage = 12;
this.currentQuery = '';
this.filters = {};
this.init();
}
init() {
$(document).ready(async () => {
try {
// Проверяем авторизацию
if (!auth.isUserAuthenticated()) {
console.log('Пользователь не авторизован, перенаправление на login.html');
window.location.href = 'login.html';
return;
}
console.log('Инициализация поиска для пользователя:', auth.getCurrentUser()?.name);
// Загружаем категории
await this.loadCategories();
// Загружаем категории в навигацию
await this.loadCategoriesNav();
// Загружаем категории в выпадающее меню
await this.loadCategoriesDropdown();
// Обновляем информацию о пользователе
this.updateUserInfo();
// Привязываем события
this.bindEvents();
// Инициализируем фильтры
this.initializeFilters();
} catch (error) {
console.error('Ошибка инициализации:', error);
auth.showNotification('Ошибка загрузки данных', 'danger');
}
});
}
bindEvents() {
// Поиск
$('#searchBtn').on('click', () => this.performSearch());
$('#searchQuery').on('keypress', (e) => {
if (e.which === 13) {
this.performSearch();
}
});
// Расширенные фильтры
$('#toggleAdvancedFilters').on('click', () => this.toggleAdvancedFilters());
$('#clearFiltersBtn').on('click', () => this.clearFilters());
// Сортировка и экспорт
$('#sortResults').on('change', () => this.applySorting());
$('#exportResultsBtn').on('click', () => this.exportResults());
// Переключение вкладок
$('#resultsTabs a').on('click', (e) => {
e.preventDefault();
$(e.target).tab('show');
});
// Обработчик выхода
$('#logoutBtn').on('click', (e) => {
e.preventDefault();
auth.logout();
window.location.href = 'login.html';
});
}
// Загрузка категорий для фильтра
async loadCategories() {
try {
const categories = await this.warehouse.getCategories();
const categoryFilter = $('#categoryFilter');
categories.forEach(category => {
const option = ``;
categoryFilter.append(option);
});
} catch (error) {
console.error('Ошибка при загрузке категорий:', error);
}
}
// Инициализация фильтров
initializeFilters() {
this.filters = {
searchType: 'all',
category: '',
minPrice: null,
maxPrice: null,
startDate: '',
endDate: ''
};
}
// Выполнение поиска
async performSearch() {
const query = $('#searchQuery').val().trim();
const searchType = $('#searchType').val();
if (!query) {
auth.showNotification('Введите поисковый запрос', 'warning');
return;
}
this.currentQuery = query;
this.filters.searchType = searchType;
this.collectFilters();
this.showLoading();
try {
await this.searchItems(query);
await this.searchOperations(query);
this.displayResults();
this.hideLoading();
} catch (error) {
console.error('Ошибка при поиске:', error);
auth.showNotification('Ошибка при выполнении поиска', 'danger');
this.hideLoading();
}
}
// Сбор фильтров
collectFilters() {
this.filters.category = $('#categoryFilter').val();
this.filters.minPrice = $('#minPrice').val() ? parseFloat($('#minPrice').val()) : null;
this.filters.maxPrice = $('#maxPrice').val() ? parseFloat($('#maxPrice').val()) : null;
this.filters.startDate = $('#startDate').val();
this.filters.endDate = $('#endDate').val();
}
// Поиск позиций
async searchItems(query) {
if (this.filters.searchType === 'operations') {
this.searchResults.items = [];
return;
}
const searchFilters = {
category: this.filters.category,
minPrice: this.filters.minPrice,
maxPrice: this.filters.maxPrice
};
this.searchResults.items = await this.warehouse.searchItems(query, searchFilters);
}
// Поиск операций
async searchOperations(query) {
if (this.filters.searchType === 'items') {
this.searchResults.operations = [];
return;
}
const operations = await this.warehouse.getOperations();
// Фильтруем операции по запросу
this.searchResults.operations = operations.filter(operation => {
const searchText = query.toLowerCase();
return (
operation.itemName.toLowerCase().includes(searchText) ||
operation.employeeName.toLowerCase().includes(searchText) ||
operation.notes.toLowerCase().includes(searchText) ||
operation.supplier.toLowerCase().includes(searchText) ||
operation.recipient.toLowerCase().includes(searchText)
);
});
// Применяем фильтры по дате
if (this.filters.startDate) {
this.searchResults.operations = this.searchResults.operations.filter(op =>
new Date(op.date) >= new Date(this.filters.startDate)
);
}
if (this.filters.endDate) {
this.searchResults.operations = this.searchResults.operations.filter(op =>
new Date(op.date) <= new Date(this.filters.endDate)
);
}
}
// Отображение результатов
displayResults() {
const totalResults = this.searchResults.items.length + this.searchResults.operations.length;
if (totalResults === 0) {
this.showNoResults();
return;
}
this.showResults();
this.updateResultsCount();
this.renderItemsResults();
this.renderOperationsResults();
this.renderPagination();
}
// Показать результаты
showResults() {
$('#emptyState').hide();
$('#loadingState').hide();
$('#searchResults').show();
}
// Показать отсутствие результатов
showNoResults() {
$('#emptyState').hide();
$('#loadingState').hide();
$('#searchResults').hide();
$('#emptyState').html(`
Результаты не найдены
По запросу "${this.currentQuery}" ничего не найдено. Попробуйте изменить поисковый запрос или фильтры.
`).show();
}
// Обновление счетчиков результатов
updateResultsCount() {
const totalResults = this.searchResults.items.length + this.searchResults.operations.length;
$('#resultsCount').text(`Найдено результатов: ${totalResults}`);
$('#itemsCount').text(this.searchResults.items.length);
$('#operationsCount').text(this.searchResults.operations.length);
}
// Рендеринг результатов товаров
renderItemsResults() {
const container = $('#itemsResultsContainer');
container.empty();
if (this.searchResults.items.length === 0) {
container.html(`
Позиции не найдены
Попробуйте изменить поисковый запрос
`);
return;
}
// Пагинация для позиций
const totalPages = Math.ceil(this.searchResults.items.length / this.resultsPerPage);
const startIndex = (this.currentPage - 1) * this.resultsPerPage;
const endIndex = startIndex + this.resultsPerPage;
const pageItems = this.searchResults.items.slice(startIndex, endIndex);
pageItems.forEach(item => {
const itemHtml = this.createItemCard(item);
container.append(itemHtml);
});
}
// Создание карточки позиции
createItemCard(item) {
const category = this.getCategoryInfo(item.category);
const stockStatus = this.getStockStatus(item);
return `
${this.highlightQuery(item.name)}
${this.highlightQuery(item.description.substring(0, 80))}${item.description.length > 80 ? '...' : ''}
${category.name}
${stockStatus.text}
Цена
${item.price.toLocaleString()} ₽
Количество
${item.quantity} шт.
`;
}
// Рендеринг результатов операций
renderOperationsResults() {
const tbody = $('#operationsResultsTable tbody');
tbody.empty();
if (this.searchResults.operations.length === 0) {
tbody.html(`
Операции не найдены
|
`);
return;
}
// Пагинация для операций
const totalPages = Math.ceil(this.searchResults.operations.length / this.resultsPerPage);
const startIndex = (this.currentPage - 1) * this.resultsPerPage;
const endIndex = startIndex + this.resultsPerPage;
const pageOperations = this.searchResults.operations.slice(startIndex, endIndex);
pageOperations.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}
|
${this.highlightQuery(operation.itemName)} |
${operation.quantity} |
${this.highlightQuery(operation.employeeName)} |
${statusText}
|
`;
}
// Подсветка поискового запроса
highlightQuery(text) {
if (!this.currentQuery) return text;
const regex = new RegExp(`(${this.escapeRegex(this.currentQuery)})`, 'gi');
return text.replace(regex, '$1');
}
// Экранирование специальных символов для regex
escapeRegex(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
// Рендеринг пагинации
renderPagination() {
const pagination = $('#resultsPagination');
pagination.empty();
const totalResults = this.searchResults.items.length + this.searchResults.operations.length;
const totalPages = Math.ceil(totalResults / this.resultsPerPage);
if (totalPages <= 1) return;
// Кнопка "Предыдущая"
const prevDisabled = this.currentPage === 1 ? 'disabled' : '';
pagination.append(`
`);
// Номера страниц
const startPage = Math.max(1, this.currentPage - 2);
const endPage = Math.min(totalPages, this.currentPage + 2);
for (let i = startPage; i <= endPage; i++) {
const active = i === this.currentPage ? 'active' : '';
pagination.append(`
${i}
`);
}
// Кнопка "Следующая"
const nextDisabled = this.currentPage === totalPages ? 'disabled' : '';
pagination.append(`
`);
}
// Переход на страницу
goToPage(page) {
this.currentPage = page;
this.renderItemsResults();
this.renderOperationsResults();
this.renderPagination();
}
// Применение сортировки
applySorting() {
const sortBy = $('#sortResults').val();
if (sortBy === 'relevance') {
// Сортировка по релевантности (по умолчанию)
return;
}
// Сортировка позиций
this.searchResults.items.sort((a, b) => {
let aValue = a[sortBy];
let bValue = b[sortBy];
if (sortBy === 'date') {
aValue = new Date(aValue);
bValue = new Date(bValue);
}
return aValue > bValue ? 1 : -1;
});
// Сортировка операций
this.searchResults.operations.sort((a, b) => {
let aValue = a[sortBy];
let bValue = b[sortBy];
if (sortBy === 'date') {
aValue = new Date(aValue);
bValue = new Date(bValue);
}
return aValue > bValue ? 1 : -1;
});
this.currentPage = 1;
this.renderItemsResults();
this.renderOperationsResults();
this.renderPagination();
}
// Переключение расширенных фильтров
toggleAdvancedFilters() {
const advancedFilters = $('#advancedFilters');
const toggleBtn = $('#toggleAdvancedFilters');
if (advancedFilters.is(':visible')) {
advancedFilters.slideUp();
toggleBtn.html('Расширенные фильтры');
} else {
advancedFilters.slideDown();
toggleBtn.html('Скрыть фильтры');
}
}
// Очистка фильтров
clearFilters() {
$('#categoryFilter').val('');
$('#minPrice').val('');
$('#maxPrice').val('');
$('#startDate').val('');
$('#endDate').val('');
this.initializeFilters();
}
// Экспорт результатов
async exportResults() {
try {
const totalResults = this.searchResults.items.length + this.searchResults.operations.length;
if (totalResults === 0) {
auth.showNotification('Нет результатов для экспорта', 'warning');
return;
}
let csv = 'Результаты поиска\n';
csv += `Запрос: ${this.currentQuery}\n`;
csv += `Дата поиска: ${new Date().toLocaleDateString('ru-RU')}\n\n`;
// Экспорт позиций
if (this.searchResults.items.length > 0) {
csv += 'ТОВАРЫ\n';
csv += 'ID,Название,Категория,Цена,Количество,Описание\n';
this.searchResults.items.forEach(item => {
csv += `${item.id},"${item.name}","${this.getCategoryInfo(item.category).name}",${item.price},${item.quantity},"${item.description}"\n`;
});
csv += '\n';
}
// Экспорт операций
if (this.searchResults.operations.length > 0) {
csv += 'ОПЕРАЦИИ\n';
csv += 'Дата,Тип,Товар,Количество,Сотрудник,Статус\n';
this.searchResults.operations.forEach(op => {
csv += `${this.formatDate(op.date)},"${op.type === 'incoming' ? 'Приход' : 'Расход'}","${op.itemName}",${op.quantity},"${op.employeeName}","${this.getStatusText(op.status)}"\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', `поиск_${this.currentQuery}_${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');
}
}
// Просмотр деталей позиции
async viewItemDetails(itemId) {
try {
const item = await this.warehouse.getItemById(itemId);
if (!item) return;
const category = this.getCategoryInfo(item.category);
const stockStatus = this.getStockStatus(item);
const detailsHtml = `
Основная информация
| ID: |
${item.id} |
| Название: |
${item.name} |
| Категория: |
${category.name} |
| Цена: |
${item.price.toLocaleString()} ₽ |
Складская информация
| Количество: |
${item.quantity} шт. |
| Статус: |
${stockStatus.text} |
| Местоположение: |
${item.location} |
| Поставщик: |
${item.supplier || 'Не указан'} |
Описание
${item.description || 'Описание отсутствует'}
${item.barcode ? `
Штрих-код
${item.barcode}
` : ''}
`;
$('#itemDetailsContent').html(detailsHtml);
$('#itemDetailsModal').modal('show');
} catch (error) {
console.error('Ошибка при загрузке деталей позиции:', error);
auth.showNotification('Ошибка при загрузке деталей позиции', 'danger');
}
}
// Получение информации о категории
getCategoryInfo(categoryId) {
const categories = [
{ id: 'electronics', name: 'Электроника', icon: 'fas fa-laptop' },
{ id: 'clothing', name: 'Одежда', icon: 'fas fa-tshirt' },
{ id: 'tools', name: 'Инструменты', icon: 'fas fa-tools' },
{ id: 'office', name: 'Офисные принадлежности', icon: 'fas fa-briefcase' },
{ id: 'furniture', name: 'Мебель', icon: 'fas fa-couch' },
{ id: 'books', name: 'Книги', icon: 'fas fa-book' }
];
return categories.find(cat => cat.id === categoryId) ||
{ id: 'unknown', name: 'Неизвестная категория', icon: 'fas fa-box' };
}
// Получение статуса запаса
getStockStatus(item) {
if (item.quantity === 0) {
return { class: 'bg-danger', text: 'Нет в наличии' };
} else if (item.quantity <= item.minQuantity) {
return { class: 'bg-warning', text: 'Заканчивается' };
} else {
return { class: 'bg-success', text: 'В наличии' };
}
}
// Получение класса статуса
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'
});
}
// Показать индикатор загрузки
showLoading() {
$('#emptyState').hide();
$('#searchResults').hide();
$('#loadingState').show();
}
// Скрыть индикатор загрузки
hideLoading() {
$('#loadingState').hide();
}
// Обновление информации о пользователе
updateUserInfo() {
const user = auth.getCurrentUser();
if (user) {
$('#currentUserInfo').text(user.name);
$('#loginLink').hide();
$('#profileDropdown').show();
// Показываем админ панель только для администраторов
if (auth.hasRole('admin')) {
$('#adminDropdown').show();
} else {
$('#adminDropdown').hide();
}
} else {
$('#currentUserInfo').text('Гость');
$('#loginLink').show();
$('#profileDropdown').hide();
$('#adminDropdown').hide();
}
// Обновляем права доступа для элементов интерфейса
auth.updateButtonPermissions();
}
// Загрузка категорий в навигацию
async loadCategoriesNav() {
try {
const categories = await this.warehouse.getCategories();
const categoriesNav = $('#categoriesNav');
// Очищаем существующие категории (кроме "Главная")
categoriesNav.find('li:not(:first-child)').remove();
// Добавляем активные категории
categories.forEach(category => {
if (category.active) {
const categoryLink = `
${category.name}
`;
categoriesNav.append(categoryLink);
}
});
} catch (error) {
console.error('Ошибка загрузки категорий:', error);
}
}
// Загрузка категорий в выпадающее меню
async loadCategoriesDropdown() {
try {
const categories = await this.warehouse.getCategories();
const dropdown = $('#catalogCategoriesDropdown');
// Очищаем существующие категории (кроме "Все позиции" и разделителя)
dropdown.find('li:not(:first-child):not(:nth-child(2))').remove();
// Добавляем активные категории
categories.forEach(category => {
if (category.active) {
const isEmpty = category.itemCount === 0;
const categoryLink = `
${category.name}
${category.itemCount || 0}
`;
dropdown.append(categoryLink);
}
});
// Инициализируем hover-функциональность
this.initHoverDropdown();
} catch (error) {
console.error('Ошибка загрузки категорий в выпадающее меню:', error);
}
}
// Инициализация hover-функциональности для выпадающего меню
initHoverDropdown() {
const $dropdown = $('.nav-item.dropdown');
const $dropdownMenu = $dropdown.find('.dropdown-menu');
let hoverTimeout;
// Показываем меню при наведении
$dropdown.on('mouseenter', function() {
clearTimeout(hoverTimeout);
$(this).find('.dropdown-menu').addClass('show');
});
// Скрываем меню при уходе мыши
$dropdown.on('mouseleave', function() {
const $menu = $(this).find('.dropdown-menu');
hoverTimeout = setTimeout(() => {
$menu.removeClass('show');
}, 150); // Небольшая задержка для плавности
});
// Предотвращаем скрытие при наведении на само меню
$dropdownMenu.on('mouseenter', function() {
clearTimeout(hoverTimeout);
});
$dropdownMenu.on('mouseleave', function() {
hoverTimeout = setTimeout(() => {
$(this).removeClass('show');
}, 150);
});
}
}
// Инициализация менеджера поиска
const searchManager = new SearchManager();