diff --git a/src/lang/en_US.ts b/src/lang/en_US.ts
index 59df4ba..034ea91 100644
--- a/src/lang/en_US.ts
+++ b/src/lang/en_US.ts
@@ -18,6 +18,7 @@ export default {
language: 'Language',
dashboard: 'Dashboard',
document: 'Document',
+ message: 'Message',
layoutSize: 'Layout Size',
selectTenant: 'Select Tenant',
layoutSetting: 'Layout Setting',
diff --git a/src/lang/zh_CN.ts b/src/lang/zh_CN.ts
index d778f7d..666a400 100644
--- a/src/lang/zh_CN.ts
+++ b/src/lang/zh_CN.ts
@@ -17,6 +17,7 @@ export default {
language: '语言',
dashboard: '首页',
document: '项目文档',
+ message: '消息',
layoutSize: '布局大小',
selectTenant: '选择租户',
layoutSetting: '布局设置',
diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue
index 3c0e45d..7818fd4 100644
--- a/src/layout/components/Navbar.vue
+++ b/src/layout/components/Navbar.vue
@@ -27,6 +27,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -81,10 +96,14 @@ import { getTenantList } from "@/api/login";
import { dynamicClear, dynamicTenant } from "@/api/system/tenant";
import { ComponentInternalInstance } from "vue";
import { TenantVO } from "@/api/types";
+import notice from './notice/index.vue';
+import useNoticeStore from '@/store/modules/notice';
const appStore = useAppStore();
const userStore = useUserStore();
const settingsStore = useSettingsStore();
+const noticeStore = storeToRefs(useNoticeStore());
+const newNotice = ref(0);
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -161,6 +180,11 @@ const handleCommand = (command: string) => {
commandMap[command]();
}
}
+
+//用深度监听 消息
+watch(() => noticeStore.state.value.notices, (newVal, oldVal) => {
+ newNotice.value = newVal.filter((item: any) => !item.read).length;
+}, { deep: true });
diff --git a/src/store/modules/notice.ts b/src/store/modules/notice.ts
new file mode 100644
index 0000000..f3f8e5a
--- /dev/null
+++ b/src/store/modules/notice.ts
@@ -0,0 +1,42 @@
+import { defineStore } from 'pinia';
+
+interface NoticeItem {
+ title?: string;
+ read: boolean;
+ message: any;
+ time: string;
+}
+
+export const useNoticeStore = defineStore('notice', () => {
+ const state = reactive({
+ notices: [] as NoticeItem[]
+ });
+
+ const addNotice = (notice: NoticeItem) => {
+ state.notices.push(notice);
+ };
+
+ const removeNotice = (notice: NoticeItem) => {
+ state.notices.splice(state.notices.indexOf(notice), 1);
+ };
+
+ //实现全部已读
+ const readAll = () => {
+ state.notices.forEach((item) => {
+ item.read = true;
+ });
+ };
+
+ const clearNotice = () => {
+ state.notices = [];
+ };
+ return {
+ state,
+ addNotice,
+ removeNotice,
+ readAll,
+ clearNotice
+ };
+});
+
+export default useNoticeStore;
diff --git a/src/utils/websocket.ts b/src/utils/websocket.ts
new file mode 100644
index 0000000..90e86bf
--- /dev/null
+++ b/src/utils/websocket.ts
@@ -0,0 +1,132 @@
+/**
+ * @module initWebSocket 初始化
+ * @module websocketonopen 连接成功
+ * @module websocketonerror 连接失败
+ * @module websocketclose 断开连接
+ * @module resetHeart 重置心跳
+ * @module sendSocketHeart 心跳发送
+ * @module reconnect 重连
+ * @module sendMsg 发送数据
+ * @module websocketonmessage 接收数据
+ * @module test 测试收到消息传递
+ * @description socket 通信
+ * @param {any} url socket地址
+ * @param {any} websocket websocket 实例
+ * @param {any} heartTime 心跳定时器实例
+ * @param {number} socketHeart 心跳次数
+ * @param {number} HeartTimeOut 心跳超时时间
+ * @param {number} socketError 错误次数
+ */
+
+import { getToken } from '@/utils/auth';
+import useNoticeStore from '@/store/modules/notice';
+
+const { addNotice } = useNoticeStore();
+
+let socketUrl: any = ''; // socket地址
+let websocket: any = null; // websocket 实例
+let heartTime: any = null; // 心跳定时器实例
+let socketHeart = 0 as number; // 心跳次数
+const HeartTimeOut = 10000; // 心跳超时时间 10000 = 10s
+let socketError = 0 as number; // 错误次数
+
+// 初始化socket
+export const initWebSocket = (url: any) => {
+ socketUrl = url;
+ // 初始化 websocket
+ websocket = new WebSocket(url + '?Authorization=Bearer ' + getToken() + '&clientid=' + import.meta.env.VITE_APP_CLIENT_ID);
+ websocketonopen();
+ websocketonmessage();
+ websocketonerror();
+ websocketclose();
+ sendSocketHeart();
+ return websocket;
+};
+
+// socket 连接成功
+export const websocketonopen = () => {
+ websocket.onopen = function () {
+ console.log('连接 websocket 成功');
+ resetHeart();
+ };
+};
+
+// socket 连接失败
+export const websocketonerror = () => {
+ websocket.onerror = function (e: any) {
+ console.log('连接 websocket 失败', e);
+ };
+};
+
+// socket 断开链接
+export const websocketclose = () => {
+ websocket.onclose = function (e: any) {
+ console.log('断开连接', e);
+ };
+};
+
+// socket 重置心跳
+export const resetHeart = () => {
+ socketHeart = 0;
+ socketError = 0;
+ clearInterval(heartTime);
+ sendSocketHeart();
+};
+
+// socket心跳发送
+export const sendSocketHeart = () => {
+ heartTime = setInterval(() => {
+ // 如果连接正常则发送心跳
+ if (websocket.readyState == 1) {
+ // if (socketHeart <= 30) {
+ websocket.send(
+ JSON.stringify({
+ type: 'ping'
+ })
+ );
+ socketHeart = socketHeart + 1;
+ } else {
+ // 重连
+ reconnect();
+ }
+ }, HeartTimeOut);
+};
+
+// socket重连
+export const reconnect = () => {
+ if (socketError <= 2) {
+ clearInterval(heartTime);
+ initWebSocket(socketUrl);
+ socketError = socketError + 1;
+ // eslint-disable-next-line prettier/prettier
+ console.log('socket重连', socketError);
+ } else {
+ // eslint-disable-next-line prettier/prettier
+ console.log('重试次数已用完');
+ clearInterval(heartTime);
+ }
+};
+
+// socket 发送数据
+export const sendMsg = (data: any) => {
+ websocket.send(data);
+};
+
+// socket 接收数据
+export const websocketonmessage = () => {
+ websocket.onmessage = function (e: any) {
+ const msg = JSON.parse(e.data) as any;
+ if (msg.type === 'heartbeat') {
+ resetHeart();
+ }
+ if (msg.type === 'ping') {
+ return;
+ }
+ addNotice({
+ message: msg,
+ read: false,
+ time: new Date().toLocaleString()
+ });
+ return msg;
+ };
+};
diff --git a/src/views/index.vue b/src/views/index.vue
index 438c1af..40c88a8 100644
--- a/src/views/index.vue
+++ b/src/views/index.vue
@@ -96,6 +96,11 @@