feat 用户全局websocket消息推送

This commit is contained in:
xxm
2022-11-21 00:19:34 +08:00
parent 331f4f2bba
commit a2e7b5b807
11 changed files with 176 additions and 11 deletions

View File

@@ -0,0 +1,9 @@
/**
* 服务端websocket事件通知类型
*/
export enum WsListenerEnum {
// 通知消息发生消息更新 主要是 未读数量更新
NOTICE_MESSAGE_UPDATE = 'notice_message_update',
// ws测试事件
EVENT_TEST_WEBSOCKET = 'event_test_websocket',
}

View File

@@ -1,6 +1,8 @@
import { defineStore } from 'pinia'
import { store } from '/@/store'
import { countByReceiveNotRead } from './SiteMessage.api'
import { listenerEvent } from '/@/logics/websocket/WebsocketNotice'
import { WsListenerEnum } from '/@/enums/wsNoticeEnum'
export const useSiteMessageStore = defineStore({
id: 'SiteMessageStore',
@@ -20,3 +22,7 @@ export const useSiteMessageStore = defineStore({
export function useSiteMessageStoreWithOut() {
return useSiteMessageStore(store)
}
// 监听 通知消息更新
listenerEvent(WsListenerEnum.NOTICE_MESSAGE_UPDATE, (event) => {
useSiteMessageStoreWithOut().updateNotReadCount()
})

View File

@@ -7,7 +7,7 @@
@visibleChange="visibleChange"
:overlayStyle="{ width: '300px' }"
>
<a-badge :count="notReadMsgCount" dot>
<a-badge :count="notReadMsgCount">
<BellOutlined />
</a-badge>
<template #content>
@@ -26,7 +26,7 @@
</a-list-item>
</a-list>
<div style="margin-top: 5px; text-align: center">
<a-button @click="toSiteMessage" type="dashed" block>查看{{ num }}更多</a-button>
<a-button @click="toSiteMessage" type="dashed" block>查看更多</a-button>
</div>
</a-spin>
</template>
@@ -94,4 +94,9 @@
noticeIconReader.init(message)
}
</script>
<style lang="less"></style>
<style lang="less">
.ant-badge-count {
top: 14px;
right: 6px;
}
</style>

View File

@@ -0,0 +1,97 @@
/**
* 用户全局websocket连接管理
*/
import { useWebSocket } from '@vueuse/core'
import { useUserStoreWithOut } from '/@/store/modules/user'
import { EVENT_NOTICE, NOTIFICATION_ERROR, NOTIFICATION_INFO, NOTIFICATION_WARN } from '/@/logics/websocket/WebSockerType'
import { publishWsEvent } from '/@/logics/websocket/WebsocketNotice'
import { useMessage } from '/@/hooks/web/useMessage'
const { notification } = useMessage()
// websocket关闭
let wsClose: WebSocket['close']
export function initWebSocket() {
const userStore = useUserStoreWithOut()
const token = userStore.getToken
const wsUrl = 'ws://localhost:9999'
const serverUrl = `${wsUrl}/ws/user?AccessToken=${token}`
const { close } = useWebSocket(serverUrl, {
autoReconnect: false,
heartbeat: true,
onMessage: onMessage,
onConnected: () => {
console.log('用户全局WebSocket连接成功')
},
onDisconnected: (ws, event) => {
console.error('用户全局WebSocket断开连接')
},
})
wsClose = close
}
/**
* 事件分发
*/
/**
* 处理接收到的消息
*/
function onMessage(ws: WebSocket, event: MessageEvent) {
const res = JSON.parse(event.data)
if ([NOTIFICATION_INFO, NOTIFICATION_INFO, NOTIFICATION_ERROR].includes(res.type)) {
wsNotification(res)
} else if ([EVENT_NOTICE].includes(res.type)) {
wsEventNotice(res)
}
}
/**
* 事件通知 弹框
*/
function wsNotification(res: WsResult) {
if (res.type === NOTIFICATION_INFO) {
notification.info({
message: '消息通知',
description: res.data,
})
} else if (res.type === NOTIFICATION_WARN) {
notification.warn({
message: '警告',
description: res.data,
})
} else if (res.type === NOTIFICATION_ERROR) {
notification.info({
message: '警告',
description: res.data,
})
}
}
/**
* 事件通知 发送到消息总线
*/
function wsEventNotice(res: WsResult) {
// 发布事件到消息总线
publishWsEvent(res.eventCode, res.data)
}
/**
* 关闭
*/
export function closeWebSocket() {
wsClose && wsClose()
}
/**
* websocket响应消息类
*/
export interface WsResult<T = any> {
// 类型编码
type: number
// 数据体
data: T
// 事件编码
eventCode: number
}

View File

@@ -0,0 +1,9 @@
// 普通通知
export const NOTIFICATION_INFO = 1001
// 警告通知
export const NOTIFICATION_WARN = 1002
// 错误通知
export const NOTIFICATION_ERROR = 1003
// 事件通知跳转
export const EVENT_NOTICE = 9001

View File

@@ -0,0 +1,37 @@
import mitt, { EventHandlerList, Handler } from '/@/utils/mitt'
import { WsListenerEnum } from '/@/enums/wsNoticeEnum'
/**
* 服务端WebSocket消息推送通知
*/
const emitter = mitt()
/**
* 发布事件
*/
export function publishWsEvent(key, data) {
emitter.emit(key, data)
}
/**
* 事件监听
*/
export function listenerEvent(key: WsListenerEnum, callback: Handler) {
emitter.on(key.toString(), callback)
}
/**
* 清除指定key所关联的事件监听
*/
export function clearEventsByKey(key: WsListenerEnum) {
// 一个key可能对应多个事件回调, 清除时
const eventHandlerList = emitter.all.get(key.toString()) as EventHandlerList
eventHandlerList.forEach((value) => emitter.off(key.toString(), value))
}
/**
* 清除指定事件的监听
*/
export function clearEvent(key: WsListenerEnum, callback: Handler) {
emitter.off(key.toString(), callback)
}

View File

@@ -8,6 +8,7 @@ import { useUserStoreWithOut } from '/@/store/modules/user'
import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic'
import { RootRoute } from '/@/router/routes'
import { initWebSocket } from "/@/logics/websocket/UserGlobalWebSocker";
// import { useDictStoreWithOut } from '/@/store/modules/dict'
const LOGIN_PATH = PageEnum.BASE_LOGIN
@@ -97,16 +98,13 @@ export function createPermissionGuard(router: Router) {
next()
return
}
//TODO 添加 websocket连接.
// 初始化 websocket连接.
initWebSocket()
// 重载菜单
console.log('重载菜单')
const routes = await permissionStore.buildRoutesAction()
// 初始化字典 改到项目加载的时候进行初始化
// console.log('初始化字典')
// await useDictStore.initDict()
routes.forEach((route) => {
router.addRoute(route as unknown as RouteRecordRaw)
})

View File

@@ -20,7 +20,7 @@ export const router = createRouter({
history: createWebHistory(import.meta.env.VITE_PUBLIC_PATH),
// 应该添加到路由的初始路由列表。
routes: basicRoutes as unknown as RouteRecordRaw[],
// 是否应该禁止尾部斜杠。默认为
// 是否应该禁止尾部斜杠。默认为
strict: true,
scrollBehavior: () => ({ left: 0, top: 0 }),
})

View File

@@ -26,6 +26,7 @@ export const useDictStore = defineStore({
name: o.name,
} as Dict
})
console.log('初始化字典')
})
},
},

