| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- import { isDevMode } from '@/utils/env'
- import { message } from 'antd'
- import type { MessageType } from '@/types/typing'
- import wsNotice from '@/components/RightContent/Book/components/wsNotice'
- import { isNullOrUnDef } from './is'
- import { tryChangeWorkStatus } from '@/components/RightContent/Book'
- enum ReadyState {
- Connecting = 0,
- Open = 1,
- Closing = 2,
- Closed = 3
- }
- export enum CmdType {
- /** @name 心跳 */
- HEART_BEAT = 0,
- /** @name 初始化接收的消息 */
- FirstMsg = 1,
- /** @name 通知、消息 */
- NoticeOrMessage = 13,
- /** @name 在线员工列表 */
- OnlineBook = 12,
- /** @name 员工工作状态 */
- WorkStatus = 14,
- /** @name 强制退出 */
- OutLine = 15
- }
- interface Options {
- reconnectLimit?: number
- reconnectInterval?: number // ♥跳间隔时长
- onOpen?: (event: WebSocketEventMap['open']) => void
- onClose?: (event: WebSocketEventMap['close']) => void
- onMessage?: (message: WebSocketEventMap['message']) => void
- onError?: (event: WebSocketEventMap['error']) => void
- }
- class Ws {
- private uid: string // 用户id
- private cid: string // 副本号
- public uri: string // websocket 链接地址
- public opts?: Options // websocket 链接相关配置
- private isLock: boolean // 断开锁
- private reconnectLimit: number // 重连限制锁
- private timer: NodeJS.Timeout | undefined // 重连 延时函数
- private sTimer: NodeJS.Timeout | undefined // ♥跳 延时函数
- private sinTimer: NodeJS.Timeout | undefined // ♥跳
- public wsIns: WebSocket | undefined // websocket实例
- // 初始化socket,一般在应用启动时初始化一次就好了,或者需要更换wsUrl
- public init(uid: string, uri: string, opts?: Options) {
- this.uid = uid
- this.uri = uri
- this.opts = opts
- this.reconnectLimit = 0
- if (isNullOrUnDef(this.uri)) {
- throw new Error('websocket连接地址不能为空')
- return
- }
- if (!this.wsIns || this.wsIns?.readyState === ReadyState.Closed) {
- this.connectWs()
- }
- }
- // 利用promise封装,解决ws可能没有实例化
- public getInstance(): Promise<WebSocket> {
- return new Promise((resolve, reject) => {
- if (this.wsIns?.readyState === ReadyState.Open) {
- resolve(this.wsIns)
- } else if (this.wsIns) {
- this.connectWs()
- resolve(this.wsIns)
- } else {
- reject()
- }
- })
- }
- private connectWs() {
- try {
- this.wsIns = new WebSocket(this.uri)
- this.wsIns.onmessage = msg => {
- this.heartCheck()
- // 心跳检测事件, 什么事情都不用干
- if (msg.data === 'pong') {
- return
- }
- const data = JSON.parse(msg.data)
- if (data.cmd === CmdType.FirstMsg) {
- // 保存当前socket副本号
- this.cid = data.CopyCount
- }
- this.opts?.onMessage?.(data)
- }
- this.wsIns.onopen = event => {
- // this.heartCheck()
- tryChangeWorkStatus(this.uid, window.location.pathname)
- this.opts?.onOpen?.(event)
- }
- this.wsIns.onclose = event => {
- this.reconnect()
- this.opts?.onClose?.(event)
- }
- this.wsIns.onerror = err => {
- this.reconnect()
- this.opts?.onError?.(err)
- }
- // 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
- window.addEventListener('beforeunload', () => {
- this.disconnect()
- })
- } catch (error) {
- if (isDevMode()) {
- message.error(error.toString())
- }
- this.wsIns = undefined
- }
- }
- /** 发送消息 */
- public sendMessage(map: MessageType) {
- const cMap = { ...map }
- const keys = Object.keys(map)
- keys.forEach(item => {
- if (/(userid)|(dstid)/.test(item)) {
- cMap[item] += `_${this.cid}`
- }
- })
- if (this.wsIns?.readyState === ReadyState.Open) {
- this.wsIns.send(JSON.stringify(cMap))
- }
- }
- /** 断开连接 */
- public disconnect() {
- if (this.wsIns?.readyState === ReadyState.Open) {
- this.isLock = true
- // this.wsIns.close()
- this.sendMessage({ cmd: CmdType.OutLine, userid: this.uid })
- }
- }
- /** ♥跳检测 */
- private heartCheck() {
- this.sTimer && clearTimeout(this.sTimer)
- this.sinTimer && clearInterval(this.sinTimer)
- this.sTimer = setTimeout(() => {
- // 发送♥跳
- this.sendMessage({ cmd: CmdType.HEART_BEAT, dstid: this.uid })
- this.sinTimer = setTimeout(() => {
- if (this.wsIns.readyState !== ReadyState.Open) {
- this.wsIns.close()
- }
- }, this.opts.reconnectInterval)
- }, this.opts.reconnectInterval)
- }
- // 重连
- private reconnect(): void {
- if (
- !this.isLock &&
- !(this.opts?.reconnectLimit && this.reconnectLimit > this.opts?.reconnectLimit)
- ) {
- this.reconnectLimit += 1
- this.timer && clearTimeout(this.timer)
- this.timer = setTimeout(() => {
- this.connectWs()
- this.isLock = false
- }, 4000)
- }
- }
- }
- export function onMessage(msg: MessageType) {
- // 这里执行所有的message监听事件
- switch (msg.cmd) {
- // 通知、消息
- case CmdType.NoticeOrMessage:
- wsNotice(msg)
- break
- // 在线员工列表
- case CmdType.OnlineBook:
- break
- default:
- break
- }
- }
- export default new Ws()
|