import { ref, reactive } from 'vue' import { useWebSocket } from '@vueuse/core' type EventHandler = (data: any) => void export function useSocketBus(deviceCode: string) { const url = `${import.meta.env.VITE_BASE_URL}/ws/${deviceCode}` // const url = `ws://192.168.188.149:8080/ws/${deviceCode}` // 响应式状态 const status = ref<'CONNECTING' | 'OPEN' | 'CLOSED'>('CLOSED') const messages = ref([]) // 事件总线 const events: Record = reactive({}) // 创建 WebSocket const { ws, open, close, send } = useWebSocket(url, { immediate: false, autoReconnect: { retries: 5, delay: 1000 }, onConnected(_ws) { console.log('✅ WebSocket 已连接') status.value = 'OPEN' }, onDisconnected() { console.log('🔌 WebSocket 已断开') status.value = 'CLOSED' }, onMessage(_ws, event) { try { const msg = JSON.parse(event.data) messages.value.push(msg) if (msg.event && events[msg.event]) { events[msg.event].forEach((cb) => cb(msg.data)) } anyHandlers.forEach((cb) => cb(msg)) } catch (err) { console.warn('非 JSON 消息:', event.data) } } }) const anyHandlers: EventHandler[] = reactive([]) // 注册所有事件回调 function onAny(cb: EventHandler) { anyHandlers.push(cb) } // 注册事件 function on(eventName: string, cb: EventHandler) { if (!events[eventName]) events[eventName] = [] events[eventName].push(cb) } // 注销事件 function off(eventName: string, cb?: EventHandler) { if (!cb) { events[eventName] = [] } else { events[eventName] = (events[eventName] || []).filter((fn) => fn !== cb) } } // 发送带事件名的消息 function sendEvent(eventName: string, data: any) { if (ws.value?.readyState === WebSocket.OPEN) { send(JSON.stringify({ event: eventName, data })) } else { console.warn('⚠️ WebSocket 未连接') } } return { ws, status, messages, on, off, sendEvent, open, close, onAny } } export const formatIotValue = (val: any) => { if (val === null || val === undefined) return { value: '0', isText: true } const strVal = String(val) if (!/\d/.test(strVal)) { return { value: strVal, isText: true } } const match = strVal.match(/^(-?\d+(?:\.\d+)?)(.*)$/) if (match) { return { value: match[1], suffix: match[2] || '', isText: false } } return { value: strVal, isText: true } } export interface HeaderItem { label: string key: string judgment?: boolean } export interface Dimensions { identifier: string name: string value: string | number color: string bgHover: string bgActive: string response?: boolean suffix?: string isText?: boolean minValue?: number maxValue?: number id?: number }