Add files via upload
v1.3.0
This commit is contained in:
100
js/auth.js
100
js/auth.js
@ -175,24 +175,108 @@ class AuthSystem {
|
||||
}
|
||||
}
|
||||
|
||||
// Обновление прав доступа для кнопок
|
||||
// Обновление прав доступа для кнопок и элементов интерфейса
|
||||
updateButtonPermissions() {
|
||||
// Кнопки, доступные только администраторам
|
||||
const adminButtons = [
|
||||
// Элементы, доступные только администраторам
|
||||
const adminElements = [
|
||||
'#addItemBtn',
|
||||
'#editItemBtn',
|
||||
'#editItemBtn',
|
||||
'#deleteItemBtn',
|
||||
'#manageUsersBtn'
|
||||
'#manageUsersBtn',
|
||||
'.admin-only',
|
||||
'[data-role="admin"]'
|
||||
];
|
||||
|
||||
adminButtons.forEach(buttonId => {
|
||||
// Элементы, доступные администраторам и менеджерам
|
||||
const managerElements = [
|
||||
'.manager-or-admin',
|
||||
'[data-role="manager"]',
|
||||
'#addCategoryBtn',
|
||||
'#editCategoryBtn'
|
||||
];
|
||||
|
||||
// Элементы, доступные всем авторизованным пользователям
|
||||
const authenticatedElements = [
|
||||
'.authenticated-only',
|
||||
'[data-role="authenticated"]',
|
||||
'#exportBtn',
|
||||
'#searchBtn'
|
||||
];
|
||||
|
||||
// Скрываем/показываем элементы для администраторов
|
||||
adminElements.forEach(selector => {
|
||||
const elements = $(selector);
|
||||
elements.each((index, element) => {
|
||||
const $element = $(element);
|
||||
if (this.hasRole('admin')) {
|
||||
$element.show();
|
||||
$element.prop('disabled', false);
|
||||
} else {
|
||||
$element.hide();
|
||||
$element.prop('disabled', true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Скрываем/показываем элементы для менеджеров и администраторов
|
||||
managerElements.forEach(selector => {
|
||||
const elements = $(selector);
|
||||
elements.each((index, element) => {
|
||||
const $element = $(element);
|
||||
if (this.hasRole('admin') || this.hasRole('manager')) {
|
||||
$element.show();
|
||||
$element.prop('disabled', false);
|
||||
} else {
|
||||
$element.hide();
|
||||
$element.prop('disabled', true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Скрываем/показываем элементы для авторизованных пользователей
|
||||
authenticatedElements.forEach(selector => {
|
||||
const elements = $(selector);
|
||||
elements.each((index, element) => {
|
||||
const $element = $(element);
|
||||
if (this.isUserAuthenticated()) {
|
||||
$element.show();
|
||||
$element.prop('disabled', false);
|
||||
} else {
|
||||
$element.hide();
|
||||
$element.prop('disabled', true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Специальная обработка для кнопок с подсказками
|
||||
this.updateButtonTooltips();
|
||||
}
|
||||
|
||||
// Обновление подсказок для кнопок
|
||||
updateButtonTooltips() {
|
||||
// Кнопки с подсказками о правах доступа
|
||||
const buttonPermissions = {
|
||||
'#addItemBtn': { roles: ['admin', 'manager'], message: 'Требуются права администратора или менеджера' },
|
||||
'#editItemBtn': { roles: ['admin', 'manager'], message: 'Требуются права администратора или менеджера' },
|
||||
'#deleteItemBtn': { roles: ['admin'], message: 'Требуются права администратора' },
|
||||
'#addCategoryBtn': { roles: ['admin', 'manager'], message: 'Требуются права администратора или менеджера' },
|
||||
'#editCategoryBtn': { roles: ['admin', 'manager'], message: 'Требуются права администратора или менеджера' },
|
||||
'#deleteCategoryBtn': { roles: ['admin'], message: 'Требуются права администратора' },
|
||||
'#exportBtn': { roles: ['admin', 'manager', 'operator', 'viewer'], message: 'Требуется авторизация' }
|
||||
};
|
||||
|
||||
Object.keys(buttonPermissions).forEach(buttonId => {
|
||||
const button = $(buttonId);
|
||||
if (button.length) {
|
||||
if (this.hasRole('admin')) {
|
||||
const permission = buttonPermissions[buttonId];
|
||||
const hasAccess = permission.roles.some(role => this.hasRole(role));
|
||||
|
||||
if (hasAccess) {
|
||||
button.prop('disabled', false);
|
||||
button.removeAttr('title');
|
||||
} else {
|
||||
button.prop('disabled', true);
|
||||
button.attr('title', 'Требуются права администратора');
|
||||
button.attr('title', permission.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -130,11 +130,6 @@ class CatalogManager {
|
||||
|
||||
// Заполнение фильтра категорий
|
||||
populateCategoryFilters(categories) {
|
||||
if (!categories || categories.length === 0) {
|
||||
console.warn('Нет категорий для отображения');
|
||||
return;
|
||||
}
|
||||
|
||||
const categoryFilter = $('#categoryFilter');
|
||||
const addItemCategory = $('#itemCategory');
|
||||
const editItemCategory = $('#editItemCategory');
|
||||
@ -144,15 +139,23 @@ class CatalogManager {
|
||||
addItemCategory.find('option:not(:first)').remove();
|
||||
editItemCategory.find('option:not(:first)').remove();
|
||||
|
||||
// Добавляем опцию "Без категории"
|
||||
const uncategorizedOption = `<option value="uncategorized">Без категории</option>`;
|
||||
categoryFilter.append(uncategorizedOption);
|
||||
addItemCategory.append(uncategorizedOption);
|
||||
editItemCategory.append(uncategorizedOption);
|
||||
|
||||
// Добавляем категории
|
||||
categories.forEach(category => {
|
||||
if (category && category.id && category.name) {
|
||||
const option = `<option value="${category.id}">${category.name}</option>`;
|
||||
categoryFilter.append(option);
|
||||
addItemCategory.append(option);
|
||||
editItemCategory.append(option);
|
||||
}
|
||||
});
|
||||
if (categories && categories.length > 0) {
|
||||
categories.forEach(category => {
|
||||
if (category && category.id && category.name) {
|
||||
const option = `<option value="${category.id}">${category.name}</option>`;
|
||||
categoryFilter.append(option);
|
||||
addItemCategory.append(option);
|
||||
editItemCategory.append(option);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Обработка поиска
|
||||
@ -441,8 +444,8 @@ class CatalogManager {
|
||||
|
||||
// Получение информации о категории
|
||||
getCategoryInfo(categoryId) {
|
||||
if (!categoryId) {
|
||||
return { id: 'unknown', name: 'Без категории', icon: 'fas fa-box' };
|
||||
if (!categoryId || categoryId === 'uncategorized') {
|
||||
return { id: 'uncategorized', name: 'Без категории', icon: 'fas fa-box' };
|
||||
}
|
||||
|
||||
// Используем категории из warehouse
|
||||
@ -723,6 +726,9 @@ class CatalogManager {
|
||||
$('#profileDropdown').hide();
|
||||
$('#adminDropdown').hide();
|
||||
}
|
||||
|
||||
// Обновляем права доступа для элементов интерфейса
|
||||
auth.updateButtonPermissions();
|
||||
}
|
||||
|
||||
// Загрузка категорий в навигацию
|
||||
@ -755,18 +761,8 @@ class CatalogManager {
|
||||
|
||||
// Обновление прав доступа
|
||||
updatePermissions() {
|
||||
const user = auth.getCurrentUser();
|
||||
if (!user) return;
|
||||
|
||||
// Показываем кнопку добавления для администраторов и менеджеров
|
||||
if (auth.hasRole('admin') || auth.hasRole('manager')) {
|
||||
$('#addItemBtn').show();
|
||||
} else {
|
||||
$('#addItemBtn').hide();
|
||||
}
|
||||
|
||||
// Показываем кнопку экспорта для всех авторизованных пользователей
|
||||
$('#exportBtn').show();
|
||||
// Используем централизованную систему управления правами из auth.js
|
||||
auth.updateButtonPermissions();
|
||||
}
|
||||
|
||||
// Загрузка категорий в выпадающее меню
|
||||
@ -781,21 +777,60 @@ class CatalogManager {
|
||||
// Добавляем активные категории
|
||||
categories.forEach(category => {
|
||||
if (category.active) {
|
||||
const isEmpty = category.itemCount === 0;
|
||||
const categoryLink = `
|
||||
<li>
|
||||
<a class="dropdown-item" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
||||
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
||||
${category.name}
|
||||
<a class="dropdown-item ${isEmpty ? 'empty-category' : ''}" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
||||
<div class="category-info">
|
||||
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
||||
<span>${category.name}</span>
|
||||
</div>
|
||||
<span class="category-count">${category.itemCount || 0}</span>
|
||||
</a>
|
||||
</li>
|
||||
`;
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
// Обработка параметра категории из URL
|
||||
handleCategoryFromUrl() {
|
||||
|
||||
@ -350,13 +350,22 @@ class CategoryManager {
|
||||
$('#currentUserInfo').text(user.name);
|
||||
$('#loginLink').hide();
|
||||
$('#profileDropdown').show();
|
||||
$('#adminDropdown').show();
|
||||
|
||||
// Показываем админ панель только для администраторов
|
||||
if (auth.hasRole('admin')) {
|
||||
$('#adminDropdown').show();
|
||||
} else {
|
||||
$('#adminDropdown').hide();
|
||||
}
|
||||
} else {
|
||||
$('#currentUserInfo').text('Гость');
|
||||
$('#loginLink').show();
|
||||
$('#profileDropdown').hide();
|
||||
$('#adminDropdown').hide();
|
||||
}
|
||||
|
||||
// Обновляем права доступа для элементов интерфейса
|
||||
auth.updateButtonPermissions();
|
||||
}
|
||||
|
||||
// Загрузка категорий в выпадающее меню
|
||||
@ -371,21 +380,60 @@ class CategoryManager {
|
||||
// Добавляем активные категории
|
||||
categories.forEach(category => {
|
||||
if (category.active) {
|
||||
const isEmpty = category.itemCount === 0;
|
||||
const categoryLink = `
|
||||
<li>
|
||||
<a class="dropdown-item" href="../catalog.html?category=${category.id}" title="${category.description || ''}">
|
||||
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
||||
${category.name}
|
||||
<a class="dropdown-item ${isEmpty ? 'empty-category' : ''}" href="../catalog.html?category=${category.id}" title="${category.description || ''}">
|
||||
<div class="category-info">
|
||||
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
||||
<span>${category.name}</span>
|
||||
</div>
|
||||
<span class="category-count">${category.itemCount || 0}</span>
|
||||
</a>
|
||||
</li>
|
||||
`;
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация при загрузке страницы
|
||||
|
||||
@ -658,13 +658,22 @@ class HistoryManager {
|
||||
$('#currentUserInfo').text(user.name);
|
||||
$('#loginLink').hide();
|
||||
$('#profileDropdown').show();
|
||||
$('#adminDropdown').show();
|
||||
|
||||
// Показываем админ панель только для администраторов
|
||||
if (auth.hasRole('admin')) {
|
||||
$('#adminDropdown').show();
|
||||
} else {
|
||||
$('#adminDropdown').hide();
|
||||
}
|
||||
} else {
|
||||
$('#currentUserInfo').text('Гость');
|
||||
$('#loginLink').show();
|
||||
$('#profileDropdown').hide();
|
||||
$('#adminDropdown').hide();
|
||||
}
|
||||
|
||||
// Обновляем права доступа для элементов интерфейса
|
||||
auth.updateButtonPermissions();
|
||||
}
|
||||
|
||||
// Загрузка категорий в навигацию
|
||||
@ -707,21 +716,60 @@ class HistoryManager {
|
||||
// Добавляем активные категории
|
||||
categories.forEach(category => {
|
||||
if (category.active) {
|
||||
const isEmpty = category.itemCount === 0;
|
||||
const categoryLink = `
|
||||
<li>
|
||||
<a class="dropdown-item" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
||||
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
||||
${category.name}
|
||||
<a class="dropdown-item ${isEmpty ? 'empty-category' : ''}" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
||||
<div class="category-info">
|
||||
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
||||
<span>${category.name}</span>
|
||||
</div>
|
||||
<span class="category-count">${category.itemCount || 0}</span>
|
||||
</a>
|
||||
</li>
|
||||
`;
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация менеджера истории
|
||||
|
||||
48
js/main.js
48
js/main.js
@ -494,6 +494,9 @@ class MainApp {
|
||||
$('#profileDropdown').hide();
|
||||
$('#adminDropdown').hide();
|
||||
}
|
||||
|
||||
// Обновляем права доступа для элементов интерфейса
|
||||
auth.updateButtonPermissions();
|
||||
}
|
||||
|
||||
// Загрузка категорий в навигацию
|
||||
@ -536,21 +539,60 @@ class MainApp {
|
||||
// Добавляем активные категории
|
||||
categories.forEach(category => {
|
||||
if (category.active) {
|
||||
const isEmpty = category.itemCount === 0;
|
||||
const categoryLink = `
|
||||
<li>
|
||||
<a class="dropdown-item" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
||||
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
||||
${category.name}
|
||||
<a class="dropdown-item ${isEmpty ? 'empty-category' : ''}" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
||||
<div class="category-info">
|
||||
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
||||
<span>${category.name}</span>
|
||||
</div>
|
||||
<span class="category-count">${category.itemCount || 0}</span>
|
||||
</a>
|
||||
</li>
|
||||
`;
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация приложения
|
||||
|
||||
@ -469,13 +469,22 @@ class ProfileManager {
|
||||
$('#currentUserInfo').text(user.name);
|
||||
$('#loginLink').hide();
|
||||
$('#profileDropdown').show();
|
||||
$('#adminDropdown').show();
|
||||
|
||||
// Показываем админ панель только для администраторов
|
||||
if (auth.hasRole('admin')) {
|
||||
$('#adminDropdown').show();
|
||||
} else {
|
||||
$('#adminDropdown').hide();
|
||||
}
|
||||
} else {
|
||||
$('#currentUserInfo').text('Гость');
|
||||
$('#loginLink').show();
|
||||
$('#profileDropdown').hide();
|
||||
$('#adminDropdown').hide();
|
||||
}
|
||||
|
||||
// Обновляем права доступа для элементов интерфейса
|
||||
auth.updateButtonPermissions();
|
||||
}
|
||||
|
||||
// Загрузка категорий в навигацию
|
||||
@ -518,21 +527,60 @@ class ProfileManager {
|
||||
// Добавляем активные категории
|
||||
categories.forEach(category => {
|
||||
if (category.active) {
|
||||
const isEmpty = category.itemCount === 0;
|
||||
const categoryLink = `
|
||||
<li>
|
||||
<a class="dropdown-item" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
||||
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
||||
${category.name}
|
||||
<a class="dropdown-item ${isEmpty ? 'empty-category' : ''}" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
||||
<div class="category-info">
|
||||
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
||||
<span>${category.name}</span>
|
||||
</div>
|
||||
<span class="category-count">${category.itemCount || 0}</span>
|
||||
</a>
|
||||
</li>
|
||||
`;
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация менеджера профиля
|
||||
|
||||
56
js/search.js
56
js/search.js
@ -705,13 +705,22 @@ class SearchManager {
|
||||
$('#currentUserInfo').text(user.name);
|
||||
$('#loginLink').hide();
|
||||
$('#profileDropdown').show();
|
||||
$('#adminDropdown').show();
|
||||
|
||||
// Показываем админ панель только для администраторов
|
||||
if (auth.hasRole('admin')) {
|
||||
$('#adminDropdown').show();
|
||||
} else {
|
||||
$('#adminDropdown').hide();
|
||||
}
|
||||
} else {
|
||||
$('#currentUserInfo').text('Гость');
|
||||
$('#loginLink').show();
|
||||
$('#profileDropdown').hide();
|
||||
$('#adminDropdown').hide();
|
||||
}
|
||||
|
||||
// Обновляем права доступа для элементов интерфейса
|
||||
auth.updateButtonPermissions();
|
||||
}
|
||||
|
||||
// Загрузка категорий в навигацию
|
||||
@ -754,21 +763,60 @@ class SearchManager {
|
||||
// Добавляем активные категории
|
||||
categories.forEach(category => {
|
||||
if (category.active) {
|
||||
const isEmpty = category.itemCount === 0;
|
||||
const categoryLink = `
|
||||
<li>
|
||||
<a class="dropdown-item" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
||||
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
||||
${category.name}
|
||||
<a class="dropdown-item ${isEmpty ? 'empty-category' : ''}" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
||||
<div class="category-info">
|
||||
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
||||
<span>${category.name}</span>
|
||||
</div>
|
||||
<span class="category-count">${category.itemCount || 0}</span>
|
||||
</a>
|
||||
</li>
|
||||
`;
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация менеджера поиска
|
||||
|
||||
153
js/warehouse.js
153
js/warehouse.js
@ -36,58 +36,7 @@ class WarehouseManager {
|
||||
|
||||
// Получение категорий по умолчанию
|
||||
getDefaultCategories() {
|
||||
return [
|
||||
{
|
||||
id: 'electronics',
|
||||
name: 'Электроника',
|
||||
description: 'Электронные компоненты и устройства',
|
||||
icon: 'fas fa-microchip',
|
||||
color: '#2c5aa0',
|
||||
active: true,
|
||||
itemCount: 0,
|
||||
createdAt: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: 'components',
|
||||
name: 'Компоненты',
|
||||
description: 'Радиодетали и электронные компоненты',
|
||||
icon: 'fas fa-cogs',
|
||||
color: '#28a745',
|
||||
active: true,
|
||||
itemCount: 0,
|
||||
createdAt: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: 'tools',
|
||||
name: 'Инструменты',
|
||||
description: 'Ручной и электроинструмент',
|
||||
icon: 'fas fa-tools',
|
||||
color: '#ffc107',
|
||||
active: true,
|
||||
itemCount: 0,
|
||||
createdAt: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: 'office',
|
||||
name: 'Офисные принадлежности',
|
||||
description: 'Канцелярские товары и офисное оборудование',
|
||||
icon: 'fas fa-briefcase',
|
||||
color: '#6c757d',
|
||||
active: true,
|
||||
itemCount: 0,
|
||||
createdAt: new Date().toISOString()
|
||||
},
|
||||
{
|
||||
id: 'furniture',
|
||||
name: 'Мебель',
|
||||
description: 'Офисная мебель и оборудование',
|
||||
icon: 'fas fa-chair',
|
||||
color: '#8b4513',
|
||||
active: true,
|
||||
itemCount: 0,
|
||||
createdAt: new Date().toISOString()
|
||||
}
|
||||
];
|
||||
return [];
|
||||
}
|
||||
|
||||
// Сохранение категорий
|
||||
@ -98,7 +47,9 @@ class WarehouseManager {
|
||||
// Обновление количества позиций в категориях
|
||||
updateCategoryItemCounts() {
|
||||
this.categories.forEach(category => {
|
||||
category.itemCount = this.items.filter(item => item.category === category.id).length;
|
||||
const itemsInCategory = this.items.filter(item => item.category === category.id);
|
||||
category.itemCount = itemsInCategory.length;
|
||||
category.totalQuantity = itemsInCategory.reduce((sum, item) => sum + (item.quantity || 0), 0);
|
||||
});
|
||||
this.saveCategories();
|
||||
}
|
||||
@ -114,78 +65,7 @@ class WarehouseManager {
|
||||
|
||||
// Получение позиций по умолчанию
|
||||
getDefaultItems() {
|
||||
return [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Ноутбук Dell Inspiron',
|
||||
category: 'electronics',
|
||||
price: 45000,
|
||||
quantity: 15,
|
||||
description: '15.6" Full HD, Intel Core i5, 8GB RAM, 256GB SSD',
|
||||
barcode: '1234567890123',
|
||||
minQuantity: 5,
|
||||
location: 'Стеллаж A-1',
|
||||
supplier: 'Dell Inc.',
|
||||
createdAt: '2025-01-15T10:00:00Z',
|
||||
updatedAt: '2025-01-15T10:00:00Z'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Принтер HP LaserJet',
|
||||
category: 'electronics',
|
||||
price: 12000,
|
||||
quantity: 8,
|
||||
description: 'Лазерный принтер, монохромный, 20 стр/мин',
|
||||
barcode: '1234567890124',
|
||||
minQuantity: 3,
|
||||
location: 'Стеллаж A-2',
|
||||
supplier: 'HP Inc.',
|
||||
createdAt: '2025-01-10T14:30:00Z',
|
||||
updatedAt: '2025-01-10T14:30:00Z'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Офисный стул',
|
||||
category: 'office',
|
||||
price: 3500,
|
||||
quantity: 25,
|
||||
description: 'Офисный стул с подлокотниками, черный',
|
||||
barcode: '1234567890125',
|
||||
minQuantity: 10,
|
||||
location: 'Стеллаж B-1',
|
||||
supplier: 'МебельСтрой',
|
||||
createdAt: '2025-01-05T09:15:00Z',
|
||||
updatedAt: '2025-01-05T09:15:00Z'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Бумага А4',
|
||||
category: 'office',
|
||||
price: 250,
|
||||
quantity: 100,
|
||||
description: 'Бумага А4, 80 г/м², 500 листов',
|
||||
barcode: '1234567890126',
|
||||
minQuantity: 20,
|
||||
location: 'Стеллаж C-1',
|
||||
supplier: 'ОфисМаркет',
|
||||
createdAt: '2025-01-12T11:45:00Z',
|
||||
updatedAt: '2025-01-12T11:45:00Z'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'Отвертка крестовая',
|
||||
category: 'tools',
|
||||
price: 150,
|
||||
quantity: 50,
|
||||
description: 'Отвертка крестовая PH2, 150мм',
|
||||
barcode: '1234567890127',
|
||||
minQuantity: 15,
|
||||
location: 'Стеллаж D-1',
|
||||
supplier: 'ИнструментСтрой',
|
||||
createdAt: '2025-01-08T16:20:00Z',
|
||||
updatedAt: '2025-01-08T16:20:00Z'
|
||||
}
|
||||
];
|
||||
return [];
|
||||
}
|
||||
|
||||
// Получение статистики
|
||||
@ -229,14 +109,14 @@ class WarehouseManager {
|
||||
const newItem = {
|
||||
id: this.getNextItemId(),
|
||||
name: itemData.itemName,
|
||||
category: itemData.itemCategory,
|
||||
category: itemData.itemCategory || 'uncategorized',
|
||||
price: parseFloat(itemData.itemPrice),
|
||||
quantity: parseInt(itemData.itemQuantity),
|
||||
description: itemData.itemDescription || '',
|
||||
barcode: itemData.itemBarcode || '',
|
||||
minQuantity: parseInt(itemData.itemQuantity) * 0.2, // 20% от начального количества
|
||||
location: 'Новый стеллаж',
|
||||
supplier: '',
|
||||
location: itemData.itemLocation || 'Новый стеллаж',
|
||||
supplier: itemData.itemSupplier || '',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString()
|
||||
};
|
||||
@ -244,6 +124,9 @@ class WarehouseManager {
|
||||
this.items.push(newItem);
|
||||
this.saveItems();
|
||||
|
||||
// Обновляем количество товаров в категории
|
||||
this.updateCategoryItemCounts();
|
||||
|
||||
// Создаем операцию прихода для новой позиции
|
||||
await this.createOperation({
|
||||
type: 'incoming',
|
||||
@ -269,6 +152,10 @@ class WarehouseManager {
|
||||
};
|
||||
|
||||
this.saveItems();
|
||||
|
||||
// Обновляем количество товаров в категории
|
||||
this.updateCategoryItemCounts();
|
||||
|
||||
return this.items[itemIndex];
|
||||
}
|
||||
|
||||
@ -281,6 +168,10 @@ class WarehouseManager {
|
||||
|
||||
this.items.splice(itemIndex, 1);
|
||||
this.saveItems();
|
||||
|
||||
// Обновляем количество товаров в категории
|
||||
this.updateCategoryItemCounts();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -298,6 +189,9 @@ class WarehouseManager {
|
||||
item.quantity += quantity;
|
||||
item.updatedAt = new Date().toISOString();
|
||||
this.saveItems();
|
||||
|
||||
// Обновляем количество товаров в категории
|
||||
this.updateCategoryItemCounts();
|
||||
|
||||
// Создаем операцию
|
||||
const operation = await this.createOperation({
|
||||
@ -330,6 +224,9 @@ class WarehouseManager {
|
||||
item.quantity -= quantity;
|
||||
item.updatedAt = new Date().toISOString();
|
||||
this.saveItems();
|
||||
|
||||
// Обновляем количество товаров в категории
|
||||
this.updateCategoryItemCounts();
|
||||
|
||||
// Создаем операцию
|
||||
const operation = await this.createOperation({
|
||||
|
||||
Reference in New Issue
Block a user