diff --git a/frontend/package.json b/frontend/package.json index 15eba81..b7594bd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,7 +12,8 @@ "lint:style": "stylelint \"**/*.{vue,css}\"", "lint:style:fix": "stylelint \"**/*.{vue,css}\" --fix", "format": "prettier --write \"**/*.{js,vue,json,md}\"", - "format:check": "prettier --check \"**/*.{js,vue,json,md}\"" + "format:check": "prettier --check \"**/*.{js,vue,json,md}\"", + "dev:styles": "node scripts/style-check.js && yarn dev" }, "dependencies": { "axios": "^1.8.4", diff --git a/frontend/scripts/style-check.js b/frontend/scripts/style-check.js new file mode 100644 index 0000000..62db251 --- /dev/null +++ b/frontend/scripts/style-check.js @@ -0,0 +1,112 @@ +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import { execSync } from 'child_process'; +import chalk from 'chalk'; // Для цветного вывода + +// ES модули не поддерживают __dirname, поэтому создаем его +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Проверка наличия пакета chalk и его установка при необходимости +try { + import('chalk'); +} catch (e) { + console.log('Устанавливаем пакет chalk для цветного вывода...'); + execSync('yarn add chalk --dev', { stdio: 'inherit' }); + console.log('Пакет chalk установлен.'); +} + +// Функция для проверки наличия файла +function checkFileExists(filePath, errorMessage) { + const fullPath = path.resolve(__dirname, '..', filePath); + if (!fs.existsSync(fullPath)) { + console.log(chalk.red(errorMessage)); + process.exit(1); + } + console.log(chalk.green(`✓ Файл ${path.basename(filePath)} найден`)); +} + +// Функция для проверки импортов стилей в App.vue +function checkStyleImports() { + const appVuePath = path.resolve(__dirname, '..', 'src', 'App.vue'); + try { + const appVueContent = fs.readFileSync(appVuePath, 'utf8'); + + const requiredImports = [ + './assets/styles/variables.css', + './assets/styles/base.css', + './assets/styles/layout.css', + './assets/styles/global.css' + ]; + + let allImportsFound = true; + + for (const importPath of requiredImports) { + if (!appVueContent.includes(`import '${importPath}'`)) { + console.log(chalk.red(`✗ Импорт ${importPath} не найден в App.vue!`)); + allImportsFound = false; + } else { + console.log(chalk.green(`✓ Импорт ${importPath} найден в App.vue`)); + } + } + + if (!allImportsFound) { + console.log(chalk.yellow('Убедитесь, что в App.vue импортируются все нужные стили:')); + requiredImports.forEach(imp => console.log(` import '${imp}';`)); + } + } catch (error) { + console.log(chalk.red(`Ошибка при чтении App.vue: ${error.message}`)); + process.exit(1); + } +} + +// Функция для проверки компонентов настроек +function checkSettingsComponents() { + const settingsDir = path.resolve(__dirname, '..', 'src', 'components', 'settings'); + const requiredComponents = [ + 'AISettings.vue', + 'BlockchainSettings.vue', + 'SecuritySettings.vue', + 'InterfaceSettings.vue' + ]; + + for (const component of requiredComponents) { + const componentPath = path.join(settingsDir, component); + if (fs.existsSync(componentPath)) { + console.log(chalk.green(`✓ Компонент ${component} найден`)); + } else { + console.log(chalk.red(`✗ Компонент ${component} не найден!`)); + } + } +} + +// Запуск скрипта +console.log(chalk.blue('=======================================')); +console.log(chalk.green('Запуск проекта с обновленными стилями')); +console.log(chalk.blue('=======================================')); + +// Проверка наличия всех файлов стилей +checkFileExists('src/assets/styles/global.css', 'Ошибка: файл global.css не найден!'); +checkFileExists('src/assets/styles/variables.css', 'Ошибка: файл variables.css не найден!'); +checkFileExists('src/assets/styles/base.css', 'Ошибка: файл base.css не найден!'); +checkFileExists('src/assets/styles/layout.css', 'Ошибка: файл layout.css не найден!'); + +// Проверка импортов стилей +console.log(chalk.yellow('Проверка imports стилей...')); +checkStyleImports(); + +// Проверка компонентов настроек +checkSettingsComponents(); + +console.log(chalk.blue('---------------------------------------')); +console.log(chalk.yellow('Запуск сервера разработки...')); +console.log(chalk.blue('---------------------------------------')); + +// Выходим успешно, т.к. сам запуск выполняется командой yarn dev:styles +try { + process.exit(0); +} catch (error) { + console.log(chalk.red(`Ошибка при запуске сервера разработки: ${error.message}`)); + process.exit(1); +} \ No newline at end of file diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 43d3ac6..b9bddde 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -4,15 +4,31 @@
- + + + diff --git a/frontend/src/assets/styles/README.md b/frontend/src/assets/styles/README.md new file mode 100644 index 0000000..08d65a7 --- /dev/null +++ b/frontend/src/assets/styles/README.md @@ -0,0 +1,95 @@ +# Структура стилей проекта + +## Обзор + +Проект использует структурированный подход к организации стилей CSS для улучшения поддерживаемости, предотвращения конфликтов и обеспечения согласованности пользовательского интерфейса. + +## Файлы стилей + +- **variables.css** - CSS-переменные (цвета, размеры, отступы) +- **base.css** - Базовые стили для всего приложения и сброс стилей +- **layout.css** - Стили для основной структуры макета приложения +- **global.css** - Общие утилитарные классы, доступные во всем приложении +- **home.css.bak** - Устаревший файл, переименован в .bak. Стили перенесены в scoped стили компонентов + +## Приоритеты использования стилей + +1. **Компонентные scoped стили** - для стилей, специфичных для компонента +2. **global.css** - для общих классов, используемых в нескольких компонентах +3. **variables.css** - для общих переменных CSS во всем проекте + +## Рекомендации по использованию + +### Для новых компонентов: + +1. Используйте scoped стили внутри файла компонента: + ```vue + + ``` + +2. Используйте глобальные классы для общих элементов: + ```html + + ``` + +3. Используйте CSS-переменные вместо жестко закодированных значений: + ```css + .element { + color: var(--color-primary); + padding: var(--spacing-md); + } + ``` + +### Для существующих компонентов: + +1. При обновлении компонента постепенно переносите стили из home.css в scoped стили компонента +2. Не удаляйте стили из home.css до полного тестирования всех зависящих компонентов + +## Глобальные CSS-классы + +### Контейнеры +- `.page-container` - Основной контейнер страницы +- `.card` - Контейнер для блока информации + +### Кнопки +- `.btn` - Базовый класс для всех кнопок +- `.btn-primary` - Основная (зеленая) кнопка +- `.btn-secondary` - Дополнительная (синяя) кнопка +- `.btn-accent` - Акцентная (фиолетовая) кнопка +- `.btn-danger` - Кнопка опасного действия (красная) + +### Формы +- `.form-control` - Элемент формы (input, select, textarea) +- `.form-group` - Группа элементов формы +- `.form-label` - Метка для элемента формы + +### Утилиты +- `.text-center` - Выравнивание текста по центру +- `.d-flex` - Включение flex-контейнера +- `.mt-*`, `.mb-*` - Отступы сверху/снизу + +## Процесс миграции + +Постепенно мы переходим от использования большого глобального файла home.css к модульным scoped стилям в компонентах и более структурированным общим стилям. + +1. Новые компоненты должны использовать только scoped стили и global.css +2. При обновлении существующих компонентов переносите стили из home.css +3. После полного перехода home.css будет удален + +## Выполненная миграция (обновлено) + +Миграция стилей завершена для следующих компонентов: + +1. **ChatInterface.vue** - перенесены стили интерфейса чата, включая адаптивные стили для мобильных устройств +2. **Message.vue** - перенесены стили для сообщений с разными типами вложений + +Файл **home.css** переименован в **home.css.bak** и больше не используется в проекте. Ссылка на него удалена из **HomeView.vue**. + +Для запуска проекта с проверкой стилей можно использовать команду: +``` +yarn dev:styles +``` \ No newline at end of file diff --git a/frontend/src/assets/styles/global.css b/frontend/src/assets/styles/global.css new file mode 100644 index 0000000..a7af0be --- /dev/null +++ b/frontend/src/assets/styles/global.css @@ -0,0 +1,150 @@ +/* frontend/src/assets/styles/global.css */ +/* Общие глобальные стили, используемые во всем приложении */ + +/* Контейнеры */ +.app-container { + display: flex; + flex-direction: column; + min-height: 100vh; + background-color: var(--color-white); +} + +.main-content { + flex: 1; + display: flex; + flex-direction: column; + max-width: 1200px; + margin: 0; + padding: 0 20px; + width: 100%; + background-color: var(--color-white); +} + +/* Стандартный контейнер для страниц */ +.page-container { + max-width: 1150px; + margin: 20px auto; + padding: var(--block-padding); + background-color: var(--color-white); + border-radius: var(--block-radius); + box-shadow: var(--shadow-md); +} + +/* Общие стили для кнопок */ +.btn { + height: var(--button-height); + padding: 0 var(--spacing-lg); + border-radius: var(--radius-lg); + font-size: var(--font-size-md); + font-weight: 500; + border: none; + cursor: pointer; + transition: var(--transition-fast); + display: flex; + align-items: center; + justify-content: center; + gap: var(--spacing-sm); + margin: 0; + text-decoration: none; +} + +.btn-primary { + background: var(--color-primary); + color: var(--color-white); +} + +.btn-primary:hover { + background: var(--color-primary-dark); +} + +.btn-secondary { + background: var(--color-secondary); + color: var(--color-white); +} + +.btn-secondary:hover { + background-color: #1976D2; /* Темнее синего */ +} + +.btn-accent { + background: var(--color-accent); + color: var(--color-white); +} + +.btn-accent:hover { + background: var(--color-accent-dark); +} + +.btn-danger { + background: var(--color-danger); + color: var(--color-white); +} + +.btn-danger:hover { + background-color: #D32F2F; /* Темнее красного */ +} + +/* Общие стили для форм */ +.form-control { + height: var(--input-height); + padding: 0 var(--spacing-lg); + border-radius: var(--radius-lg); + border: 1px solid var(--color-grey-light); + font-size: var(--font-size-md); + width: 100%; + background: var(--color-white); +} + +.form-group { + margin-bottom: var(--spacing-md); +} + +.form-label { + display: block; + margin-bottom: var(--spacing-xs); + color: var(--color-text); +} + +/* Общие стили для карточек/блоков */ +.card { + padding: var(--block-padding); + margin-bottom: var(--block-margin); + background: var(--color-white); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-sm); +} + +.card-header { + margin: 0 0 var(--spacing-md) 0; + font-size: var(--font-size-xl); + color: var(--color-dark); + border-bottom: 1px solid var(--color-grey-light); + padding-bottom: var(--spacing-sm); +} + +/* Общие утилиты */ +.text-center { text-align: center; } +.text-right { text-align: right; } +.d-flex { display: flex; } +.flex-column { flex-direction: column; } +.align-center { align-items: center; } +.justify-center { justify-content: center; } +.justify-between { justify-content: space-between; } +.mt-1 { margin-top: var(--spacing-xs); } +.mt-2 { margin-top: var(--spacing-sm); } +.mt-3 { margin-top: var(--spacing-md); } +.mb-1 { margin-bottom: var(--spacing-xs); } +.mb-2 { margin-bottom: var(--spacing-sm); } +.mb-3 { margin-bottom: var(--spacing-md); } + +/* Адаптивные стили */ +@media (max-width: 768px) { + .page-container { + padding: var(--block-padding-mobile); + } + + .btn, .form-control { + height: var(--button-height-mobile); + font-size: var(--font-size-sm); + } +} \ No newline at end of file diff --git a/frontend/src/assets/styles/home.css b/frontend/src/assets/styles/home.css.bak similarity index 100% rename from frontend/src/assets/styles/home.css rename to frontend/src/assets/styles/home.css.bak diff --git a/frontend/src/assets/styles/variables.css b/frontend/src/assets/styles/variables.css index d9d8059..430c4ec 100644 --- a/frontend/src/assets/styles/variables.css +++ b/frontend/src/assets/styles/variables.css @@ -1,79 +1,120 @@ /* frontend/src/assets/styles/variables.css */ :root { - /* Цвета */ - --color-primary: #4CAF50; - --color-primary-dark: #45a049; - --color-secondary: #2196F3; - --color-danger: #F44336; - --color-warning: #FF9800; - --color-light: #f5f5f5; - --color-dark: #333333; - --color-grey: #777777; - --color-grey-light: #e0e0e0; - --color-white: #ffffff; - --color-black: #000000; - --color-telegram: #0088cc; - --color-error: #e74c3c; + /* + * ЦВЕТОВАЯ СХЕМА + * Основная палитра цветов приложения + */ + --color-primary: #4CAF50; /* Основной цвет (зеленый) */ + --color-primary-dark: #45a049; /* Темно-зеленый для наведения и акцентов */ + --color-secondary: #2196F3; /* Второстепенный цвет (синий) */ + --color-accent: #5E35B1; /* Акцентный цвет (фиолетовый) */ + --color-accent-dark: #4527A0; /* Темно-фиолетовый для наведения */ + + /* Статусные цвета */ + --color-danger: #F44336; /* Ошибки, удаление, опасные действия */ + --color-warning: #FF9800; /* Предупреждения */ + --color-error: #e74c3c; /* Текст ошибок */ + + /* Нейтральные цвета */ + --color-light: #f5f5f5; /* Светлый фон, фон секций */ + --color-dark: #333333; /* Основной текст, заголовки */ + --color-grey: #777777; /* Второстепенный текст */ + --color-grey-light: #e0e0e0; /* Границы, разделители */ + --color-white: #ffffff; /* Белый */ + --color-black: #000000; /* Черный */ + + /* Цвета текста */ + --color-text: #333333; /* Основной текст */ + --color-text-light: #999999; /* Неакцентированный текст */ + --color-border: #e0e0e0; /* Цвет рамок */ + + /* Цвета брендов */ + --color-telegram: #0088cc; /* Фирменный цвет Telegram */ /* Цвета сообщений */ - --color-user-message: #EFFAFF; - --color-ai-message: #F8F8F8; - --color-system-message: #FFF3E0; - --color-system-text: #FF5722; + --color-user-message: #EFFAFF; /* Фон сообщений пользователя */ + --color-ai-message: #F8F8F8; /* Фон сообщений ИИ */ + --color-system-message: #FFF3E0; /* Фон системных сообщений */ + --color-system-text: #FF5722; /* Текст системных сообщений */ - /* Тени */ - --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.1); - --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1); - --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1); + /* + * ТЕНИ + * Для создания эффекта глубины и иерархии элементов + */ + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.1); /* Легкая тень */ + --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1); /* Средняя тень */ + --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1); /* Глубокая тень */ - /* Отступы */ - --spacing-xs: 5px; - --spacing-sm: 10px; - --spacing-md: 15px; - --spacing-lg: 20px; - --spacing-xl: 30px; + /* + * ОТСТУПЫ + * Для обеспечения консистентных интервалов и отступов + */ + --spacing-xs: 5px; /* Очень маленькие отступы (между близкими элементами) */ + --spacing-sm: 10px; /* Маленькие отступы */ + --spacing-md: 15px; /* Средние отступы */ + --spacing-lg: 20px; /* Большие отступы */ + --spacing-xl: 30px; /* Очень большие отступы (между крупными блоками) */ - /* Размеры шрифтов */ - --font-size-xs: 12px; - --font-size-sm: 13px; - --font-size-md: 14px; - --font-size-lg: 16px; - --font-size-xl: 18px; - --font-size-xxl: 24px; + /* + * РАЗМЕРЫ ШРИФТОВ + * Типографическая шкала + */ + --font-size-xs: 12px; /* Маленькие надписи, подписи к полям */ + --font-size-sm: 13px; /* Второстепенный текст */ + --font-size-md: 14px; /* Основной текст */ + --font-size-lg: 16px; /* Подзаголовки */ + --font-size-xl: 18px; /* Заголовки разделов */ + --font-size-xxl: 24px; /* Главные заголовки */ - /* Радиусы скругления */ - --radius-sm: 4px; - --radius-md: 6px; - --radius-lg: 8px; + /* + * РАДИУСЫ СКРУГЛЕНИЯ + * Для элементов интерфейса + */ + --radius-sm: 4px; /* Небольшое скругление (кнопки, поля ввода) */ + --radius-md: 6px; /* Среднее скругление (карточки, панели) */ + --radius-lg: 8px; /* Большое скругление (модальные окна, боковые панели) */ - /* Переходы */ - --transition-fast: 0.2s ease; - --transition-normal: 0.3s ease; + /* + * ПЕРЕХОДЫ + * Для плавных анимаций + */ + --transition-fast: 0.2s ease; /* Быстрые переходы (ховеры, небольшие анимации) */ + --transition-normal: 0.3s ease; /* Стандартные переходы (появление элементов) */ - /* Размеры компонентов (Удаляем старые sidebar width) */ - /* --sidebar-width: 110px; */ - /* --sidebar-expanded-width: 325px; */ + /* + * РАЗМЕРЫ КОМПОНЕНТОВ + * Стандартные размеры для элементов интерфейса + */ --nav-btn-size: 40px; --chat-input-min-height: 100px; --chat-input-max-height: 200px; --chat-input-focus-min-height: 170px; --chat-input-focus-max-height: 300px; - /* Унифицированные размеры для кнопок и форм */ + /* + * УНИФИЦИРОВАННЫЕ РАЗМЕРЫ + * Для кнопок и форм + */ --button-height: 48px; --button-height-mobile: 42px; --button-padding: 0 var(--spacing-lg); --button-gap: var(--spacing-md); + --form-gap: var(--spacing-md); + --block-padding: 24px; --block-padding-mobile: 16px; --block-margin: 24px; --block-margin-mobile: 16px; + --input-height: 48px; --input-height-mobile: 42px; --input-padding: 0 var(--spacing-lg); - /* Общие стили */ + /* + * ОБЩИЕ СТИЛИ + * Производные параметры для единого стиля + */ --button-radius: var(--radius-lg); --input-radius: var(--radius-lg); --block-radius: var(--radius-lg); diff --git a/frontend/src/components/BaseLayout.vue b/frontend/src/components/BaseLayout.vue index 213de65..17e1d01 100644 --- a/frontend/src/components/BaseLayout.vue +++ b/frontend/src/components/BaseLayout.vue @@ -15,11 +15,12 @@ \ No newline at end of file diff --git a/frontend/src/components/Header.vue b/frontend/src/components/Header.vue index 99c140e..54ac178 100644 --- a/frontend/src/components/Header.vue +++ b/frontend/src/components/Header.vue @@ -65,7 +65,7 @@ onBeforeUnmount(() => { \ No newline at end of file diff --git a/frontend/src/components/Sidebar.vue b/frontend/src/components/Sidebar.vue index ed5a9fd..c07d0c1 100644 --- a/frontend/src/components/Sidebar.vue +++ b/frontend/src/components/Sidebar.vue @@ -33,25 +33,69 @@ + + + + + + + \ No newline at end of file diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 656b635..37e83a1 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -1,5 +1,10 @@ import { createRouter, createWebHistory } from 'vue-router'; import HomeView from '../views/HomeView.vue'; +// Импортируем (пока не созданные) компоненты для подстраниц настроек +const SettingsAiView = () => import('../views/settings/AiSettingsView.vue'); +const SettingsBlockchainView = () => import('../views/settings/BlockchainSettingsView.vue'); +const SettingsSecurityView = () => import('../views/settings/SecuritySettingsView.vue'); +const SettingsInterfaceView = () => import('../views/settings/InterfaceSettingsView.vue'); import axios from 'axios'; console.log('router/index.js: Script loaded'); @@ -19,6 +24,35 @@ const routes = [ path: '/settings', name: 'settings', component: () => import('../views/SettingsView.vue'), + // Добавляем дочерние маршруты + children: [ + { + path: 'ai', + name: 'settings-ai', + component: SettingsAiView, + }, + { + path: 'blockchain', + name: 'settings-blockchain', + component: SettingsBlockchainView, + }, + { + path: 'security', + name: 'settings-security', + component: SettingsSecurityView, + }, + { + path: 'interface', + name: 'settings-interface', + component: SettingsInterfaceView, + }, + // Опционально: перенаправление со /settings на первую подстраницу + { + path: '', + name: 'settings-index', + redirect: { name: 'settings-ai' } + } + ] }, ]; diff --git a/frontend/src/views/CrmView.vue b/frontend/src/views/CrmView.vue index a5553f2..0808dae 100644 --- a/frontend/src/views/CrmView.vue +++ b/frontend/src/views/CrmView.vue @@ -1,5 +1,11 @@ \ No newline at end of file diff --git a/frontend/src/views/settings/AiSettingsView.vue b/frontend/src/views/settings/AiSettingsView.vue new file mode 100644 index 0000000..d5e490f --- /dev/null +++ b/frontend/src/views/settings/AiSettingsView.vue @@ -0,0 +1,180 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/views/settings/BlockchainSettingsView.vue b/frontend/src/views/settings/BlockchainSettingsView.vue new file mode 100644 index 0000000..8588e99 --- /dev/null +++ b/frontend/src/views/settings/BlockchainSettingsView.vue @@ -0,0 +1,123 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/views/settings/InterfaceSettingsView.vue b/frontend/src/views/settings/InterfaceSettingsView.vue new file mode 100644 index 0000000..69d8284 --- /dev/null +++ b/frontend/src/views/settings/InterfaceSettingsView.vue @@ -0,0 +1,94 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/views/settings/SecuritySettingsView.vue b/frontend/src/views/settings/SecuritySettingsView.vue new file mode 100644 index 0000000..e8a78bc --- /dev/null +++ b/frontend/src/views/settings/SecuritySettingsView.vue @@ -0,0 +1,171 @@ + + + + + \ No newline at end of file