View File

@@ -16,6 +16,7 @@ import { h } from 'vue'
import { getFilePreviewUrlPrefix } from '/@/api/common/FileUpload'
// @ts-ignore
import { getUserInfo } from '/@/api/sys/user'
import { closeWebSocket, initWebSocket } from "/@/logics/websocket/UserGlobalWebSocker";
interface UserState {
userInfo: Nullable<UserInfo>
@@ -103,7 +104,7 @@ export const useUserStore = defineStore({
*/
async afterLoginAction(goHome?: boolean) {
if (!this.getToken) return null
// 获取用户信息
// 刷新登陆后用户信息
await this.refreshUserInfoAction()
const sessionTimeout = this.sessionTimeout
// 超时
@@ -121,6 +122,8 @@ export const useUserStore = defineStore({
router.addRoute(PAGE_NOT_FOUND_ROUTE as unknown as RouteRecordRaw)
permissionStore.setDynamicAddedRoute(true)
}
// 初始化 websocket连接.
initWebSocket()
goHome && (await router.replace(PageEnum.BASE_HOME))
}
},
@@ -147,6 +150,7 @@ export const useUserStore = defineStore({
this.setToken(undefined)
this.setSessionTimeout(false)
this.setUserInfo(null)
closeWebSocket()
goLogin && router.push(PageEnum.BASE_LOGIN)
},

View File

@@ -114,7 +114,6 @@
}
// 保存
function handleOk() {
console.log(form.efficientTime)
formRef.validate().then(async () => {
confirmLoading.value = true
await saveOrUpdate(form)