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

This commit is contained in:
2025-07-31 13:49:46 +03:00
parent 848b2627e6
commit 33a10ea13a
22 changed files with 768 additions and 579 deletions

View File

@@ -15,20 +15,61 @@ import { ref, onMounted, onUnmounted } from 'vue';
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; // Уже подключены
}
const wsProtocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
ws.value = new WebSocket(`${wsProtocol}://${window.location.host}/ws`);
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) => {
@@ -36,6 +77,12 @@ export function useTablesWebSocket() {
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) {
@@ -55,20 +102,37 @@ export function useTablesWebSocket() {
}
};
ws.value.onclose = () => {
console.log('[TablesWebSocket] Соединение закрыто');
ws.value.onclose = (event) => {
console.log('[TablesWebSocket] Соединение закрыто', {
code: event.code,
reason: event.reason,
wasClean: event.wasClean
});
isConnected.value = false;
// Переподключение через 3 секунды
setTimeout(() => {
if (!isConnected.value) {
connect();
}
}, 3000);
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;
};
}
@@ -117,10 +181,20 @@ export function useTablesWebSocket() {
function disconnect() {
if (ws.value) {
ws.value.close();
// Останавливаем 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;
}
onMounted(() => {

View File

@@ -33,24 +33,16 @@ export function useTagsWebSocket() {
}
function handleTagsUpdate(data) {
console.log('🏷️ [useTagsWebSocket] Получено обновление тегов:', data);
console.log('🏷️ [useTagsWebSocket] Получено уведомление об обновлении тегов:', data);
// Очищаем предыдущий таймер
if (debounceTimer) {
clearTimeout(debounceTimer);
}
// Устанавливаем новый таймер для дебаунсинга
debounceTimer = setTimeout(() => {
console.log('🏷️ [useTagsWebSocket] Выполняем обновление тегов после дебаунсинга');
tagsUpdateCallbacks.value.forEach(callback => {
try {
callback(data);
} catch (error) {
console.error('Ошибка в callback обновления тегов:', error);
}
});
}, DEBOUNCE_DELAY);
// Вызываем все зарегистрированные колбэки
tagsUpdateCallbacks.value.forEach(callback => {
try {
callback(data);
} catch (error) {
console.error('🏷️ [useTagsWebSocket] Ошибка в колбэке:', error);
}
});
}
onMounted(() => {