# обязательно подключение глобальных массивов globalThis.renderFns = require 'pug.json' globalThis.stylFns = require 'styl.json' # подключение мета информации (строго в данном фиде) document.head.insertAdjacentHTML 'beforeend','' document.head.insertAdjacentHTML 'beforeend','' document.head.insertAdjacentHTML('beforeend',' Кохи Борбад - Концертный зал Душанбе') # Настройка tailwind tailwind.config = require 'tailwind.config.js' # подключение основных стилей ## tailwind document.head.insertAdjacentHTML('beforeend','') ## базовой стиль приложения document.head.insertAdjacentHTML('beforeend','') # Создаем глобальную шину событий class AppEventBus constructor: -> @events = {} on: (event, callback) -> if !@events[event] @events[event] = [] @events[event].push(callback) emit: (event, data) -> if @events[event] for callback in @events[event] try callback(data) catch error debug.log "Event bus error: " + error off: (event, callback) -> if @events[event] @events[event] = @events[event].filter (cb) -> cb != callback # Создаем глобально globalThis.EventBus = new AppEventBus() # Класс для работы с мультиязычными данными class MultilingualData constructor: -> @currentLanguage = 'ru' @availableLanguages = ['ru', 'en', 'tj'] @fallbackLanguage = 'ru' # Установка текущего языка setLanguage: (language) -> if language in @availableLanguages @currentLanguage = language EventBus.emit('language_changed', language) debug.log "Язык изменен на: "+language # Получение текста для текущего языка getText: (textArray, fallback = '') -> if not textArray or not Array.isArray(textArray) return fallback languageIndex = @availableLanguages.indexOf(@currentLanguage) if languageIndex is -1 or languageIndex >= textArray.length languageIndex = @availableLanguages.indexOf(@fallbackLanguage) return textArray[languageIndex] or fallback # Получение всех текстов для отладки getAllTexts: (textArray) -> if not textArray or not Array.isArray(textArray) return {} result = {} for lang, index in @availableLanguages if textArray[index] result[lang] = textArray[index] return result # Класс для работы с базой данных class AppDatabase constructor: -> @db = null @baseUrl = 'https://oleg:631074@couchdb.favt.ru.net' @dbName = 'borbad_events' @initialized = false @multilingual = new MultilingualData() # Инициализация базы данных initialize: -> try PouchDB = require 'pouchdb' @db = new PouchDB(@baseUrl+"/"+@dbName) # Проверяем соединение info = await @db.info() debug.log "База данных подключена: "+info.db_name @initialized = true EventBus.emit('database_ready', @db) catch error debug.log "Ошибка подключения к базе данных: "+error EventBus.emit('database_error', error) # Получение документов по типу с мультиязычной обработкой getDocumentsByType: (type, options = {}) -> if not @initialized throw new Error("База данных не инициализирована") try viewName = options.view or 'published_by_domain_language' designDoc = options.designDoc or 'multilingual_content' result = await @db.query(designDoc+'/'+viewName, { startkey: [options.domain or 'borbad.s5l.ru', @multilingual.currentLanguage] endkey: [options.domain or 'borbad.s5l.ru', @multilingual.currentLanguage, {}] include_docs: true descending: options.descending or true limit: options.limit or 10 skip: options.skip or 0 }) # Обрабатываем мультиязычные данные processedDocs = result.rows.map (row) => @processMultilingualDocument(row.doc) return processedDocs catch error debug.log "Ошибка получения документов типа "+type+": "+error return [] # Обработка мультиязычного документа processMultilingualDocument: (doc) -> if not doc return null processedDoc = Object.assign({}, doc) # Обрабатываем мультиязычные поля multilingualFields = ['title', 'content', 'excerpt', 'author', 'name', 'description', 'location'] for field in multilingualFields if doc[field] and Array.isArray(doc[field]) processedDoc[field] = @multilingual.getText(doc[field]) # Обрабатываем вложенные мультиязычные структуры if doc.seo processedDoc.seo = {} if doc.seo.description and Array.isArray(doc.seo.description) processedDoc.seo.description = @multilingual.getText(doc.seo.description) if doc.seo.title and Array.isArray(doc.seo.title) processedDoc.seo.title = @multilingual.getText(doc.seo.title) if doc.seo.keywords and Array.isArray(doc.seo.keywords) processedDoc.seo.keywords = @multilingual.getText(doc.seo.keywords, []) # Обрабатываем специфичные данные для разных типов if doc.type is 'event' and doc.event_data processedDoc.event_data = Object.assign({}, doc.event_data) if doc.event_data.location and Array.isArray(doc.event_data.location) processedDoc.event_data.location = @multilingual.getText(doc.event_data.location) if doc.event_data.price and Array.isArray(doc.event_data.price) processedDoc.event_data.price = @multilingual.getText(doc.event_data.price) if doc.type is 'product' and doc.product_data processedDoc.product_data = Object.assign({}, doc.product_data) if doc.product_data.price and Array.isArray(doc.product_data.price) processedDoc.product_data.price = @multilingual.getText(doc.product_data.price) if doc.type is 'slide' and doc.slide_data processedDoc.slide_data = Object.assign({}, doc.slide_data) if doc.slide_data.button_text and Array.isArray(doc.slide_data.button_text) processedDoc.slide_data.button_text = @multilingual.getText(doc.slide_data.button_text) if doc.slide_data.button_link and Array.isArray(doc.slide_data.button_link) processedDoc.slide_data.button_link = @multilingual.getText(doc.slide_data.button_link) return processedDoc # Получение блог постов getBlogPosts: (options = {}) -> options.type = 'blog_post' return await @getDocumentsByType('blog_post', options) # Получение мероприятий getEvents: (options = {}) -> options.type = 'event' options.view = 'by_date_multilingual' return await @getDocumentsByType('event', options) # Получение предстоящих мероприятий getUpcomingEvents: (options = {}) -> options.type = 'event' options.view = 'upcoming_events' return await @getDocumentsByType('event', options) # Получение товаров getProducts: (options = {}) -> options.type = 'product' options.view = 'by_status_multilingual' return await @getDocumentsByType('product', options) # Получение слайдов getSlides: (options = {}) -> options.type = 'slide' options.view = 'active_ordered_multilingual' options.descending = false return await @getDocumentsByType('slide', options) # Получение категорий getCategories: (options = {}) -> if not @initialized throw new Error("База данных не инициализирована") try result = await @db.query('categories_hierarchical_multilingual/by_level_multilingual', { startkey: [options.domain or 'borbad.s5l.ru', @multilingual.currentLanguage, 0] endkey: [options.domain or 'borbad.s5l.ru', @multilingual.currentLanguage, 0, {}] include_docs: true ascending: true }) processedCategories = result.rows.map (row) => @processMultilingualDocument(row.doc) return processedCategories catch error debug.log "Ошибка получения категорий: "+error return [] # Поиск по контенту searchContent: (query, options = {}) -> if not @initialized throw new Error("База данных не инициализирована") try result = await @db.query('universal_multilingual_search/content_search', { startkey: [options.domain or 'borbad.s5l.ru', @multilingual.currentLanguage, query.toLowerCase()] endkey: [options.domain or 'borbad.s5l.ru', @multilingual.currentLanguage, query.toLowerCase() + "\ufff0"] include_docs: true limit: options.limit or 20 }) processedResults = result.rows.map (row) => @processMultilingualDocument(row.doc) return processedResults catch error debug.log "Ошибка поиска: "+error return [] # Получение документа по ID getDocumentById: (id) -> if not @initialized throw new Error("База данных не инициализирована") try doc = await @db.get(id) return @processMultilingualDocument(doc) catch error debug.log "Ошибка получения документа "+id+": "+error return null # Создаем глобальные экземпляры globalThis.AppDB = new AppDatabase() globalThis.Multilingual = new MultilingualData() # Маршруты routes = [ { path: '/', component: require 'app/pages/Page' } { path: '/events', component: require 'app/pages/Events' } { path: '/events/:id', component: require 'app/pages/EventDetail' } # { path: '/blog', component: require 'app/pages/Blog' } # { path: '/blog/:id', component: require 'app/pages/BlogDetail' } # { path: '/products', component: require 'app/pages/Products' } # { path: '/products/:id', component: require 'app/pages/ProductDetail' } { path: '/:path(.*)*', component: require 'app/pages/Page' } # Динамические страницы # Универсальный обработчик страниц ] # Глобальное определение vuejs приложения app = Vue.createApp name: 'app' data: ()-> return dbReady: false loading: true error: null currentLanguage: 'ru' availableLanguages: ['ru', 'en', 'tj'] # Глобальное состояние приложения appState: slides: [] featuredEvents: [] blogPosts: [] products: [] categories: [] loading: true error: null # Состояние модальных окон modalState: isVisible: false component: null props: {} beforeMount: ()-> debug.log "start beforeMount" # определение контекста vuejs приложения как глобальной переменной _ globalThis._ = @ # Инициализация базы данных AppDB.initialize().then => @dbReady = true @loadInitialData() # Слушаем события смены языка EventBus.on 'language_changed', (language) => @currentLanguage = language @loadInitialData() mounted: -> debug.log "App mounted" methods: # Загрузка начальных данных loadInitialData: -> @appState.loading = true @appState.error = null Promise.all([ @loadSlides() @loadFeaturedEvents() @loadBlogPosts() @loadCategories() ]).then => @appState.loading = false .catch (error) => @appState.error = "Ошибка загрузки данных: "+error @appState.loading = false # Загрузка слайдов loadSlides: -> AppDB.getSlides(limit: 6).then (slides) => @appState.slides = slides .catch (error) => debug.log "Ошибка загрузки слайдов: "+error # Загрузка избранных мероприятий loadFeaturedEvents: -> AppDB.getUpcomingEvents(limit: 4).then (events) => @appState.featuredEvents = events .catch (error) => debug.log "Ошибка загрузки мероприятий: "+error # Загрузка блог постов loadBlogPosts: -> AppDB.getBlogPosts(limit: 6).then (posts) => @appState.blogPosts = posts .catch (error) => debug.log "Ошибка загрузки блог постов: "+error # Загрузка категорий loadCategories: -> AppDB.getCategories().then (categories) => @appState.categories = categories .catch (error) => debug.log "Ошибка загрузки категорий: "+error # Смена языка changeLanguage: (language) -> if language in @availableLanguages AppDB.multilingual.setLanguage(language) @currentLanguage = language # Поиск по сайту search: (query) -> if query and query.length > 2 AppDB.searchContent(query).then (results) -> EventBus.emit('search_results', results) # Открытие модального окна openModal: (component, props = {}) -> @modalState.component = component @modalState.props = props @modalState.isVisible = true # Закрытие модального окна closeModal: -> @modalState.isVisible = false @modalState.component = null @modalState.props = {} render: (new Function '_ctx', '_cache', renderFns['app/temp.pug'])() components: 'themetoggle': require 'app/shared/ThemeToggle' 'multilevelmenu': require 'app/shared/MultiLevelMenu' 'imageslider': require 'app/shared/ImageSlider' 'app-link': require 'app/shared/AppLink' 'language-switcher': require 'app/shared/LanguageSwitcher' app.use(VueRouter.createRouter({ routes: routes history: VueRouter.createWebHistory() scrollBehavior: (to, from, savedPosition) -> if savedPosition return savedPosition else return { x: 0, y: 0 } })) # подключаем в body ОБЯЗАТЕЛЬНО!!! app.mount('body')