!57 发布 vue 版本 5.1.1 与 cloud 版本2.1.1
Merge pull request !57 from 疯狂的狮子Li/dev
This commit is contained in:
		| @ -23,3 +23,6 @@ VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbf | |||||||
|  |  | ||||||
| # 客户端id | # 客户端id | ||||||
| VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e' | VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e' | ||||||
|  |  | ||||||
|  | # websocket 开关 | ||||||
|  | VITE_APP_WEBSOCKET = true | ||||||
|  | |||||||
| @ -26,3 +26,6 @@ VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbf | |||||||
|  |  | ||||||
| # 客户端id | # 客户端id | ||||||
| VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e' | VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e' | ||||||
|  |  | ||||||
|  | # websocket 开关 | ||||||
|  | VITE_APP_WEBSOCKET = true | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|   "name": "ruoyi-vue-plus", |   "name": "ruoyi-vue-plus", | ||||||
|   "version": "5.1.0", |   "version": "5.1.1", | ||||||
|   "description": "RuoYi-Vue-Plus多租户管理系统", |   "description": "RuoYi-Vue-Plus多租户管理系统", | ||||||
|   "author": "LionLi", |   "author": "LionLi", | ||||||
|   "license": "MIT", |   "license": "MIT", | ||||||
|  | |||||||
| @ -29,6 +29,11 @@ export function login(data: LoginData): AxiosPromise<LoginResult> { | |||||||
|  |  | ||||||
| // 注册方法 | // 注册方法 | ||||||
| export function register(data: any) { | export function register(data: any) { | ||||||
|  |   const params = { | ||||||
|  |     ...data, | ||||||
|  |     clientId: clientId, | ||||||
|  |     grantType: 'password' | ||||||
|  |   }; | ||||||
|   return request({ |   return request({ | ||||||
|     url: '/auth/register', |     url: '/auth/register', | ||||||
|     headers: { |     headers: { | ||||||
| @ -36,7 +41,7 @@ export function register(data: any) { | |||||||
|       isEncrypt: true |       isEncrypt: true | ||||||
|     }, |     }, | ||||||
|     method: 'post', |     method: 'post', | ||||||
|     data: data |     data: params | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ | |||||||
|         <el-button circle icon="Refresh" @click="refresh()" /> |         <el-button circle icon="Refresh" @click="refresh()" /> | ||||||
|       </el-tooltip> |       </el-tooltip> | ||||||
|       <el-tooltip class="item" effect="dark" content="显示/隐藏列" placement="top" v-if="columns"> |       <el-tooltip class="item" effect="dark" content="显示/隐藏列" placement="top" v-if="columns"> | ||||||
|         <div> |         <div class="show-btn"> | ||||||
|           <el-popover placement="bottom" trigger="click"> |           <el-popover placement="bottom" trigger="click"> | ||||||
|             <div class="tree-header">显示/隐藏列</div> |             <div class="tree-header">显示/隐藏列</div> | ||||||
|             <el-tree |             <el-tree | ||||||
| @ -98,4 +98,7 @@ onMounted(() => { | |||||||
|   line-height: 24px; |   line-height: 24px; | ||||||
|   text-align: center; |   text-align: center; | ||||||
| } | } | ||||||
|  | .show-btn { | ||||||
|  |   margin-left: 12px; | ||||||
|  | } | ||||||
| </style> | </style> | ||||||
|  | |||||||
| @ -18,6 +18,7 @@ export default { | |||||||
|     language: 'Language', |     language: 'Language', | ||||||
|     dashboard: 'Dashboard', |     dashboard: 'Dashboard', | ||||||
|     document: 'Document', |     document: 'Document', | ||||||
|  |     message: 'Message', | ||||||
|     layoutSize: 'Layout Size', |     layoutSize: 'Layout Size', | ||||||
|     selectTenant: 'Select Tenant', |     selectTenant: 'Select Tenant', | ||||||
|     layoutSetting: 'Layout Setting', |     layoutSetting: 'Layout Setting', | ||||||
|  | |||||||
| @ -17,6 +17,7 @@ export default { | |||||||
|     language: '语言', |     language: '语言', | ||||||
|     dashboard: '首页', |     dashboard: '首页', | ||||||
|     document: '项目文档', |     document: '项目文档', | ||||||
|  |     message: '消息', | ||||||
|     layoutSize: '布局大小', |     layoutSize: '布局大小', | ||||||
|     selectTenant: '选择租户', |     selectTenant: '选择租户', | ||||||
|     layoutSetting: '布局设置', |     layoutSetting: '布局设置', | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ | |||||||
|       :key="item.path" |       :key="item.path" | ||||||
|       :iframeId="'iframe' + index" |       :iframeId="'iframe' + index" | ||||||
|       v-show="route.path === item.path" |       v-show="route.path === item.path" | ||||||
|       :src="item.meta ? item.meta.link : ''" |       :src="iframeUrl(item.meta ? item.meta.link : '', item.query)" | ||||||
|     ></inner-link> |     ></inner-link> | ||||||
|   </transition-group> |   </transition-group> | ||||||
| </template> | </template> | ||||||
| @ -15,5 +15,13 @@ import InnerLink from "../InnerLink/index.vue"; | |||||||
| import useTagsViewStore from '@/store/modules/tagsView'; | import useTagsViewStore from '@/store/modules/tagsView'; | ||||||
|  |  | ||||||
| const route = useRoute(); | const route = useRoute(); | ||||||
| const tagsViewStore = useTagsViewStore() | const tagsViewStore = useTagsViewStore(); | ||||||
|  |  | ||||||
|  | function iframeUrl(url: string, query: any) { | ||||||
|  |   if (Object.keys(query).length > 0) { | ||||||
|  |     let params = Object.keys(query).map((key) => key + "=" + query[key]).join("&"); | ||||||
|  |     return url + "?" + params; | ||||||
|  |   } | ||||||
|  |   return url; | ||||||
|  | } | ||||||
| </script> | </script> | ||||||
|  | |||||||
| @ -27,6 +27,21 @@ | |||||||
|             <svg-icon class-name="search-icon" icon-class="search" /> |             <svg-icon class-name="search-icon" icon-class="search" /> | ||||||
|           </div> |           </div> | ||||||
|         </el-tooltip> |         </el-tooltip> | ||||||
|  |         <!-- 消息 --> | ||||||
|  |         <el-tooltip :content="$t('navbar.message')" effect="dark" placement="bottom"> | ||||||
|  |           <div> | ||||||
|  |             <el-popover placement="bottom" trigger="click" transition="el-zoom-in-top" :width="300" :persistent="false"> | ||||||
|  |               <template #reference> | ||||||
|  |                 <el-badge :value="newNotice > 0 ? newNotice : ''" :max="99"> | ||||||
|  |                   <svg-icon icon-class="message" /> | ||||||
|  |                 </el-badge> | ||||||
|  |               </template> | ||||||
|  |               <template #default> | ||||||
|  |                 <notice></notice> | ||||||
|  |               </template> | ||||||
|  |             </el-popover> | ||||||
|  |           </div> | ||||||
|  |         </el-tooltip> | ||||||
|         <el-tooltip content="Github" effect="dark" placement="bottom"> |         <el-tooltip content="Github" effect="dark" placement="bottom"> | ||||||
|           <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" /> |           <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" /> | ||||||
|         </el-tooltip> |         </el-tooltip> | ||||||
| @ -81,10 +96,14 @@ import { getTenantList } from "@/api/login"; | |||||||
| import { dynamicClear, dynamicTenant } from "@/api/system/tenant"; | import { dynamicClear, dynamicTenant } from "@/api/system/tenant"; | ||||||
| import { ComponentInternalInstance } from "vue"; | import { ComponentInternalInstance } from "vue"; | ||||||
| import { TenantVO } from "@/api/types"; | import { TenantVO } from "@/api/types"; | ||||||
|  | import notice from './notice/index.vue'; | ||||||
|  | import useNoticeStore from '@/store/modules/notice'; | ||||||
|  |  | ||||||
| const appStore = useAppStore(); | const appStore = useAppStore(); | ||||||
| const userStore = useUserStore(); | const userStore = useUserStore(); | ||||||
| const settingsStore = useSettingsStore(); | const settingsStore = useSettingsStore(); | ||||||
|  | const noticeStore = storeToRefs(useNoticeStore()); | ||||||
|  | const newNotice = ref(<number>0); | ||||||
|  |  | ||||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||||
|  |  | ||||||
| @ -161,6 +180,11 @@ const handleCommand = (command: string) => { | |||||||
|         commandMap[command](); |         commandMap[command](); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | //用深度监听 消息 | ||||||
|  | watch(() => noticeStore.state.value.notices, (newVal, oldVal) => { | ||||||
|  |   newNotice.value = newVal.filter((item: any) => !item.read).length; | ||||||
|  | }, { deep: true }); | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <style lang="scss" scoped> | <style lang="scss" scoped> | ||||||
| @ -169,6 +193,10 @@ const handleCommand = (command: string) => { | |||||||
|   height:30px; |   height:30px; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | :deep(.el-badge__content.is-fixed){ | ||||||
|  |     top: 12px; | ||||||
|  | } | ||||||
|  |  | ||||||
| .flex { | .flex { | ||||||
|   display: flex; |   display: flex; | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										134
									
								
								src/layout/components/notice/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/layout/components/notice/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | |||||||
|  | <template> | ||||||
|  |   <div class="layout-navbars-breadcrumb-user-news" v-loading="state.loading"> | ||||||
|  |     <div class="head-box"> | ||||||
|  |       <div class="head-box-title">通知公告</div> | ||||||
|  |       <div class="head-box-btn" @click="readAll">全部已读</div> | ||||||
|  |     </div> | ||||||
|  |     <div class="content-box" v-loading="state.loading"> | ||||||
|  |       <template v-if="newsList.length > 0"> | ||||||
|  |         <div class="content-box-item" v-for="(v, k) in newsList" :key="k" @click="onNewsClick(k)"> | ||||||
|  |           <div class="item-conten"> | ||||||
|  |             <div>{{ v.message }}</div> | ||||||
|  |             <div class="content-box-msg"></div> | ||||||
|  |             <div class="content-box-time">{{ v.time }}</div> | ||||||
|  |           </div> | ||||||
|  |           <!-- 已读/未读 --> | ||||||
|  |           <span v-if="v.read" class="el-tag el-tag--success el-tag--mini read">已读</span> | ||||||
|  |           <span v-else class="el-tag el-tag--danger el-tag--mini read">未读</span> | ||||||
|  |         </div> | ||||||
|  |       </template> | ||||||
|  |       <el-empty :description="'消息为空'" v-else></el-empty> | ||||||
|  |     </div> | ||||||
|  |     <div class="foot-box" @click="onGoToGiteeClick" v-if="newsList.length > 0">前往gitee</div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  |  | ||||||
|  | <script setup lang="ts" name="layoutBreadcrumbUserNews"> | ||||||
|  | import { ref } from "vue"; | ||||||
|  | import { storeToRefs } from 'pinia' | ||||||
|  | import { nextTick, onMounted, reactive } from "vue"; | ||||||
|  | import useNoticeStore from '@/store/modules/notice'; | ||||||
|  |  | ||||||
|  | const noticeStore = storeToRefs(useNoticeStore()); | ||||||
|  | const {readAll} = useNoticeStore(); | ||||||
|  |  | ||||||
|  | // 定义变量内容 | ||||||
|  | const state = reactive({ | ||||||
|  |   loading: false, | ||||||
|  | }); | ||||||
|  | const newsList =ref([]) as any; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * 初始化数据 | ||||||
|  |  * @returns | ||||||
|  |  */ | ||||||
|  | const getTableData = async () => { | ||||||
|  |   state.loading = true; | ||||||
|  |   newsList.value = noticeStore.state.value.notices; | ||||||
|  |   state.loading = false; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | //点击消息,写入已读 | ||||||
|  | const onNewsClick = (item: any) => { | ||||||
|  |   newsList.value[item].read = true; | ||||||
|  |   //并且写入pinia | ||||||
|  |   noticeStore.state.value.notices = newsList.value; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | // 前往通知中心点击 | ||||||
|  | const onGoToGiteeClick = () => { | ||||||
|  |   window.open("https://gitee.com/dromara/RuoYi-Vue-Plus/tree/5.X/"); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | onMounted(() => { | ||||||
|  |   nextTick(() => { | ||||||
|  |     getTableData(); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
|  | </script> | ||||||
|  |  | ||||||
|  | <style scoped lang="scss"> | ||||||
|  | .layout-navbars-breadcrumb-user-news { | ||||||
|  |   .head-box { | ||||||
|  |     display: flex; | ||||||
|  |     border-bottom: 1px solid var(--el-border-color-lighter); | ||||||
|  |     box-sizing: border-box; | ||||||
|  |     color: var(--el-text-color-primary); | ||||||
|  |     justify-content: space-between; | ||||||
|  |     height: 35px; | ||||||
|  |     align-items: center; | ||||||
|  |     .head-box-btn { | ||||||
|  |       color: var(--el-color-primary); | ||||||
|  |       font-size: 13px; | ||||||
|  |       cursor: pointer; | ||||||
|  |       opacity: 0.8; | ||||||
|  |       &:hover { | ||||||
|  |         opacity: 1; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   .content-box { | ||||||
|  |     height: 300px; | ||||||
|  |     overflow: auto; | ||||||
|  |     font-size: 13px; | ||||||
|  |     .content-box-item { | ||||||
|  |       padding-top: 12px; | ||||||
|  |       display: flex; | ||||||
|  |       &:last-of-type { | ||||||
|  |         padding-bottom: 12px; | ||||||
|  |       } | ||||||
|  |       .content-box-msg { | ||||||
|  |         color: var(--el-text-color-secondary); | ||||||
|  |         margin-top: 5px; | ||||||
|  |         margin-bottom: 5px; | ||||||
|  |       } | ||||||
|  |       .content-box-time { | ||||||
|  |         color: var(--el-text-color-secondary); | ||||||
|  |       } | ||||||
|  |       .item-conten { | ||||||
|  |         width: 100%; | ||||||
|  |         display: flex; | ||||||
|  |         flex-direction: column; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   .foot-box { | ||||||
|  |     height: 35px; | ||||||
|  |     color: var(--el-color-primary); | ||||||
|  |     font-size: 13px; | ||||||
|  |     cursor: pointer; | ||||||
|  |     opacity: 0.8; | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |     justify-content: center; | ||||||
|  |     border-top: 1px solid var(--el-border-color-lighter); | ||||||
|  |     &:hover { | ||||||
|  |       opacity: 1; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   :deep(.el-empty__description p) { | ||||||
|  |     font-size: 13px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </style> | ||||||
							
								
								
									
										42
									
								
								src/store/modules/notice.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/store/modules/notice.ts
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||||
| @ -100,6 +100,10 @@ export const usePermissionStore = defineStore('permission', () => { | |||||||
|       } |       } | ||||||
|       if (lastRouter) { |       if (lastRouter) { | ||||||
|         el.path = lastRouter.path + '/' + el.path; |         el.path = lastRouter.path + '/' + el.path; | ||||||
|  |         if (el.children && el.children.length) { | ||||||
|  |           children = children.concat(filterChildren(el.children, el)) | ||||||
|  |           return | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|       children = children.concat(el); |       children = children.concat(el); | ||||||
|     }); |     }); | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								src/types/env.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								src/types/env.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -69,6 +69,7 @@ interface ImportMetaEnv { | |||||||
|   VITE_APP_ENV: string; |   VITE_APP_ENV: string; | ||||||
|   VITE_APP_RSA_PUBLIC_KEY: string; |   VITE_APP_RSA_PUBLIC_KEY: string; | ||||||
|   VITE_APP_CLIENT_ID: string; |   VITE_APP_CLIENT_ID: string; | ||||||
|  |   VITE_APP_WEBSOCKET: boolean; | ||||||
| } | } | ||||||
| interface ImportMeta { | interface ImportMeta { | ||||||
|   readonly env: ImportMetaEnv; |   readonly env: ImportMetaEnv; | ||||||
|  | |||||||
							
								
								
									
										141
									
								
								src/utils/websocket.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								src/utils/websocket.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,141 @@ | |||||||
|  | /** | ||||||
|  |  * @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'; | ||||||
|  | import { ElNotification } from "element-plus"; | ||||||
|  |  | ||||||
|  | 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) => { | ||||||
|  |   if (!import.meta.env.VITE_APP_WEBSOCKET) { | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   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) { | ||||||
|  |     if (e.data.indexOf('heartbeat') > 0) { | ||||||
|  |       resetHeart(); | ||||||
|  |     } | ||||||
|  |     if (e.data.indexOf('ping') > 0) { | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     addNotice({ | ||||||
|  |       message: e.data, | ||||||
|  |       read: false, | ||||||
|  |       time: new Date().toLocaleString() | ||||||
|  |     }); | ||||||
|  |     ElNotification({ | ||||||
|  |       title: '消息', | ||||||
|  |       message: e.data, | ||||||
|  |       type: 'success', | ||||||
|  |       duration: 3000 | ||||||
|  |     }) | ||||||
|  |     return e.data; | ||||||
|  |   }; | ||||||
|  | }; | ||||||
| @ -33,7 +33,7 @@ | |||||||
|           * 部署方式 Docker 容器编排 一键部署业务集群<br /> |           * 部署方式 Docker 容器编排 一键部署业务集群<br /> | ||||||
|           * 国际化 SpringMessage Spring标准国际化方案<br /> |           * 国际化 SpringMessage Spring标准国际化方案<br /> | ||||||
|         </p> |         </p> | ||||||
|         <p><b>当前版本:</b> <span>v5.1.0</span></p> |         <p><b>当前版本:</b> <span>v5.1.1</span></p> | ||||||
|         <p> |         <p> | ||||||
|           <el-tag type="danger">¥免费开源</el-tag> |           <el-tag type="danger">¥免费开源</el-tag> | ||||||
|         </p> |         </p> | ||||||
| @ -78,7 +78,7 @@ | |||||||
|           * 分布式监控 Prometheus、Grafana 全方位性能监控<br /> |           * 分布式监控 Prometheus、Grafana 全方位性能监控<br /> | ||||||
|           * 其余与 Vue 版本一致<br /> |           * 其余与 Vue 版本一致<br /> | ||||||
|         </p> |         </p> | ||||||
|         <p><b>当前版本:</b> <span>v2.1.0</span></p> |         <p><b>当前版本:</b> <span>v2.1.1</span></p> | ||||||
|         <p> |         <p> | ||||||
|           <el-tag type="danger">¥免费开源</el-tag> |           <el-tag type="danger">¥免费开源</el-tag> | ||||||
|         </p> |         </p> | ||||||
| @ -96,6 +96,12 @@ | |||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script setup name="Index" lang="ts"> | <script setup name="Index" lang="ts"> | ||||||
|  | import { initWebSocket } from '@/utils/websocket'; | ||||||
|  |  | ||||||
|  | onMounted(() => { | ||||||
|  |   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) => { | const goTarget = (url:string) => { | ||||||
|   window.open(url, '__blank') |   window.open(url, '__blank') | ||||||
|  | |||||||
| @ -76,6 +76,12 @@ | |||||||
|           sortable="custom" |           sortable="custom" | ||||||
|           :sort-orders="['descending', 'ascending']" |           :sort-orders="['descending', 'ascending']" | ||||||
|         /> |         /> | ||||||
|  |         <el-table-column label="客户端" align="center" prop="clientKey" :show-overflow-tooltip="true" /> | ||||||
|  |         <el-table-column label="设备类型" align="center"> | ||||||
|  |           <template #default="scope"> | ||||||
|  |             <dict-tag :options="sys_device_type" :value="scope.row.deviceType" /> | ||||||
|  |           </template> | ||||||
|  |         </el-table-column> | ||||||
|         <el-table-column label="地址" align="center" prop="ipaddr" :show-overflow-tooltip="true" /> |         <el-table-column label="地址" align="center" prop="ipaddr" :show-overflow-tooltip="true" /> | ||||||
|         <el-table-column label="登录地点" align="center" prop="loginLocation" :show-overflow-tooltip="true" /> |         <el-table-column label="登录地点" align="center" prop="loginLocation" :show-overflow-tooltip="true" /> | ||||||
|         <el-table-column label="操作系统" align="center" prop="os" :show-overflow-tooltip="true" /> |         <el-table-column label="操作系统" align="center" prop="os" :show-overflow-tooltip="true" /> | ||||||
| @ -103,6 +109,7 @@ import { list, delLoginInfo, cleanLoginInfo, unlockLoginInfo } from "@/api/monit | |||||||
| import { LoginInfoQuery, LoginInfoVO } from "@/api/monitor/loginInfo/types"; | import { LoginInfoQuery, LoginInfoVO } from "@/api/monitor/loginInfo/types"; | ||||||
|  |  | ||||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||||
|  | const { sys_device_type } = toRefs<any>(proxy?.useDict("sys_device_type")); | ||||||
| const { sys_common_status } = toRefs<any>(proxy?.useDict("sys_common_status")); | const { sys_common_status } = toRefs<any>(proxy?.useDict("sys_common_status")); | ||||||
|  |  | ||||||
| const loginInfoList = ref<LoginInfoVO[]>([]); | const loginInfoList = ref<LoginInfoVO[]>([]); | ||||||
|  | |||||||
| @ -29,6 +29,12 @@ | |||||||
|         </el-table-column> |         </el-table-column> | ||||||
|         <el-table-column label="会话编号" align="center" prop="tokenId" :show-overflow-tooltip="true" /> |         <el-table-column label="会话编号" align="center" prop="tokenId" :show-overflow-tooltip="true" /> | ||||||
|         <el-table-column label="登录名称" align="center" prop="userName" :show-overflow-tooltip="true" /> |         <el-table-column label="登录名称" align="center" prop="userName" :show-overflow-tooltip="true" /> | ||||||
|  |         <el-table-column label="客户端" align="center" prop="clientKey" :show-overflow-tooltip="true" /> | ||||||
|  |         <el-table-column label="设备类型" align="center"> | ||||||
|  |           <template #default="scope"> | ||||||
|  |             <dict-tag :options="sys_device_type" :value="scope.row.deviceType" /> | ||||||
|  |           </template> | ||||||
|  |         </el-table-column> | ||||||
|         <el-table-column label="所属部门" align="center" prop="deptName" :show-overflow-tooltip="true" /> |         <el-table-column label="所属部门" align="center" prop="deptName" :show-overflow-tooltip="true" /> | ||||||
|         <el-table-column label="主机" align="center" prop="ipaddr" :show-overflow-tooltip="true" /> |         <el-table-column label="主机" align="center" prop="ipaddr" :show-overflow-tooltip="true" /> | ||||||
|         <el-table-column label="登录地点" align="center" prop="loginLocation" :show-overflow-tooltip="true" /> |         <el-table-column label="登录地点" align="center" prop="loginLocation" :show-overflow-tooltip="true" /> | ||||||
| @ -59,6 +65,7 @@ import { forceLogout, list as initData } from "@/api/monitor/online"; | |||||||
| import { OnlineQuery, OnlineVO } from "@/api/monitor/online/types"; | import { OnlineQuery, OnlineVO } from "@/api/monitor/online/types"; | ||||||
|  |  | ||||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||||
|  | const { sys_device_type } = toRefs<any>(proxy?.useDict("sys_device_type")); | ||||||
|  |  | ||||||
| const onlineList = ref<OnlineVO[]>([]); | const onlineList = ref<OnlineVO[]>([]); | ||||||
| const loading = ref(true); | const loading = ref(true); | ||||||
|  | |||||||
| @ -20,7 +20,7 @@ | |||||||
|     <div id="git-user-binding"> |     <div id="git-user-binding"> | ||||||
|       <h4 class="provider-desc">你可以绑定以下第三方帐号</h4> |       <h4 class="provider-desc">你可以绑定以下第三方帐号</h4> | ||||||
|       <div id="authlist" class="user-bind"> |       <div id="authlist" class="user-bind"> | ||||||
|         <a class="third-app" href="#" @click="authUrl('wechar');" title="使用 微信 账号授权登录"> |         <a class="third-app" href="#" @click="authUrl('wechat');" title="使用 微信 账号授权登录"> | ||||||
|           <div class="git-other-login-icon"> |           <div class="git-other-login-icon"> | ||||||
|             <svg-icon icon-class="wechat" /> |             <svg-icon icon-class="wechat" /> | ||||||
|           </div> |           </div> | ||||||
|  | |||||||
| @ -28,6 +28,7 @@ export default defineConfig(({ mode, command }: ConfigEnv): UserConfig => { | |||||||
|         [env.VITE_APP_BASE_API]: { |         [env.VITE_APP_BASE_API]: { | ||||||
|           target: 'http://localhost:8080', |           target: 'http://localhost:8080', | ||||||
|           changeOrigin: true, |           changeOrigin: true, | ||||||
|  |           ws: true, | ||||||
|           rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '') |           rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '') | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 疯狂的狮子Li
					疯狂的狮子Li