ваше сообщение коммита

This commit is contained in:
2025-08-01 12:33:18 +03:00
parent 33a10ea13a
commit 3ee29f16bd
11 changed files with 651 additions and 456 deletions

View File

@@ -526,7 +526,7 @@ async function loadMultiRelationOptions() {
// Дебаунсинг для loadMultiRelationValues
let loadMultiRelationValuesTimer = null;
const LOAD_DEBOUNCE_DELAY = 100; // 100ms
const LOAD_DEBOUNCE_DELAY = 50; // 50ms (уменьшено для ускорения)
async function loadMultiRelationValues() {
// Проверяем, не загружены ли уже данные
@@ -625,13 +625,20 @@ async function saveMultiRelation() {
to_row_ids: editMultiRelationValues.value
};
console.log('[saveMultiRelation] POST payload:', payload);
const response = await fetch(`/api/tables/${props.column.table_id}/row/${props.rowId}/multirelations`, {
console.log('[TableCell] Отправляем запрос на обновление relations для строки:', props.rowId);
console.log('[TableCell] Данные запроса:', payload);
const response = await fetch(`/api/tables/${props.column.table_id}/row/${props.rowId}/relations`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
const result = await response.json().catch(() => ({}));
console.log('[saveMultiRelation] API response status:', response.status, 'result:', result);
console.log('[TableCell] Ответ сервера для строки:', props.rowId, 'статус:', response.status, 'результат:', result);
if (response.ok) {
console.log('[TableCell] Успешно сохранены теги для строки:', props.rowId);
} else {
console.error('[TableCell] Ошибка сохранения тегов для строки:', props.rowId, 'статус:', response.status);
}
editing.value = false;
await loadMultiRelationValues();
console.log('[saveMultiRelation] emitting update with:', editMultiRelationValues.value);
@@ -682,6 +689,9 @@ async function addTag() {
]);
console.log('[addTag] Тег добавлен в выбранные:', editMultiRelationValues.value);
// Сохраняем изменения, чтобы отправить WebSocket уведомление
await saveMultiRelation();
} catch (e) {
console.error('[addTag] Ошибка при добавлении тега:', e);
}
@@ -707,6 +717,9 @@ async function deleteTag(tagId) {
await loadMultiRelationOptions();
console.log('[deleteTag] Тег удален:', tagId);
// Сохраняем изменения, чтобы отправить WebSocket уведомление
await saveMultiRelation();
} catch (e) {
console.error('[deleteTag] Ошибка при удалении тега:', e);
}

View File

@@ -90,13 +90,19 @@
:resizable="false"
>
<template #header>
<button class="add-col-btn" @click="addColumn" title="Добавить столбец">
<button class="add-col-btn" @click.stop="openAddMenu($event)" title="Добавить">
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="11" cy="11" r="10" fill="#f3f4f6" stroke="#b6c6e6"/>
<rect x="10" y="5.5" width="2" height="11" rx="1" fill="#4f8cff"/>
<rect x="5.5" y="10" width="11" height="2" rx="1" fill="#4f8cff"/>
</svg>
</button>
<teleport to="body">
<div v-if="showAddMenu" class="context-menu" :style="addMenuStyle">
<button class="menu-item" @click="addColumn">Добавить столбец</button>
<button class="menu-item" @click="addRow">Добавить строку</button>
</div>
</teleport>
</template>
<template #default="{ row }">
<button class="row-menu" @click.stop="openRowMenu(row, $event)"></button>
@@ -118,7 +124,7 @@
<!-- <button class="menu-item" @click="addColumn">Добавить столбец</button> -->
</div>
</teleport>
<div v-if="openedColMenuId || openedRowMenuId" class="menu-overlay" @click="closeMenus"></div>
<div v-if="openedColMenuId || openedRowMenuId || showAddMenu" class="menu-overlay" @click="closeMenus"></div>
<!-- Модалка добавления столбца -->
<div v-if="showAddColModal" class="modal-backdrop">
<div class="modal add-col-modal">
@@ -169,7 +175,9 @@ import axios from 'axios';
import { ElSelect, ElOption, ElButton } from 'element-plus';
import websocketService from '../../services/websocketService';
import cacheService from '../../services/cacheService';
import { useTagsWebSocket } from '../../composables/useTagsWebSocket';
let unsubscribeFromTableUpdate = null;
let unsubscribeFromTagsUpdate = null;
const { isAdmin } = useAuthContext();
const rebuilding = ref(false);
@@ -269,6 +277,10 @@ const openedRowMenuId = ref(null);
const colMenuStyle = ref('');
const rowMenuStyle = ref('');
// Меню добавления
const showAddMenu = ref(false);
const addMenuStyle = ref('');
function closeAddColModal() {
showAddColModal.value = false;
newColName.value = '';
@@ -546,12 +558,77 @@ onMounted(() => {
cacheService.clearTableCache(props.tableId);
fetchTable();
});
// Подписка на WebSocket обновления тегов
const { onTagsUpdate } = useTagsWebSocket();
console.log('[UserTableView] Подписываемся на обновления тегов для таблицы:', props.tableId);
console.log('[UserTableView] onTagsUpdate функция:', typeof onTagsUpdate);
unsubscribeFromTagsUpdate = onTagsUpdate(async (data) => {
console.log('[UserTableView] 🔔 ПОЛУЧЕНО СОБЫТИЕ TAGS-UPDATED!');
console.log('[UserTableView] Получено событие tags-updated, обновляем данные для таблицы:', props.tableId, data);
// Если есть информация о конкретной строке, обновляем только её
if (data && data.rowId) {
console.log('[UserTableView] Точечное обновление для строки:', data.rowId);
try {
// Очищаем кэш relations только для конкретной строки
const tagColumns = columns.value.filter(col =>
col.type === 'multirelation' &&
col.options?.relatedTableId
);
for (const col of tagColumns) {
cacheService.clearRelationsData(data.rowId, col.id);
}
console.log('[UserTableView] Кэш relations очищен для строки, обновляем данные строки:', data.rowId);
// Обновляем только данные конкретной строки
await updateRowData(data.rowId);
console.log('[UserTableView] Данные строки обновлены:', data.rowId);
} catch (error) {
console.error('[UserTableView] Ошибка при точечном обновлении:', error);
// Fallback: полная перезагрузка при ошибке
await fetchTable();
}
} else {
// Если нет информации о строке, используем старую логику
console.log('[UserTableView] Общее обновление тегов');
try {
// Очищаем кэш relations для всех строк этой таблицы
const tableRows = rows.value || [];
for (const row of tableRows) {
// Находим колонки с мульти-связями (теги)
const tagColumns = columns.value.filter(col =>
col.type === 'multirelation' &&
col.options?.relatedTableId
);
for (const col of tagColumns) {
cacheService.clearRelationsData(row.id, col.id);
}
}
console.log('[UserTableView] Кэш relations очищен, перезагружаем данные таблицы:', props.tableId);
await fetchTable();
console.log('[UserTableView] Данные таблицы перезагружены:', props.tableId);
} catch (error) {
console.error('[UserTableView] Ошибка при обновлении после tags-updated:', error);
// Fallback: полная перезагрузка при ошибке
cacheService.clearTableCache(props.tableId);
await fetchTable();
}
}
});
});
onUnmounted(() => {
if (unsubscribeFromTableUpdate) {
unsubscribeFromTableUpdate();
}
if (unsubscribeFromTagsUpdate) {
unsubscribeFromTagsUpdate();
}
});
// Для редактирования ячеек
@@ -619,6 +696,14 @@ function openRowMenu(row, event) {
function closeMenus() {
openedColMenuId.value = null;
openedRowMenuId.value = null;
showAddMenu.value = false;
}
function openAddMenu(event) {
showAddMenu.value = true;
openedColMenuId.value = null;
openedRowMenuId.value = null;
setMenuPosition(event, addMenuStyle);
}
function setMenuPosition(event, styleRef) {
// Позиционируем меню под кнопкой
@@ -676,6 +761,54 @@ async function rebuildIndex() {
}
}
// Функция для точечного обновления данных конкретной строки
async function updateRowData(rowId) {
const startTime = Date.now();
console.log(`[UserTableView] 🔄 Начало обновления данных строки ${rowId}`);
try {
// Находим строку в текущих данных
const rowIndex = rows.value.findIndex(row => row.id === rowId);
if (rowIndex === -1) {
console.log(`[UserTableView] Строка ${rowId} не найдена в текущих данных`);
return;
}
// Загружаем relations только для этой строки
const tagColumns = columns.value.filter(col =>
col.type === 'multirelation' &&
col.options?.relatedTableId
);
if (tagColumns.length > 0) {
console.log(`[UserTableView] 🔄 Загружаем relations для строки ${rowId} (${tagColumns.length} столбцов)`);
const relationPromises = tagColumns.map(col =>
fetch(`/api/tables/${col.table_id}/row/${rowId}/relations`)
.then(res => res.json())
.then(relations => {
// Сохраняем в кэш
cacheService.setRelationsData(rowId, col.id, relations);
return { rowId, colId: col.id, relations };
})
.catch(error => {
console.error(`[UserTableView] Ошибка загрузки relations для row:${rowId} col:${col.id}:`, error);
return { rowId, colId: col.id, relations: [] };
})
);
await Promise.all(relationPromises);
console.log(`[UserTableView] ✅ Relations для строки ${rowId} обновлены`);
}
const endTime = Date.now();
console.log(`[UserTableView] ✅ Завершено обновление строки ${rowId} за ${endTime - startTime}ms`);
} catch (error) {
console.error(`[UserTableView] ❌ Ошибка при обновлении строки ${rowId}:`, error);
throw error;
}
}
</script>
<style scoped>

View File

@@ -10,206 +10,40 @@
* GitHub: https://github.com/HB3-ACCELERATOR
*/
import { ref, onMounted, onUnmounted } from 'vue';
import { onMounted, onUnmounted } from 'vue';
import websocketServiceModule from '@/services/websocketService.js';
const { websocketService, onTableUpdate } = websocketServiceModule;
export function useTablesWebSocket() {
const ws = ref(null);
const isConnected = ref(false);
const isConnecting = ref(false); // Добавляем флаг для предотвращения множественных подключений
const tableUpdateCallbacks = ref(new Map()); // tableId -> callback
const tableRelationsUpdateCallbacks = ref(new Map()); // `${tableId}-${rowId}` -> callback
const pingInterval = ref(null); // Интервал для ping сообщений
function connect() {
if (ws.value && ws.value.readyState === WebSocket.OPEN) {
console.log('[TablesWebSocket] Уже подключены, пропускаем');
return; // Уже подключены
}
if (isConnecting.value) {
console.log('[TablesWebSocket] Уже пытаемся подключиться, пропускаем');
return; // Уже пытаемся подключиться
}
isConnecting.value = true;
// Определяем правильный URL для WebSocket
let wsUrl;
if (import.meta.env.DEV) {
// В режиме разработки используем прокси через Vite
wsUrl = `${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${window.location.host}/ws`;
} else {
// В продакшене используем тот же хост
wsUrl = `${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${window.location.host}/ws`;
}
console.log('[TablesWebSocket] Подключение к:', wsUrl);
console.log('[TablesWebSocket] Текущий хост:', window.location.host);
console.log('[TablesWebSocket] Протокол:', window.location.protocol);
try {
ws.value = new WebSocket(wsUrl);
} catch (error) {
console.error('[TablesWebSocket] Ошибка создания WebSocket:', error);
isConnecting.value = false;
return;
}
ws.value.onopen = () => {
console.log('[TablesWebSocket] Соединение установлено');
isConnected.value = true;
isConnecting.value = false;
// Запускаем ping каждые 30 секунд
pingInterval.value = setInterval(() => {
if (ws.value && ws.value.readyState === WebSocket.OPEN) {
try {
ws.value.send(JSON.stringify({ type: 'ping', timestamp: Date.now() }));
} catch (error) {
console.error('[TablesWebSocket] Ошибка отправки ping:', error);
}
}
}, 30000);
};
ws.value.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
console.log('[TablesWebSocket] Получено сообщение:', data);
// Обрабатываем pong ответ
if (data.type === 'pong') {
console.log('[TablesWebSocket] Получен pong ответ');
return;
}
if (data.type === 'table-updated') {
const callbacks = tableUpdateCallbacks.value.get(data.tableId);
if (callbacks) {
callbacks.forEach(callback => callback(data));
}
}
if (data.type === 'table-relations-updated') {
const key = `${data.tableId}-${data.rowId}`;
const callbacks = tableRelationsUpdateCallbacks.value.get(key);
if (callbacks) {
callbacks.forEach(callback => callback(data));
}
}
} catch (error) {
console.error('[TablesWebSocket] Ошибка обработки сообщения:', error);
}
};
ws.value.onclose = (event) => {
console.log('[TablesWebSocket] Соединение закрыто', {
code: event.code,
reason: event.reason,
wasClean: event.wasClean
});
isConnected.value = false;
isConnecting.value = false;
// Останавливаем ping интервал
if (pingInterval.value) {
clearInterval(pingInterval.value);
pingInterval.value = null;
}
// Переподключение только если это не было намеренное закрытие
if (event.code !== 1000) {
setTimeout(() => {
if (!isConnected.value && !isConnecting.value) {
console.log('[TablesWebSocket] Попытка переподключения...');
connect();
}
}, 3000);
}
};
ws.value.onerror = (error) => {
console.error('[TablesWebSocket] Ошибка соединения:', error);
console.error('[TablesWebSocket] WebSocket readyState:', ws.value?.readyState);
isConnected.value = false;
isConnecting.value = false;
};
}
// Подписка на обновления таблиц
function subscribeToTableUpdates(tableId, callback) {
if (!tableUpdateCallbacks.value.has(tableId)) {
tableUpdateCallbacks.value.set(tableId, []);
}
tableUpdateCallbacks.value.get(tableId).push(callback);
// Возвращаем функцию для отписки
return () => {
const callbacks = tableUpdateCallbacks.value.get(tableId);
if (callbacks) {
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
}
if (callbacks.length === 0) {
tableUpdateCallbacks.value.delete(tableId);
}
}
};
return onTableUpdate(tableId, callback);
}
// Подписка на обновления связей (relations)
function subscribeToTableRelationsUpdates(tableId, rowId, callback) {
const key = `${tableId}-${rowId}`;
if (!tableRelationsUpdateCallbacks.value.has(key)) {
tableRelationsUpdateCallbacks.value.set(key, []);
}
tableRelationsUpdateCallbacks.value.get(key).push(callback);
// Возвращаем функцию для отписки
return () => {
const callbacks = tableRelationsUpdateCallbacks.value.get(key);
if (callbacks) {
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
}
if (callbacks.length === 0) {
tableRelationsUpdateCallbacks.value.delete(key);
}
// Используем глобальный обработчик и фильтруем по tableId/rowId
const handler = (data) => {
if (data.tableId === tableId && data.rowId === rowId) {
callback(data);
}
};
}
function disconnect() {
if (ws.value) {
// Останавливаем ping интервал
if (pingInterval.value) {
clearInterval(pingInterval.value);
pingInterval.value = null;
}
// Корректно закрываем соединение
if (ws.value.readyState === WebSocket.OPEN) {
ws.value.close(1000, 'Manual disconnect');
}
ws.value = null;
}
isConnected.value = false;
isConnecting.value = false;
websocketService.on('table-relations-updated', handler);
// Возвращаем функцию для отписки
return () => websocketService.off('table-relations-updated', handler);
}
onMounted(() => {
connect();
// Соединение управляется websocketService, ничего не делаем
});
onUnmounted(() => {
disconnect();
// Соединение управляется websocketService, ничего не делаем
});
return {
isConnected,
connect,
disconnect,
subscribeToTableUpdates,
subscribeToTableRelationsUpdates
subscribeToTableRelationsUpdates,
};
}

View File

@@ -16,44 +16,78 @@ import websocketServiceModule from '../services/websocketService';
const { websocketService } = websocketServiceModule;
export function useTagsWebSocket() {
console.log('🏷️ [useTagsWebSocket] Композабл создан');
const tagsUpdateCallbacks = ref([]);
let debounceTimer = null;
const DEBOUNCE_DELAY = 1000; // 1 секунда
const isSubscribed = ref(false);
function onTagsUpdate(callback) {
console.log('🏷️ [useTagsWebSocket] Регистрация колбэка');
// Проверяем, не зарегистрирован ли уже этот колбэк
if (tagsUpdateCallbacks.value.includes(callback)) {
console.log('🏷️ [useTagsWebSocket] Колбэк уже зарегистрирован, пропускаем');
return () => {
const index = tagsUpdateCallbacks.value.indexOf(callback);
if (index > -1) {
tagsUpdateCallbacks.value.splice(index, 1);
}
};
}
tagsUpdateCallbacks.value.push(callback);
console.log('🏷️ [useTagsWebSocket] Количество колбэков:', tagsUpdateCallbacks.value.length);
// Возвращаем функцию для отписки
return () => {
console.log('🏷️ [useTagsWebSocket] Отписка колбэка');
const index = tagsUpdateCallbacks.value.indexOf(callback);
if (index > -1) {
tagsUpdateCallbacks.value.splice(index, 1);
console.log('🏷️ [useTagsWebSocket] Колбэк удален, осталось:', tagsUpdateCallbacks.value.length);
}
};
}
function handleTagsUpdate(data) {
console.log('🏷️ [useTagsWebSocket] Получено уведомление об обновлении тегов:', data);
console.log('🏷️ [useTagsWebSocket] Количество активных колбэков:', tagsUpdateCallbacks.value.length);
// Вызываем все зарегистрированные колбэки
tagsUpdateCallbacks.value.forEach(callback => {
tagsUpdateCallbacks.value.forEach((callback, index) => {
try {
console.log('🏷️ [useTagsWebSocket] Выполняем колбэк #', index);
callback(data);
} catch (error) {
console.error('🏷️ [useTagsWebSocket] Ошибка в колбэке:', error);
console.error('🏷️ [useTagsWebSocket] Ошибка в колбэке #', index, ':', error);
}
});
}
onMounted(() => {
console.log('🏷️ [useTagsWebSocket] onMounted вызван');
// Проверяем, не подписаны ли уже
if (isSubscribed.value) {
console.log('🏷️ [useTagsWebSocket] Уже подписаны, пропускаем');
return;
}
console.log('🏷️ [useTagsWebSocket] Подписываемся на tags-updated');
websocketService.on('tags-updated', handleTagsUpdate);
isSubscribed.value = true;
console.log('🏷️ [useTagsWebSocket] Подписка завершена');
});
onUnmounted(() => {
if (debounceTimer) {
clearTimeout(debounceTimer);
console.log('🏷️ [useTagsWebSocket] onUnmounted вызван');
if (isSubscribed.value) {
websocketService.off('tags-updated', handleTagsUpdate);
isSubscribed.value = false;
console.log('🏷️ [useTagsWebSocket] Отписка завершена');
}
websocketService.off('tags-updated', handleTagsUpdate);
// Очищаем все колбэки
tagsUpdateCallbacks.value = [];
console.log('🏷️ [useTagsWebSocket] Колбэки очищены');
});
return {

View File

@@ -57,20 +57,20 @@ export default {
},
// --- Работа с тегами пользователя ---
async addTagsToContact(contactId, tagIds) {
// PATCH /users/:id/tags { tags: [...] }
const res = await api.patch(`/users/${contactId}/tags`, { tags: tagIds });
return res.data;
},
async getContactTags(contactId) {
// GET /users/:id/tags
const res = await api.get(`/users/${contactId}/tags`);
return res.data.tags || [];
},
async removeTagFromContact(contactId, tagId) {
// DELETE /users/:id/tags/:tagId
const res = await api.delete(`/users/${contactId}/tags/${tagId}`);
return res.data;
}
// PATCH /api/tags/user/:id { tags: [...] }
const res = await api.patch(`/tags/user/${contactId}`, { tags: tagIds });
return res.data;
},
async getContactTags(contactId) {
// GET /api/tags/user/:id
const res = await api.get(`/tags/user/${contactId}`);
return res.data.tags || [];
},
async removeTagFromContact(contactId, tagId) {
// DELETE /api/tags/user/:id/tag/:tagId
const res = await api.delete(`/tags/user/${contactId}/tag/${tagId}`);
return res.data;
}
};
export async function getContacts() {

View File

@@ -16,6 +16,7 @@
class WebSocketService {
constructor() {
console.log('🔌 [WebSocket] Конструктор вызван');
this.ws = null;
this.isConnected = false;
this.reconnectAttempts = 0;
@@ -23,10 +24,12 @@ class WebSocketService {
this.reconnectDelay = 1000; // 1 секунда
this.listeners = new Map();
this.userId = null;
console.log('🔌 [WebSocket] Конструктор завершен');
}
// Подключение к WebSocket серверу
connect(userId = null) {
console.log('🔌 [WebSocket] Попытка подключения, userId:', userId);
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
console.log('🔌 [WebSocket] Уже подключен');
return;
@@ -37,11 +40,11 @@ class WebSocketService {
try {
// Определяем WebSocket URL
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
// В Docker окружении backend работает на порту 8000
const backendHost = window.location.hostname + ':8000';
const wsUrl = `${protocol}//${backendHost}/ws`;
// В Docker окружении используем тот же хост, что и для HTTP
const wsUrl = `${protocol}//${window.location.host}/ws`;
console.log('🔌 [WebSocket] Подключение к:', wsUrl);
console.log('🔌 [WebSocket] Текущий хост:', window.location.host);
this.ws = new WebSocket(wsUrl);
@@ -61,10 +64,35 @@ class WebSocketService {
this.emit('connected');
};
this.ws.onclose = (event) => {
console.log('🔌 [WebSocket] Соединение закрыто:', event.code, event.reason);
this.isConnected = false;
this.emit('disconnected', event);
// Попытка переподключения
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
console.log(`🔄 [WebSocket] Попытка переподключения ${this.reconnectAttempts}/${this.maxReconnectAttempts}`);
setTimeout(() => {
this.connect(this.userId);
}, this.reconnectDelay * this.reconnectAttempts);
} else {
console.error('❌ [WebSocket] Превышено максимальное количество попыток переподключения');
this.emit('reconnect-failed');
}
};
this.ws.onerror = (error) => {
console.error('❌ [WebSocket] Ошибка соединения:', error);
this.emit('error', error);
};
this.ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
console.log('📨 [WebSocket] Получено сообщение:', data);
console.log('📨 [WebSocket] Тип сообщения:', data.type);
this.handleMessage(data);
} catch (error) {
console.error('❌ [WebSocket] Ошибка парсинга сообщения:', error);
@@ -139,8 +167,9 @@ class WebSocketService {
break;
case 'tags-updated':
console.log('🔔 [websocketService] Получено сообщение tags-updated');
this.emit('tags-updated');
console.log('🔔 [websocketService] Получено сообщение tags-updated:', data);
console.log('🔔 [websocketService] Количество слушателей tags-updated:', this.listeners.get('tags-updated')?.length || 0);
this.emit('tags-updated', data);
break;
case 'table-updated':
@@ -158,10 +187,12 @@ class WebSocketService {
// Подписка на события
on(event, callback) {
console.log('🔌 [WebSocket] Подписка на событие:', event);
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event).push(callback);
console.log('🔌 [WebSocket] Количество слушателей для', event, ':', this.listeners.get(event).length);
}
// Отписка от событий
@@ -177,14 +208,20 @@ class WebSocketService {
// Эмиссия событий
emit(event, data) {
console.log('🔌 [WebSocket] Эмиссия события:', event, 'с данными:', data);
if (this.listeners.has(event)) {
this.listeners.get(event).forEach(callback => {
const callbacks = this.listeners.get(event);
console.log('🔌 [WebSocket] Количество колбэков для', event, ':', callbacks.length);
callbacks.forEach((callback, index) => {
try {
console.log('🔌 [WebSocket] Выполняем колбэк #', index, 'для события', event);
callback(data);
} catch (error) {
console.error(`❌ [WebSocket] Ошибка в обработчике события ${event}:`, error);
}
});
} else {
console.log('🔌 [WebSocket] Нет слушателей для события:', event);
}
}
@@ -212,6 +249,7 @@ class WebSocketService {
// Создаем единственный экземпляр
const websocketService = new WebSocketService();
console.log('🔌 [WebSocket] Сервис создан');
// Подписчики на обновления таблиц: tableId -> [callback]
const tableUpdateSubscribers = {};
@@ -230,4 +268,18 @@ function onTableUpdate(tableId, callback) {
export default {
websocketService,
onTableUpdate,
};
};
console.log('🔌 [WebSocket] Экспорт завершен');
// Автоматически подключаемся при загрузке модуля
console.log('🔌 [WebSocket] Автоматическое подключение...');
setTimeout(() => {
console.log('🔌 [WebSocket] Подключаемся через 1 секунду...');
websocketService.connect();
}, 1000);
// Добавляем периодическую проверку состояния соединения
setInterval(() => {
const status = websocketService.getStatus();
console.log('🔌 [WebSocket] Статус соединения:', status);
}, 10000); // Проверяем каждые 10 секунд