🎨 Добавлен полный редактор стилей и поле image_url для туров
✨ Новые функции: - Поле image_url в модели туров для изменения изображений через админ-панель - Расширенная модель настроек сайта с категориями: colors, typography, images, effects, layout - Динамический CSS генератор на основе настроек (/dynamic-styles.css) - API для управления настройками сайта (/api/site-settings) 🎯 Редактор стилей: - Управление цветами (основные, акцентные, текст, фон) - Настройка типографики (шрифты, размеры, межстрочный интервал) - Управление изображениями (фоны, логотипы, фавикон) - Эффекты (прозрачность, тени, размытие, скругления) - Макет (высота секций, размеры контейнеров) - Пользовательский CSS код 🛠️ Техническая реализация: - SiteSettingsHelper с кешированием для производительности - CSS переменные для динамического изменения стилей - Автоматическая миграция базы данных - Интеграция с AdminJS для удобного управления - Загрузка настроек в шаблоны для доступности 📊 База данных: - Расширена таблица site_settings (добавлено поле category) - Новые типы настроек: color, file - 27 предустановленных настроек для полного контроля над дизайном - Автоматическое применение миграций при старте приложения
This commit is contained in:
@@ -55,6 +55,30 @@ export async function initDatabase() {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if style editor migration is applied
|
||||
try {
|
||||
const result = await db.query("SELECT 1 FROM site_settings WHERE setting_key = 'primary_color' LIMIT 1");
|
||||
if (result.rows.length === 0) {
|
||||
console.log('🎨 Installing style editor features...');
|
||||
const styleMigrationPath = path.join(__dirname, 'style-editor-migration.sql');
|
||||
if (fs.existsSync(styleMigrationPath)) {
|
||||
const styleMigration = fs.readFileSync(styleMigrationPath, 'utf8');
|
||||
await db.query(styleMigration);
|
||||
console.log('✅ Style editor installed successfully');
|
||||
}
|
||||
} else {
|
||||
console.log('ℹ️ Style editor already installed');
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('🎨 Installing style editor features...');
|
||||
const styleMigrationPath = path.join(__dirname, 'style-editor-migration.sql');
|
||||
if (fs.existsSync(styleMigrationPath)) {
|
||||
const styleMigration = fs.readFileSync(styleMigrationPath, 'utf8');
|
||||
await db.query(styleMigration);
|
||||
console.log('✅ Style editor installed successfully');
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✨ Database initialization completed successfully!');
|
||||
|
||||
} catch (error) {
|
||||
|
||||
78
database/style-editor-migration.sql
Normal file
78
database/style-editor-migration.sql
Normal file
@@ -0,0 +1,78 @@
|
||||
-- Миграция для добавления полей image_url и расширения настроек сайта
|
||||
|
||||
-- Добавление поля image_url в таблицу routes (если его нет)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'routes' AND column_name = 'image_url') THEN
|
||||
ALTER TABLE routes ADD COLUMN image_url VARCHAR(255);
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Добавление поля category в таблицу site_settings (если его нет)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'site_settings' AND column_name = 'category') THEN
|
||||
ALTER TABLE site_settings ADD COLUMN category VARCHAR(50) DEFAULT 'general';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Обновление типов в таблице site_settings
|
||||
DO $$
|
||||
BEGIN
|
||||
-- Удаляем старое ограничение типов если оно есть
|
||||
ALTER TABLE site_settings DROP CONSTRAINT IF EXISTS site_settings_setting_type_check;
|
||||
|
||||
-- Добавляем новое ограничение с расширенными типами
|
||||
ALTER TABLE site_settings ADD CONSTRAINT site_settings_setting_type_check
|
||||
CHECK (setting_type IN ('text', 'number', 'boolean', 'json', 'color', 'file'));
|
||||
END $$;
|
||||
|
||||
-- Добавление расширенных настроек сайта для редактора стилей
|
||||
INSERT INTO site_settings (setting_key, setting_value, setting_type, description, category, updated_at) VALUES
|
||||
-- Основные цвета темы
|
||||
('primary_color', '#2563eb', 'color', 'Основной цвет сайта', 'colors', NOW()),
|
||||
('secondary_color', '#dc2626', 'color', 'Вторичный цвет сайта', 'colors', NOW()),
|
||||
('accent_color', '#059669', 'color', 'Акцентный цвет', 'colors', NOW()),
|
||||
('text_color', '#334155', 'color', 'Основной цвет текста', 'colors', NOW()),
|
||||
('background_color', '#ffffff', 'color', 'Цвет фона', 'colors', NOW()),
|
||||
('card_background', '#f8fafc', 'color', 'Цвет фона карточек', 'colors', NOW()),
|
||||
|
||||
-- Фоновые изображения
|
||||
('hero_background_url', '/images/korea-hero.jpg', 'file', 'Фоновое изображение главной страницы', 'images', NOW()),
|
||||
('default_tour_image', '/images/placeholder.jpg', 'file', 'Изображение тура по умолчанию', 'images', NOW()),
|
||||
('site_logo_url', '/images/korea-logo.png', 'file', 'Логотип сайта', 'images', NOW()),
|
||||
('favicon_url', '/images/favicon.ico', 'file', 'Иконка сайта', 'images', NOW()),
|
||||
|
||||
-- Типографика
|
||||
('font_family_primary', 'Noto Sans KR, Malgun Gothic, 맑은 고딕, sans-serif', 'text', 'Основной шрифт', 'typography', NOW()),
|
||||
('font_family_display', 'Playfair Display, serif', 'text', 'Декоративный шрифт', 'typography', NOW()),
|
||||
('font_size_base', '16', 'number', 'Базовый размер шрифта (px)', 'typography', NOW()),
|
||||
('line_height_base', '1.7', 'number', 'Базовая высота строки', 'typography', NOW()),
|
||||
|
||||
-- Эффекты и наложения
|
||||
('hero_overlay_opacity', '0.8', 'number', 'Прозрачность наложения на hero фоне (0-1)', 'effects', NOW()),
|
||||
('hero_overlay_color', '#2563eb', 'color', 'Цвет наложения на hero фоне', 'effects', NOW()),
|
||||
('card_shadow', '0 4px 6px -1px rgba(0, 0, 0, 0.1)', 'text', 'Тень карточек (CSS shadow)', 'effects', NOW()),
|
||||
('border_radius', '8', 'number', 'Радиус скругления углов (px)', 'effects', NOW()),
|
||||
('blur_effect', '10', 'number', 'Сила размытия эффектов (px)', 'effects', NOW()),
|
||||
|
||||
-- Макет и размеры
|
||||
('hero_height_desktop', '70', 'number', 'Высота hero секции на десктопе (vh)', 'layout', NOW()),
|
||||
('hero_height_mobile', '50', 'number', 'Высота hero секции на мобильных (vh)', 'layout', NOW()),
|
||||
('compact_hero_height', '25', 'number', 'Высота компактных hero секций (vh)', 'layout', NOW()),
|
||||
('container_max_width', '1200', 'number', 'Максимальная ширина контейнера (px)', 'layout', NOW()),
|
||||
('navbar_height', '76', 'number', 'Высота навигационной панели (px)', 'layout', NOW()),
|
||||
|
||||
-- Дополнительные стили
|
||||
('custom_css', '', 'text', 'Дополнительный CSS код', 'theme', NOW()),
|
||||
('google_fonts_url', '', 'text', 'URL для подключения Google Fonts', 'typography', NOW()),
|
||||
('animation_duration', '0.3', 'number', 'Длительность анимаций (секунды)', 'effects', NOW())
|
||||
|
||||
ON CONFLICT (setting_key) DO UPDATE SET
|
||||
setting_value = EXCLUDED.setting_value,
|
||||
setting_type = EXCLUDED.setting_type,
|
||||
description = EXCLUDED.description,
|
||||
category = EXCLUDED.category,
|
||||
updated_at = NOW();
|
||||
Reference in New Issue
Block a user