| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- import { notification } from 'ant-design-vue'
- import i18n from '@/locales'
- import router from '@/router'
- import { useNotificationStore } from '@/stores/notification'
- const PING_INTERVAL = 10000
- const RECONNECT_DELAY = 5000
- function resolveCallType(callType) {
- if (Array.isArray(callType)) return callType[0] || 'mesh'
- return callType || 'mesh'
- }
- function getWsUrl(token) {
- const path = import.meta.env.VITE_PATH || import.meta.env.VITE_API_BASE_URL || ''
- let host = window.location.host
- if (path) {
- try {
- host = new URL(path).host
- } catch {
- // keep current host
- }
- }
- const protocol = import.meta.env.DEV || path.startsWith('https') || window.location.protocol === 'https:'
- ? 'wss:'
- : 'ws:'
- return `${protocol}//${host}/ucenter/ws/${encodeURIComponent(token)}`
- }
- class WsClient {
- ws = null
- pingTimer = null
- reconnectTimer = null
- manualClose = false
- currentToken = ''
- connect(token) {
- if (!token) return
- if (this.currentToken === token && this.ws?.readyState === WebSocket.OPEN) return
- this.disconnect(true)
- this.currentToken = token
- this.manualClose = false
- try {
- this.ws = new WebSocket(getWsUrl(token))
- } catch (err) {
- console.error('[ws] connect failed', err)
- this.scheduleReconnect()
- return
- }
- this.ws.onopen = () => {
- this.startPing()
- }
- this.ws.onmessage = (event) => {
- try {
- const message = JSON.parse(event.data)
- this.handleMessage(message)
- } catch (err) {
- console.error('[ws] parse message failed', err)
- }
- }
- this.ws.onclose = () => {
- this.stopPing()
- if (!this.manualClose) {
- this.scheduleReconnect()
- }
- }
- this.ws.onerror = () => {
- this.stopPing()
- this.ws?.close()
- }
- }
- handleMessage(message) {
- const { command, content } = message || {}
- if (command === 'open' || command === 'pong') return
- if (command === 'scene_compute_done') {
- const data = content?.data || {}
- const notificationStore = useNotificationStore()
- notificationStore.markUnread(data)
- const { t } = i18n.global
- const key = `scene_compute_done_${data.num || ''}_${Date.now()}`
- notification.open({
- key,
- message: t('notification.sceneComputeDoneTitle'),
- description: t('notification.sceneComputeDoneDesc', {
- title: data.title || data.num || '',
- }),
- duration: 0,
- onClick: () => {
- const type = resolveCallType(data.callType)
- const num = data.num
- if (num) {
- router.push({ path: `/sceneomore/${type}/${num}` })
- }
- notification.close(key)
- notificationStore.clearUnread()
- },
- })
- }
- }
- sendPing() {
- if (this.ws?.readyState !== WebSocket.OPEN) return
- this.ws.send(JSON.stringify({ command: 'ping', content: {} }))
- }
- startPing() {
- this.stopPing()
- this.sendPing()
- this.pingTimer = setInterval(() => this.sendPing(), PING_INTERVAL)
- }
- stopPing() {
- if (this.pingTimer) {
- clearInterval(this.pingTimer)
- this.pingTimer = null
- }
- }
- scheduleReconnect() {
- if (this.reconnectTimer || !this.currentToken || this.manualClose) return
- this.reconnectTimer = setTimeout(() => {
- this.reconnectTimer = null
- if (!this.currentToken || this.manualClose) return
- this.connect(this.currentToken)
- }, RECONNECT_DELAY)
- }
- disconnect(resetToken = false) {
- this.manualClose = true
- this.stopPing()
- if (this.reconnectTimer) {
- clearTimeout(this.reconnectTimer)
- this.reconnectTimer = null
- }
- if (this.ws) {
- this.ws.close()
- this.ws = null
- }
- if (resetToken) {
- this.currentToken = ''
- }
- }
- }
- let client = null
- export function getWsClient() {
- if (!client) {
- client = new WsClient()
- }
- return client
- }
|