feat: новая функция

This commit is contained in:
2025-10-22 09:55:33 +03:00
parent 6efc976246
commit 0cbc31812a
8 changed files with 517 additions and 144 deletions

View File

@@ -273,10 +273,11 @@ onMounted(() => {
display: flex;
flex-direction: column;
max-width: calc(100% - 350px);
padding: 0 20px;
padding: 0 20px 20px 20px; /* Уменьшаем отступ снизу */
background-color: var(--color-white);
height: 100vh;
overflow: hidden;
min-height: 100vh; /* Изменяем на min-height для возможности прокрутки */
overflow-y: auto; /* Разрешаем вертикальную прокрутку */
overflow-x: hidden;
}
.main-content.no-right-sidebar {
@@ -293,18 +294,7 @@ onMounted(() => {
@media (max-width: 768px) {
.main-content {
max-width: 100%;
padding-bottom: 20px; /* Убираем большой отступ, так как панель теперь полноэкранная */
}
.main-content.no-right-sidebar {
padding-bottom: 20px;
}
}
@media (max-width: 480px) {
.main-content {
padding: 0 10px;
padding-bottom: 10px; /* Убираем большой отступ */
padding-bottom: 10px; /* Уменьшаем отступ для мобильных устройств */
}
.main-content.no-right-sidebar {
@@ -312,5 +302,16 @@ onMounted(() => {
}
}
@media (max-width: 480px) {
.main-content {
padding: 0 10px;
padding-bottom: 5px; /* Минимальный отступ для очень маленьких экранов */
}
.main-content.no-right-sidebar {
padding-bottom: 5px;
}
}
</style>

View File

@@ -583,6 +583,8 @@ async function handleAiReply() {
transition: all var(--transition-normal);
z-index: 10;
box-shadow: 0 -2px 5px rgba(0, 0, 0, 0.05);
position: sticky;
bottom: 0;
}
.chat-input textarea {
@@ -769,7 +771,8 @@ async function handleAiReply() {
@media (max-width: 480px) {
.chat-input {
position: static !important;
position: sticky !important;
bottom: 0 !important;
border-radius: 0 !important;
padding: 8px 12px !important;
background: #f8f8f8 !important;
@@ -783,7 +786,8 @@ async function handleAiReply() {
@media (max-width: 600px) {
.chat-input {
position: static !important;
position: sticky !important;
bottom: 0 !important;
border-radius: 0 !important;
padding: 8px 12px !important;
background: #f8f8f8 !important;

View File

@@ -77,7 +77,10 @@ const pageTitle = computed(() => {
return 'Создать новое DLE (Digital Legal Entity)';
}
if (route.name === 'settings-dle-v2-deploy') {
return 'Создать современное DLE v2 (Digital Legal Entity)';
return 'Деплой контракта DLE (Digital Legal Entity)';
}
if (route.name === 'settings-security') {
return ''; // Убираем заголовок для страницы безопасности, так как он есть внутри компонента
}
return 'Настройки';
});
@@ -119,7 +122,8 @@ onBeforeUnmount(() => {
border-radius: var(--block-radius);
box-shadow: var(--shadow-md);
margin-top: 20px;
margin-bottom: 20px;
margin-bottom: 20px; /* Уменьшаем отступ, так как он уже есть в BaseLayout */
min-height: auto; /* Убираем фиксированную высоту */
}
/* Заголовки */

View File

@@ -34,8 +34,8 @@
</select>
</div>
<!-- Российские классификаторы (отображается только для России) -->
<div v-if="dleSettings.jurisdiction === '643'">
<!-- Классификаторы видов деятельности -->
<div v-if="dleSettings.jurisdiction">
<div v-if="isLoadingRussianClassifiers" class="loading-section">
<p><i class="fas fa-spinner fa-spin"></i> Загрузка российских классификаторов...</p>
</div>
@@ -196,12 +196,14 @@
</div>
</div>
<!-- ОКВЭД - Виды экономической деятельности -->
<!-- Виды экономической деятельности -->
<div class="form-group okved-section">
<label class="form-label okved-title">ОКВЭД (виды экономической деятельности):</label>
<label class="form-label okved-title">
{{ dleSettings.jurisdiction === '643' ? 'ОКВЭД (виды экономической деятельности)' : 'ISIC (виды экономической деятельности)' }}:
</label>
<!-- Простой 2-уровневый выбор ОКВЭД -->
<div class="okved-cascade">
<!-- Форма для России (ОКВЭД) -->
<div v-if="dleSettings.jurisdiction === '643'" class="okved-cascade">
<!-- Уровень 1: Класс (01.11, 01.12...) -->
<div class="form-group">
<label class="form-label-small">Выберите класс деятельности:</label>
@@ -241,6 +243,77 @@
</div>
</div>
<!-- Форма для других стран (ISIC) -->
<div v-else class="isic-cascade">
<!-- Уровень 1: Раздел (A, B, C...) -->
<div class="form-group">
<label class="form-label-small">Выберите раздел деятельности:</label>
<select v-model="selectedIsicLevel1" class="form-control" :disabled="isLoadingIsicLevel1">
<option value="">-- {{ isLoadingIsicLevel1 ? 'Загрузка разделов...' : 'Выберите раздел' }} --</option>
<option
v-for="option in isicLevel1Options"
:key="option.value"
:value="option.value"
>
{{ option.text }}
</option>
</select>
</div>
<!-- Уровень 2: Группа (01, 02, 03...) -->
<div class="form-group" v-if="selectedIsicLevel1">
<label class="form-label-small">Выберите группу деятельности:</label>
<select v-model="selectedIsicLevel2" class="form-control" :disabled="isLoadingIsicLevel2">
<option value="">-- {{ isLoadingIsicLevel2 ? 'Загрузка групп...' : 'Выберите группу' }} --</option>
<option
v-for="option in isicLevel2Options"
:key="option.value"
:value="option.value"
>
{{ option.text }}
</option>
</select>
</div>
<!-- Уровень 3: Класс (011, 012, 013...) -->
<div class="form-group" v-if="selectedIsicLevel2">
<label class="form-label-small">Выберите класс деятельности:</label>
<select v-model="selectedIsicLevel3" class="form-control" :disabled="isLoadingIsicLevel3">
<option value="">-- {{ isLoadingIsicLevel3 ? 'Загрузка классов...' : 'Выберите класс' }} --</option>
<option
v-for="option in isicLevel3Options"
:key="option.value"
:value="option.value"
>
{{ option.text }}
</option>
</select>
</div>
<!-- Уровень 4: Подкласс (0111, 0112, 0113...) -->
<div class="form-group" v-if="selectedIsicLevel3">
<label class="form-label-small">Выберите подкласс деятельности:</label>
<select v-model="selectedIsicLevel4" class="form-control" :disabled="isLoadingIsicLevel4">
<option value="">-- {{ isLoadingIsicLevel4 ? 'Загрузка подклассов...' : 'Выберите подкласс' }} --</option>
<option
v-for="option in isicLevel4Options"
:key="option.value"
:value="option.value"
>
{{ option.text }}
</option>
</select>
</div>
<!-- Выбранный код ISIC -->
<div v-if="currentSelectedIsicText" class="current-isic-selection">
<p><strong>Выбранный код:</strong> {{ currentSelectedIsicText }}</p>
<button @click="addIsicCode" class="btn btn-success btn-sm" :disabled="!currentSelectedIsicCode">
Добавить код деятельности
</button>
</div>
</div>
<!-- Основной код ОКВЭД (оставляем для совместимости) -->
<div class="okved-main" style="display: none;">
<select v-model="dleSettings.mainOkvedCode" class="form-control">
@@ -1117,17 +1190,29 @@ const autoSelectedOktmoInfo = computed(() => {
return russianClassifiers.oktmo.find(oktmo => oktmo.code === dleSettings.selectedOktmo);
});
// ===== КАСКАДНАЯ СИСТЕМА ОКВЭД =====
// ===== КАСКАДНАЯ СИСТЕМА КЛАССИФИКАТОРОВ =====
// Состояние для загрузки и опций ОКВЭД
// Состояние для загрузки и опций ОКВЭД/ISIC
const okvedLevel1Options = ref([]);
const okvedLevel2Options = ref([]);
const okvedLevel3Options = ref([]);
// Состояние для загрузки ISIC кодов
const isicLevel1Options = ref([]);
const isicLevel2Options = ref([]);
const isicLevel3Options = ref([]);
const isicLevel4Options = ref([]);
const okvedLevel4Options = ref([]);
const isLoadingOkvedLevel1 = ref(false);
const isLoadingOkvedLevel2 = ref(false);
const isLoadingOkvedLevel3 = ref(false);
// Состояние загрузки ISIC
const isLoadingIsicLevel1 = ref(false);
const isLoadingIsicLevel2 = ref(false);
const isLoadingIsicLevel3 = ref(false);
const isLoadingIsicLevel4 = ref(false);
const isLoadingOkvedLevel4 = ref(false);
// Состояние для КПП кодов
@@ -1140,10 +1225,20 @@ const selectedOkvedLevel2 = ref('');
const selectedOkvedLevel3 = ref('');
const selectedOkvedLevel4 = ref('');
// Выбранные значения на каждом уровне ISIC
const selectedIsicLevel1 = ref('');
const selectedIsicLevel2 = ref('');
const selectedIsicLevel3 = ref('');
const selectedIsicLevel4 = ref('');
// Текущий выбранный код ОКВЭД
const currentSelectedOkvedCode = ref('');
const currentSelectedOkvedText = ref('');
// Текущий выбранный код ISIC
const currentSelectedIsicCode = ref('');
const currentSelectedIsicText = ref('');
// Функция определения уровня ОКВЭД кода
const getOkvedLevel = (code) => {
@@ -1156,6 +1251,42 @@ const getOkvedLevel = (code) => {
return parts.length + 1; // для более глубоких уровней
};
// Функция для загрузки ISIC кодов определенного уровня
const fetchIsicCodes = async (level, parentCode, optionsRef, loadingRef) => {
loadingRef.value = true;
optionsRef.value = [];
try {
console.log(`[DleDeployForm] Загрузка ISIC уровень ${level}, родитель: ${parentCode || 'root'}`);
const params = {
level: level,
limit: 1000 // Увеличиваем лимит для получения всех кодов
};
if (parentCode) {
params.parent_code = parentCode;
}
const response = await api.get('/isic/codes', { params });
if (response.data && response.data.codes) {
optionsRef.value = response.data.codes.map(code => ({
value: code.code,
text: `${code.code} - ${code.description}`
}));
console.log(`[DleDeployForm] Загружено ISIC кодов уровня ${level}: ${optionsRef.value.length}`);
} else {
console.error('[DleDeployForm] Ошибка ответа API ISIC:', response.data);
}
} catch (error) {
console.error('[DleDeployForm] Ошибка при загрузке ISIC кодов:', error);
} finally {
loadingRef.value = false;
}
};
// Функция для загрузки ОКВЭД кодов определенного уровня
const fetchOkvedCodes = async (level, parentCode, optionsRef, loadingRef) => {
// console.log(`🔍 fetchOkvedCodes вызвана: level=${level}, parentCode=${parentCode || 'root'}`);
@@ -1204,6 +1335,45 @@ const fetchOkvedCodes = async (level, parentCode, optionsRef, loadingRef) => {
}
};
// Функция для обновления текущего выбранного кода ISIC
const updateCurrentIsicSelection = () => {
let code = '';
let text = '';
let optionsToSearch = [];
let valueToFind = '';
// Приоритет: сначала подкласс, потом класс, потом группа, потом раздел
if (selectedIsicLevel4.value) {
code = selectedIsicLevel4.value;
optionsToSearch = isicLevel4Options.value;
valueToFind = selectedIsicLevel4.value;
} else if (selectedIsicLevel3.value) {
code = selectedIsicLevel3.value;
optionsToSearch = isicLevel3Options.value;
valueToFind = selectedIsicLevel3.value;
} else if (selectedIsicLevel2.value) {
code = selectedIsicLevel2.value;
optionsToSearch = isicLevel2Options.value;
valueToFind = selectedIsicLevel2.value;
} else if (selectedIsicLevel1.value) {
code = selectedIsicLevel1.value;
optionsToSearch = isicLevel1Options.value;
valueToFind = selectedIsicLevel1.value;
}
if (code && optionsToSearch.length > 0 && valueToFind) {
const foundOption = optionsToSearch.find(opt => opt.value === valueToFind);
if (foundOption) {
text = foundOption.text;
} else {
text = code;
}
}
currentSelectedIsicCode.value = code;
currentSelectedIsicText.value = text;
};
// Функция для обновления текущего выбранного кода ОКВЭД
const updateCurrentOkvedSelection = () => {
let code = '';
@@ -1250,6 +1420,69 @@ watch(selectedOkvedLevel2, () => {
updateCurrentOkvedSelection();
});
// Watchers для ISIC
watch(selectedIsicLevel1, (newVal) => {
selectedIsicLevel2.value = '';
selectedIsicLevel3.value = '';
selectedIsicLevel4.value = '';
if (newVal) {
fetchIsicCodes(2, newVal, isicLevel2Options, isLoadingIsicLevel2);
} else {
isicLevel2Options.value = [];
isicLevel3Options.value = [];
isicLevel4Options.value = [];
}
updateCurrentIsicSelection();
});
watch(selectedIsicLevel2, (newVal) => {
selectedIsicLevel3.value = '';
selectedIsicLevel4.value = '';
if (newVal) {
fetchIsicCodes(3, newVal, isicLevel3Options, isLoadingIsicLevel3);
} else {
isicLevel3Options.value = [];
isicLevel4Options.value = [];
}
updateCurrentIsicSelection();
});
watch(selectedIsicLevel3, (newVal) => {
selectedIsicLevel4.value = '';
if (newVal) {
fetchIsicCodes(4, newVal, isicLevel4Options, isLoadingIsicLevel4);
} else {
isicLevel4Options.value = [];
}
updateCurrentIsicSelection();
});
watch(selectedIsicLevel4, () => {
updateCurrentIsicSelection();
});
// Функция добавления выбранного ISIC кода в список
const addIsicCode = () => {
if (currentSelectedIsicCode.value && currentSelectedIsicText.value) {
const alreadyExists = dleSettings.selectedOkved.find(c => c === currentSelectedIsicCode.value);
if (!alreadyExists) {
dleSettings.selectedOkved.push(currentSelectedIsicCode.value);
dleSettings.mainOkvedCode = currentSelectedIsicCode.value; // Обновляем основной код
// Сбрасываем селекторы для выбора следующего кода
selectedIsicLevel1.value = '';
selectedIsicLevel2.value = '';
selectedIsicLevel3.value = '';
selectedIsicLevel4.value = '';
// Остальные опции сбросятся через watchers
} else {
alert('Этот код уже добавлен.');
}
} else {
alert('Код не выбран полностью.');
}
};
// Функция добавления выбранного ОКВЭД кода в список
const addOkvedCode = () => {
if (currentSelectedOkvedCode.value && currentSelectedOkvedText.value) {
@@ -1940,7 +2173,59 @@ const loadCountries = async () => {
}
};
// Функция загрузки российских классификаторов
// Функция загрузки классификаторов в зависимости от выбранной страны
const loadClassifiers = async () => {
isLoadingRussianClassifiers.value = true;
try {
if (dleSettings.jurisdiction === '643') {
// Для России загружаем российские классификаторы
console.log('Загружаем российские классификаторы...');
const response = await api.get('/russian-classifiers/all');
if (response.data && response.data.success) {
const data = response.data.data;
russianClassifiers.oktmo = data.oktmo || [];
russianClassifiers.okved = data.okved || [];
console.log('Российские классификаторы загружены:', {
oktmo: russianClassifiers.oktmo.length,
okved: russianClassifiers.okved.length
});
// Инициализируем каскадную систему ОКВЭД
if (russianClassifiers.okved.length > 0) {
console.log('🎯 Инициализируем каскадную систему ОКВЭД...');
await fetchOkvedCodes(1, null, okvedLevel1Options, isLoadingOkvedLevel1);
if (selectedOkvedLevel1.value) {
await fetchOkvedCodes(2, selectedOkvedLevel1.value, okvedLevel2Options, isLoadingOkvedLevel2);
}
}
loadKppCodes();
}
} else {
// Для других стран загружаем ISIC
console.log('Загружаем ISIC классификаторы...');
// Инициализируем каскадную систему ISIC
console.log('🎯 Инициализируем каскадную систему ISIC...');
await fetchIsicCodes(1, null, isicLevel1Options, isLoadingIsicLevel1);
if (selectedIsicLevel1.value) {
await fetchIsicCodes(2, selectedIsicLevel1.value, isicLevel2Options, isLoadingIsicLevel2);
}
}
} catch (error) {
console.error('Ошибка при загрузке классификаторов:', error);
alert('Не удалось загрузить классификаторы с сервера.');
} finally {
isLoadingRussianClassifiers.value = false;
}
};
// Функция загрузки российских классификаторов (для совместимости)
const loadRussianClassifiers = async () => {
isLoadingRussianClassifiers.value = true;
try {
@@ -2324,9 +2609,9 @@ watch(() => dleSettings.jurisdiction, (newJurisdiction, oldJurisdiction) => {
autoSelectedOktmo.value = false;
lastApiResult.value = null;
// Загружаем российские классификаторы при выборе России
if (newJurisdiction === '643') {
loadRussianClassifiers();
// Загружаем классификаторы в зависимости от выбранной страны
if (newJurisdiction) {
loadClassifiers();
}
// Автосохранение
@@ -2840,20 +3125,21 @@ async function submitDeploy() {
.explorer-keys-grid { grid-template-columns: 1fr 1fr; }
}
.settings-panel {
padding: var(--block-padding);
background-color: var(--color-light);
border-radius: var(--radius-md);
margin-top: var(--spacing-lg);
padding: 0; /* Убираем отступы, так как они уже есть в родительском контейнере */
background-color: transparent; /* Убираем фон, так как он уже есть в родительском контейнере */
border-radius: 0; /* Убираем скругление углов */
margin-top: 0; /* Убираем отступ сверху */
animation: fadeIn var(--transition-normal);
}
.settings-block {
background: #fff;
border-radius: var(--radius-lg, 16px);
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 20px;
margin-top: 20px;
margin-bottom: 20px;
background: white;
border-radius: 12px; /* Согласуем с основными блоками */
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); /* Согласуем тень */
border: 1px solid #e9ecef; /* Добавляем границу как у основных блоков */
padding: 2rem; /* Увеличиваем отступы */
margin-top: 2rem; /* Увеличиваем отступ сверху */
margin-bottom: 2rem; /* Увеличиваем отступ снизу */
width: 100%;
position: relative;
overflow-x: auto;
@@ -2878,7 +3164,8 @@ async function submitDeploy() {
.form-section h3 {
color: var(--color-primary);
margin-bottom: 1rem;
font-size: 1.2rem;
font-size: 1.5rem;
font-weight: 600; /* Согласуем с основными заголовками */
}
.form-group {
@@ -2910,16 +3197,16 @@ async function submitDeploy() {
.form-control {
width: 100%;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 6px;
border: 1px solid #e9ecef; /* Согласуем с общими стилями */
border-radius: 8px; /* Согласуем с кнопками */
font-size: 1rem;
transition: border-color 0.2s;
transition: all 0.2s; /* Добавляем плавный переход для всех свойств */
}
.form-control:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2); /* Согласуем с основными стилями */
}
.address-input-group {
@@ -3012,9 +3299,10 @@ async function submitDeploy() {
.btn {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 6px;
border-radius: 8px; /* Согласуем с основными кнопками */
cursor: pointer;
font-size: 1rem;
font-weight: 600; /* Добавляем жирность */
transition: all 0.2s;
text-decoration: none;
display: inline-flex;
@@ -3029,11 +3317,13 @@ async function submitDeploy() {
.btn-primary:hover {
background: var(--color-primary-dark);
transform: translateY(-1px); /* Добавляем эффект hover */
}
.btn-primary:disabled {
background: #ccc;
cursor: not-allowed;
transform: none; /* Убираем эффект hover для отключенных кнопок */
}
.btn-secondary {
@@ -3606,6 +3896,24 @@ async function submitDeploy() {
color: #155724;
}
.current-isic-selection {
margin-top: 1rem;
padding: 0.75rem;
background: #e8f5e8;
border-radius: 4px;
border: 1px solid #28a745;
}
.current-isic-selection p {
margin: 0 0 0.5rem 0;
font-weight: 500;
color: #155724;
}
.isic-cascade {
margin-bottom: 1rem;
}
.selected-okved-codes {
margin-top: 1rem;
}

View File

@@ -11,51 +11,56 @@
-->
<template>
<div class="security-settings settings-panel">
<div class="security-settings">
<button class="close-btn" @click="goBack">×</button>
<h2>Настройки безопасности и подключения к блокчейну</h2>
<!-- Заголовок в стиле основной страницы настроек -->
<div class="management-header">
<div class="header-content">
<h1>Настройки безопасности</h1>
</div>
</div>
<div class="settings-cards">
<!-- Блок RPC Провайдеры -->
<div class="info-card">
<h3>RPC Провайдеры</h3>
<div class="info-row">
<span class="info-label">Провайдеры:</span>
<span class="info-value">{{ securitySettings.rpcConfigs.length > 0 ? `${securitySettings.rpcConfigs.length} настроено` : 'Не настроено' }}</span>
</div>
<div class="card-actions">
<button class="btn btn-info" @click="handleRpcDetailsClick">
<i class="fas fa-info-circle"></i> Подробнее
<!-- Блоки настроек в едином стиле -->
<div class="management-blocks">
<!-- Столбец 1 -->
<div class="blocks-column">
<!-- Блок RPC Провайдеры -->
<div class="management-block">
<h3>RPC Провайдеры</h3>
<p>{{ securitySettings.rpcConfigs.length > 0 ? `${securitySettings.rpcConfigs.length} провайдеров настроено` : 'RPC провайдеры не настроены' }}</p>
<button class="details-btn" @click="handleRpcDetailsClick">
Подробнее
</button>
</div>
</div>
<!-- Блок Аутентификация -->
<div class="info-card">
<h3>Аутентификация</h3>
<div class="info-row">
<span class="info-label">Токены:</span>
<span class="info-value">{{ securitySettings.authTokens.length > 0 ? `${securitySettings.authTokens.length} настроено` : 'Не настроено' }}</span>
</div>
<div class="card-actions">
<button class="btn btn-info" @click="showAuthSettings = !showAuthSettings">
<i class="fas fa-info-circle"></i> Подробнее
<!-- Столбец 2 -->
<div class="blocks-column">
<!-- Блок Аутентификация -->
<div class="management-block">
<h3>Аутентификация</h3>
<p>{{ securitySettings.authTokens.length > 0 ? `${securitySettings.authTokens.length} токенов настроено` : 'Токены аутентификации не настроены' }}</p>
<button class="details-btn" @click="showAuthSettings = !showAuthSettings">
Подробнее
</button>
</div>
</div>
</div>
<RpcProvidersSettings
v-if="showRpcSettings"
:rpcConfigs="securitySettings.rpcConfigs"
@update="loadSettings"
@test="testRpcHandler"
/>
<AuthTokensSettings
v-if="showAuthSettings"
:authTokens="securitySettings.authTokens"
@update="loadSettings"
/>
<div v-if="showRpcSettings" class="detail-panel">
<RpcProvidersSettings
:rpcConfigs="securitySettings.rpcConfigs"
@update="loadSettings"
@test="testRpcHandler"
/>
</div>
<div v-if="showAuthSettings" class="detail-panel">
<AuthTokensSettings
:authTokens="securitySettings.authTokens"
@update="loadSettings"
/>
</div>
<!-- Модальное окно "Нет доступа" -->
<NoAccessModal
@@ -367,82 +372,105 @@ const goBack = () => router.push('/settings');
</script>
<style scoped>
.settings-panel {
padding: var(--block-padding, 20px);
background-color: var(--color-light, #fff);
border-radius: var(--radius-md, 8px);
margin-top: var(--spacing-lg, 20px);
animation: fadeIn var(--transition-normal, 0.3s);
.security-settings {
position: relative;
}
h2 {
margin-bottom: var(--spacing-lg, 20px);
border-bottom: 1px solid var(--color-grey-light, #eee);
padding-bottom: var(--spacing-md, 15px);
color: var(--color-dark, #333);
/* Заголовок в стиле основной страницы настроек */
.management-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 2px solid #e9ecef;
}
h3 {
margin-bottom: var(--spacing-md, 15px);
color: var(--color-primary, #4caf50);
.header-content h1 {
margin: 0;
color: var(--color-primary);
font-size: 2rem;
font-weight: 700;
}
h4 {
margin-bottom: var(--spacing-sm, 10px);
color: var(--color-dark, #333);
}
.settings-cards {
/* Блоки настроек в едином стиле */
.management-blocks {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: var(--spacing-md, 15px);
margin-bottom: var(--spacing-lg, 20px);
grid-template-columns: repeat(2, 1fr);
gap: 2rem;
}
.info-card {
border: 1px solid var(--color-grey-light, #eee);
border-radius: var(--radius-md, 8px);
padding: var(--spacing-md, 15px);
background-color: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
transition: box-shadow 0.3s ease, transform 0.3s ease;
.blocks-column {
display: flex;
flex-direction: column;
gap: 1.5rem;
align-items: stretch;
}
.info-card:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
.management-block {
background: white;
border-radius: 12px;
padding: 2rem;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
border: 1px solid #e9ecef;
transition: all 0.3s ease;
text-align: center;
display: flex;
flex-direction: column;
justify-content: space-between;
height: 250px;
}
.management-block:hover {
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
transform: translateY(-2px);
border-color: var(--color-primary);
}
.info-row {
display: flex;
margin-bottom: var(--spacing-xs, 5px);
.management-block h3 {
margin: 0 0 1rem 0;
color: var(--color-primary);
font-size: 1.5rem;
font-weight: 600;
flex-shrink: 0;
}
.info-label {
font-weight: 500;
color: var(--color-primary, #4caf50);
margin-right: var(--spacing-sm, 10px);
min-width: 80px;
.management-block p {
margin: 0 0 1.5rem 0;
color: #666;
font-size: 1rem;
line-height: 1.5;
flex-grow: 1;
}
.info-value {
color: var(--color-dark, #333);
.details-btn {
background: var(--color-primary);
color: #fff;
border: none;
border-radius: 8px;
padding: 0.75rem 1.5rem;
cursor: pointer;
font-size: 1rem;
font-weight: 600;
transition: all 0.2s;
min-width: 120px;
flex-shrink: 0;
margin-top: auto;
}
.card-actions {
display: flex;
justify-content: flex-end;
margin-top: var(--spacing-md, 15px);
.details-btn:hover {
background: var(--color-primary-dark);
transform: translateY(-1px);
}
.detail-panel {
margin-top: var(--spacing-md, 15px);
margin-bottom: var(--spacing-lg, 20px);
padding: var(--spacing-md, 15px);
border: 1px solid var(--color-grey-light, #eee);
border-radius: var(--radius-md, 8px);
background-color: var(--color-light, #fafafa);
margin-top: 3rem; /* Увеличиваем отступ сверху */
margin-bottom: 2rem; /* Добавляем отступ снизу */
padding: 2rem; /* Увеличиваем внутренние отступы */
border: 1px solid #e9ecef;
border-radius: 12px; /* Согласуем с основными блоками */
background-color: white; /* Белый фон как у основных блоков */
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); /* Добавляем тень */
animation: slideDown 0.3s ease;
}
@@ -656,13 +684,41 @@ small {
right: 18px;
background: none;
border: none;
font-size: 2rem;
font-size: 1.5rem;
cursor: pointer;
color: #bbb;
transition: color 0.2s;
color: #666;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: all 0.2s;
z-index: 10;
}
.close-btn:hover {
background: #f0f0f0;
color: #333;
}
/* Адаптивность */
@media (max-width: 1024px) {
.management-blocks {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.management-header {
flex-direction: column;
align-items: flex-start;
gap: 1rem;
}
.header-content h1 {
font-size: 1.5rem;
}
}
</style>

View File

@@ -62,10 +62,10 @@
<style scoped>
.settings-management {
padding: 20px;
background-color: var(--color-white);
border-radius: var(--radius-lg);
min-height: 100vh;
padding: 0; /* Убираем отступы, так как они уже есть в родительском контейнере */
background-color: transparent; /* Убираем фон, так как он уже есть в родительском контейнере */
border-radius: 0; /* Убираем скругление углов */
min-height: auto; /* Убираем фиксированную высоту */
}
.management-header {