+
{
line-height: 24px;
text-align: center;
}
+.show-btn {
+ margin-left: 12px;
+}
From 631f338be23d0a91936bf7f048b38e91be4efaf1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Wed, 1 Nov 2023 10:46:42 +0800
Subject: [PATCH 05/15] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8D=20=E7=94=A8?=
=?UTF-8?q?=E6=88=B7=E6=B3=A8=E5=86=8C=E7=BC=BA=E5=A4=B1=20clientid=20?=
=?UTF-8?q?=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/api/login.ts | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/api/login.ts b/src/api/login.ts
index 7419fb3..100a5e9 100644
--- a/src/api/login.ts
+++ b/src/api/login.ts
@@ -29,6 +29,11 @@ export function login(data: LoginData): AxiosPromise {
// 注册方法
export function register(data: any) {
+ const params = {
+ ...data,
+ clientId: clientId,
+ grantType: 'password'
+ };
return request({
url: '/auth/register',
headers: {
@@ -36,7 +41,7 @@ export function register(data: any) {
isEncrypt: true
},
method: 'post',
- data: data
+ data: params
});
}
From a8a334b3c326610be70dd762978e8bf1a8766adc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=B8=89=E4=B8=AA=E4=B8=89?= <2029364173@qq.com>
Date: Thu, 2 Nov 2023 04:36:31 +0000
Subject: [PATCH 06/15] =?UTF-8?q?!50=20add=20=E6=96=B0=E5=A2=9E=20?=
=?UTF-8?q?=E5=89=8D=E7=AB=AF=E6=8E=A5=E5=85=A5websocket=E6=8E=A5=E6=94=B6?=
=?UTF-8?q?=E6=B6=88=E6=81=AF=20*=20=20add=20=E6=96=B0=E5=A2=9E=20?=
=?UTF-8?q?=E5=89=8D=E7=AB=AF=E6=8E=A5=E5=85=A5websocket=E6=8E=A5=E6=94=B6?=
=?UTF-8?q?=E6=B6=88=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/lang/en_US.ts | 1 +
src/lang/zh_CN.ts | 1 +
src/layout/components/Navbar.vue | 28 ++++++
src/layout/components/notice/index.vue | 134 +++++++++++++++++++++++++
src/store/modules/notice.ts | 42 ++++++++
src/utils/websocket.ts | 132 ++++++++++++++++++++++++
src/views/index.vue | 5 +
vite.config.ts | 1 +
8 files changed, 344 insertions(+)
create mode 100644 src/layout/components/notice/index.vue
create mode 100644 src/store/modules/notice.ts
create mode 100644 src/utils/websocket.ts
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 @@
From 0a8c33c3cb2265d19520adc89402a62ae6fea4cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Fri, 10 Nov 2023 15:59:09 +0800
Subject: [PATCH 13/15] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8D=20=E4=BA=94?=
=?UTF-8?q?=E7=BA=A7=E8=B7=AF=E7=94=B1=E7=BC=93=E5=AD=98=E6=97=A0=E6=95=88?=
=?UTF-8?q?=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/store/modules/permission.ts | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/store/modules/permission.ts b/src/store/modules/permission.ts
index dcb3cd4..ed64e46 100644
--- a/src/store/modules/permission.ts
+++ b/src/store/modules/permission.ts
@@ -100,6 +100,10 @@ export const usePermissionStore = defineStore('permission', () => {
}
if (lastRouter) {
el.path = lastRouter.path + '/' + el.path;
+ if (el.children && el.children.length) {
+ children = children.concat(filterChildren(el.children, el))
+ return
+ }
}
children = children.concat(el);
});
From 35bebf403ca0729174d49e86feb8fb6e468c69de Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Mon, 13 Nov 2023 13:02:09 +0800
Subject: [PATCH 14/15] =?UTF-8?q?update=20=E9=80=82=E9=85=8D=20websocket?=
=?UTF-8?q?=E5=9C=A8https=E4=B8=8B=E7=9A=84=E8=BF=9E=E6=8E=A5=E6=96=B9?=
=?UTF-8?q?=E5=BC=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/views/index.vue | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/views/index.vue b/src/views/index.vue
index 3bb7396..b86c019 100644
--- a/src/views/index.vue
+++ b/src/views/index.vue
@@ -99,7 +99,8 @@
import { initWebSocket } from '@/utils/websocket';
onMounted(() => {
- initWebSocket("ws://" + window.location.host + import.meta.env.VITE_APP_BASE_API + "/resource/websocket");
+ let protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://'
+ initWebSocket(protocol + window.location.host + import.meta.env.VITE_APP_BASE_API + "/resource/websocket");
});
const goTarget = (url:string) => {
From 6e6062e824dc53b0c2fcb54bd87c87c6b438be39 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Tue, 14 Nov 2023 10:51:12 +0800
Subject: [PATCH 15/15] =?UTF-8?q?=F0=9F=98=B4=E5=8F=91=E5=B8=83=20vue=20?=
=?UTF-8?q?=E7=89=88=E6=9C=AC=205.1.1=20=E4=B8=8E=20cloud=20=E7=89=88?=
=?UTF-8?q?=E6=9C=AC2.1.1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
package.json | 2 +-
src/views/index.vue | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/package.json b/package.json
index 9b26e96..13e0f92 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ruoyi-vue-plus",
- "version": "5.1.0",
+ "version": "5.1.1",
"description": "RuoYi-Vue-Plus多租户管理系统",
"author": "LionLi",
"license": "MIT",
diff --git a/src/views/index.vue b/src/views/index.vue
index b86c019..7f658d8 100644
--- a/src/views/index.vue
+++ b/src/views/index.vue
@@ -33,7 +33,7 @@
* 部署方式 Docker 容器编排 一键部署业务集群
* 国际化 SpringMessage Spring标准国际化方案
- 当前版本: v5.1.0
+ 当前版本: v5.1.1
¥免费开源
@@ -78,7 +78,7 @@
* 分布式监控 Prometheus、Grafana 全方位性能监控
* 其余与 Vue 版本一致
- 当前版本: v2.1.0
+ 当前版本: v2.1.1
¥免费开源