Files
DLE/frontend/src/components/BaseLayout.vue

235 lines
8.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container">
<!-- Основной контент -->
<div class="main-content" :class="{ 'no-right-sidebar': !showWalletSidebar }">
<!-- Шапка сайта -->
<Header
:is-sidebar-open="showWalletSidebar"
@toggle-sidebar="toggleWalletSidebar"
/>
<!-- Основной контент страницы (передается через слот) -->
<slot></slot>
</div>
<!-- Правая панель с информацией о кошельке -->
<Sidebar
v-model="showWalletSidebar"
:is-authenticated="isAuthenticated"
:telegram-auth="telegramAuth"
:email-auth="emailAuth"
:token-balances="tokenBalances"
:identities="identities"
:is-loading-tokens="isLoadingTokens"
@wallet-auth="handleWalletAuth"
@disconnect-wallet="disconnectWallet"
@telegram-auth="handleTelegramAuth"
@cancel-telegram-auth="cancelTelegramAuth"
@email-auth="showEmailForm"
@send-email-verification="sendEmailVerification"
@verify-email-code="verifyEmailCode"
@cancel-email-auth="cancelEmailAuth"
/>
<!-- Компонент для отображения уведомлений -->
<NotificationDisplay :notifications="notifications.value" />
</div>
</template>
<script setup>
import { ref, onMounted, watch, onBeforeUnmount, defineProps, defineEmits } from 'vue';
import { useAuthContext } from '../composables/useAuth';
import { useAuthFlow } from '../composables/useAuthFlow';
import { useNotifications } from '../composables/useNotifications';
import { getFromStorage, setToStorage, removeFromStorage } from '../utils/storage';
import { connectWithWallet } from '../services/wallet';
import api from '../api/axios';
import eventBus from '../utils/eventBus';
import Header from './Header.vue';
import Sidebar from './Sidebar.vue';
import NotificationDisplay from './NotificationDisplay.vue';
// =====================================================================
// 1. ИСПОЛЬЗОВАНИЕ COMPOSABLES
// =====================================================================
const auth = useAuthContext();
const { notifications, showSuccessMessage, showErrorMessage } = useNotifications();
// Определяем props, которые будут приходить от родительского View
const props = defineProps({
isAuthenticated: Boolean,
identities: Array,
tokenBalances: Object,
isLoadingTokens: Boolean
});
// Определяем emits
const emit = defineEmits(['auth-action-completed']);
// Callback после успешной аутентификации/привязки через Email/Telegram
const handleAuthFlowSuccess = (authType) => {
console.log(`[BaseLayout] Auth flow success: ${authType}`);
// Отправляем событие для обновления данных на страницах
eventBus.emit('auth-success', { authType });
};
const {
telegramAuth,
handleTelegramAuth,
cancelTelegramAuth,
emailAuth,
showEmailForm,
sendEmailVerification,
verifyEmailCode,
cancelEmailAuth,
} = useAuthFlow({ onAuthSuccess: handleAuthFlowSuccess });
// =====================================================================
// 2. СОСТОЯНИЯ КОМПОНЕНТА
// =====================================================================
const showWalletSidebar = ref(false);
const isConnectingWallet = ref(false); // Флаг процесса подключения кошелька
// =====================================================================
// 3. ФУНКЦИИ
// =====================================================================
/**
* Обрабатывает аутентификацию через кошелек
*/
const handleWalletAuth = async () => {
if (isConnectingWallet.value) return;
isConnectingWallet.value = true;
try {
const result = await connectWithWallet();
console.log('[BaseLayout] Результат подключения кошелька:', result);
if (result.success) {
if (auth.isAuthenticated.value) {
// Связывание кошелька с существующим аккаунтом
const linkResult = await auth.linkIdentity('wallet', result.address);
if (linkResult.success) {
showSuccessMessage('Кошелек успешно подключен к вашему аккаунту!');
emit('auth-action-completed');
} else {
showErrorMessage(linkResult.error || 'Не удалось подключить кошелек');
}
} else {
// Новая аутентификация через кошелек
const authResponse = await auth.checkAuth();
if (authResponse.authenticated && authResponse.authType === 'wallet') {
console.log('[BaseLayout] Кошелёк успешно подключен и аутентифицирован');
showSuccessMessage('Кошелёк успешно подключен!');
emit('auth-action-completed');
} else {
showErrorMessage('Не удалось завершить аутентификацию через кошелек.');
}
}
} else {
console.error('[BaseLayout] Не удалось подключить кошелёк:', result.error);
showErrorMessage(result.error || 'Не удалось подключить кошелёк');
}
} catch (error) {
console.error('[BaseLayout] Ошибка при подключении кошелька:', error);
showErrorMessage('Произошла ошибка при подключении кошелька');
} finally {
isConnectingWallet.value = false;
}
};
/**
* Выполняет выход из аккаунта
*/
const disconnectWallet = async () => {
console.log('[BaseLayout] Выполняется выход из системы...');
try {
await api.post('/api/auth/logout');
showSuccessMessage('Вы успешно вышли из системы');
removeFromStorage('guestMessages');
removeFromStorage('hasUserSentMessage');
emit('auth-action-completed');
} catch (error) {
console.error('[BaseLayout] Ошибка при выходе из системы:', error);
showErrorMessage('Произошла ошибка при выходе из системы');
}
};
/**
* Переключает отображение боковой панели
*/
const toggleWalletSidebar = () => {
showWalletSidebar.value = !showWalletSidebar.value;
setToStorage('showWalletSidebar', showWalletSidebar.value);
};
// =====================================================================
// 4. ЖИЗНЕННЫЙ ЦИКЛ
// =====================================================================
onMounted(() => {
console.log('[BaseLayout] Компонент загружен');
// Загружаем сохраненное состояние боковой панели
const savedSidebarState = getFromStorage('showWalletSidebar');
if (savedSidebarState !== null) {
showWalletSidebar.value = savedSidebarState;
} else {
showWalletSidebar.value = true;
setToStorage('showWalletSidebar', true);
}
});
</script>
<style scoped>
.app-container {
display: flex;
min-height: 100vh;
position: relative;
background-color: var(--color-light);
}
.main-content {
flex-grow: 1;
transition: all var(--transition-normal);
display: flex;
flex-direction: column;
max-width: calc(100% - 350px);
padding: 0 20px;
background-color: var(--color-white);
}
.main-content.no-right-sidebar {
max-width: 100%;
}
/* Адаптивный дизайн */
@media (max-width: 1199px) {
.main-content {
max-width: calc(100% - 320px);
}
}
@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; /* Убираем большой отступ */
}
.main-content.no-right-sidebar {
padding-bottom: 10px;
}
}
</style>