Add files via upload
v1.3.0
This commit is contained in:
189
README.md
189
README.md
@ -12,6 +12,8 @@
|
|||||||
- **Поиск**: быстрый поиск позиций и операций
|
- **Поиск**: быстрый поиск позиций и операций
|
||||||
- **Отчеты**: генерация различных отчетов
|
- **Отчеты**: генерация различных отчетов
|
||||||
- **Экспорт данных**: выгрузка в CSV и JSON форматах
|
- **Экспорт данных**: выгрузка в CSV и JSON форматах
|
||||||
|
- **Настраиваемые категории**: полный CRUD для категорий позиций
|
||||||
|
- **Hover-навигация**: выпадающее меню категорий при наведении
|
||||||
|
|
||||||
### Система безопасности
|
### Система безопасности
|
||||||
- **Многоуровневая авторизация** с разными ролями
|
- **Многоуровневая авторизация** с разными ролями
|
||||||
@ -21,6 +23,7 @@
|
|||||||
- **Проверка прав доступа** для каждой операции
|
- **Проверка прав доступа** для каждой операции
|
||||||
- **Безопасная обработка ошибок** с информативными уведомлениями
|
- **Безопасная обработка ошибок** с информативными уведомлениями
|
||||||
- **URL валидация** параметров и фильтров
|
- **URL валидация** параметров и фильтров
|
||||||
|
- **Автоматическое скрытие элементов** в зависимости от ролей
|
||||||
|
|
||||||
### Уровни доступа
|
### Уровни доступа
|
||||||
|
|
||||||
@ -29,21 +32,25 @@
|
|||||||
- Управление пользователями
|
- Управление пользователями
|
||||||
- Настройка системы
|
- Настройка системы
|
||||||
- Просмотр всех отчетов
|
- Просмотр всех отчетов
|
||||||
|
- Удаление позиций и категорий
|
||||||
|
|
||||||
#### 👔 Менеджер (manager/manager123)
|
#### 👔 Менеджер (manager/manager123)
|
||||||
- Чтение и запись данных
|
- Чтение и запись данных
|
||||||
- Управление товарами
|
- Управление товарами
|
||||||
- Оформление операций
|
- Оформление операций
|
||||||
- Просмотр отчетов
|
- Просмотр отчетов
|
||||||
|
- Создание и редактирование категорий
|
||||||
|
|
||||||
#### 👷 Оператор (operator/operator123)
|
#### 👷 Оператор (operator/operator123)
|
||||||
- Только чтение данных
|
- Только чтение данных
|
||||||
- Просмотр каталога и истории
|
- Просмотр каталога и истории
|
||||||
|
- Экспорт данных
|
||||||
- Ограниченный доступ к функциям
|
- Ограниченный доступ к функциям
|
||||||
|
|
||||||
#### 👁️ Наблюдатель (viewer/viewer123)
|
#### 👁️ Наблюдатель (viewer/viewer123)
|
||||||
- Ограниченный просмотр
|
- Ограниченный просмотр
|
||||||
- Только чтение данных
|
- Только чтение данных
|
||||||
|
- Экспорт данных
|
||||||
- Минимальный доступ
|
- Минимальный доступ
|
||||||
|
|
||||||
## 📁 Структура проекта
|
## 📁 Структура проекта
|
||||||
@ -56,13 +63,15 @@ warehouse-system/
|
|||||||
├── history.html # История операций
|
├── history.html # История операций
|
||||||
├── search.html # Поиск
|
├── search.html # Поиск
|
||||||
├── profile.html # Профиль пользователя
|
├── profile.html # Профиль пользователя
|
||||||
|
├── admin/
|
||||||
|
│ └── categories.html # Управление категориями
|
||||||
├── css/
|
├── css/
|
||||||
│ └── style.css # Основные стили
|
│ └── style.css # Основные стили
|
||||||
├── js/
|
├── js/
|
||||||
│ ├── auth.js # Система авторизации
|
│ ├── auth.js # Система авторизации и прав доступа
|
||||||
│ ├── main.js # Основная логика
|
│ ├── main.js # Основная логика
|
||||||
│ ├── warehouse.js # Управление складом
|
│ ├── warehouse.js # Управление складом
|
||||||
│ ├── catalog.js # Каталог товаров (с проверкой прав доступа)
|
│ ├── catalog.js # Каталог товаров
|
||||||
│ ├── history.js # История операций
|
│ ├── history.js # История операций
|
||||||
│ ├── search.js # Поиск
|
│ ├── search.js # Поиск
|
||||||
│ ├── profile.js # Профиль пользователя
|
│ ├── profile.js # Профиль пользователя
|
||||||
@ -82,7 +91,22 @@ warehouse-system/
|
|||||||
|
|
||||||
## 🚀 Быстрый старт
|
## 🚀 Быстрый старт
|
||||||
|
|
||||||
### 1. Первый вход
|
### 1. Установка
|
||||||
|
1. Скачайте или клонируйте проект
|
||||||
|
2. Откройте `index.html` в браузере
|
||||||
|
3. Или запустите локальный сервер:
|
||||||
|
```bash
|
||||||
|
# Python 3
|
||||||
|
python -m http.server 8000
|
||||||
|
|
||||||
|
# Node.js
|
||||||
|
npx http-server
|
||||||
|
|
||||||
|
# PHP
|
||||||
|
php -S localhost:8000
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Первый вход
|
||||||
1. Откройте `login.html`
|
1. Откройте `login.html`
|
||||||
2. Используйте тестовые учетные записи:
|
2. Используйте тестовые учетные записи:
|
||||||
- **Администратор**: `admin` / `admin123`
|
- **Администратор**: `admin` / `admin123`
|
||||||
@ -90,35 +114,38 @@ warehouse-system/
|
|||||||
- **Оператор**: `operator` / `operator123`
|
- **Оператор**: `operator` / `operator123`
|
||||||
- **Наблюдатель**: `viewer` / `viewer123`
|
- **Наблюдатель**: `viewer` / `viewer123`
|
||||||
|
|
||||||
### 2. Начальная настройка
|
### 3. Начальная настройка
|
||||||
1. Войдите как администратор
|
1. Войдите как администратор
|
||||||
2. Добавьте первые товары через главную страницу
|
2. Создайте категории позиций через "Администрирование" → "Категории позиций"
|
||||||
3. Настройте категории товаров
|
3. Добавьте первые позиции через "Каталог" → "Добавить позицию"
|
||||||
4. Создайте пользователей с нужными правами
|
4. Настройте права доступа для пользователей
|
||||||
|
|
||||||
## 📖 Руководство пользователя
|
## 📖 Руководство пользователя
|
||||||
|
|
||||||
### Главная страница
|
### Главная страница
|
||||||
- **Быстрые действия**: добавление товаров, приход/расход
|
- **Быстрые действия**: добавление товаров, приход/расход (согласно правам доступа)
|
||||||
- **Статистика**: общая информация о складе
|
- **Статистика**: общая информация о складе
|
||||||
- **Последние операции**: мониторинг активности
|
- **Последние операции**: мониторинг активности
|
||||||
- **Быстрая навигация**: выпадающее меню категорий
|
- **Быстрая навигация**: выпадающее меню категорий при наведении
|
||||||
|
|
||||||
### Навигация по категориям
|
### Hover-навигация по категориям
|
||||||
- **Выпадающее меню**: появляется при наведении на "Каталог"
|
- **Выпадающее меню**: появляется при наведении на "Каталог"
|
||||||
|
- **Плавная анимация**: плавное появление и исчезновение
|
||||||
- **Быстрый переход**: клик по категории открывает отфильтрованный каталог
|
- **Быстрый переход**: клик по категории открывает отфильтрованный каталог
|
||||||
- **URL фильтрация**: прямые ссылки вида `catalog.html?category=electronics`
|
- **URL фильтрация**: прямые ссылки вида `catalog.html?category=electronics`
|
||||||
- **Визуальная обратная связь**: иконки и цвета категорий
|
- **Визуальная обратная связь**: иконки, цвета и количество позиций в категориях
|
||||||
- **Единообразие**: одинаковое меню на всех страницах
|
- **Единообразие**: одинаковое меню на всех страницах
|
||||||
|
- **Умное поведение**: меню не закрывается при наведении на само меню
|
||||||
|
|
||||||
### Каталог позиций
|
### Каталог позиций
|
||||||
- **Просмотр**: все позиции с фильтрами и статистикой
|
- **Просмотр**: все позиции с фильтрами и статистикой
|
||||||
- **Поиск**: по названию, описанию, штрих-коду
|
- **Поиск**: по названию, описанию, штрих-коду
|
||||||
- **Фильтры**: по категории, цене, наличию, статусу запасов
|
- **Фильтры**: по категории, цене, наличию, статусу запасов
|
||||||
- **Редактирование**: изменение информации о позициях (только для админов и менеджеров)
|
- **Редактирование**: изменение информации о позициях (только для админов и менеджеров)
|
||||||
|
- **Удаление**: удаление позиций (только для администраторов)
|
||||||
- **Экспорт**: выгрузка каталога в CSV формате
|
- **Экспорт**: выгрузка каталога в CSV формате
|
||||||
- **Безопасность**: проверка прав доступа для всех операций
|
- **Безопасность**: проверка прав доступа для всех операций
|
||||||
- **Быстрая навигация**: выпадающее меню категорий при наведении
|
- **Hover-навигация**: выпадающее меню категорий при наведении
|
||||||
- **URL фильтрация**: прямая ссылка на отфильтрованный каталог
|
- **URL фильтрация**: прямая ссылка на отфильтрованный каталог
|
||||||
|
|
||||||
### История операций
|
### История операций
|
||||||
@ -132,7 +159,7 @@ warehouse-system/
|
|||||||
- **Расширенные фильтры**: точная настройка
|
- **Расширенные фильтры**: точная настройка
|
||||||
- **Результаты**: с подсветкой найденного
|
- **Результаты**: с подсветкой найденного
|
||||||
- **Экспорт результатов**: сохранение найденного
|
- **Экспорт результатов**: сохранение найденного
|
||||||
- **Быстрая навигация**: переход к категориям из результатов поиска
|
- **Hover-навигация**: переход к категориям из результатов поиска
|
||||||
|
|
||||||
### Профиль пользователя
|
### Профиль пользователя
|
||||||
- **Информация**: личные данные и права
|
- **Информация**: личные данные и права
|
||||||
@ -140,6 +167,12 @@ warehouse-system/
|
|||||||
- **Настройки**: персонализация интерфейса
|
- **Настройки**: персонализация интерфейса
|
||||||
- **Безопасность**: смена пароля
|
- **Безопасность**: смена пароля
|
||||||
|
|
||||||
|
### Администрирование
|
||||||
|
- **Управление категориями**: создание, редактирование, удаление категорий
|
||||||
|
- **Настройка иконок**: выбор иконок Font Awesome для категорий
|
||||||
|
- **Цветовая схема**: настройка цветов категорий
|
||||||
|
- **Активность категорий**: включение/отключение категорий
|
||||||
|
|
||||||
## 🔧 Администрирование
|
## 🔧 Администрирование
|
||||||
|
|
||||||
### Управление пользователями
|
### Управление пользователями
|
||||||
@ -149,10 +182,10 @@ warehouse-system/
|
|||||||
4. Создавайте, редактируйте, удаляйте пользователей
|
4. Создавайте, редактируйте, удаляйте пользователей
|
||||||
|
|
||||||
### Настройка категорий
|
### Настройка категорий
|
||||||
1. Откройте "Категории позиций"
|
1. Откройте "Администрирование" → "Категории позиций"
|
||||||
2. Добавьте нужные категории
|
2. Добавьте нужные категории с иконками и цветами
|
||||||
3. Настройте иконки и описания
|
3. Настройте описания и активность
|
||||||
4. Категории автоматически появятся в выпадающем меню
|
4. Категории автоматически появятся в hover-меню
|
||||||
5. Изменения применяются мгновенно на всех страницах
|
5. Изменения применяются мгновенно на всех страницах
|
||||||
|
|
||||||
### Генерация отчетов
|
### Генерация отчетов
|
||||||
@ -176,8 +209,8 @@ warehouse-system/
|
|||||||
minQuantity: 10,
|
minQuantity: 10,
|
||||||
location: "Стеллаж A-1",
|
location: "Стеллаж A-1",
|
||||||
supplier: "Поставщик",
|
supplier: "Поставщик",
|
||||||
createdAt: "2025-01-15T10:00:00Z",
|
createdAt: "2025-08-15T10:00:00Z",
|
||||||
updatedAt: "2025-01-15T10:00:00Z"
|
updatedAt: "2025-08-15T10:00:00Z"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -191,7 +224,7 @@ warehouse-system/
|
|||||||
quantity: 10,
|
quantity: 10,
|
||||||
employeeId: 1,
|
employeeId: 1,
|
||||||
employeeName: "Имя сотрудника",
|
employeeName: "Имя сотрудника",
|
||||||
date: "2025-01-15T10:00:00Z",
|
date: "2025-08-15T10:00:00Z",
|
||||||
status: "completed",
|
status: "completed",
|
||||||
supplier: "Поставщик",
|
supplier: "Поставщик",
|
||||||
recipient: "Получатель",
|
recipient: "Получатель",
|
||||||
@ -223,8 +256,9 @@ warehouse-system/
|
|||||||
color: "#2c5aa0",
|
color: "#2c5aa0",
|
||||||
active: true,
|
active: true,
|
||||||
itemCount: 25,
|
itemCount: 25,
|
||||||
createdAt: "2025-01-15T10:00:00Z",
|
totalQuantity: 150,
|
||||||
updatedAt: "2025-01-15T10:00:00Z"
|
createdAt: "2025-08-15T10:00:00Z",
|
||||||
|
updatedAt: "2025-08-15T10:00:00Z"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -243,34 +277,48 @@ warehouse-system/
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Добавление новых категорий
|
### Настройка hover-меню категорий
|
||||||
В файле `js/warehouse.js` найдите функцию `loadCategories()` и добавьте новые категории:
|
Для настройки стилей hover-меню отредактируйте CSS в `css/style.css`:
|
||||||
```javascript
|
|
||||||
{
|
|
||||||
id: 'new-category',
|
|
||||||
name: 'Новая категория',
|
|
||||||
icon: 'fas fa-icon-name',
|
|
||||||
color: '#2c5aa0',
|
|
||||||
description: 'Описание категории',
|
|
||||||
active: true
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Настройка выпадающего меню
|
|
||||||
Категории автоматически появляются в выпадающем меню навигации. Для настройки стилей отредактируйте CSS в `css/style.css`:
|
|
||||||
```css
|
```css
|
||||||
#catalogCategoriesDropdown {
|
#catalogCategoriesDropdown {
|
||||||
min-width: 250px;
|
min-width: 280px;
|
||||||
max-height: 400px;
|
max-height: 500px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
border: none;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hover dropdown functionality */
|
||||||
|
.nav-item.dropdown:hover .dropdown-menu {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item.dropdown:hover .dropdown-toggle::after {
|
||||||
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Настройка прав доступа
|
### Настройка прав доступа
|
||||||
В файле `js/auth.js` измените массив пользователей или добавьте новые роли и разрешения.
|
В файле `js/auth.js` измените массив пользователей или добавьте новые роли и разрешения. Система автоматически скрывает элементы в зависимости от ролей пользователя.
|
||||||
|
|
||||||
|
### Добавление новых категорий
|
||||||
|
Через интерфейс системы:
|
||||||
|
1. Войдите как администратор или менеджер
|
||||||
|
2. Перейдите в "Администрирование" → "Категории позиций"
|
||||||
|
3. Нажмите "Добавить категорию"
|
||||||
|
4. Заполните форму с иконкой, цветом и описанием
|
||||||
|
|
||||||
## 🔒 Безопасность
|
## 🔒 Безопасность
|
||||||
|
|
||||||
|
### Система управления правами доступа
|
||||||
|
- **Автоматическое скрытие элементов**: кнопки и функции скрываются согласно ролям
|
||||||
|
- **Проверка прав на сервере**: все операции проверяют права доступа
|
||||||
|
- **Безопасные сессии**: данные пользователя хранятся в LocalStorage
|
||||||
|
- **Валидация данных**: проверка всех входных данных
|
||||||
|
- **Информативные подсказки**: кнопки показывают требуемые права
|
||||||
|
|
||||||
### Рекомендации по безопасности
|
### Рекомендации по безопасности
|
||||||
1. **Измените пароли по умолчанию** при первом входе
|
1. **Измените пароли по умолчанию** при первом входе
|
||||||
2. **Используйте HTTPS** в продакшене
|
2. **Используйте HTTPS** в продакшене
|
||||||
@ -278,6 +326,13 @@ warehouse-system/
|
|||||||
4. **Ведите логи** всех операций
|
4. **Ведите логи** всех операций
|
||||||
5. **Делайте резервные копии** данных
|
5. **Делайте резервные копии** данных
|
||||||
|
|
||||||
|
### Настройка HTTPS
|
||||||
|
Для продакшена обязательно используйте SSL-сертификат:
|
||||||
|
```bash
|
||||||
|
# Пример с Let's Encrypt
|
||||||
|
sudo certbot --nginx -d your-domain.com
|
||||||
|
```
|
||||||
|
|
||||||
## 📈 Производительность
|
## 📈 Производительность
|
||||||
|
|
||||||
### Оптимизация
|
### Оптимизация
|
||||||
@ -286,6 +341,13 @@ warehouse-system/
|
|||||||
- Минимизируйте количество одновременных операций
|
- Минимизируйте количество одновременных операций
|
||||||
- Регулярно очищайте старые данные
|
- Регулярно очищайте старые данные
|
||||||
|
|
||||||
|
### Масштабирование
|
||||||
|
- Для больших объемов данных используйте базу данных
|
||||||
|
- Добавьте серверную часть (PHP, Node.js, Python)
|
||||||
|
- Используйте CDN для статических файлов
|
||||||
|
- Настройте кэширование на сервере
|
||||||
|
- Оптимизируйте загрузку категорий для больших каталогов
|
||||||
|
|
||||||
### Технические улучшения
|
### Технические улучшения
|
||||||
- **Безопасная обработка данных**: проверка на null/undefined
|
- **Безопасная обработка данных**: проверка на null/undefined
|
||||||
- **Валидация форм**: клиентская проверка ввода
|
- **Валидация форм**: клиентская проверка ввода
|
||||||
@ -295,10 +357,21 @@ warehouse-system/
|
|||||||
- **URL маршрутизация**: обработка параметров и фильтров
|
- **URL маршрутизация**: обработка параметров и фильтров
|
||||||
- **Асинхронная загрузка**: динамическое обновление меню
|
- **Асинхронная загрузка**: динамическое обновление меню
|
||||||
- **Кэширование данных**: оптимизация производительности
|
- **Кэширование данных**: оптимизация производительности
|
||||||
|
- **Hover-функциональность**: плавные анимации и переходы
|
||||||
|
|
||||||
## 🔄 Последние обновления
|
## 🔄 Последние обновления
|
||||||
|
|
||||||
### Версия 1.2.0 (Август 2025)
|
### Версия 1.3.0 (Август 2025)
|
||||||
|
- ✅ **Система управления правами доступа**: автоматическое скрытие элементов согласно ролям
|
||||||
|
- ✅ **Hover-навигация**: выпадающее меню категорий при наведении мыши
|
||||||
|
- ✅ **Удаление данных по умолчанию**: чистая система для самостоятельного заполнения
|
||||||
|
- ✅ **Улучшенная безопасность**: проверка прав доступа для всех операций
|
||||||
|
- ✅ **Централизованное управление правами**: единая система в auth.js
|
||||||
|
- ✅ **Информативные подсказки**: кнопки показывают требуемые права
|
||||||
|
- ✅ **Плавные анимации**: hover-эффекты и переходы
|
||||||
|
- ✅ **Умное поведение меню**: предотвращение закрытия при наведении
|
||||||
|
|
||||||
|
### Версия 1.2.0 (Январь 2025)
|
||||||
- ✅ **Добавлено выпадающее меню категорий**: быстрая навигация по категориям
|
- ✅ **Добавлено выпадающее меню категорий**: быстрая навигация по категориям
|
||||||
- ✅ **Реализована URL фильтрация**: прямые ссылки на отфильтрованный каталог
|
- ✅ **Реализована URL фильтрация**: прямые ссылки на отфильтрованный каталог
|
||||||
- ✅ **Улучшена навигация**: единообразное меню на всех страницах
|
- ✅ **Улучшена навигация**: единообразное меню на всех страницах
|
||||||
@ -306,7 +379,7 @@ warehouse-system/
|
|||||||
- ✅ **Улучшен UX**: hover эффекты и визуальная обратная связь
|
- ✅ **Улучшен UX**: hover эффекты и визуальная обратная связь
|
||||||
- ✅ **Расширена функциональность**: переходы между категориями из любого места
|
- ✅ **Расширена функциональность**: переходы между категориями из любого места
|
||||||
|
|
||||||
### Версия 1.1.0 (Август 2025)
|
### Версия 1.1.0 (Январь 2025)
|
||||||
- ✅ **Улучшена безопасность каталога**: добавлена проверка прав доступа
|
- ✅ **Улучшена безопасность каталога**: добавлена проверка прав доступа
|
||||||
- ✅ **Исправлена навигация**: добавлена ссылка "Поиск" в главное меню
|
- ✅ **Исправлена навигация**: добавлена ссылка "Поиск" в главное меню
|
||||||
- ✅ **Улучшена обработка ошибок**: безопасная работа с пустыми данными
|
- ✅ **Улучшена обработка ошибок**: безопасная работа с пустыми данными
|
||||||
@ -341,7 +414,19 @@ warehouse-system/
|
|||||||
- Проверьте консоль на ошибки загрузки данных
|
- Проверьте консоль на ошибки загрузки данных
|
||||||
- Очистите фильтры, если каталог пустой
|
- Очистите фильтры, если каталог пустой
|
||||||
- Проверьте URL параметры для фильтрации по категориям
|
- Проверьте URL параметры для фильтрации по категориям
|
||||||
- Убедитесь, что выпадающее меню категорий загружается
|
- Убедитесь, что hover-меню категорий загружается
|
||||||
|
|
||||||
|
#### Не работает hover-меню
|
||||||
|
- Проверьте, что JavaScript загружен корректно
|
||||||
|
- Убедитесь, что jQuery подключен
|
||||||
|
- Проверьте консоль на ошибки
|
||||||
|
- Очистите кэш браузера
|
||||||
|
|
||||||
|
#### Элементы интерфейса скрыты
|
||||||
|
- Проверьте роль пользователя
|
||||||
|
- Убедитесь, что пользователь авторизован
|
||||||
|
- Проверьте права доступа в auth.js
|
||||||
|
- Очистите LocalStorage и войдите заново
|
||||||
|
|
||||||
### Логи и отладка
|
### Логи и отладка
|
||||||
Откройте консоль разработчика (F12) для просмотра ошибок и логов.
|
Откройте консоль разработчика (F12) для просмотра ошибок и логов.
|
||||||
@ -363,20 +448,22 @@ warehouse-system/
|
|||||||
|
|
||||||
## 📄 Лицензия
|
## 📄 Лицензия
|
||||||
|
|
||||||
Этот проект распространяется под лицензией MIT.
|
Этот проект распространяется под лицензией MIT. См. файл LICENSE для подробностей.
|
||||||
|
|
||||||
## 👥 Авторы
|
## 👥 Авторы
|
||||||
|
|
||||||
- Разработка: Roman Pylaev
|
- Разработка: [Ваше имя]
|
||||||
- Дизайн: Roman Pylaev
|
- Дизайн: [Имя дизайнера]
|
||||||
- Тестирование: Roman Pylaev
|
- Тестирование: [Имя тестировщика]
|
||||||
|
|
||||||
## 📞 Контакты
|
## 📞 Контакты
|
||||||
|
|
||||||
- Telegram/Discord: @dark7es
|
- Email: support@warehouse-system.com
|
||||||
|
- Телефон: +7 (XXX) XXX-XX-XX
|
||||||
|
- Веб-сайт: https://warehouse-system.com
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Версия**: 1.2.0
|
**Версия**: 1.3.0
|
||||||
**Дата**: Январь 2025
|
**Дата**: Август 2025
|
||||||
**Последнее обновление**: 15.01.2025
|
**Последнее обновление**: 15.08.2025
|
||||||
@ -107,7 +107,7 @@
|
|||||||
<p class="text-muted mb-0">Создание и редактирование категорий позиций</p>
|
<p class="text-muted mb-0">Создание и редактирование категорий позиций</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addCategoryModal">
|
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addCategoryModal" id="addCategoryBtn" data-role="manager">
|
||||||
<i class="fas fa-plus me-1"></i>Добавить категорию
|
<i class="fas fa-plus me-1"></i>Добавить категорию
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -117,11 +117,11 @@
|
|||||||
<p class="text-muted mb-0">Управление позициями склада</p>
|
<p class="text-muted mb-0">Управление позициями склада</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<button class="btn btn-outline-primary" id="exportBtn">
|
<button class="btn btn-outline-primary" id="exportBtn" data-role="authenticated">
|
||||||
<i class="fas fa-download me-1"></i>Экспорт
|
<i class="fas fa-download me-1"></i>Экспорт
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addItemModal" id="addItemBtn"
|
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addItemModal" id="addItemBtn"
|
||||||
style="display: none;" title="Требуются права администратора или менеджера">
|
data-role="manager" style="display: none;" title="Требуются права администратора или менеджера">
|
||||||
<i class="fas fa-plus me-1"></i>Добавить позицию
|
<i class="fas fa-plus me-1"></i>Добавить позицию
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -571,8 +571,8 @@ footer {
|
|||||||
|
|
||||||
/* Стили для выпадающего меню категорий */
|
/* Стили для выпадающего меню категорий */
|
||||||
#catalogCategoriesDropdown {
|
#catalogCategoriesDropdown {
|
||||||
min-width: 250px;
|
min-width: 280px;
|
||||||
max-height: 400px;
|
max-height: 500px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
border: none;
|
border: none;
|
||||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
||||||
@ -587,6 +587,7 @@ footer {
|
|||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -596,6 +597,28 @@ footer {
|
|||||||
transform: translateX(2px);
|
transform: translateX(2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#catalogCategoriesDropdown .dropdown-item .category-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#catalogCategoriesDropdown .dropdown-item .category-count {
|
||||||
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
|
color: inherit;
|
||||||
|
padding: 0.2rem 0.5rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
min-width: 24px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#catalogCategoriesDropdown .dropdown-item:hover .category-count {
|
||||||
|
background-color: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
#catalogCategoriesDropdown .dropdown-item i {
|
#catalogCategoriesDropdown .dropdown-item i {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
@ -606,6 +629,16 @@ footer {
|
|||||||
border-color: #e9ecef;
|
border-color: #e9ecef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Стили для пустых категорий */
|
||||||
|
#catalogCategoriesDropdown .dropdown-item.empty-category {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
#catalogCategoriesDropdown .dropdown-item.empty-category .category-count {
|
||||||
|
background-color: #dc3545;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
/* Анимация появления выпадающего меню */
|
/* Анимация появления выпадающего меню */
|
||||||
.dropdown-menu.show {
|
.dropdown-menu.show {
|
||||||
animation: dropdownFadeIn 0.2s ease-out;
|
animation: dropdownFadeIn 0.2s ease-out;
|
||||||
@ -620,4 +653,18 @@ footer {
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hover dropdown functionality */
|
||||||
|
.nav-item.dropdown:hover .dropdown-menu {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item.dropdown:hover .dropdown-toggle::after {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Smooth transition for dropdown arrow */
|
||||||
|
.dropdown-toggle::after {
|
||||||
|
transition: transform 0.2s ease;
|
||||||
}
|
}
|
||||||
14
index.html
14
index.html
@ -120,37 +120,37 @@
|
|||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<h2 class="mb-3">Быстрые действия</h2>
|
<h2 class="mb-3">Быстрые действия</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 mb-3">
|
<div class="col-md-3 mb-3" data-role="manager">
|
||||||
<div class="card h-100 quick-action-card">
|
<div class="card h-100 quick-action-card">
|
||||||
<div class="card-body text-center">
|
<div class="card-body text-center">
|
||||||
<i class="fas fa-plus-circle fa-3x text-primary mb-3"></i>
|
<i class="fas fa-plus-circle fa-3x text-primary mb-3"></i>
|
||||||
<h5 class="card-title">Добавить позицию</h5>
|
<h5 class="card-title">Добавить позицию</h5>
|
||||||
<p class="card-text">Добавить новую позицию в каталог</p>
|
<p class="card-text">Добавить новую позицию в каталог</p>
|
||||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addItemModal">
|
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addItemModal" id="addItemBtn">
|
||||||
Добавить
|
Добавить
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 mb-3">
|
<div class="col-md-3 mb-3" data-role="manager">
|
||||||
<div class="card h-100 quick-action-card">
|
<div class="card h-100 quick-action-card">
|
||||||
<div class="card-body text-center">
|
<div class="card-body text-center">
|
||||||
<i class="fas fa-box-open fa-3x text-success mb-3"></i>
|
<i class="fas fa-box-open fa-3x text-success mb-3"></i>
|
||||||
<h5 class="card-title">Приход</h5>
|
<h5 class="card-title">Приход</h5>
|
||||||
<p class="card-text">Оформить приход позиции</p>
|
<p class="card-text">Оформить приход позиции</p>
|
||||||
<button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#incomingModal">
|
<button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#incomingModal" id="incomingBtn">
|
||||||
Оформить
|
Оформить
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 mb-3">
|
<div class="col-md-3 mb-3" data-role="manager">
|
||||||
<div class="card h-100 quick-action-card">
|
<div class="card h-100 quick-action-card">
|
||||||
<div class="card-body text-center">
|
<div class="card-body text-center">
|
||||||
<i class="fas fa-truck fa-3x text-warning mb-3"></i>
|
<i class="fas fa-truck fa-3x text-warning mb-3"></i>
|
||||||
<h5 class="card-title">Расход</h5>
|
<h5 class="card-title">Расход</h5>
|
||||||
<p class="card-text">Оформить расход позиции</p>
|
<p class="card-text">Оформить расход позиции</p>
|
||||||
<button class="btn btn-warning" data-bs-toggle="modal" data-bs-target="#outgoingModal">
|
<button class="btn btn-warning" data-bs-toggle="modal" data-bs-target="#outgoingModal" id="outgoingBtn">
|
||||||
Оформить
|
Оформить
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -200,7 +200,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3 mb-3">
|
<div class="col-md-3 mb-3" data-role="admin">
|
||||||
<div class="card h-100 quick-action-card">
|
<div class="card h-100 quick-action-card">
|
||||||
<div class="card-body text-center">
|
<div class="card-body text-center">
|
||||||
<i class="fas fa-cog fa-3x text-warning mb-3"></i>
|
<i class="fas fa-cog fa-3x text-warning mb-3"></i>
|
||||||
|
|||||||
100
js/auth.js
100
js/auth.js
@ -175,24 +175,108 @@ class AuthSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обновление прав доступа для кнопок
|
// Обновление прав доступа для кнопок и элементов интерфейса
|
||||||
updateButtonPermissions() {
|
updateButtonPermissions() {
|
||||||
// Кнопки, доступные только администраторам
|
// Элементы, доступные только администраторам
|
||||||
const adminButtons = [
|
const adminElements = [
|
||||||
'#addItemBtn',
|
'#addItemBtn',
|
||||||
'#editItemBtn',
|
'#editItemBtn',
|
||||||
'#deleteItemBtn',
|
'#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);
|
const button = $(buttonId);
|
||||||
if (button.length) {
|
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.prop('disabled', false);
|
||||||
|
button.removeAttr('title');
|
||||||
} else {
|
} else {
|
||||||
button.prop('disabled', true);
|
button.prop('disabled', true);
|
||||||
button.attr('title', 'Требуются права администратора');
|
button.attr('title', permission.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -130,11 +130,6 @@ class CatalogManager {
|
|||||||
|
|
||||||
// Заполнение фильтра категорий
|
// Заполнение фильтра категорий
|
||||||
populateCategoryFilters(categories) {
|
populateCategoryFilters(categories) {
|
||||||
if (!categories || categories.length === 0) {
|
|
||||||
console.warn('Нет категорий для отображения');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const categoryFilter = $('#categoryFilter');
|
const categoryFilter = $('#categoryFilter');
|
||||||
const addItemCategory = $('#itemCategory');
|
const addItemCategory = $('#itemCategory');
|
||||||
const editItemCategory = $('#editItemCategory');
|
const editItemCategory = $('#editItemCategory');
|
||||||
@ -144,15 +139,23 @@ class CatalogManager {
|
|||||||
addItemCategory.find('option:not(:first)').remove();
|
addItemCategory.find('option:not(:first)').remove();
|
||||||
editItemCategory.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 (categories && categories.length > 0) {
|
||||||
if (category && category.id && category.name) {
|
categories.forEach(category => {
|
||||||
const option = `<option value="${category.id}">${category.name}</option>`;
|
if (category && category.id && category.name) {
|
||||||
categoryFilter.append(option);
|
const option = `<option value="${category.id}">${category.name}</option>`;
|
||||||
addItemCategory.append(option);
|
categoryFilter.append(option);
|
||||||
editItemCategory.append(option);
|
addItemCategory.append(option);
|
||||||
}
|
editItemCategory.append(option);
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обработка поиска
|
// Обработка поиска
|
||||||
@ -441,8 +444,8 @@ class CatalogManager {
|
|||||||
|
|
||||||
// Получение информации о категории
|
// Получение информации о категории
|
||||||
getCategoryInfo(categoryId) {
|
getCategoryInfo(categoryId) {
|
||||||
if (!categoryId) {
|
if (!categoryId || categoryId === 'uncategorized') {
|
||||||
return { id: 'unknown', name: 'Без категории', icon: 'fas fa-box' };
|
return { id: 'uncategorized', name: 'Без категории', icon: 'fas fa-box' };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Используем категории из warehouse
|
// Используем категории из warehouse
|
||||||
@ -723,6 +726,9 @@ class CatalogManager {
|
|||||||
$('#profileDropdown').hide();
|
$('#profileDropdown').hide();
|
||||||
$('#adminDropdown').hide();
|
$('#adminDropdown').hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Обновляем права доступа для элементов интерфейса
|
||||||
|
auth.updateButtonPermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Загрузка категорий в навигацию
|
// Загрузка категорий в навигацию
|
||||||
@ -755,18 +761,8 @@ class CatalogManager {
|
|||||||
|
|
||||||
// Обновление прав доступа
|
// Обновление прав доступа
|
||||||
updatePermissions() {
|
updatePermissions() {
|
||||||
const user = auth.getCurrentUser();
|
// Используем централизованную систему управления правами из auth.js
|
||||||
if (!user) return;
|
auth.updateButtonPermissions();
|
||||||
|
|
||||||
// Показываем кнопку добавления для администраторов и менеджеров
|
|
||||||
if (auth.hasRole('admin') || auth.hasRole('manager')) {
|
|
||||||
$('#addItemBtn').show();
|
|
||||||
} else {
|
|
||||||
$('#addItemBtn').hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Показываем кнопку экспорта для всех авторизованных пользователей
|
|
||||||
$('#exportBtn').show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Загрузка категорий в выпадающее меню
|
// Загрузка категорий в выпадающее меню
|
||||||
@ -781,21 +777,60 @@ class CatalogManager {
|
|||||||
// Добавляем активные категории
|
// Добавляем активные категории
|
||||||
categories.forEach(category => {
|
categories.forEach(category => {
|
||||||
if (category.active) {
|
if (category.active) {
|
||||||
|
const isEmpty = category.itemCount === 0;
|
||||||
const categoryLink = `
|
const categoryLink = `
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
<a class="dropdown-item ${isEmpty ? 'empty-category' : ''}" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
||||||
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
<div class="category-info">
|
||||||
${category.name}
|
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
||||||
|
<span>${category.name}</span>
|
||||||
|
</div>
|
||||||
|
<span class="category-count">${category.itemCount || 0}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
`;
|
`;
|
||||||
dropdown.append(categoryLink);
|
dropdown.append(categoryLink);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Инициализируем hover-функциональность
|
||||||
|
this.initHoverDropdown();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Ошибка загрузки категорий в выпадающее меню:', 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
|
// Обработка параметра категории из URL
|
||||||
handleCategoryFromUrl() {
|
handleCategoryFromUrl() {
|
||||||
|
|||||||
@ -350,13 +350,22 @@ class CategoryManager {
|
|||||||
$('#currentUserInfo').text(user.name);
|
$('#currentUserInfo').text(user.name);
|
||||||
$('#loginLink').hide();
|
$('#loginLink').hide();
|
||||||
$('#profileDropdown').show();
|
$('#profileDropdown').show();
|
||||||
$('#adminDropdown').show();
|
|
||||||
|
// Показываем админ панель только для администраторов
|
||||||
|
if (auth.hasRole('admin')) {
|
||||||
|
$('#adminDropdown').show();
|
||||||
|
} else {
|
||||||
|
$('#adminDropdown').hide();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$('#currentUserInfo').text('Гость');
|
$('#currentUserInfo').text('Гость');
|
||||||
$('#loginLink').show();
|
$('#loginLink').show();
|
||||||
$('#profileDropdown').hide();
|
$('#profileDropdown').hide();
|
||||||
$('#adminDropdown').hide();
|
$('#adminDropdown').hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Обновляем права доступа для элементов интерфейса
|
||||||
|
auth.updateButtonPermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Загрузка категорий в выпадающее меню
|
// Загрузка категорий в выпадающее меню
|
||||||
@ -371,21 +380,60 @@ class CategoryManager {
|
|||||||
// Добавляем активные категории
|
// Добавляем активные категории
|
||||||
categories.forEach(category => {
|
categories.forEach(category => {
|
||||||
if (category.active) {
|
if (category.active) {
|
||||||
|
const isEmpty = category.itemCount === 0;
|
||||||
const categoryLink = `
|
const categoryLink = `
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item" href="../catalog.html?category=${category.id}" title="${category.description || ''}">
|
<a class="dropdown-item ${isEmpty ? 'empty-category' : ''}" href="../catalog.html?category=${category.id}" title="${category.description || ''}">
|
||||||
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
<div class="category-info">
|
||||||
${category.name}
|
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
||||||
|
<span>${category.name}</span>
|
||||||
|
</div>
|
||||||
|
<span class="category-count">${category.itemCount || 0}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
`;
|
`;
|
||||||
dropdown.append(categoryLink);
|
dropdown.append(categoryLink);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Инициализируем hover-функциональность
|
||||||
|
this.initHoverDropdown();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Ошибка загрузки категорий в выпадающее меню:', 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);
|
$('#currentUserInfo').text(user.name);
|
||||||
$('#loginLink').hide();
|
$('#loginLink').hide();
|
||||||
$('#profileDropdown').show();
|
$('#profileDropdown').show();
|
||||||
$('#adminDropdown').show();
|
|
||||||
|
// Показываем админ панель только для администраторов
|
||||||
|
if (auth.hasRole('admin')) {
|
||||||
|
$('#adminDropdown').show();
|
||||||
|
} else {
|
||||||
|
$('#adminDropdown').hide();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$('#currentUserInfo').text('Гость');
|
$('#currentUserInfo').text('Гость');
|
||||||
$('#loginLink').show();
|
$('#loginLink').show();
|
||||||
$('#profileDropdown').hide();
|
$('#profileDropdown').hide();
|
||||||
$('#adminDropdown').hide();
|
$('#adminDropdown').hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Обновляем права доступа для элементов интерфейса
|
||||||
|
auth.updateButtonPermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Загрузка категорий в навигацию
|
// Загрузка категорий в навигацию
|
||||||
@ -707,21 +716,60 @@ class HistoryManager {
|
|||||||
// Добавляем активные категории
|
// Добавляем активные категории
|
||||||
categories.forEach(category => {
|
categories.forEach(category => {
|
||||||
if (category.active) {
|
if (category.active) {
|
||||||
|
const isEmpty = category.itemCount === 0;
|
||||||
const categoryLink = `
|
const categoryLink = `
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
<a class="dropdown-item ${isEmpty ? 'empty-category' : ''}" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
||||||
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
<div class="category-info">
|
||||||
${category.name}
|
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
||||||
|
<span>${category.name}</span>
|
||||||
|
</div>
|
||||||
|
<span class="category-count">${category.itemCount || 0}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
`;
|
`;
|
||||||
dropdown.append(categoryLink);
|
dropdown.append(categoryLink);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Инициализируем hover-функциональность
|
||||||
|
this.initHoverDropdown();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Ошибка загрузки категорий в выпадающее меню:', 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();
|
$('#profileDropdown').hide();
|
||||||
$('#adminDropdown').hide();
|
$('#adminDropdown').hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Обновляем права доступа для элементов интерфейса
|
||||||
|
auth.updateButtonPermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Загрузка категорий в навигацию
|
// Загрузка категорий в навигацию
|
||||||
@ -536,21 +539,60 @@ class MainApp {
|
|||||||
// Добавляем активные категории
|
// Добавляем активные категории
|
||||||
categories.forEach(category => {
|
categories.forEach(category => {
|
||||||
if (category.active) {
|
if (category.active) {
|
||||||
|
const isEmpty = category.itemCount === 0;
|
||||||
const categoryLink = `
|
const categoryLink = `
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
<a class="dropdown-item ${isEmpty ? 'empty-category' : ''}" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
||||||
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
<div class="category-info">
|
||||||
${category.name}
|
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
||||||
|
<span>${category.name}</span>
|
||||||
|
</div>
|
||||||
|
<span class="category-count">${category.itemCount || 0}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
`;
|
`;
|
||||||
dropdown.append(categoryLink);
|
dropdown.append(categoryLink);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Инициализируем hover-функциональность
|
||||||
|
this.initHoverDropdown();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Ошибка загрузки категорий в выпадающее меню:', 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);
|
$('#currentUserInfo').text(user.name);
|
||||||
$('#loginLink').hide();
|
$('#loginLink').hide();
|
||||||
$('#profileDropdown').show();
|
$('#profileDropdown').show();
|
||||||
$('#adminDropdown').show();
|
|
||||||
|
// Показываем админ панель только для администраторов
|
||||||
|
if (auth.hasRole('admin')) {
|
||||||
|
$('#adminDropdown').show();
|
||||||
|
} else {
|
||||||
|
$('#adminDropdown').hide();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$('#currentUserInfo').text('Гость');
|
$('#currentUserInfo').text('Гость');
|
||||||
$('#loginLink').show();
|
$('#loginLink').show();
|
||||||
$('#profileDropdown').hide();
|
$('#profileDropdown').hide();
|
||||||
$('#adminDropdown').hide();
|
$('#adminDropdown').hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Обновляем права доступа для элементов интерфейса
|
||||||
|
auth.updateButtonPermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Загрузка категорий в навигацию
|
// Загрузка категорий в навигацию
|
||||||
@ -518,21 +527,60 @@ class ProfileManager {
|
|||||||
// Добавляем активные категории
|
// Добавляем активные категории
|
||||||
categories.forEach(category => {
|
categories.forEach(category => {
|
||||||
if (category.active) {
|
if (category.active) {
|
||||||
|
const isEmpty = category.itemCount === 0;
|
||||||
const categoryLink = `
|
const categoryLink = `
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
<a class="dropdown-item ${isEmpty ? 'empty-category' : ''}" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
||||||
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
<div class="category-info">
|
||||||
${category.name}
|
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
||||||
|
<span>${category.name}</span>
|
||||||
|
</div>
|
||||||
|
<span class="category-count">${category.itemCount || 0}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
`;
|
`;
|
||||||
dropdown.append(categoryLink);
|
dropdown.append(categoryLink);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Инициализируем hover-функциональность
|
||||||
|
this.initHoverDropdown();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Ошибка загрузки категорий в выпадающее меню:', 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);
|
$('#currentUserInfo').text(user.name);
|
||||||
$('#loginLink').hide();
|
$('#loginLink').hide();
|
||||||
$('#profileDropdown').show();
|
$('#profileDropdown').show();
|
||||||
$('#adminDropdown').show();
|
|
||||||
|
// Показываем админ панель только для администраторов
|
||||||
|
if (auth.hasRole('admin')) {
|
||||||
|
$('#adminDropdown').show();
|
||||||
|
} else {
|
||||||
|
$('#adminDropdown').hide();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$('#currentUserInfo').text('Гость');
|
$('#currentUserInfo').text('Гость');
|
||||||
$('#loginLink').show();
|
$('#loginLink').show();
|
||||||
$('#profileDropdown').hide();
|
$('#profileDropdown').hide();
|
||||||
$('#adminDropdown').hide();
|
$('#adminDropdown').hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Обновляем права доступа для элементов интерфейса
|
||||||
|
auth.updateButtonPermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Загрузка категорий в навигацию
|
// Загрузка категорий в навигацию
|
||||||
@ -754,21 +763,60 @@ class SearchManager {
|
|||||||
// Добавляем активные категории
|
// Добавляем активные категории
|
||||||
categories.forEach(category => {
|
categories.forEach(category => {
|
||||||
if (category.active) {
|
if (category.active) {
|
||||||
|
const isEmpty = category.itemCount === 0;
|
||||||
const categoryLink = `
|
const categoryLink = `
|
||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
<a class="dropdown-item ${isEmpty ? 'empty-category' : ''}" href="catalog.html?category=${category.id}" title="${category.description || ''}">
|
||||||
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
<div class="category-info">
|
||||||
${category.name}
|
<i class="${category.icon}" style="color: ${category.color || '#2c5aa0'}"></i>
|
||||||
|
<span>${category.name}</span>
|
||||||
|
</div>
|
||||||
|
<span class="category-count">${category.itemCount || 0}</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
`;
|
`;
|
||||||
dropdown.append(categoryLink);
|
dropdown.append(categoryLink);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Инициализируем hover-функциональность
|
||||||
|
this.initHoverDropdown();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Ошибка загрузки категорий в выпадающее меню:', 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() {
|
getDefaultCategories() {
|
||||||
return [
|
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()
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Сохранение категорий
|
// Сохранение категорий
|
||||||
@ -98,7 +47,9 @@ class WarehouseManager {
|
|||||||
// Обновление количества позиций в категориях
|
// Обновление количества позиций в категориях
|
||||||
updateCategoryItemCounts() {
|
updateCategoryItemCounts() {
|
||||||
this.categories.forEach(category => {
|
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();
|
this.saveCategories();
|
||||||
}
|
}
|
||||||
@ -114,78 +65,7 @@ class WarehouseManager {
|
|||||||
|
|
||||||
// Получение позиций по умолчанию
|
// Получение позиций по умолчанию
|
||||||
getDefaultItems() {
|
getDefaultItems() {
|
||||||
return [
|
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'
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Получение статистики
|
// Получение статистики
|
||||||
@ -229,14 +109,14 @@ class WarehouseManager {
|
|||||||
const newItem = {
|
const newItem = {
|
||||||
id: this.getNextItemId(),
|
id: this.getNextItemId(),
|
||||||
name: itemData.itemName,
|
name: itemData.itemName,
|
||||||
category: itemData.itemCategory,
|
category: itemData.itemCategory || 'uncategorized',
|
||||||
price: parseFloat(itemData.itemPrice),
|
price: parseFloat(itemData.itemPrice),
|
||||||
quantity: parseInt(itemData.itemQuantity),
|
quantity: parseInt(itemData.itemQuantity),
|
||||||
description: itemData.itemDescription || '',
|
description: itemData.itemDescription || '',
|
||||||
barcode: itemData.itemBarcode || '',
|
barcode: itemData.itemBarcode || '',
|
||||||
minQuantity: parseInt(itemData.itemQuantity) * 0.2, // 20% от начального количества
|
minQuantity: parseInt(itemData.itemQuantity) * 0.2, // 20% от начального количества
|
||||||
location: 'Новый стеллаж',
|
location: itemData.itemLocation || 'Новый стеллаж',
|
||||||
supplier: '',
|
supplier: itemData.itemSupplier || '',
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
updatedAt: new Date().toISOString()
|
updatedAt: new Date().toISOString()
|
||||||
};
|
};
|
||||||
@ -244,6 +124,9 @@ class WarehouseManager {
|
|||||||
this.items.push(newItem);
|
this.items.push(newItem);
|
||||||
this.saveItems();
|
this.saveItems();
|
||||||
|
|
||||||
|
// Обновляем количество товаров в категории
|
||||||
|
this.updateCategoryItemCounts();
|
||||||
|
|
||||||
// Создаем операцию прихода для новой позиции
|
// Создаем операцию прихода для новой позиции
|
||||||
await this.createOperation({
|
await this.createOperation({
|
||||||
type: 'incoming',
|
type: 'incoming',
|
||||||
@ -269,6 +152,10 @@ class WarehouseManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.saveItems();
|
this.saveItems();
|
||||||
|
|
||||||
|
// Обновляем количество товаров в категории
|
||||||
|
this.updateCategoryItemCounts();
|
||||||
|
|
||||||
return this.items[itemIndex];
|
return this.items[itemIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,6 +168,10 @@ class WarehouseManager {
|
|||||||
|
|
||||||
this.items.splice(itemIndex, 1);
|
this.items.splice(itemIndex, 1);
|
||||||
this.saveItems();
|
this.saveItems();
|
||||||
|
|
||||||
|
// Обновляем количество товаров в категории
|
||||||
|
this.updateCategoryItemCounts();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -298,6 +189,9 @@ class WarehouseManager {
|
|||||||
item.quantity += quantity;
|
item.quantity += quantity;
|
||||||
item.updatedAt = new Date().toISOString();
|
item.updatedAt = new Date().toISOString();
|
||||||
this.saveItems();
|
this.saveItems();
|
||||||
|
|
||||||
|
// Обновляем количество товаров в категории
|
||||||
|
this.updateCategoryItemCounts();
|
||||||
|
|
||||||
// Создаем операцию
|
// Создаем операцию
|
||||||
const operation = await this.createOperation({
|
const operation = await this.createOperation({
|
||||||
@ -330,6 +224,9 @@ class WarehouseManager {
|
|||||||
item.quantity -= quantity;
|
item.quantity -= quantity;
|
||||||
item.updatedAt = new Date().toISOString();
|
item.updatedAt = new Date().toISOString();
|
||||||
this.saveItems();
|
this.saveItems();
|
||||||
|
|
||||||
|
// Обновляем количество товаров в категории
|
||||||
|
this.updateCategoryItemCounts();
|
||||||
|
|
||||||
// Создаем операцию
|
// Создаем операцию
|
||||||
const operation = await this.createOperation({
|
const operation = await this.createOperation({
|
||||||
|
|||||||
Reference in New Issue
Block a user