!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 | ||||
| VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e' | ||||
|  | ||||
| # websocket 开关 | ||||
| VITE_APP_WEBSOCKET = true | ||||
|  | ||||
| @ -26,3 +26,6 @@ VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbf | ||||
|  | ||||
| # 客户端id | ||||
| VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e' | ||||
|  | ||||
| # websocket 开关 | ||||
| VITE_APP_WEBSOCKET = true | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "ruoyi-vue-plus", | ||||
|   "version": "5.1.0", | ||||
|   "version": "5.1.1", | ||||
|   "description": "RuoYi-Vue-Plus多租户管理系统", | ||||
|   "author": "LionLi", | ||||
|   "license": "MIT", | ||||
|  | ||||
| @ -29,6 +29,11 @@ export function login(data: LoginData): AxiosPromise<LoginResult> { | ||||
|  | ||||
| // 注册方法 | ||||
| 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 | ||||
|   }); | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -8,7 +8,7 @@ | ||||
|         <el-button circle icon="Refresh" @click="refresh()" /> | ||||
|       </el-tooltip> | ||||
|       <el-tooltip class="item" effect="dark" content="显示/隐藏列" placement="top" v-if="columns"> | ||||
|         <div> | ||||
|         <div class="show-btn"> | ||||
|           <el-popover placement="bottom" trigger="click"> | ||||
|             <div class="tree-header">显示/隐藏列</div> | ||||
|             <el-tree | ||||
| @ -98,4 +98,7 @@ onMounted(() => { | ||||
|   line-height: 24px; | ||||
|   text-align: center; | ||||
| } | ||||
| .show-btn { | ||||
|   margin-left: 12px; | ||||
| } | ||||
| </style> | ||||
|  | ||||
| @ -18,6 +18,7 @@ export default { | ||||
|     language: 'Language', | ||||
|     dashboard: 'Dashboard', | ||||
|     document: 'Document', | ||||
|     message: 'Message', | ||||
|     layoutSize: 'Layout Size', | ||||
|     selectTenant: 'Select Tenant', | ||||
|     layoutSetting: 'Layout Setting', | ||||
|  | ||||
| @ -17,6 +17,7 @@ export default { | ||||
|     language: '语言', | ||||
|     dashboard: '首页', | ||||
|     document: '项目文档', | ||||
|     message: '消息', | ||||
|     layoutSize: '布局大小', | ||||
|     selectTenant: '选择租户', | ||||
|     layoutSetting: '布局设置', | ||||
|  | ||||
| @ -5,7 +5,7 @@ | ||||
|       :key="item.path" | ||||
|       :iframeId="'iframe' + index" | ||||
|       v-show="route.path === item.path" | ||||
|       :src="item.meta ? item.meta.link : ''" | ||||
|       :src="iframeUrl(item.meta ? item.meta.link : '', item.query)" | ||||
|     ></inner-link> | ||||
|   </transition-group> | ||||
| </template> | ||||
| @ -15,5 +15,13 @@ import InnerLink from "../InnerLink/index.vue"; | ||||
| import useTagsViewStore from '@/store/modules/tagsView'; | ||||
|  | ||||
| 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> | ||||
|  | ||||
| @ -27,6 +27,21 @@ | ||||
|             <svg-icon class-name="search-icon" icon-class="search" /> | ||||
|           </div> | ||||
|         </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"> | ||||
|           <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" /> | ||||
|         </el-tooltip> | ||||
| @ -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(<number>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 }); | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| @ -169,6 +193,10 @@ const handleCommand = (command: string) => { | ||||
|   height:30px; | ||||
| } | ||||
|  | ||||
| :deep(.el-badge__content.is-fixed){ | ||||
|     top: 12px; | ||||
| } | ||||
|  | ||||
| .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) { | ||||
|         el.path = lastRouter.path + '/' + el.path; | ||||
|         if (el.children && el.children.length) { | ||||
|           children = children.concat(filterChildren(el.children, el)) | ||||
|           return | ||||
|         } | ||||
|       } | ||||
|       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_RSA_PUBLIC_KEY: string; | ||||
|   VITE_APP_CLIENT_ID: string; | ||||
|   VITE_APP_WEBSOCKET: boolean; | ||||
| } | ||||
| interface ImportMeta { | ||||
|   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 /> | ||||
|           * 国际化 SpringMessage Spring标准国际化方案<br /> | ||||
|         </p> | ||||
|         <p><b>当前版本:</b> <span>v5.1.0</span></p> | ||||
|         <p><b>当前版本:</b> <span>v5.1.1</span></p> | ||||
|         <p> | ||||
|           <el-tag type="danger">¥免费开源</el-tag> | ||||
|         </p> | ||||
| @ -78,7 +78,7 @@ | ||||
|           * 分布式监控 Prometheus、Grafana 全方位性能监控<br /> | ||||
|           * 其余与 Vue 版本一致<br /> | ||||
|         </p> | ||||
|         <p><b>当前版本:</b> <span>v2.1.0</span></p> | ||||
|         <p><b>当前版本:</b> <span>v2.1.1</span></p> | ||||
|         <p> | ||||
|           <el-tag type="danger">¥免费开源</el-tag> | ||||
|         </p> | ||||
| @ -96,6 +96,12 @@ | ||||
| </template> | ||||
|  | ||||
| <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) => { | ||||
|   window.open(url, '__blank') | ||||
|  | ||||
| @ -76,6 +76,12 @@ | ||||
|           sortable="custom" | ||||
|           :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="loginLocation" :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"; | ||||
|  | ||||
| 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 loginInfoList = ref<LoginInfoVO[]>([]); | ||||
|  | ||||
| @ -29,6 +29,12 @@ | ||||
|         </el-table-column> | ||||
|         <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="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="ipaddr" :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"; | ||||
|  | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
| const { sys_device_type } = toRefs<any>(proxy?.useDict("sys_device_type")); | ||||
|  | ||||
| const onlineList = ref<OnlineVO[]>([]); | ||||
| const loading = ref(true); | ||||
|  | ||||
| @ -20,7 +20,7 @@ | ||||
|     <div id="git-user-binding"> | ||||
|       <h4 class="provider-desc">你可以绑定以下第三方帐号</h4> | ||||
|       <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"> | ||||
|             <svg-icon icon-class="wechat" /> | ||||
|           </div> | ||||
|  | ||||
| @ -28,6 +28,7 @@ export default defineConfig(({ mode, command }: ConfigEnv): UserConfig => { | ||||
|         [env.VITE_APP_BASE_API]: { | ||||
|           target: 'http://localhost:8080', | ||||
|           changeOrigin: true, | ||||
|           ws: true, | ||||
|           rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '') | ||||
|         } | ||||
|       } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 疯狂的狮子Li
					疯狂的狮子Li