|
|
@@ -1,24 +1,15 @@
|
|
|
# Текущая задача
|
|
|
|
|
|
-Проверь описание компонентов, опиши все необходимые глобальные переменные, такие как текщий язык, тема, и другие, учти что мы убрали EventBus, и передаём состояние через глобальную переменную _
|
|
|
-
|
|
|
-
|
|
|
- ├── AppLink/ ( описание: https://gogs.osvoj.ru/s5l.ru/borbad.s5l.ru/raw/master/vue/app/shared/AppLink/README.md)
|
|
|
- ├── ThemeToggle/ ( описание: https://gogs.osvoj.ru/s5l.ru/borbad.s5l.ru/raw/master/vue/app/shared/ThemeToggle/README.md)
|
|
|
- ├── LanguageSwitcher/ ( описание: https://gogs.osvoj.ru/s5l.ru/borbad.s5l.ru/raw/master/vue/app/shared/LanguageSwitcher/README.md)
|
|
|
- ├── MultiLevelMenu/ ( описание: https://gogs.osvoj.ru/s5l.ru/borbad.s5l.ru/raw/master/vue/app/shared/MultiLevelMenu/README.md)
|
|
|
- ├── ImageSlider/ ( описание: https://gogs.osvoj.ru/s5l.ru/borbad.s5l.ru/raw/master/vue/app/shared/ImageSlider/README.md)
|
|
|
- ├── ModalWindow/ ( описание: https://gogs.osvoj.ru/s5l.ru/borbad.s5l.ru/raw/master/vue/app/shared/ModalWindow/README.md)
|
|
|
- ├── FormValidator/ ( описание: https://gogs.osvoj.ru/s5l.ru/borbad.s5l.ru/raw/master/vue/app/shared/FormValidator/README.md)
|
|
|
- ├── FilterSort/ ( описание: https://gogs.osvoj.ru/s5l.ru/borbad.s5l.ru/raw/master/vue/app/shared/FilterSort/README.md)
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
+опиги app/shared/pages как универсальный компонент для VueRouter, который будет получать из него урл, загружая запись блога в соответствии с привязанным урлом, и её отображать. Учти что в записи блога могут быть описаны используемые компаненты.
|
|
|
+основной контент пишется на markdown, нужно добавить в него тег определяющий отдельные классы страниц.
|
|
|
|
|
|
# файл с правилами
|
|
|
https://gogs.osvoj.ru/s5l.ru/borbad.s5l.ru/raw/master/README.md
|
|
|
|
|
|
+# Важно соблюдай или предлагай доработки к
|
|
|
+*Глобальные переменные состояния приложения*
|
|
|
+из файла https://gogs.osvoj.ru/s5l.ru/borbad.s5l.ru/raw/master/README.md
|
|
|
+
|
|
|
# git ВАЖНО
|
|
|
для анализа кода бери версии файлов СТРОГО из репозитария:
|
|
|
https://gogs.osvoj.ru/s5l.ru/borbad.s5l.ru/src/master
|
|
|
@@ -271,6 +262,485 @@ app/
|
|
|
├── index.pug
|
|
|
├── index.styl
|
|
|
|
|
|
+
|
|
|
+Проанализирую компоненты и опишу все необходимые глобальные переменные, которые должны быть доступны через `_`.
|
|
|
+
|
|
|
+## Глобальные переменные состояния приложения
|
|
|
+
|
|
|
+### Основные глобальные переменные в `_`
|
|
|
+
|
|
|
+```coffee
|
|
|
+# В app/temp.coffee - данные приложения
|
|
|
+data: ()->
|
|
|
+ return
|
|
|
+ # Состояние загрузки и ошибок
|
|
|
+ dbReady: false
|
|
|
+ loading: true
|
|
|
+ error: null
|
|
|
+
|
|
|
+ # Языковые настройки
|
|
|
+ currentLanguage: 'ru'
|
|
|
+ availableLanguages: ['ru', 'en', 'tj']
|
|
|
+
|
|
|
+ # Тема оформления
|
|
|
+ currentTheme: 'light' # 'light' | 'dark' | 'auto'
|
|
|
+ availableThemes: ['light', 'dark', 'auto']
|
|
|
+
|
|
|
+ # Глобальное состояние приложения
|
|
|
+ appState:
|
|
|
+ slides: []
|
|
|
+ featuredEvents: []
|
|
|
+ blogPosts: []
|
|
|
+ products: []
|
|
|
+ categories: []
|
|
|
+ loading: true
|
|
|
+ error: null
|
|
|
+
|
|
|
+ # Состояние модальных окон
|
|
|
+ modalState:
|
|
|
+ isVisible: false
|
|
|
+ component: null
|
|
|
+ props: {}
|
|
|
+
|
|
|
+ # Состояние меню
|
|
|
+ menuState:
|
|
|
+ isOpen: false
|
|
|
+ activeMenu: null
|
|
|
+ breadcrumbs: []
|
|
|
+
|
|
|
+ # Состояние фильтров и сортировки
|
|
|
+ filterState:
|
|
|
+ activeFilters: {}
|
|
|
+ sortBy: 'created_at'
|
|
|
+ sortOrder: 'desc'
|
|
|
+ searchQuery: ''
|
|
|
+
|
|
|
+ # Состояние форм
|
|
|
+ formState:
|
|
|
+ submitting: false
|
|
|
+ errors: {}
|
|
|
+ touched: {}
|
|
|
+```
|
|
|
+
|
|
|
+## Описание компонентов и их зависимостей от глобальных переменных
|
|
|
+
|
|
|
+### 1. AppLink
|
|
|
+**Файл:** `vue/app/shared/AppLink/README.md`
|
|
|
+
|
|
|
+**Зависимости от `_`:**
|
|
|
+- Не имеет прямых зависимостей от глобального состояния
|
|
|
+- Использует Vue Router через `router-link` для внутренних ссылок
|
|
|
+
|
|
|
+**Методы использования:**
|
|
|
+```pug
|
|
|
+app-link(to="/about") О нас
|
|
|
+app-link(to="https://example.com" target="_blank") Внешняя ссылка
|
|
|
+app-link(:to="{ name: 'EventDetail', params: { id: event._id } }") Детали
|
|
|
+```
|
|
|
+
|
|
|
+### 2. ThemeToggle
|
|
|
+**Файл:** `vue/app/shared/ThemeToggle/README.md`
|
|
|
+
|
|
|
+**Зависимости от `_`:**
|
|
|
+```coffee
|
|
|
+# Необходимые глобальные переменные:
|
|
|
+_.currentTheme # Текущая тема ('light', 'dark', 'auto')
|
|
|
+_.availableThemes # Доступные темы
|
|
|
+
|
|
|
+# Необходимые методы:
|
|
|
+_.changeTheme(theme) # Метод для смены темы
|
|
|
+```
|
|
|
+
|
|
|
+**Реализация в temp.coffee:**
|
|
|
+```coffee
|
|
|
+methods:
|
|
|
+ changeTheme: (theme) ->
|
|
|
+ if theme in @availableThemes
|
|
|
+ @currentTheme = theme
|
|
|
+ # Сохранение в localStorage
|
|
|
+ localStorage.setItem('theme', theme)
|
|
|
+ # Применение темы к документу
|
|
|
+ if theme is 'dark' or (theme is 'auto' and window.matchMedia('(prefers-color-scheme: dark)').matches)
|
|
|
+ document.documentElement.classList.add('dark')
|
|
|
+ else
|
|
|
+ document.documentElement.classList.remove('dark')
|
|
|
+```
|
|
|
+
|
|
|
+### 3. LanguageSwitcher
|
|
|
+**Файл:** `vue/app/shared/LanguageSwitcher/README.md`
|
|
|
+
|
|
|
+**Зависимости от `_`:**
|
|
|
+```coffee
|
|
|
+# Необходимые глобальные переменные:
|
|
|
+_.currentLanguage # Текущий язык ('ru', 'en', 'tj')
|
|
|
+_.availableLanguages # Доступные языки
|
|
|
+
|
|
|
+# Необходимые методы:
|
|
|
+_.changeLanguage(lang) # Метод для смены языка
|
|
|
+```
|
|
|
+
|
|
|
+**Реализация в temp.coffee:**
|
|
|
+```coffee
|
|
|
+methods:
|
|
|
+ changeLanguage: (language) ->
|
|
|
+ if language in @availableLanguages
|
|
|
+ @currentLanguage = language
|
|
|
+ AppDB.multilingual.setLanguage(language)
|
|
|
+ # Сохранение в localStorage
|
|
|
+ localStorage.setItem('language', language)
|
|
|
+ # Перезагрузка данных с новым языком
|
|
|
+ @loadInitialData()
|
|
|
+```
|
|
|
+
|
|
|
+### 4. MultiLevelMenu
|
|
|
+**Файл:** `vue/app/shared/MultiLevelMenu/README.md`
|
|
|
+
|
|
|
+**Зависимости от `_`:**
|
|
|
+```coffee
|
|
|
+# Необходимые глобальные переменные:
|
|
|
+_.menuState.isOpen # Открыто ли меню
|
|
|
+_.menuState.activeMenu # Активный пункт меню
|
|
|
+_.menuState.breadcrumbs # Хлебные крошки
|
|
|
+_.appState.categories # Категории для меню
|
|
|
+_.currentLanguage # Текущий язык для мультиязычных названий
|
|
|
+
|
|
|
+# Необходимые методы:
|
|
|
+_.toggleMenu() # Переключение видимости меню
|
|
|
+_.setActiveMenu(menuId) # Установка активного меню
|
|
|
+```
|
|
|
+
|
|
|
+**Реализация в temp.coffee:**
|
|
|
+```coffee
|
|
|
+methods:
|
|
|
+ toggleMenu: ->
|
|
|
+ @menuState.isOpen = !@menuState.isOpen
|
|
|
+
|
|
|
+ setActiveMenu: (menuId) ->
|
|
|
+ @menuState.activeMenu = menuId
|
|
|
+ # Обновление хлебных крошек на основе активного меню
|
|
|
+```
|
|
|
+
|
|
|
+### 5. ImageSlider
|
|
|
+**Файл:** `vue/app/shared/ImageSlider/README.md`
|
|
|
+
|
|
|
+**Зависимости от `_`:**
|
|
|
+```coffee
|
|
|
+# Необходимые глобальные переменные:
|
|
|
+_.appState.slides # Массив слайдов
|
|
|
+_.currentLanguage # Текущий язык для мультиязычного контента
|
|
|
+
|
|
|
+# Необходимые методы:
|
|
|
+_.getText(textArray) # Получение текста для текущего языка
|
|
|
+```
|
|
|
+
|
|
|
+**Пример данных слайдов:**
|
|
|
+```coffee
|
|
|
+slides: [
|
|
|
+ {
|
|
|
+ title: ["Заголовок слайда", "Slide title"]
|
|
|
+ content: ["Описание слайда", "Slide description"]
|
|
|
+ image: "/assets/slide1.jpg"
|
|
|
+ button_text: ["Узнать больше", "Learn more"]
|
|
|
+ button_link: ["/about", "/about"]
|
|
|
+ }
|
|
|
+]
|
|
|
+```
|
|
|
+
|
|
|
+### 6. ModalWindow
|
|
|
+**Файл:** `vue/app/shared/ModalWindow/README.md`
|
|
|
+
|
|
|
+**Зависимости от `_`:**
|
|
|
+```coffee
|
|
|
+# Необходимые глобальные переменные:
|
|
|
+_.modalState.isVisible # Видимость модального окна
|
|
|
+_.modalState.component # Компонент для отображения
|
|
|
+_.modalState.props # Props для компонента
|
|
|
+
|
|
|
+# Необходимые методы:
|
|
|
+_.openModal(component, props) # Открытие модального окна
|
|
|
+_.closeModal() # Закрытие модального окна
|
|
|
+```
|
|
|
+
|
|
|
+**Реализация в temp.coffee:**
|
|
|
+```coffee
|
|
|
+methods:
|
|
|
+ openModal: (component, props = {}) ->
|
|
|
+ @modalState.component = component
|
|
|
+ @modalState.props = props
|
|
|
+ @modalState.isVisible = true
|
|
|
+
|
|
|
+ closeModal: ->
|
|
|
+ @modalState.isVisible = false
|
|
|
+ @modalState.component = null
|
|
|
+ @modalState.props = {}
|
|
|
+```
|
|
|
+
|
|
|
+### 7. FormValidator
|
|
|
+**Файл:** `vue/app/shared/FormValidator/README.md`
|
|
|
+
|
|
|
+**Зависимости от `_`:**
|
|
|
+```coffee
|
|
|
+# Необходимые глобальные переменные:
|
|
|
+_.formState.submitting # Статус отправки формы
|
|
|
+_.formState.errors # Ошибки валидации
|
|
|
+_.formState.touched # Поля, которые были изменены
|
|
|
+
|
|
|
+# Необходимые методы:
|
|
|
+_.validateForm(formData, rules) # Валидация формы
|
|
|
+_.resetForm() # Сброс формы
|
|
|
+```
|
|
|
+
|
|
|
+**Реализация в temp.coffee:**
|
|
|
+```coffee
|
|
|
+methods:
|
|
|
+ validateForm: (formData, rules) ->
|
|
|
+ errors = {}
|
|
|
+ for field, rule of rules
|
|
|
+ if rule.required and not formData[field]
|
|
|
+ errors[field] = "Поле обязательно для заполнения"
|
|
|
+ if rule.minLength and formData[field]?.length < rule.minLength
|
|
|
+ errors[field] = "Минимальная длина: "+rule.minLength
|
|
|
+ @formState.errors = errors
|
|
|
+ return Object.keys(errors).length === 0
|
|
|
+
|
|
|
+ resetForm: ->
|
|
|
+ @formState.errors = {}
|
|
|
+ @formState.touched = {}
|
|
|
+ @formState.submitting = false
|
|
|
+```
|
|
|
+
|
|
|
+### 8. FilterSort
|
|
|
+**Файл:** `vue/app/shared/FilterSort/README.md`
|
|
|
+
|
|
|
+**Зависимости от `_`:**
|
|
|
+```coffee
|
|
|
+# Необходимые глобальные переменные:
|
|
|
+_.filterState.activeFilters # Активные фильтры
|
|
|
+_.filterState.sortBy # Поле сортировки
|
|
|
+_.filterState.sortOrder # Порядок сортировки
|
|
|
+_.filterState.searchQuery # Поисковый запрос
|
|
|
+_.currentLanguage # Текущий язык для фильтров
|
|
|
+
|
|
|
+# Необходимые методы:
|
|
|
+_.applyFilters(filters) # Применение фильтров
|
|
|
+_.clearFilters() # Очистка фильтров
|
|
|
+_.setSort(field, order) # Установка сортировки
|
|
|
+```
|
|
|
+
|
|
|
+**Реализация в temp.coffee:**
|
|
|
+```coffee
|
|
|
+methods:
|
|
|
+ applyFilters: (filters) ->
|
|
|
+ @filterState.activeFilters = { ...@filterState.activeFilters, ...filters }
|
|
|
+ # Обновление отображаемых данных
|
|
|
+ @loadFilteredData()
|
|
|
+
|
|
|
+ clearFilters: ->
|
|
|
+ @filterState.activeFilters = {}
|
|
|
+ @filterState.searchQuery = ''
|
|
|
+ @loadFilteredData()
|
|
|
+
|
|
|
+ setSort: (field, order = 'desc') ->
|
|
|
+ @filterState.sortBy = field
|
|
|
+ @filterState.sortOrder = order
|
|
|
+ @loadFilteredData()
|
|
|
+```
|
|
|
+
|
|
|
+## Полная структура глобального состояния
|
|
|
+
|
|
|
+### app/temp.coffee (обновленная версия)
|
|
|
+
|
|
|
+```coffee
|
|
|
+# ... imports and initial setup ...
|
|
|
+
|
|
|
+app = Vue.createApp
|
|
|
+ name: 'app'
|
|
|
+ data: ()->
|
|
|
+ return
|
|
|
+ # Состояние инициализации
|
|
|
+ dbReady: false
|
|
|
+ loading: true
|
|
|
+ error: null
|
|
|
+
|
|
|
+ # Языковые настройки
|
|
|
+ currentLanguage: localStorage.getItem('language') || 'ru'
|
|
|
+ availableLanguages: ['ru', 'en', 'tj']
|
|
|
+
|
|
|
+ # Тема оформления
|
|
|
+ currentTheme: localStorage.getItem('theme') || 'auto'
|
|
|
+ availableThemes: ['light', 'dark', 'auto']
|
|
|
+
|
|
|
+ # Глобальное состояние приложения
|
|
|
+ appState:
|
|
|
+ slides: []
|
|
|
+ featuredEvents: []
|
|
|
+ blogPosts: []
|
|
|
+ products: []
|
|
|
+ categories: []
|
|
|
+ loading: true
|
|
|
+ error: null
|
|
|
+
|
|
|
+ # Состояние UI компонентов
|
|
|
+ modalState:
|
|
|
+ isVisible: false
|
|
|
+ component: null
|
|
|
+ props: {}
|
|
|
+
|
|
|
+ menuState:
|
|
|
+ isOpen: false
|
|
|
+ activeMenu: null
|
|
|
+ breadcrumbs: []
|
|
|
+
|
|
|
+ filterState:
|
|
|
+ activeFilters: {}
|
|
|
+ sortBy: 'created_at'
|
|
|
+ sortOrder: 'desc'
|
|
|
+ searchQuery: ''
|
|
|
+
|
|
|
+ formState:
|
|
|
+ submitting: false
|
|
|
+ errors: {}
|
|
|
+ touched: {}
|
|
|
+
|
|
|
+ # Пользовательские настройки
|
|
|
+ userPreferences:
|
|
|
+ notifications: true
|
|
|
+ fontSize: 'medium'
|
|
|
+ reduceMotion: false
|
|
|
+
|
|
|
+ beforeMount: ()->
|
|
|
+ debug.log "start beforeMount"
|
|
|
+ globalThis._ = @
|
|
|
+
|
|
|
+ # Применяем сохраненную тему
|
|
|
+ @applyTheme(@currentTheme)
|
|
|
+
|
|
|
+ # Инициализация базы данных
|
|
|
+ AppDB.initialize().then =>
|
|
|
+ @dbReady = true
|
|
|
+ @loadInitialData()
|
|
|
+
|
|
|
+ methods:
|
|
|
+ # Управление темой
|
|
|
+ changeTheme: (theme) ->
|
|
|
+ if theme in @availableThemes
|
|
|
+ @currentTheme = theme
|
|
|
+ localStorage.setItem('theme', theme)
|
|
|
+ @applyTheme(theme)
|
|
|
+
|
|
|
+ applyTheme: (theme) ->
|
|
|
+ if theme is 'dark' or (theme is 'auto' and window.matchMedia('(prefers-color-scheme: dark)').matches)
|
|
|
+ document.documentElement.classList.add('dark')
|
|
|
+ else
|
|
|
+ document.documentElement.classList.remove('dark')
|
|
|
+
|
|
|
+ # Управление языком
|
|
|
+ changeLanguage: (language) ->
|
|
|
+ if language in @availableLanguages
|
|
|
+ @currentLanguage = language
|
|
|
+ AppDB.multilingual.setLanguage(language)
|
|
|
+ localStorage.setItem('language', language)
|
|
|
+ @loadInitialData()
|
|
|
+
|
|
|
+ # Управление меню
|
|
|
+ toggleMenu: ->
|
|
|
+ @menuState.isOpen = !@menuState.isOpen
|
|
|
+
|
|
|
+ setActiveMenu: (menuId) ->
|
|
|
+ @menuState.activeMenu = menuId
|
|
|
+
|
|
|
+ # Управление модальными окнами
|
|
|
+ openModal: (component, props = {}) ->
|
|
|
+ @modalState.component = component
|
|
|
+ @modalState.props = props
|
|
|
+ @modalState.isVisible = true
|
|
|
+
|
|
|
+ closeModal: ->
|
|
|
+ @modalState.isVisible = false
|
|
|
+ @modalState.component = null
|
|
|
+ @modalState.props = {}
|
|
|
+
|
|
|
+ # Управление фильтрами
|
|
|
+ applyFilters: (filters) ->
|
|
|
+ @filterState.activeFilters = { ...@filterState.activeFilters, ...filters }
|
|
|
+ @loadFilteredData()
|
|
|
+
|
|
|
+ clearFilters: ->
|
|
|
+ @filterState.activeFilters = {}
|
|
|
+ @filterState.searchQuery = ''
|
|
|
+ @loadFilteredData()
|
|
|
+
|
|
|
+ setSort: (field, order = 'desc') ->
|
|
|
+ @filterState.sortBy = field
|
|
|
+ @filterState.sortOrder = order
|
|
|
+ @loadFilteredData()
|
|
|
+
|
|
|
+ # Управление формами
|
|
|
+ validateForm: (formData, rules) ->
|
|
|
+ errors = {}
|
|
|
+ for field, rule of rules
|
|
|
+ if rule.required and not formData[field]
|
|
|
+ errors[field] = @getText(["Поле обязательно для заполнения", "Field is required"])
|
|
|
+ if rule.minLength and formData[field]?.length < rule.minLength
|
|
|
+ errors[field] = @getText(["Минимальная длина: ", "Minimum length: "]) + rule.minLength
|
|
|
+ @formState.errors = errors
|
|
|
+ return Object.keys(errors).length === 0
|
|
|
+
|
|
|
+ resetForm: ->
|
|
|
+ @formState.errors = {}
|
|
|
+ @formState.touched = {}
|
|
|
+ @formState.submitting = false
|
|
|
+
|
|
|
+ # Вспомогательные методы
|
|
|
+ getText: (textArray) ->
|
|
|
+ return AppDB.multilingual.getText(textArray, '')
|
|
|
+
|
|
|
+ # Загрузка данных
|
|
|
+ loadInitialData: ->
|
|
|
+ @appState.loading = true
|
|
|
+ Promise.all([
|
|
|
+ @loadSlides()
|
|
|
+ @loadFeaturedEvents()
|
|
|
+ @loadBlogPosts()
|
|
|
+ @loadCategories()
|
|
|
+ ]).then =>
|
|
|
+ @appState.loading = false
|
|
|
+ .catch (error) =>
|
|
|
+ @appState.error = error
|
|
|
+ @appState.loading = false
|
|
|
+
|
|
|
+ loadFilteredData: ->
|
|
|
+ # Реализация загрузки данных с учетом фильтров
|
|
|
+ pass
|
|
|
+
|
|
|
+ # ... остальные методы загрузки данных ...
|
|
|
+
|
|
|
+ # ... render, components, router ...
|
|
|
+```
|
|
|
+
|
|
|
+## Использование в компонентах
|
|
|
+
|
|
|
+### Пример компонента с доступом к глобальному состоянию:
|
|
|
+
|
|
|
+```coffee
|
|
|
+# app/shared/ThemeToggle/index.coffee
|
|
|
+module.exports =
|
|
|
+ name: 'ThemeToggle'
|
|
|
+
|
|
|
+ computed:
|
|
|
+ currentTheme: ->
|
|
|
+ return _.currentTheme
|
|
|
+
|
|
|
+ availableThemes: ->
|
|
|
+ return _.availableThemes
|
|
|
+
|
|
|
+ methods:
|
|
|
+ changeTheme: (theme) ->
|
|
|
+ _.changeTheme(theme)
|
|
|
+```
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
# Структура хранимых данных
|
|
|
## Описание всех хранимых объектов
|
|
|
|