фикс
This commit is contained in:
parent
4ef4c5f428
commit
71b48b5d25
34
src/App.vue
34
src/App.vue
@ -63,6 +63,18 @@
|
||||
</router-link>
|
||||
</template>
|
||||
</nav>
|
||||
|
||||
<!-- Контейнер для уведомлений -->
|
||||
<div class="toast-list">
|
||||
<Toast
|
||||
v-for="toast in toasts"
|
||||
:key="toast.id"
|
||||
:message="toast.message"
|
||||
:type="toast.type"
|
||||
:duration="toast.duration"
|
||||
@close="removeToast(toast.id)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -72,10 +84,16 @@ import { ref, watch, onMounted, computed, onUnmounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import api from '@/services/api';
|
||||
import { getSocket, connectSocket } from '@/services/socketService';
|
||||
import Toast from '@/components/Toast.vue';
|
||||
import toastService from '@/services/toastService';
|
||||
|
||||
const { user, isAuthenticated, logout, fetchUser } = useAuth();
|
||||
const route = useRoute();
|
||||
|
||||
// Импортируем уведомления из сервиса
|
||||
const toasts = toastService.toasts;
|
||||
const removeToast = toastService.remove;
|
||||
|
||||
const userData = ref(null);
|
||||
const authState = ref(false);
|
||||
const conversations = ref([]);
|
||||
@ -270,6 +288,22 @@ body {
|
||||
will-change: transform; /* Ускорение для анимаций */
|
||||
}
|
||||
|
||||
/* Контейнер для уведомлений */
|
||||
.toast-list {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 2000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.toast-list > * {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
#app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
117
src/components/Toast.vue
Normal file
117
src/components/Toast.vue
Normal file
@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<transition name="toast-fade">
|
||||
<div v-if="show" class="toast-container" :class="type">
|
||||
<div class="toast-content">
|
||||
<i v-if="type === 'success'" class="bi-check-circle-fill"></i>
|
||||
<i v-else-if="type === 'error'" class="bi-exclamation-circle-fill"></i>
|
||||
<i v-else-if="type === 'warning'" class="bi-exclamation-triangle-fill"></i>
|
||||
<i v-else class="bi-info-circle-fill"></i>
|
||||
<span>{{ message }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
||||
|
||||
export default {
|
||||
name: 'Toast',
|
||||
props: {
|
||||
message: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
duration: {
|
||||
type: Number,
|
||||
default: 3000
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: 'info',
|
||||
validator(value) {
|
||||
return ['info', 'success', 'warning', 'error'].includes(value);
|
||||
}
|
||||
}
|
||||
},
|
||||
emits: ['close'],
|
||||
setup(props, { emit }) {
|
||||
const show = ref(false);
|
||||
let timer = null;
|
||||
|
||||
const startTimer = () => {
|
||||
timer = setTimeout(() => {
|
||||
show.value = false;
|
||||
setTimeout(() => emit('close'), 300); // Даем время для завершения анимации
|
||||
}, props.duration);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
show.value = true;
|
||||
startTimer();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearTimeout(timer);
|
||||
});
|
||||
|
||||
return {
|
||||
show
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.toast-container {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
padding: 12px 20px;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
.toast-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.toast-content i {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.success {
|
||||
background-color: #4caf50;
|
||||
}
|
||||
|
||||
.info {
|
||||
background-color: #2196f3;
|
||||
}
|
||||
|
||||
.warning {
|
||||
background-color: #ff9800;
|
||||
}
|
||||
|
||||
.error {
|
||||
background-color: #f44336;
|
||||
}
|
||||
|
||||
/* Анимация появления и исчезновения */
|
||||
.toast-fade-enter-active,
|
||||
.toast-fade-leave-active {
|
||||
transition: opacity 0.3s, transform 0.3s;
|
||||
}
|
||||
|
||||
.toast-fade-enter-from,
|
||||
.toast-fade-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
</style>
|
48
src/services/toastService.js
Normal file
48
src/services/toastService.js
Normal file
@ -0,0 +1,48 @@
|
||||
// Простой сервис для отображения всплывающих уведомлений
|
||||
import { ref, reactive } from 'vue';
|
||||
|
||||
const toasts = reactive([]);
|
||||
let toastId = 0;
|
||||
|
||||
// Добавление нового уведомления
|
||||
const add = (message, type = 'info', duration = 3000) => {
|
||||
const id = toastId++;
|
||||
toasts.push({ id, message, type, duration });
|
||||
|
||||
// Автоматически убираем уведомление через duration + 300ms анимации
|
||||
setTimeout(() => {
|
||||
remove(id);
|
||||
}, duration + 300);
|
||||
|
||||
return id;
|
||||
};
|
||||
|
||||
// Удаление уведомления по ID
|
||||
const remove = (id) => {
|
||||
const index = toasts.findIndex(toast => toast.id === id);
|
||||
if (index > -1) {
|
||||
toasts.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
// Показ информационного уведомления
|
||||
const info = (message, duration) => add(message, 'info', duration);
|
||||
|
||||
// Показ уведомления об успехе
|
||||
const success = (message, duration) => add(message, 'success', duration);
|
||||
|
||||
// Показ предупреждения
|
||||
const warning = (message, duration) => add(message, 'warning', duration);
|
||||
|
||||
// Показ уведомления об ошибке
|
||||
const error = (message, duration) => add(message, 'error', duration);
|
||||
|
||||
export default {
|
||||
toasts,
|
||||
add,
|
||||
remove,
|
||||
info,
|
||||
success,
|
||||
warning,
|
||||
error
|
||||
};
|
@ -142,6 +142,7 @@
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import axios from 'axios';
|
||||
import toastService from '@/services/toastService';
|
||||
|
||||
export default {
|
||||
name: 'AdminUserDetail',
|
||||
@ -214,11 +215,12 @@ export default {
|
||||
// Обновляем статус пользователя
|
||||
user.value.isActive = response.data.isActive;
|
||||
|
||||
// Показываем уведомление (можно добавить компонент уведомлений)
|
||||
alert(response.data.message);
|
||||
// Заменяем alert на уведомление через сервис
|
||||
const actionType = user.value.isActive ? 'success' : 'warning';
|
||||
toastService.add(response.data.message, actionType, 3000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка при изменении статуса пользователя:', err);
|
||||
alert('Ошибка при изменении статуса пользователя');
|
||||
toastService.error('Ошибка при изменении статуса пользователя');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
@ -167,6 +167,7 @@
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import axios from 'axios';
|
||||
import toastService from '@/services/toastService';
|
||||
|
||||
export default {
|
||||
name: 'AdminUsers',
|
||||
@ -270,11 +271,12 @@ export default {
|
||||
users.value[userIndex].isActive = response.data.isActive;
|
||||
}
|
||||
|
||||
// Показываем уведомление (можно добавить компонент уведомлений)
|
||||
alert(response.data.message);
|
||||
// Заменяем стандартный alert на наш сервис уведомлений
|
||||
const type = response.data.isActive ? 'success' : 'warning';
|
||||
toastService.add(response.data.message, type, 3000);
|
||||
} catch (err) {
|
||||
console.error('Ошибка при изменении статуса пользователя:', err);
|
||||
alert('Ошибка при изменении статуса пользователя');
|
||||
toastService.error('Ошибка при изменении статуса пользователя');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user