refactor: 重构项目结构
- 优化 `车站-设备-告警` 轮询机制 - 改进设备卡片的布局 - 支持修改设备 - 告警轮询中获取完整告警数据 - 车站告警详情支持导出完整的 `今日告警列表` - 支持将状态持久化到 `IndexedDB` - 新增轮询控制 (调试模式) - 新增离线开发模式 (调试模式) - 新增 `IndexedDB` 数据控制 (调试模式)
This commit is contained in:
1
src/composables/stomp/index.ts
Normal file
1
src/composables/stomp/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './use-stomp-client';
|
||||
123
src/composables/stomp/use-stomp-client.ts
Normal file
123
src/composables/stomp/use-stomp-client.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import type { NdmDeviceAlarmLogResultVO, Station, SyncCameraResult } from '@/apis';
|
||||
import { ALARM_TOPIC, SYNC_CAMERA_STATUS_TOPIC } from '@/constants';
|
||||
import { useAlarmStore, useSettingStore, useStationStore } from '@/stores';
|
||||
import { Client } from '@stomp/stompjs';
|
||||
import { watchDebounced } from '@vueuse/core';
|
||||
import destr from 'destr';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||
import { useStationAlarmsMutation } from '../query';
|
||||
|
||||
const getBrokerUrl = () => {
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const { host } = window.location;
|
||||
const endpoint = '/ws';
|
||||
const brokerURL = `${protocol}//${host}${endpoint}`;
|
||||
return brokerURL;
|
||||
};
|
||||
|
||||
export const useStompClient = () => {
|
||||
const stationStore = useStationStore();
|
||||
const { stations } = storeToRefs(stationStore);
|
||||
const alarmStore = useAlarmStore();
|
||||
const { unreadLineAlarms } = storeToRefs(alarmStore);
|
||||
const settingStore = useSettingStore();
|
||||
const { offlineDev } = storeToRefs(settingStore);
|
||||
|
||||
const { mutate: refreshStationAlarms } = useStationAlarmsMutation();
|
||||
|
||||
const stompClient = ref<Client | null>(null);
|
||||
|
||||
const syncCameraResult = ref<Record<Station['code'], SyncCameraResult>>({});
|
||||
|
||||
onMounted(() => {
|
||||
stompClient.value = new Client({
|
||||
brokerURL: getBrokerUrl(),
|
||||
reconnectDelay: 5000,
|
||||
heartbeatIncoming: 10000,
|
||||
heartbeatOutgoing: 10000,
|
||||
onConnect: () => {
|
||||
console.log('Stomp连接成功');
|
||||
stompClient.value?.subscribe(ALARM_TOPIC, (message) => {
|
||||
const alarm = destr<NdmDeviceAlarmLogResultVO>(message.body);
|
||||
if (alarm.alarmCategory === '1') {
|
||||
alarmStore.pushUnreadAlarm(alarm);
|
||||
}
|
||||
});
|
||||
stompClient.value?.subscribe(SYNC_CAMERA_STATUS_TOPIC, (message) => {
|
||||
const { stationCode, startTime, endTime, insertList, updateList, deleteList } = destr<SyncCameraResult>(message.body);
|
||||
syncCameraResult.value[stationCode] = { stationCode, startTime, endTime, insertList, updateList, deleteList };
|
||||
});
|
||||
},
|
||||
onDisconnect: () => {
|
||||
console.log('Stomp连接断开');
|
||||
stompClient.value?.unsubscribe(ALARM_TOPIC);
|
||||
stompClient.value?.unsubscribe(SYNC_CAMERA_STATUS_TOPIC);
|
||||
},
|
||||
onStompError: (frame) => {
|
||||
console.log('Stomp错误', frame);
|
||||
window.$message.error('Stomp错误');
|
||||
},
|
||||
onWebSocketError: (event: Event) => {
|
||||
console.log('WebSocket错误', event);
|
||||
window.$message.error('WebSocket错误');
|
||||
},
|
||||
});
|
||||
if (!offlineDev.value) {
|
||||
stompClient.value.activate();
|
||||
}
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
stompClient.value?.deactivate();
|
||||
stompClient.value = null;
|
||||
});
|
||||
|
||||
watch(offlineDev, (offline) => {
|
||||
if (offline) {
|
||||
stompClient.value?.deactivate();
|
||||
} else {
|
||||
stompClient.value?.activate();
|
||||
}
|
||||
});
|
||||
|
||||
// 当有车站的未读报警变化,即新收到告警时,需要同步告警数据,
|
||||
// 但告警可能非常频繁,所以需要防抖处理
|
||||
const abortControllerMap = ref(new Map<Station['code'], AbortController>());
|
||||
watchDebounced(
|
||||
() => Object.entries(unreadLineAlarms.value).map(([stationCode, stationAlarms]) => ({ stationCode, count: stationAlarms['unclassified'].length })),
|
||||
(newValue, oldValue) => {
|
||||
// 启用离线模式时,跳过处理
|
||||
if (offlineDev.value) return;
|
||||
if (newValue.length === 0) return;
|
||||
const codes: Station['code'][] = [];
|
||||
newValue.forEach(({ stationCode, count }) => {
|
||||
const prevState = oldValue.find((stat) => stat.stationCode === stationCode);
|
||||
if (!prevState || count !== prevState.count) {
|
||||
codes.push(stationCode);
|
||||
}
|
||||
});
|
||||
// console.log('以下车站收到新告警:', codes);
|
||||
for (const stationCode of codes) {
|
||||
const station = stations.value.find((station) => station.code === stationCode);
|
||||
if (!station) continue;
|
||||
abortControllerMap.value.get(stationCode)?.abort();
|
||||
abortControllerMap.value.set(stationCode, new AbortController());
|
||||
refreshStationAlarms({ station, signal: abortControllerMap.value.get(stationCode)?.signal });
|
||||
}
|
||||
},
|
||||
{
|
||||
debounce: 2500,
|
||||
maxWait: 5000,
|
||||
},
|
||||
);
|
||||
|
||||
return {
|
||||
stompClient,
|
||||
|
||||
syncCameraResult,
|
||||
afterCheckSyncCameraResult: () => {
|
||||
syncCameraResult.value = {};
|
||||
},
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user