From 9e89ab5bae8e52ca8d7bb17940cb7860ca573968 Mon Sep 17 00:00:00 2001 From: 疯狂的狮子Li <15040126243@163.com> Date: 星期二, 14 十一月 2023 11:11:07 +0800 Subject: [PATCH] !57 发布 vue 版本 5.1.1 与 cloud 版本2.1.1 Merge pull request !57 from 疯狂的狮子Li/dev --- src/views/index.vue | 10 + src/layout/components/Navbar.vue | 28 ++++ src/layout/components/notice/index.vue | 134 +++++++++++++++++++ src/store/modules/notice.ts | 42 ++++++ src/api/login.ts | 7 src/types/env.d.ts | 1 src/components/RightToolbar/index.vue | 5 src/views/monitor/online/index.vue | 7 + src/lang/en_US.ts | 1 src/views/monitor/logininfor/index.vue | 7 + src/views/system/user/profile/thirdParty.vue | 2 .env.development | 3 vite.config.ts | 1 src/lang/zh_CN.ts | 1 src/store/modules/permission.ts | 4 src/layout/components/IframeToggle/index.vue | 12 + package.json | 2 src/utils/websocket.ts | 141 ++++++++++++++++++++ .env.production | 3 19 files changed, 403 insertions(+), 8 deletions(-) diff --git a/.env.development b/.env.development index 6d2f8dd..b2b28c8 100644 --- a/.env.development +++ b/.env.development @@ -23,3 +23,6 @@ # 瀹㈡埛绔痠d VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e' + +# websocket 寮�鍏� +VITE_APP_WEBSOCKET = true diff --git a/.env.production b/.env.production index d723d2a..c463542 100644 --- a/.env.production +++ b/.env.production @@ -26,3 +26,6 @@ # 瀹㈡埛绔痠d VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e' + +# websocket 寮�鍏� +VITE_APP_WEBSOCKET = true 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/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 register(data: any) { + const params = { + ...data, + clientId: clientId, + grantType: 'password' + }; return request({ url: '/auth/register', headers: { @@ -36,7 +41,7 @@ isEncrypt: true }, method: 'post', - data: data + data: params }); } diff --git a/src/components/RightToolbar/index.vue b/src/components/RightToolbar/index.vue index 6f7b14f..6be382f 100644 --- a/src/components/RightToolbar/index.vue +++ b/src/components/RightToolbar/index.vue @@ -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 @@ line-height: 24px; text-align: center; } +.show-btn { + margin-left: 12px; +} </style> 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 @@ 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 @@ language: '璇█', dashboard: '棣栭〉', document: '椤圭洰鏂囨。', + message: '娑堟伅', layoutSize: '甯冨眬澶у皬', selectTenant: '閫夋嫨绉熸埛', layoutSetting: '甯冨眬璁剧疆', diff --git a/src/layout/components/IframeToggle/index.vue b/src/layout/components/IframeToggle/index.vue index 9ffae00..efb2b7a 100644 --- a/src/layout/components/IframeToggle/index.vue +++ b/src/layout/components/IframeToggle/index.vue @@ -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 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> 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 @@ <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 { 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 @@ 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 @@ height:30px; } +:deep(.el-badge__content.is-fixed){ + top: 12px; +} + .flex { display: flex; } diff --git a/src/layout/components/notice/index.vue b/src/layout/components/notice/index.vue new file mode 100644 index 0000000..ef4a6a9 --- /dev/null +++ b/src/layout/components/notice/index.vue @@ -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> 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/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 @@ } 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); }); diff --git a/src/types/env.d.ts b/src/types/env.d.ts index fabf641..8389dc5 100644 --- a/src/types/env.d.ts +++ b/src/types/env.d.ts @@ -69,6 +69,7 @@ VITE_APP_ENV: string; VITE_APP_RSA_PUBLIC_KEY: string; VITE_APP_CLIENT_ID: string; + VITE_APP_WEBSOCKET: boolean; } interface ImportMeta { readonly env: ImportMetaEnv; diff --git a/src/utils/websocket.ts b/src/utils/websocket.ts new file mode 100644 index 0000000..5ba0243 --- /dev/null +++ b/src/utils/websocket.ts @@ -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; // 閿欒娆℃暟 + +// 鍒濆鍖杝ocket +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(() => { + // 濡傛灉杩炴帴姝e父鍒欏彂閫佸績璺� + 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; + }; +}; diff --git a/src/views/index.vue b/src/views/index.vue index 438c1af..7f658d8 100644 --- a/src/views/index.vue +++ b/src/views/index.vue @@ -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銆丟rafana 鍏ㄦ柟浣嶆�ц兘鐩戞帶<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') diff --git a/src/views/monitor/logininfor/index.vue b/src/views/monitor/logininfor/index.vue index 38ab1ad..067790e 100644 --- a/src/views/monitor/logininfor/index.vue +++ b/src/views/monitor/logininfor/index.vue @@ -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 { 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[]>([]); diff --git a/src/views/monitor/online/index.vue b/src/views/monitor/online/index.vue index ddbd385..1a25dc5 100644 --- a/src/views/monitor/online/index.vue +++ b/src/views/monitor/online/index.vue @@ -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 { 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); diff --git a/src/views/system/user/profile/thirdParty.vue b/src/views/system/user/profile/thirdParty.vue index c84805e..4170894 100644 --- a/src/views/system/user/profile/thirdParty.vue +++ b/src/views/system/user/profile/thirdParty.vue @@ -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> diff --git a/vite.config.ts b/vite.config.ts index cadaf62..788aace 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -28,6 +28,7 @@ [env.VITE_APP_BASE_API]: { target: 'http://localhost:8080', changeOrigin: true, + ws: true, rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '') } } -- Gitblit v1.9.3