2025-05-21 22:13:09 +07:00
|
|
|
|
<template>
|
|
|
|
|
<div id="app-container">
|
|
|
|
|
<header class="app-header bg-dark text-white p-3 mb-4 shadow-sm">
|
|
|
|
|
<nav class="container d-flex justify-content-between align-items-center">
|
|
|
|
|
<router-link to="/" class="navbar-brand text-white fs-4">DatingApp</router-link>
|
|
|
|
|
<div>
|
|
|
|
|
<template v-if="authState">
|
|
|
|
|
<span class="me-3">Привет, {{ userData?.name || 'Пользователь' }}!</span>
|
|
|
|
|
<router-link to="/swipe" class="btn btn-outline-light me-2">Поиск</router-link>
|
|
|
|
|
<router-link to="/chats" class="btn btn-outline-light me-2">Чаты</router-link> <!-- НОВАЯ ССЫЛКА -->
|
|
|
|
|
<router-link to="/profile" class="btn btn-outline-light me-2">Профиль</router-link>
|
|
|
|
|
<button @click="handleLogout" class="btn btn-light">Выход</button>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-else>
|
|
|
|
|
<router-link to="/login" class="btn btn-outline-light me-2">Войти</router-link>
|
|
|
|
|
<router-link to="/register" class="btn btn-light">Регистрация</router-link>
|
|
|
|
|
</template>
|
|
|
|
|
</div>
|
|
|
|
|
</nav>
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
<main class="container">
|
|
|
|
|
<router-view /> <!-- Сюда Vue Router будет рендерить компонент текущего маршрута -->
|
|
|
|
|
</main>
|
|
|
|
|
|
|
|
|
|
<footer class="app-footer mt-auto py-3 bg-light text-center">
|
|
|
|
|
<div class="container">
|
|
|
|
|
<span class="text-muted">© {{ new Date().getFullYear() }} Dating App PWA</span>
|
|
|
|
|
</div>
|
|
|
|
|
</footer>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
import { useAuth } from '@/auth'; // Убедись, что путь '@/auth' правильный
|
|
|
|
|
import { ref, watch, onMounted } from 'vue';
|
|
|
|
|
|
|
|
|
|
const { user, isAuthenticated, logout, fetchUser } = useAuth();
|
|
|
|
|
|
|
|
|
|
// Используем локальные переменные для отслеживания состояния
|
|
|
|
|
const userData = ref(null);
|
|
|
|
|
const authState = ref(false);
|
|
|
|
|
|
|
|
|
|
// Обновляем локальные переменные при изменении состояния аутентификации
|
|
|
|
|
watch(() => isAuthenticated.value, (newVal) => {
|
|
|
|
|
console.log('isAuthenticated изменился:', newVal);
|
|
|
|
|
authState.value = newVal;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Обновляем локальную копию данных пользователя
|
|
|
|
|
watch(() => user.value, (newVal) => {
|
|
|
|
|
console.log('Данные пользователя изменились:', newVal);
|
|
|
|
|
userData.value = newVal;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// При монтировании компонента синхронизируем состояние
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
console.log('App.vue монтирован, проверка состояния аутентификации');
|
|
|
|
|
authState.value = isAuthenticated.value;
|
|
|
|
|
userData.value = user.value;
|
|
|
|
|
|
|
|
|
|
// Убедимся, что у нас есть актуальные данные пользователя
|
|
|
|
|
if (localStorage.getItem('userToken') && !isAuthenticated.value) {
|
|
|
|
|
fetchUser();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const handleLogout = async () => {
|
|
|
|
|
try {
|
|
|
|
|
await logout();
|
|
|
|
|
console.log('Пользователь успешно вышел из системы (из App.vue)');
|
|
|
|
|
authState.value = false;
|
|
|
|
|
userData.value = null;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Ошибка при выходе:', error);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
/* Глобальные стили */
|
|
|
|
|
html, body {
|
|
|
|
|
height: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
body {
|
|
|
|
|
margin: 0;
|
|
|
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
|
|
|
|
color: #212529;
|
|
|
|
|
background-color: #f8f9fa; /* Светлый фон для всего приложения */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#app-container {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-23 17:13:32 +07:00
|
|
|
|
/* Global Responsive Styles */
|
|
|
|
|
.app {
|
|
|
|
|
position: relative;
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-21 22:13:09 +07:00
|
|
|
|
.app-header .navbar-brand {
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app-header .btn {
|
|
|
|
|
min-width: 100px; /* Чтобы кнопки были примерно одинаковой ширины */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
main.container {
|
|
|
|
|
flex-grow: 1;
|
|
|
|
|
padding-top: 1rem;
|
|
|
|
|
padding-bottom: 1rem;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-23 17:13:32 +07:00
|
|
|
|
/* Адаптивные глобальные стили */
|
|
|
|
|
@media (max-width: 576px) {
|
|
|
|
|
.app-header .navbar-brand {
|
|
|
|
|
font-size: 1.2rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app-header .btn {
|
|
|
|
|
min-width: 80px;
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
padding: 0.4rem 0.8rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.container {
|
|
|
|
|
padding-left: 15px;
|
|
|
|
|
padding-right: 15px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Small phones */
|
|
|
|
|
@media (max-width: 375px) {
|
|
|
|
|
.app-header .navbar-brand {
|
|
|
|
|
font-size: 1.1rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.app-header .btn {
|
|
|
|
|
min-width: 70px;
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
padding: 0.35rem 0.7rem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Landscape orientation on mobile */
|
|
|
|
|
@media (max-height: 450px) and (orientation: landscape) {
|
|
|
|
|
.app-header {
|
|
|
|
|
padding-top: 0.25rem !important;
|
|
|
|
|
padding-bottom: 0.25rem !important;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-21 22:13:09 +07:00
|
|
|
|
</style>
|