Reflex/src/views/admin/AdminConversations.vue
Professional 398fe61565 фикс
2025-05-26 00:00:26 +07:00

333 lines
7.8 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="admin-conversations">
<h2>Управление диалогами</h2>
<div class="search-bar">
<input
type="text"
v-model="searchUserId"
placeholder="Поиск диалогов по ID пользователя..."
/>
<button @click="loadConversations" class="search-btn">Поиск</button>
</div>
<div v-if="loading" class="loading-indicator">
Загрузка диалогов...
</div>
<div v-if="error" class="error-message">
{{ error }}
</div>
<div v-if="!loading && !error" class="conversations-table-container">
<table class="conversations-table">
<thead>
<tr>
<th>ID диалога</th>
<th>Участники</th>
<th>Последнее сообщение</th>
<th>Дата создания</th>
<th>Последняя активность</th>
<th>Действия</th>
</tr>
</thead>
<tbody>
<tr v-for="conversation in conversations" :key="conversation._id">
<td>{{ shortenId(conversation._id) }}</td>
<td class="participants-cell">
<div v-for="participant in conversation.participants" :key="participant._id" class="participant">
{{ participant.name }} ({{ participant.email }})
</div>
</td>
<td>
{{ conversation.lastMessage ? truncateText(conversation.lastMessage.text, 30) : 'Нет сообщений' }}
</td>
<td>{{ formatDate(conversation.createdAt) }}</td>
<td>{{ formatDate(conversation.updatedAt) }}</td>
<td class="actions">
<button @click="viewConversation(conversation._id)" class="btn view-btn">
Просмотр
</button>
</td>
</tr>
</tbody>
</table>
<div v-if="conversations.length === 0" class="no-results">
Диалоги не найдены
</div>
<div v-if="pagination.pages > 1" class="pagination">
<button
@click="changePage(currentPage - 1)"
:disabled="currentPage === 1"
class="pagination-btn"
>
&laquo; Назад
</button>
<span class="pagination-info">
Страница {{ currentPage }} из {{ pagination.pages }}
</span>
<button
@click="changePage(currentPage + 1)"
:disabled="currentPage === pagination.pages"
class="pagination-btn"
>
Вперёд &raquo;
</button>
</div>
</div>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import axios from 'axios';
export default {
name: 'AdminConversations',
setup() {
const router = useRouter();
const conversations = ref([]);
const loading = ref(false);
const error = ref(null);
const searchUserId = ref('');
const currentPage = ref(1);
const pagination = ref({
page: 1,
limit: 20,
total: 0,
pages: 0
});
const loadConversations = async () => {
loading.value = true;
error.value = null;
try {
// Определяем параметры запроса
const params = {
page: currentPage.value,
limit: pagination.value.limit
};
// Добавляем поиск по ID пользователя, если указан
if (searchUserId.value) {
params.userId = searchUserId.value;
}
const token = localStorage.getItem('userToken'); // Исправлено с 'token' на 'userToken'
const response = await axios.get(`/api/admin/conversations`, {
params,
headers: {
Authorization: `Bearer ${token}`
}
});
conversations.value = response.data.conversations;
pagination.value = response.data.pagination;
} catch (err) {
console.error('Ошибка при загрузке диалогов:', err);
error.value = 'Ошибка при загрузке списка диалогов. Пожалуйста, попробуйте позже.';
} finally {
loading.value = false;
}
};
// Функция для сокращения ID (показывать только первые 6 символов)
const shortenId = (id) => {
return id ? id.substring(0, 6) + '...' : '';
};
// Форматирование даты
const formatDate = (dateString) => {
if (!dateString) return '';
const date = new Date(dateString);
return date.toLocaleString('ru-RU', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
});
};
// Обрезка длинного текста
const truncateText = (text, maxLength) => {
if (!text) return '';
if (text.length <= maxLength) return text;
return text.substring(0, maxLength) + '...';
};
// Переход на страницу детальной информации о диалоге
const viewConversation = (conversationId) => {
router.push({ name: 'AdminConversationDetail', params: { id: conversationId } });
};
// Изменение страницы пагинации
const changePage = (page) => {
if (page < 1 || page > pagination.value.pages) return;
currentPage.value = page;
loadConversations();
};
onMounted(() => {
loadConversations();
});
return {
conversations,
loading,
error,
searchUserId,
currentPage,
pagination,
loadConversations,
shortenId,
formatDate,
truncateText,
viewConversation,
changePage
};
}
}
</script>
<style scoped>
.admin-conversations {
padding-bottom: 2rem;
}
h2 {
margin-top: 0;
margin-bottom: 1.5rem;
color: #333;
}
.search-bar {
display: flex;
gap: 0.75rem;
margin-bottom: 1.5rem;
}
.search-bar input {
flex: 1;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 4px;
}
.search-btn {
padding: 0 1.5rem;
background-color: #ff3e68;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
}
.loading-indicator {
text-align: center;
padding: 2rem;
color: #666;
}
.error-message {
padding: 1rem;
background-color: #ffebee;
color: #d32f2f;
border-radius: 4px;
margin-bottom: 1rem;
}
.conversations-table-container {
overflow-x: auto;
}
.conversations-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 1rem;
}
.conversations-table th, .conversations-table td {
text-align: left;
padding: 0.75rem;
border-bottom: 1px solid #eee;
}
.conversations-table th {
background-color: #f5f5f5;
font-weight: 500;
color: #444;
}
.participants-cell {
max-width: 300px;
}
.participant {
margin-bottom: 0.25rem;
}
.participant:last-child {
margin-bottom: 0;
}
.actions {
white-space: nowrap;
}
.btn {
padding: 0.4rem 0.75rem;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
font-size: 0.875rem;
}
.view-btn {
background-color: #e3f2fd;
color: #1976d2;
}
.no-results {
text-align: center;
padding: 2rem;
color: #666;
}
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: 1rem;
margin-top: 1.5rem;
}
.pagination-btn {
padding: 0.5rem 1rem;
background-color: #f5f5f5;
border: 1px solid #ddd;
border-radius: 4px;
cursor: pointer;
}
.pagination-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.pagination-info {
color: #666;
}
</style>