user-behavior-monitor 2.0.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/user-behavior-monitor.common.js +145 -40
- package/dist/user-behavior-monitor.umd.js +145 -40
- package/dist/user-behavior-monitor.umd.min.js +1 -2
- package/package.json +1 -1
- package/src/components/UserBehaviorMonitor.vue +120 -14
- package/src/components/UserBehaviorMonitor2.vue +259 -96
- package/dist/user-behavior-monitor.common.js.map +0 -1
- package/dist/user-behavior-monitor.umd.js.map +0 -1
- package/dist/user-behavior-monitor.umd.min.js.map +0 -1
|
@@ -1,2 +1 @@
|
|
|
1
|
-
(function(e,t){"object"===typeof exports&&"object"===typeof module?module.exports=t():"function"===typeof define&&define.amd?define([],t):"object"===typeof exports?exports["user-behavior-monitor"]=t():e["user-behavior-monitor"]=t()})("undefined"!==typeof self?self:this,(function(){return function(e){var t={};function n(i){if(t[i])return t[i].exports;var o=t[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(i,o,function(t){return e[t]}.bind(null,o));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s="fb15")}({2350:function(e,t){function n(e,t){var n=e[1]||"",o=e[3];if(!o)return n;if(t&&"function"===typeof btoa){var r=i(o),s=o.sources.map((function(e){return"/*# sourceURL="+o.sourceRoot+e+" */"}));return[n].concat(s).concat([r]).join("\n")}return[n].join("\n")}function i(e){var t=btoa(unescape(encodeURIComponent(JSON.stringify(e)))),n="sourceMappingURL=data:application/json;charset=utf-8;base64,"+t;return"/*# "+n+" */"}e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var i=n(t,e);return t[2]?"@media "+t[2]+"{"+i+"}":i})).join("")},t.i=function(e,n){"string"===typeof e&&(e=[[null,e,""]]);for(var i={},o=0;o<this.length;o++){var r=this[o][0];"number"===typeof r&&(i[r]=!0)}for(o=0;o<e.length;o++){var s=e[o];"number"===typeof s[0]&&i[s[0]]||(n&&!s[2]?s[2]=n:n&&(s[2]="("+s[2]+") and ("+n+")"),t.push(s))}},t}},3505:function(e,t,n){"use strict";n("f079")},"499e":function(e,t,n){"use strict";function i(e,t){for(var n=[],i={},o=0;o<t.length;o++){var r=t[o],s=r[0],a=r[1],c=r[2],u=r[3],d={id:e+":"+o,css:a,media:c,sourceMap:u};i[s]?i[s].parts.push(d):n.push(i[s]={id:s,parts:[d]})}return n}n.r(t),n.d(t,"default",(function(){return v}));var o="undefined"!==typeof document;if("undefined"!==typeof DEBUG&&DEBUG&&!o)throw new Error("vue-style-loader cannot be used in a non-browser environment. Use { target: 'node' } in your Webpack config to indicate a server-rendering environment.");var r={},s=o&&(document.head||document.getElementsByTagName("head")[0]),a=null,c=0,u=!1,d=function(){},l=null,h="data-vue-ssr-id",m="undefined"!==typeof navigator&&/msie [6-9]\b/.test(navigator.userAgent.toLowerCase());function v(e,t,n,o){u=n,l=o||{};var s=i(e,t);return f(s),function(t){for(var n=[],o=0;o<s.length;o++){var a=s[o],c=r[a.id];c.refs--,n.push(c)}t?(s=i(e,t),f(s)):s=[];for(o=0;o<n.length;o++){c=n[o];if(0===c.refs){for(var u=0;u<c.parts.length;u++)c.parts[u]();delete r[c.id]}}}}function f(e){for(var t=0;t<e.length;t++){var n=e[t],i=r[n.id];if(i){i.refs++;for(var o=0;o<i.parts.length;o++)i.parts[o](n.parts[o]);for(;o<n.parts.length;o++)i.parts.push(g(n.parts[o]));i.parts.length>n.parts.length&&(i.parts.length=n.parts.length)}else{var s=[];for(o=0;o<n.parts.length;o++)s.push(g(n.parts[o]));r[n.id]={id:n.id,refs:1,parts:s}}}}function p(){var e=document.createElement("style");return e.type="text/css",s.appendChild(e),e}function g(e){var t,n,i=document.querySelector("style["+h+'~="'+e.id+'"]');if(i){if(u)return d;i.parentNode.removeChild(i)}if(m){var o=c++;i=a||(a=p()),t=w.bind(null,i,o,!1),n=w.bind(null,i,o,!0)}else i=p(),t=b.bind(null,i),n=function(){i.parentNode.removeChild(i)};return t(e),function(i){if(i){if(i.css===e.css&&i.media===e.media&&i.sourceMap===e.sourceMap)return;t(e=i)}else n()}}var y=function(){var e=[];return function(t,n){return e[t]=n,e.filter(Boolean).join("\n")}}();function w(e,t,n,i){var o=n?"":i.css;if(e.styleSheet)e.styleSheet.cssText=y(t,o);else{var r=document.createTextNode(o),s=e.childNodes;s[t]&&e.removeChild(s[t]),s.length?e.insertBefore(r,s[t]):e.appendChild(r)}}function b(e,t){var n=t.css,i=t.media,o=t.sourceMap;if(i&&e.setAttribute("media",i),l.ssrId&&e.setAttribute(h,t.id),o&&(n+="\n/*# sourceURL="+o.sources[0]+" */",n+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(o))))+" */"),e.styleSheet)e.styleSheet.cssText=n;else{while(e.firstChild)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(n))}}},a042:function(e,t,n){t=e.exports=n("2350")(!1),t.push([e.i,".behavior-warning-dialog.el-dialog.el-dialog--center{text-align:left}",""])},f079:function(e,t,n){var i=n("a042");i.__esModule&&(i=i.default),"string"===typeof i&&(i=[[e.i,i,""]]),i.locals&&(e.exports=i.locals);var o=n("499e").default;o("1c569d63",i,!0,{sourceMap:!1,shadowMode:!1})},fb15:function(e,t,n){"use strict";var i;(n.r(t),n.d(t,"UserBehaviorMonitor",(function(){return d})),"undefined"!==typeof window)&&((i=window.document.currentScript)&&(i=i.src.match(/(.+\/)[^/]+\.js(\?.*)?$/))&&(n.p=i[1]));var o=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{ref:"behaviorMonitor",staticClass:"user-behavior-monitor"},[n("el-dialog",{attrs:{title:"提示",visible:e.showWarning,"show-close":!1,modal:!0,width:"30%",center:"","custom-class":"behavior-warning-dialog"},on:{"update:visible":function(t){e.showWarning=t}}},[n("span",[e._v(e._s(e.warningMessage))])])],1)},r=[],s={name:"UserBehaviorMonitor",props:{timeoutMinutes:{type:Number,default:10},warningMinutes:{type:Number,default:1}},data(){return{mouseMoveThrottled:!1,socket:null,countdownTimer:null,warningTimer:null,showWarning:!1,warningMessage:`您已${this.timeoutMinutes}分钟未操作,将在1分钟后自动退出`,lastActivityTime:null,isMonitoring:!1,currentTimeoutMinutes:10,currentWarningMinutes:1}},mounted(){this.isLoginRoute()||this.initMonitor()},beforeDestroy(){this.destroyMonitor()},watch:{$route(e,t){const n=t.path.includes("/login")||t.href&&t.href.includes("/login"),i=this.isLoginRoute();!n||i||this.isMonitoring?!n&&i&&this.isMonitoring&&this.destroyMonitor():this.initMonitor()}},methods:{isLoginRoute(){return!!(this.$route&&this.$route.path&&this.$route.path.includes("/login"))||"undefined"!==typeof window&&(window.location.pathname.includes("/login")||window.location.href.includes("/login"))},updateTimeoutSettings(e,t){void 0!==e&&(this.currentTimeoutMinutes=e,localStorage.setItem("userBehavior_timeoutMinutes",e.toString())),void 0!==t&&(this.currentWarningMinutes=t,localStorage.setItem("userBehavior_warningMinutes",t.toString())),this.warningMessage=`您已${this.currentTimeoutMinutes}分钟未操作,将在1分钟后自动退出`,this.resetTimer()},initMonitor(){this.isLoginRoute()?this.destroyMonitor():this.isMonitoring||(this.isMonitoring=!0,this.lastActivityTime=Date.now(),this.initWebSocket(),this.bindEventListeners())},destroyMonitor(){this.isMonitoring=!1,this.countdownTimer&&clearInterval(this.countdownTimer),this.warningTimer&&clearTimeout(this.warningTimer),this.socket&&(this.socket.close(),this.socket=null),this.unbindEventListeners()},initWebSocket(){try{const t=`wss://${location.host}/xy-api/auth-server/ws/user-activity`;let n="";try{const e=localStorage.getItem("api_header");e&&(n=JSON.parse(e).Authorization||"")}catch(e){n=localStorage.getItem("token")||""}this.socket=new WebSocket(`${t}?token=${encodeURIComponent(n)}`),this.socket.onopen=()=>{console.log("WebSocket连接已建立"),this.sendUserBehavior(),this.$emit("websocket-open")},this.socket.onmessage=t=>{try{const e=JSON.parse(t.data);console.log("收到用户活动状态消息:",e),this.handleActivityStatus(e),this.$emit("websocket-message",e)}catch(e){console.error("解析WebSocket消息失败:",e)}},this.socket.onclose=e=>{console.log("WebSocket连接已断开:",e.reason),this.$emit("websocket-close")},this.socket.onerror=e=>{console.error("WebSocket错误:",e),this.$emit("websocket-error",e)}}catch(t){console.error("WebSocket初始化失败:",t),this.$emit("websocket-error",t)}},handleActivityStatus(e){if("ACTIVITY_STATUS"===e.type&&e.data){const t=e.data;this.currentTimeoutMinutes=t.timeoutMinutes||10,this.currentWarningMinutes=t.reminderMinutes||1,this.warningMessage=`您已${this.currentTimeoutMinutes}分钟未操作,将在${this.currentWarningMinutes}分钟后自动退出`,t.needReminder&&this.showWarningWarning(t.remainingMillis)}},handleInactiveStatus(){localStorage.clear(),sessionStorage.clear(),window.location.href="/login",this.$emit("logout")},sendUserBehavior(e){if(console.log("用户行为监测:"),this.warningTimer&&clearTimeout(this.warningTimer),this.socket&&this.socket.readyState===WebSocket.OPEN){const e={type:"HEARTBEAT",message:"心跳"};console.log("用户行为监测:",JSON.stringify(e)),this.socket.send(JSON.stringify(e))}},bindEventListeners(){document.addEventListener("click",this.handleUserActivity,!0),document.addEventListener("dblclick",this.handleUserActivity,!0),document.addEventListener("mousedown",this.handleUserActivity,!0),document.addEventListener("mouseup",this.handleUserActivity,!0),document.addEventListener("mousemove",this.handleMouseMove,!0),document.addEventListener("mouseover",this.handleUserActivity,!0),document.addEventListener("mouseout",this.handleUserActivity,!0),document.addEventListener("keydown",this.handleUserActivity,!0),document.addEventListener("keyup",this.handleUserActivity,!0),document.addEventListener("input",this.handleUserActivity,!0),document.addEventListener("change",this.handleUserActivity,!0),document.addEventListener("focus",this.handleUserActivity,!0),document.addEventListener("blur",this.handleUserActivity,!0),document.addEventListener("scroll",this.debounce(this.handleUserActivity,300),!0),window.addEventListener("resize",this.handleUserActivity,!0),window.addEventListener("beforeunload",this.handleBeforeUnload)},unbindEventListeners(){document.removeEventListener("click",this.handleUserActivity,!0),document.removeEventListener("dblclick",this.handleUserActivity,!0),document.removeEventListener("mousedown",this.handleUserActivity,!0),document.removeEventListener("mouseup",this.handleUserActivity,!0),document.removeEventListener("mousemove",this.handleMouseMove,!0),document.removeEventListener("mouseover",this.handleUserActivity,!0),document.removeEventListener("mouseout",this.handleUserActivity,!0),document.removeEventListener("keydown",this.handleUserActivity,!0),document.removeEventListener("keyup",this.handleUserActivity,!0),document.removeEventListener("input",this.handleUserActivity,!0),document.removeEventListener("change",this.handleUserActivity,!0),document.removeEventListener("focus",this.handleUserActivity,!0),document.removeEventListener("blur",this.handleUserActivity,!0),document.removeEventListener("scroll",this.debounce(this.handleUserActivity,300),!0),window.removeEventListener("resize",this.handleUserActivity,!0),window.removeEventListener("beforeunload",this.handleBeforeUnload)},handleUserActivity(e){if(this.isAutomaticEvent(e))return;this.resetTimer();const t={eventType:e.type,target:e.target.tagName,timestamp:Date.now(),url:window.location.href};"click"===e.type?(t.clientX=e.clientX,t.clientY=e.clientY):"keydown"===e.type&&(t.key=e.key,t.ctrlKey=e.ctrlKey,t.altKey=e.altKey,t.shiftKey=e.shiftKey),this.sendUserBehavior(t)},handleMouseMove(e){this.mouseMoveThrottled||(this.handleUserActivity(e),this.mouseMoveThrottled=!0,setTimeout(()=>{this.mouseMoveThrottled=!1},500))},isAutomaticEvent(e){return"scroll"===e.type&&!this.isUserScrolling},debounce(e,t){let n;return function(...i){const o=()=>{clearTimeout(n),e.apply(this,i)};clearTimeout(n),n=setTimeout(o,t)}},resetTimer(){this.lastActivityTime=Date.now(),this.showWarning=!1,this.warningTimer&&(clearTimeout(this.warningTimer),this.warningTimer=null),this.$emit("user-active")},startCountdown(){this.countdownTimer&&clearInterval(this.countdownTimer),this.countdownTimer=setInterval(()=>{const e=Date.now(),t=(e-this.lastActivityTime)/5e4;t>=this.currentTimeoutMinutes-this.currentWarningMinutes&&!this.warningTimer&&this.showWarningWarning(),t>=this.currentTimeoutMinutes&&this.handleTimeout()},1e3)},showWarningWarning(e){let t=e||50;console.log("Setting showWarning to true"),this.showWarning=!0,this.$emit("timeout-warning"),this.warningTimer&&clearTimeout(this.warningTimer),this.warningTimer=setTimeout(()=>{this.handleTimeout()},1*t*1e3)},handleTimeout(){this.showWarning=!1,this.$emit("timeout"),this.logout()},logout(){if(localStorage.clear(),sessionStorage.clear(),location.reload(),this.socket&&this.socket.readyState===WebSocket.OPEN){const e={type:"logout",timestamp:Date.now()};this.socket.send(JSON.stringify(e))}this.$emit("logout"),this.countdownTimer&&clearInterval(this.countdownTimer),this.warningTimer&&clearTimeout(this.warningTimer)},handleBeforeUnload(e){if(this.socket&&this.socket.readyState===WebSocket.OPEN){const e={type:"page_unload",timestamp:Date.now()};this.socket.send(JSON.stringify(e))}},reset(){this.isLoginRoute()?this.destroyMonitor():this.resetTimer()},reconnect(){this.isLoginRoute()?this.destroyMonitor():(this.socket&&this.socket.close(),this.initWebSocket())}}},a=s;n("3505");function c(e,t,n,i,o,r,s,a){var c,u="function"===typeof e?e.options:e;if(t&&(u.render=t,u.staticRenderFns=n,u._compiled=!0),i&&(u.functional=!0),r&&(u._scopeId="data-v-"+r),s?(c=function(e){e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,e||"undefined"===typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),o&&o.call(this,e),e&&e._registeredComponents&&e._registeredComponents.add(s)},u._ssrRegister=c):o&&(c=a?function(){o.call(this,(u.functional?this.parent:this).$root.$options.shadowRoot)}:o),c)if(u.functional){u._injectStyles=c;var d=u.render;u.render=function(e,t){return c.call(t),d(e,t)}}else{var l=u.beforeCreate;u.beforeCreate=l?[].concat(l,c):[c]}return{exports:e,options:u}}var u=c(a,o,r,!1,null,null,null),d=u.exports;d.install=function(e){e.component(d.name,d)};var l=d;"undefined"!==typeof window&&window.Vue&&d.install(window.Vue);t["default"]=l}})}));
|
|
2
|
-
//# sourceMappingURL=user-behavior-monitor.umd.min.js.map
|
|
1
|
+
(function(e,t){"object"===typeof exports&&"object"===typeof module?module.exports=t():"function"===typeof define&&define.amd?define([],t):"object"===typeof exports?exports["user-behavior-monitor"]=t():e["user-behavior-monitor"]=t()})("undefined"!==typeof self?self:this,(function(){return function(e){var t={};function n(i){if(t[i])return t[i].exports;var o=t[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"===typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(i,o,function(t){return e[t]}.bind(null,o));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s="fb15")}({2350:function(e,t){function n(e,t){var n=e[1]||"",o=e[3];if(!o)return n;if(t&&"function"===typeof btoa){var s=i(o),r=o.sources.map((function(e){return"/*# sourceURL="+o.sourceRoot+e+" */"}));return[n].concat(r).concat([s]).join("\n")}return[n].join("\n")}function i(e){var t=btoa(unescape(encodeURIComponent(JSON.stringify(e)))),n="sourceMappingURL=data:application/json;charset=utf-8;base64,"+t;return"/*# "+n+" */"}e.exports=function(e){var t=[];return t.toString=function(){return this.map((function(t){var i=n(t,e);return t[2]?"@media "+t[2]+"{"+i+"}":i})).join("")},t.i=function(e,n){"string"===typeof e&&(e=[[null,e,""]]);for(var i={},o=0;o<this.length;o++){var s=this[o][0];"number"===typeof s&&(i[s]=!0)}for(o=0;o<e.length;o++){var r=e[o];"number"===typeof r[0]&&i[r[0]]||(n&&!r[2]?r[2]=n:n&&(r[2]="("+r[2]+") and ("+n+")"),t.push(r))}},t}},"2cd8":function(e,t,n){"use strict";n("89b5")},"499e":function(e,t,n){"use strict";function i(e,t){for(var n=[],i={},o=0;o<t.length;o++){var s=t[o],r=s[0],a=s[1],c=s[2],u=s[3],h={id:e+":"+o,css:a,media:c,sourceMap:u};i[r]?i[r].parts.push(h):n.push(i[r]={id:r,parts:[h]})}return n}n.r(t),n.d(t,"default",(function(){return v}));var o="undefined"!==typeof document;if("undefined"!==typeof DEBUG&&DEBUG&&!o)throw new Error("vue-style-loader cannot be used in a non-browser environment. Use { target: 'node' } in your Webpack config to indicate a server-rendering environment.");var s={},r=o&&(document.head||document.getElementsByTagName("head")[0]),a=null,c=0,u=!1,h=function(){},l=null,d="data-vue-ssr-id",m="undefined"!==typeof navigator&&/msie [6-9]\b/.test(navigator.userAgent.toLowerCase());function v(e,t,n,o){u=n,l=o||{};var r=i(e,t);return f(r),function(t){for(var n=[],o=0;o<r.length;o++){var a=r[o],c=s[a.id];c.refs--,n.push(c)}t?(r=i(e,t),f(r)):r=[];for(o=0;o<n.length;o++){c=n[o];if(0===c.refs){for(var u=0;u<c.parts.length;u++)c.parts[u]();delete s[c.id]}}}}function f(e){for(var t=0;t<e.length;t++){var n=e[t],i=s[n.id];if(i){i.refs++;for(var o=0;o<i.parts.length;o++)i.parts[o](n.parts[o]);for(;o<n.parts.length;o++)i.parts.push(g(n.parts[o]));i.parts.length>n.parts.length&&(i.parts.length=n.parts.length)}else{var r=[];for(o=0;o<n.parts.length;o++)r.push(g(n.parts[o]));s[n.id]={id:n.id,refs:1,parts:r}}}}function p(){var e=document.createElement("style");return e.type="text/css",r.appendChild(e),e}function g(e){var t,n,i=document.querySelector("style["+d+'~="'+e.id+'"]');if(i){if(u)return h;i.parentNode.removeChild(i)}if(m){var o=c++;i=a||(a=p()),t=w.bind(null,i,o,!1),n=w.bind(null,i,o,!0)}else i=p(),t=b.bind(null,i),n=function(){i.parentNode.removeChild(i)};return t(e),function(i){if(i){if(i.css===e.css&&i.media===e.media&&i.sourceMap===e.sourceMap)return;t(e=i)}else n()}}var y=function(){var e=[];return function(t,n){return e[t]=n,e.filter(Boolean).join("\n")}}();function w(e,t,n,i){var o=n?"":i.css;if(e.styleSheet)e.styleSheet.cssText=y(t,o);else{var s=document.createTextNode(o),r=e.childNodes;r[t]&&e.removeChild(r[t]),r.length?e.insertBefore(s,r[t]):e.appendChild(s)}}function b(e,t){var n=t.css,i=t.media,o=t.sourceMap;if(i&&e.setAttribute("media",i),l.ssrId&&e.setAttribute(d,t.id),o&&(n+="\n/*# sourceURL="+o.sources[0]+" */",n+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(o))))+" */"),e.styleSheet)e.styleSheet.cssText=n;else{while(e.firstChild)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(n))}}},"89b5":function(e,t,n){var i=n("9f1b");i.__esModule&&(i=i.default),"string"===typeof i&&(i=[[e.i,i,""]]),i.locals&&(e.exports=i.locals);var o=n("499e").default;o("9b2fa8ce",i,!0,{sourceMap:!1,shadowMode:!1})},"9f1b":function(e,t,n){t=e.exports=n("2350")(!1),t.push([e.i,".behavior-warning-dialog.el-dialog.el-dialog--center{text-align:left}",""])},fb15:function(e,t,n){"use strict";var i;(n.r(t),n.d(t,"UserBehaviorMonitor",(function(){return h})),"undefined"!==typeof window)&&((i=window.document.currentScript)&&(i=i.src.match(/(.+\/)[^/]+\.js(\?.*)?$/))&&(n.p=i[1]));var o=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{ref:"behaviorMonitor",staticClass:"user-behavior-monitor"},[n("el-dialog",{attrs:{title:"提示",visible:e.showWarning,"show-close":!1,modal:!0,width:"30%",center:"","custom-class":"behavior-warning-dialog"},on:{"update:visible":function(t){e.showWarning=t}}},[n("span",[e._v(e._s(e.warningMessage))])])],1)},s=[],r={name:"UserBehaviorMonitor",props:{timeoutMinutes:{type:Number,default:10},warningMinutes:{type:Number,default:1}},data(){return{mouseMoveThrottled:!1,socket:null,countdownTimer:null,warningTimer:null,showWarning:!1,warningMessage:`您已${this.timeoutMinutes}分钟未操作,将在1分钟后自动退出`,lastActivityTime:null,isMonitoring:!1,currentTimeoutMinutes:10,currentWarningMinutes:1,reconnectAttempts:0,maxReconnectAttempts:3,reconnectDelay:3e3,isLocalDevelopment:this.checkIfLocalDevelopment()}},mounted(){this.isLoginRoute()||this.initMonitor()},beforeDestroy(){this.destroyMonitor()},watch:{$route(e,t){const n=t.path.includes("/login")||t.href&&t.href.includes("/login"),i=this.isLoginRoute();!n||i||this.isMonitoring?!n&&i&&this.isMonitoring&&this.destroyMonitor():this.initMonitor()}},methods:{isLoginRoute(){return!!(this.$route&&this.$route.path&&this.$route.path.includes("/login"))||"undefined"!==typeof window&&(window.location.pathname.includes("/login")||window.location.href.includes("/login"))},checkIfLocalDevelopment(){if("undefined"!==typeof window){const e=window.location.hostname;return"localhost"===e||"127.0.0.1"===e||"0.0.0.0"===e||/^192\.168\./.test(e)||/^10\./.test(e)||/^172\.(1[6-9]|2[0-9]|3[01])\./.test(e)}return!1},updateTimeoutSettings(e,t){void 0!==e&&(this.currentTimeoutMinutes=e,localStorage.setItem("userBehavior_timeoutMinutes",e.toString())),void 0!==t&&(this.currentWarningMinutes=t,localStorage.setItem("userBehavior_warningMinutes",t.toString())),this.warningMessage=`您已${this.currentTimeoutMinutes}分钟未操作,将在1分钟后自动退出`,this.resetTimer()},initMonitor(){this.isLoginRoute()?this.destroyMonitor():this.isMonitoring||(this.isMonitoring=!0,this.lastActivityTime=Date.now(),this.reconnectAttempts=0,this.initWebSocket(),this.bindEventListeners())},destroyMonitor(){this.isMonitoring=!1,this.countdownTimer&&clearInterval(this.countdownTimer),this.warningTimer&&clearTimeout(this.warningTimer),this.socket&&(this.socket.close(),this.socket=null),this.unbindEventListeners()},initWebSocket(){try{const t=`wss://${location.host}/xy-api/auth-server/ws/user-activity`;let n="";try{const e=localStorage.getItem("api_header");e&&(n=JSON.parse(e).Authorization||"")}catch(e){n=localStorage.getItem("token")||""}this.socket=new WebSocket(`${t}?token=${encodeURIComponent(n)}`),this.socket.onopen=()=>{this.sendUserBehavior(),this.$emit("websocket-open"),this.reconnectAttempts=0},this.socket.onmessage=t=>{try{const e=JSON.parse(t.data);this.handleActivityStatus(e),this.$emit("websocket-message",e)}catch(e){}},this.socket.onclose=e=>{this.$emit("websocket-close"),this.shouldAttemptReconnect()?this.attemptReconnect():!this.isLoginRoute()&&this.isMonitoring&&(this.isLocalDevelopment||this.handleReconnectFailure())},this.socket.onerror=e=>{this.$emit("websocket-error",e),this.shouldAttemptReconnect()?this.attemptReconnect():!this.isLoginRoute()&&this.isMonitoring&&(this.isLocalDevelopment||this.handleReconnectFailure())}}catch(t){this.$emit("websocket-error",t),this.shouldAttemptReconnect()?this.attemptReconnect():!this.isLoginRoute()&&this.isMonitoring&&(this.isLocalDevelopment||this.handleReconnectFailure())}},shouldAttemptReconnect(){return this.isMonitoring&&!this.isLoginRoute()&&this.reconnectAttempts<this.maxReconnectAttempts},attemptReconnect(){this.isLoginRoute()||(this.reconnectAttempts++,this.isLocalDevelopment&&this.reconnectAttempts>=this.maxReconnectAttempts?this.$emit("websocket-reconnect-failed"):!this.isLocalDevelopment&&this.reconnectAttempts>=this.maxReconnectAttempts?this.handleReconnectFailure():setTimeout(()=>{this.initWebSocket()},this.reconnectDelay))},handleReconnectFailure(){localStorage.clear(),sessionStorage.clear(),location.reload(),this.$emit("logout")},handleActivityStatus(e){if("ACTIVITY_STATUS"===e.type&&e.data){const t=e.data;this.currentTimeoutMinutes=t.timeoutMinutes||10,this.currentWarningMinutes=t.reminderMinutes||1,this.warningMessage=`您已${this.currentTimeoutMinutes}分钟未操作,将在${this.currentWarningMinutes}分钟后自动退出`,t.needReminder&&this.showWarningWarning(t.remainingMillis)}},handleInactiveStatus(){localStorage.clear(),sessionStorage.clear(),location.reload(),this.$emit("logout")},sendUserBehavior(e){if(this.socket&&this.socket.readyState===WebSocket.OPEN){const e={type:"HEARTBEAT",message:"心跳"};this.socket.send(JSON.stringify(e))}},bindEventListeners(){document.addEventListener("click",this.handleUserActivity,!0),document.addEventListener("dblclick",this.handleUserActivity,!0),document.addEventListener("mousedown",this.handleUserActivity,!0),document.addEventListener("mouseup",this.handleUserActivity,!0),document.addEventListener("mousemove",this.handleMouseMove,!0),document.addEventListener("mouseover",this.handleUserActivity,!0),document.addEventListener("mouseout",this.handleUserActivity,!0),document.addEventListener("keydown",this.handleUserActivity,!0),document.addEventListener("keyup",this.handleUserActivity,!0),document.addEventListener("input",this.handleUserActivity,!0),document.addEventListener("change",this.handleUserActivity,!0),document.addEventListener("focus",this.handleUserActivity,!0),document.addEventListener("blur",this.handleUserActivity,!0),document.addEventListener("scroll",this.debounce(this.handleUserActivity,300),!0),window.addEventListener("resize",this.handleUserActivity,!0),window.addEventListener("beforeunload",this.handleBeforeUnload)},unbindEventListeners(){document.removeEventListener("click",this.handleUserActivity,!0),document.removeEventListener("dblclick",this.handleUserActivity,!0),document.removeEventListener("mousedown",this.handleUserActivity,!0),document.removeEventListener("mouseup",this.handleUserActivity,!0),document.removeEventListener("mousemove",this.handleMouseMove,!0),document.removeEventListener("mouseover",this.handleUserActivity,!0),document.removeEventListener("mouseout",this.handleUserActivity,!0),document.removeEventListener("keydown",this.handleUserActivity,!0),document.removeEventListener("keyup",this.handleUserActivity,!0),document.removeEventListener("input",this.handleUserActivity,!0),document.removeEventListener("change",this.handleUserActivity,!0),document.removeEventListener("focus",this.handleUserActivity,!0),document.removeEventListener("blur",this.handleUserActivity,!0),document.removeEventListener("scroll",this.debounce(this.handleUserActivity,300),!0),window.removeEventListener("resize",this.handleUserActivity,!0),window.removeEventListener("beforeunload",this.handleBeforeUnload)},handleUserActivity(e){if(this.isAutomaticEvent(e))return;this.resetTimer();const t={eventType:e.type,target:e.target.tagName,timestamp:Date.now(),url:window.location.href};"click"===e.type?(t.clientX=e.clientX,t.clientY=e.clientY):"keydown"===e.type&&(t.key=e.key,t.ctrlKey=e.ctrlKey,t.altKey=e.altKey,t.shiftKey=e.shiftKey),this.sendUserBehavior(t)},handleMouseMove(e){this.mouseMoveThrottled||(this.handleUserActivity(e),this.mouseMoveThrottled=!0,setTimeout(()=>{this.mouseMoveThrottled=!1},500))},isAutomaticEvent(e){return"scroll"===e.type&&!this.isUserScrolling},debounce(e,t){let n;return function(...i){const o=()=>{clearTimeout(n),e.apply(this,i)};clearTimeout(n),n=setTimeout(o,t)}},resetTimer(){this.lastActivityTime=Date.now(),this.showWarning=!1,this.reconnectAttempts=0,this.warningTimer&&(clearTimeout(this.warningTimer),this.warningTimer=null),this.$emit("user-active")},startCountdown(){this.countdownTimer&&clearInterval(this.countdownTimer),this.countdownTimer=setInterval(()=>{const e=Date.now(),t=(e-this.lastActivityTime)/5e4;t>=this.currentTimeoutMinutes-this.currentWarningMinutes&&!this.warningTimer&&this.showWarningWarning(),t>=this.currentTimeoutMinutes&&this.handleTimeout()},1e3)},showWarningWarning(e){let t=e||5e4;this.showWarning=!0,this.$emit("timeout-warning"),this.warningTimer&&clearTimeout(this.warningTimer),this.warningTimer=setTimeout(()=>{this.handleTimeout()},t)},handleTimeout(){this.showWarning=!1,this.$emit("timeout"),this.logout()},logout(){if(localStorage.clear(),sessionStorage.clear(),location.reload(),this.socket&&this.socket.readyState===WebSocket.OPEN){const e={type:"logout",timestamp:Date.now()};this.socket.send(JSON.stringify(e))}this.$emit("logout"),this.countdownTimer&&clearInterval(this.countdownTimer),this.warningTimer&&clearTimeout(this.warningTimer)},handleBeforeUnload(e){if(this.socket&&this.socket.readyState===WebSocket.OPEN){const e={type:"page_unload",timestamp:Date.now()};this.socket.send(JSON.stringify(e))}},reset(){this.isLoginRoute()?this.destroyMonitor():this.resetTimer()},reconnect(){this.isLoginRoute()?this.destroyMonitor():(this.reconnectAttempts=0,this.socket&&this.socket.close(),this.initWebSocket())}}},a=r;n("2cd8");function c(e,t,n,i,o,s,r,a){var c,u="function"===typeof e?e.options:e;if(t&&(u.render=t,u.staticRenderFns=n,u._compiled=!0),i&&(u.functional=!0),s&&(u._scopeId="data-v-"+s),r?(c=function(e){e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,e||"undefined"===typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),o&&o.call(this,e),e&&e._registeredComponents&&e._registeredComponents.add(r)},u._ssrRegister=c):o&&(c=a?function(){o.call(this,(u.functional?this.parent:this).$root.$options.shadowRoot)}:o),c)if(u.functional){u._injectStyles=c;var h=u.render;u.render=function(e,t){return c.call(t),h(e,t)}}else{var l=u.beforeCreate;u.beforeCreate=l?[].concat(l,c):[c]}return{exports:e,options:u}}var u=c(a,o,s,!1,null,null,null),h=u.exports;h.install=function(e){e.component(h.name,h)};var l=h;"undefined"!==typeof window&&window.Vue&&h.install(window.Vue);t["default"]=l}})}));
|
package/package.json
CHANGED
|
@@ -47,7 +47,11 @@ export default {
|
|
|
47
47
|
lastActivityTime: null,
|
|
48
48
|
isMonitoring: false,
|
|
49
49
|
currentTimeoutMinutes: 10,
|
|
50
|
-
currentWarningMinutes: 1
|
|
50
|
+
currentWarningMinutes: 1,
|
|
51
|
+
reconnectAttempts: 0,
|
|
52
|
+
maxReconnectAttempts: 3,
|
|
53
|
+
reconnectDelay: 3000,
|
|
54
|
+
isLocalDevelopment: this.checkIfLocalDevelopment()
|
|
51
55
|
};
|
|
52
56
|
},
|
|
53
57
|
mounted() {
|
|
@@ -97,6 +101,20 @@ export default {
|
|
|
97
101
|
|
|
98
102
|
return false;
|
|
99
103
|
},
|
|
104
|
+
|
|
105
|
+
// 检查是否为本地开发环境
|
|
106
|
+
checkIfLocalDevelopment() {
|
|
107
|
+
if (typeof window !== 'undefined') {
|
|
108
|
+
const hostname = window.location.hostname;
|
|
109
|
+
return hostname === 'localhost' ||
|
|
110
|
+
hostname === '127.0.0.1' ||
|
|
111
|
+
hostname === '0.0.0.0' ||
|
|
112
|
+
/^192\.168\./.test(hostname) || // 局域网IP
|
|
113
|
+
/^10\./.test(hostname) || // 局域网IP
|
|
114
|
+
/^172\.(1[6-9]|2[0-9]|3[01])\./.test(hostname); // 局域网IP
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
},
|
|
100
118
|
|
|
101
119
|
updateTimeoutSettings(timeoutMinutes, warningMinutes) {
|
|
102
120
|
if (timeoutMinutes !== undefined) {
|
|
@@ -129,6 +147,7 @@ export default {
|
|
|
129
147
|
|
|
130
148
|
this.isMonitoring = true;
|
|
131
149
|
this.lastActivityTime = Date.now();
|
|
150
|
+
this.reconnectAttempts = 0; // 重置重连尝试次数
|
|
132
151
|
|
|
133
152
|
// 初始化WebSocket连接
|
|
134
153
|
this.initWebSocket();
|
|
@@ -180,39 +199,122 @@ export default {
|
|
|
180
199
|
|
|
181
200
|
// 设置连接成功回调
|
|
182
201
|
this.socket.onopen = () => {
|
|
183
|
-
console.log('WebSocket连接已建立');
|
|
202
|
+
// console.log('WebSocket连接已建立');
|
|
184
203
|
this.sendUserBehavior();
|
|
185
204
|
this.$emit('websocket-open');
|
|
205
|
+
// 连接成功时重置重连尝试次数
|
|
206
|
+
this.reconnectAttempts = 0;
|
|
186
207
|
};
|
|
187
208
|
|
|
188
209
|
// 设置接收消息回调
|
|
189
210
|
this.socket.onmessage = (event) => {
|
|
190
211
|
try {
|
|
191
212
|
const data = JSON.parse(event.data);
|
|
192
|
-
console.log('收到用户活动状态消息:', data);
|
|
213
|
+
// console.log('收到用户活动状态消息:', data);
|
|
193
214
|
this.handleActivityStatus(data);
|
|
194
215
|
this.$emit('websocket-message', data);
|
|
195
216
|
} catch (e) {
|
|
196
|
-
console.error('解析WebSocket消息失败:', e);
|
|
217
|
+
// console.error('解析WebSocket消息失败:', e);
|
|
197
218
|
}
|
|
198
219
|
};
|
|
199
220
|
|
|
200
221
|
// 设置连接关闭回调
|
|
201
222
|
this.socket.onclose = (event) => {
|
|
202
|
-
console.log('WebSocket连接已断开:', event.reason);
|
|
223
|
+
// console.log('WebSocket连接已断开:', event.reason);
|
|
203
224
|
this.$emit('websocket-close');
|
|
225
|
+
|
|
226
|
+
// 如果不是在登录页面且监控仍在运行,尝试重连
|
|
227
|
+
if (this.shouldAttemptReconnect()) {
|
|
228
|
+
this.attemptReconnect();
|
|
229
|
+
} else if (!this.isLoginRoute() && this.isMonitoring) {
|
|
230
|
+
// 如果不应该重连但仍在监控状态,根据环境决定是否跳转
|
|
231
|
+
if (!this.isLocalDevelopment) {
|
|
232
|
+
this.handleReconnectFailure();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
204
235
|
};
|
|
205
236
|
|
|
206
237
|
// 设置连接错误回调
|
|
207
238
|
this.socket.onerror = (error) => {
|
|
208
|
-
console.error('WebSocket错误:', error);
|
|
239
|
+
// console.error('WebSocket错误:', error);
|
|
209
240
|
this.$emit('websocket-error', error);
|
|
241
|
+
|
|
242
|
+
// 如果不是在登录页面且监控仍在运行,尝试重连
|
|
243
|
+
if (this.shouldAttemptReconnect()) {
|
|
244
|
+
this.attemptReconnect();
|
|
245
|
+
} else if (!this.isLoginRoute() && this.isMonitoring) {
|
|
246
|
+
// 如果不应该重连但仍在监控状态,根据环境决定是否跳转
|
|
247
|
+
if (!this.isLocalDevelopment) {
|
|
248
|
+
this.handleReconnectFailure();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
210
251
|
};
|
|
211
252
|
|
|
212
253
|
} catch (error) {
|
|
213
|
-
console.error('WebSocket初始化失败:', error);
|
|
254
|
+
// console.error('WebSocket初始化失败:', error);
|
|
214
255
|
this.$emit('websocket-error', error);
|
|
256
|
+
|
|
257
|
+
// 初始化失败时也可以尝试重连(如果不是在登录页面)
|
|
258
|
+
if (this.shouldAttemptReconnect()) {
|
|
259
|
+
this.attemptReconnect();
|
|
260
|
+
} else if (!this.isLoginRoute() && this.isMonitoring) {
|
|
261
|
+
// 如果不应该重连但仍在监控状态,根据环境决定是否跳转
|
|
262
|
+
if (!this.isLocalDevelopment) {
|
|
263
|
+
this.handleReconnectFailure();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
// 判断是否应该尝试重连
|
|
270
|
+
shouldAttemptReconnect() {
|
|
271
|
+
return this.isMonitoring &&
|
|
272
|
+
!this.isLoginRoute() &&
|
|
273
|
+
this.reconnectAttempts < this.maxReconnectAttempts;
|
|
274
|
+
},
|
|
275
|
+
|
|
276
|
+
// 添加重连方法
|
|
277
|
+
attemptReconnect() {
|
|
278
|
+
// 再次检查是否为登录页面
|
|
279
|
+
if (this.isLoginRoute()) {
|
|
280
|
+
// console.log('当前在登录页面,取消重连');
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
this.reconnectAttempts++;
|
|
285
|
+
// console.log(`尝试第${this.reconnectAttempts}次重连...`);
|
|
286
|
+
|
|
287
|
+
// 如果是本地开发环境且重连次数已达到最大值,则不跳转登录页
|
|
288
|
+
if (this.isLocalDevelopment && this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
289
|
+
// console.log('本地开发环境,重连失败但不跳转到登录页');
|
|
290
|
+
this.$emit('websocket-reconnect-failed');
|
|
291
|
+
return;
|
|
215
292
|
}
|
|
293
|
+
|
|
294
|
+
// 如果是线上环境且重连次数已达到最大值,则跳转到登录页
|
|
295
|
+
if (!this.isLocalDevelopment && this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
296
|
+
// console.log('线上环境,重连失败,跳转到登录页');
|
|
297
|
+
this.handleReconnectFailure();
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
setTimeout(() => {
|
|
302
|
+
this.initWebSocket();
|
|
303
|
+
}, this.reconnectDelay);
|
|
304
|
+
},
|
|
305
|
+
|
|
306
|
+
// 处理重连失败
|
|
307
|
+
handleReconnectFailure() {
|
|
308
|
+
// 清空缓存
|
|
309
|
+
localStorage.clear();
|
|
310
|
+
sessionStorage.clear();
|
|
311
|
+
|
|
312
|
+
// 跳转到登录页
|
|
313
|
+
|
|
314
|
+
location.reload();
|
|
315
|
+
|
|
316
|
+
// 触发登出事件
|
|
317
|
+
this.$emit('logout');
|
|
216
318
|
},
|
|
217
319
|
|
|
218
320
|
// 处理活动状态信息
|
|
@@ -254,7 +356,9 @@ export default {
|
|
|
254
356
|
//this.$router.push('/login');
|
|
255
357
|
|
|
256
358
|
// 或者使用window.location
|
|
257
|
-
|
|
359
|
+
|
|
360
|
+
location.reload();
|
|
361
|
+
// window.location.href = '/login';
|
|
258
362
|
|
|
259
363
|
// 触发登出事件
|
|
260
364
|
this.$emit('logout');
|
|
@@ -262,14 +366,12 @@ export default {
|
|
|
262
366
|
|
|
263
367
|
// 发送用户行为数据到后端
|
|
264
368
|
sendUserBehavior(data) {
|
|
265
|
-
console.log('用户行为监测:')
|
|
266
|
-
if (this.warningTimer) clearTimeout(this.warningTimer);
|
|
267
369
|
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
|
268
370
|
const message = {
|
|
269
371
|
"type": "HEARTBEAT",
|
|
270
372
|
"message": "心跳",
|
|
271
373
|
};
|
|
272
|
-
console.log('用户行为监测:', JSON.stringify(message));
|
|
374
|
+
// console.log('用户行为监测:', JSON.stringify(message));
|
|
273
375
|
this.socket.send(JSON.stringify(message));
|
|
274
376
|
}
|
|
275
377
|
},
|
|
@@ -391,6 +493,7 @@ export default {
|
|
|
391
493
|
resetTimer() {
|
|
392
494
|
this.lastActivityTime = Date.now();
|
|
393
495
|
this.showWarning = false;
|
|
496
|
+
this.reconnectAttempts = 0; // 重置重连尝试次数
|
|
394
497
|
|
|
395
498
|
// 清除警告定时器
|
|
396
499
|
if (this.warningTimer) {
|
|
@@ -428,15 +531,15 @@ export default {
|
|
|
428
531
|
|
|
429
532
|
// 显示超时警告
|
|
430
533
|
showWarningWarning(timeoutMinutes) {
|
|
431
|
-
let time=timeoutMinutes||
|
|
432
|
-
console.log('Setting showWarning to true');
|
|
534
|
+
let time=timeoutMinutes||50000;
|
|
535
|
+
// console.log('Setting showWarning to true');
|
|
433
536
|
this.showWarning = true;
|
|
434
537
|
this.$emit('timeout-warning');
|
|
435
538
|
if (this.warningTimer) clearTimeout(this.warningTimer);
|
|
436
539
|
// 设置超时处理
|
|
437
540
|
this.warningTimer = setTimeout(() => {
|
|
438
541
|
this.handleTimeout();
|
|
439
|
-
},
|
|
542
|
+
}, time);
|
|
440
543
|
},
|
|
441
544
|
|
|
442
545
|
// 处理超时
|
|
@@ -506,6 +609,9 @@ export default {
|
|
|
506
609
|
return;
|
|
507
610
|
}
|
|
508
611
|
|
|
612
|
+
// 重置重连尝试次数
|
|
613
|
+
this.reconnectAttempts = 0;
|
|
614
|
+
|
|
509
615
|
if (this.socket) {
|
|
510
616
|
this.socket.close();
|
|
511
617
|
}
|