user-behavior-monitor 1.0.0 → 4.0.1

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.
@@ -2,24 +2,16 @@
2
2
  <div ref="behaviorMonitor" class="user-behavior-monitor">
3
3
  <!-- 提示框 -->
4
4
  <el-dialog
5
+ title="提示"
5
6
  :visible.sync="showWarning"
6
7
  :show-close="false"
7
8
  :modal="true"
8
9
  width="30%"
9
10
  center
10
11
  custom-class="behavior-warning-dialog"
11
- :append-to-body="true"
12
- :modal-append-to-body="true"
13
- :close-on-click-modal="false"
14
- :close-on-press-escape="false"
15
12
  >
16
13
  <span>{{ warningMessage }}</span>
17
14
  </el-dialog>
18
-
19
-
20
- <button @click="testWarning" style="position: fixed; top: 10px; right: 10px; z-index: 10000;">
21
- 测试警告
22
- </button>
23
15
  </div>
24
16
  </template>
25
17
 
@@ -28,10 +20,10 @@ export default {
28
20
  name: 'UserBehaviorMonitor',
29
21
  props: {
30
22
  // WebSocket服务器地址
31
- websocketUrl: {
32
- type: String,
33
- required: true
34
- },
23
+ // websocketUrl: {
24
+ // type: String,
25
+ // required: true
26
+ // },
35
27
  // 倒计时时长(分钟)
36
28
  timeoutMinutes: {
37
29
  type: Number,
@@ -45,39 +37,123 @@ export default {
45
37
  },
46
38
  data() {
47
39
  return {
48
- mouseMoveThrottled:false,
49
- websocket: null,
40
+ mouseMoveThrottled: false,
41
+ socket: null,
50
42
  countdownTimer: null,
51
43
  warningTimer: null,
52
44
  showWarning: false,
53
- warningMessage: `您已${this.timeoutMinutes}分钟未操作,将在${this.warningMinutes}分钟后自动退出`,
45
+ // warningMinutes
46
+ warningMessage: `您已${this.timeoutMinutes}分钟未操作,将在1分钟后自动退出`,
54
47
  lastActivityTime: null,
55
- isMonitoring: false
48
+ isMonitoring: false,
49
+ currentTimeoutMinutes: 10,
50
+ currentWarningMinutes: 1,
51
+ reconnectAttempts: 0,
52
+ maxReconnectAttempts: 3,
53
+ reconnectDelay: 3000,
54
+ isLocalDevelopment: this.checkIfLocalDevelopment()
56
55
  };
57
56
  },
58
57
  mounted() {
59
- console.log('开始开始! mounted');
60
- this.initMonitor();
58
+ // 检查当前路由是否包含/login,如果不包含则初始化监控
59
+ if (!this.isLoginRoute()) {
60
+ this.initMonitor();
61
+ }
61
62
  },
62
63
  beforeDestroy() {
63
- console.log('开始开始! beforeDestroy');
64
64
  this.destroyMonitor();
65
65
  },
66
+ watch: {
67
+ // 监听路由变化
68
+ '$route'(to, from) {
69
+ // 安全检查 from.href 是否存在
70
+ const isFromLogin = from.path.includes('/login') ||
71
+ (from.href && from.href.includes('/login'));
72
+
73
+ // 检查当前是否为登录路由
74
+ const isToLogin = this.isLoginRoute();
75
+
76
+ // 如果从登录页跳转到非登录页,且监控未启动,则初始化监控
77
+ if (isFromLogin && !isToLogin && !this.isMonitoring) {
78
+ this.initMonitor();
79
+ }
80
+ // 如果从非登录页跳转到登录页,且监控正在运行,则销毁监控
81
+ else if (!isFromLogin && isToLogin && this.isMonitoring) {
82
+ this.destroyMonitor();
83
+ }
84
+ }
85
+ },
66
86
  methods: {
67
- testWarning() {
68
- console.log('Testing warning display');
69
- this.showWarning = true;
87
+ // 检查当前路由是否为登录页面
88
+ isLoginRoute() {
89
+ // 优先检查 Vue Router 路由
90
+ if (this.$route && this.$route.path) {
91
+ if (this.$route.path.includes('/login')) {
92
+ return true;
93
+ }
94
+ }
95
+
96
+ // 检查浏览器地址栏作为后备
97
+ if (typeof window !== 'undefined') {
98
+ return window.location.pathname.includes('/login') ||
99
+ window.location.href.includes('/login');
100
+ }
101
+
102
+ return false;
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
+ },
118
+
119
+ updateTimeoutSettings(timeoutMinutes, warningMinutes) {
120
+ if (timeoutMinutes !== undefined) {
121
+ this.currentTimeoutMinutes = timeoutMinutes;
122
+ localStorage.setItem('userBehavior_timeoutMinutes', timeoutMinutes.toString());
123
+ }
124
+
125
+ if (warningMinutes !== undefined) {
126
+ this.currentWarningMinutes = warningMinutes;
127
+ localStorage.setItem('userBehavior_warningMinutes', warningMinutes.toString());
128
+ }
129
+
130
+ // 更新警告消息
131
+ // ${this.currentWarningMinutes}
132
+ this.warningMessage = `您已${this.currentTimeoutMinutes}分钟未操作,将在1分钟后自动退出`;
133
+
134
+ // 重置计时器
135
+ this.resetTimer();
70
136
  },
71
137
  // 初始化监控
72
138
  initMonitor() {
73
- console.log('Initializing monitor');
139
+ // 再次检查确保不在登录页面
140
+ if (this.isLoginRoute()) {
141
+ // 如果在登录页面,确保清理所有监控资源
142
+ this.destroyMonitor();
143
+ return;
144
+ }
145
+
74
146
  if (this.isMonitoring) return;
75
147
 
76
148
  this.isMonitoring = true;
77
149
  this.lastActivityTime = Date.now();
150
+ this.reconnectAttempts = 0; // 重置重连尝试次数
151
+
152
+ // 初始化WebSocket连接
153
+ this.initWebSocket();
78
154
 
79
155
  // 启动倒计时
80
- this.startCountdown();
156
+ // this.startCountdown();
81
157
 
82
158
  // 绑定事件监听器
83
159
  this.bindEventListeners();
@@ -91,71 +167,263 @@ export default {
91
167
  if (this.countdownTimer) clearInterval(this.countdownTimer);
92
168
  if (this.warningTimer) clearTimeout(this.warningTimer);
93
169
 
170
+ // 关闭WebSocket连接
171
+ if (this.socket) {
172
+ this.socket.close();
173
+ this.socket = null;
174
+ }
175
+
94
176
  // 解绑事件监听器
95
177
  this.unbindEventListeners();
96
178
  },
97
179
 
180
+ // 初始化WebSocket连接
181
+ initWebSocket() {
182
+ try {
183
+ // 使用传入的WebSocket URL
184
+ const websocketUrl = `wss://${location.host}/xy-api/auth-server/ws/user-activity`;
185
+
186
+ // 获取token(假设存储在localStorage中)
187
+ let token = '';
188
+ try {
189
+ const apiHeader = localStorage.getItem('api_header');
190
+ if (apiHeader) {
191
+ token = JSON.parse(apiHeader).Authorization || '';
192
+ }
193
+ } catch (e) {
194
+ token = localStorage.getItem('token') || '';
195
+ }
196
+
197
+ // 创建WebSocket连接
198
+ this.socket = new WebSocket(`${websocketUrl}?token=${encodeURIComponent(token)}`);
199
+
200
+ // 设置连接成功回调
201
+ this.socket.onopen = () => {
202
+ console.log('WebSocket连接已建立');
203
+ this.sendUserBehavior();
204
+ this.$emit('websocket-open');
205
+ // 连接成功时重置重连尝试次数
206
+ this.reconnectAttempts = 0;
207
+ };
208
+
209
+ // 设置接收消息回调
210
+ this.socket.onmessage = (event) => {
211
+ try {
212
+ const data = JSON.parse(event.data);
213
+ console.log('收到用户活动状态消息:', data);
214
+ this.handleActivityStatus(data);
215
+ this.$emit('websocket-message', data);
216
+ } catch (e) {
217
+ console.error('解析WebSocket消息失败:', e);
218
+ }
219
+ };
220
+
221
+ // 设置连接关闭回调
222
+ this.socket.onclose = (event) => {
223
+ console.log('WebSocket连接已断开:', event.reason);
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
+ }
235
+ };
236
+
237
+ // 设置连接错误回调
238
+ this.socket.onerror = (error) => {
239
+ console.error('WebSocket错误:', error);
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
+ }
251
+ };
252
+
253
+ } catch (error) {
254
+ console.error('WebSocket初始化失败:', error);
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;
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
+ window.location.href = '/login';
314
+
315
+ // 触发登出事件
316
+ this.$emit('logout');
317
+ },
318
+
319
+ // 处理活动状态信息
320
+ handleActivityStatus(data) {
321
+ if (data.type === 'ACTIVITY_STATUS' && data.data) {
322
+ const activityData = data.data;
323
+
324
+ // 更新超时和警告时间配置
325
+ this.currentTimeoutMinutes = activityData.timeoutMinutes || 10;
326
+ this.currentWarningMinutes = activityData.reminderMinutes || 1;
327
+
328
+ // 更新警告消息
329
+ this.warningMessage = `您已${this.currentTimeoutMinutes}分钟未操作,将在${this.currentWarningMinutes}分钟后自动退出`;
330
+ if(activityData.needReminder){
331
+ this.showWarningWarning(activityData.remainingMillis);
332
+ }
333
+ // remainingMillis
334
+
335
+
336
+ // 检查是否仍然活跃
337
+ //if (!activityData.isActive) {
338
+ //this.handleInactiveStatus();
339
+ //}
340
+ }
341
+ // 检查是否需要显示提醒
342
+ // let dataReminder=data.data;
343
+ // if (dataReminder&&dataReminder.needReminder) {
344
+ // this.showWarningWarning(dataReminder.remainingSeconds);
345
+ // }
346
+ },
347
+
348
+ // 处理非活跃状态
349
+ handleInactiveStatus() {
350
+ // 清空缓存
351
+ localStorage.clear();
352
+ sessionStorage.clear();
353
+
354
+ // 跳转到登录页
355
+ //this.$router.push('/login');
356
+
357
+ // 或者使用window.location
358
+ window.location.href = '/login';
359
+
360
+ // 触发登出事件
361
+ this.$emit('logout');
362
+ },
363
+
98
364
  // 发送用户行为数据到后端
99
365
  sendUserBehavior(data) {
100
- // 用于测试:在控制台输出用户行为信息
101
- console.log('用户行为监测:', data);
366
+ if (this.socket && this.socket.readyState === WebSocket.OPEN) {
367
+ const message = {
368
+ "type": "HEARTBEAT",
369
+ "message": "心跳",
370
+ };
371
+ console.log('用户行为监测:', JSON.stringify(message));
372
+ this.socket.send(JSON.stringify(message));
373
+ }
102
374
  },
103
375
 
104
376
  // 绑定事件监听器
105
377
  bindEventListeners() {
106
- console.log('Binding event listeners');
107
- // 使用箭头函数或bind来保持this上下文
108
378
  // 鼠标事件
109
- document.addEventListener('click', this.handleUserActivity.bind(this), true);
110
- document.addEventListener('dblclick', this.handleUserActivity.bind(this), true);
111
- document.addEventListener('mousedown', this.handleUserActivity.bind(this), true);
112
- document.addEventListener('mouseup', this.handleUserActivity.bind(this), true);
113
- document.addEventListener('mousemove', this.handleMouseMove.bind(this), true);
114
- document.addEventListener('mouseover', this.handleUserActivity.bind(this), true);
115
- document.addEventListener('mouseout', this.handleUserActivity.bind(this), true);
379
+ document.addEventListener('click', this.handleUserActivity, true);
380
+ document.addEventListener('dblclick', this.handleUserActivity, true);
381
+ document.addEventListener('mousedown', this.handleUserActivity, true);
382
+ document.addEventListener('mouseup', this.handleUserActivity, true);
383
+ document.addEventListener('mousemove', this.handleMouseMove, true);
384
+ document.addEventListener('mouseover', this.handleUserActivity, true);
385
+ document.addEventListener('mouseout', this.handleUserActivity, true);
116
386
 
117
387
  // 键盘事件
118
- document.addEventListener('keydown', this.handleUserActivity.bind(this), true);
119
- document.addEventListener('keyup', this.handleUserActivity.bind(this), true);
388
+ document.addEventListener('keydown', this.handleUserActivity, true);
389
+ document.addEventListener('keyup', this.handleUserActivity, true);
120
390
 
121
391
  // 表单事件
122
- document.addEventListener('input', this.handleUserActivity.bind(this), true);
123
- document.addEventListener('change', this.handleUserActivity.bind(this), true);
124
- document.addEventListener('focus', this.handleUserActivity.bind(this), true);
125
- document.addEventListener('blur', this.handleUserActivity.bind(this), true);
392
+ document.addEventListener('input', this.handleUserActivity, true);
393
+ document.addEventListener('change', this.handleUserActivity, true);
394
+ document.addEventListener('focus', this.handleUserActivity, true);
395
+ document.addEventListener('blur', this.handleUserActivity, true);
126
396
 
127
397
  // 滚动事件(防抖处理)
128
- document.addEventListener('scroll', this.debounce(this.handleUserActivity, 300).bind(this), true);
398
+ document.addEventListener('scroll', this.debounce(this.handleUserActivity, 300), true);
129
399
 
130
400
  // 窗口事件
131
- window.addEventListener('resize', this.handleUserActivity.bind(this), true);
132
- window.addEventListener('beforeunload', this.handleBeforeUnload.bind(this));
401
+ window.addEventListener('resize', this.handleUserActivity, true);
402
+ window.addEventListener('beforeunload', this.handleBeforeUnload);
133
403
  },
134
404
 
135
405
  // 解绑事件监听器
136
406
  unbindEventListeners() {
137
- console.log('Unbinding event listeners');
138
- // 解绑时也要使用相同的方式
139
- document.removeEventListener('click', this.handleUserActivity.bind(this), true);
140
- document.removeEventListener('dblclick', this.handleUserActivity.bind(this), true);
141
- document.removeEventListener('mousedown', this.handleUserActivity.bind(this), true);
142
- document.removeEventListener('mouseup', this.handleUserActivity.bind(this), true);
143
- document.removeEventListener('mousemove', this.handleMouseMove.bind(this), true);
144
- document.removeEventListener('mouseover', this.handleUserActivity.bind(this), true);
145
- document.removeEventListener('mouseout', this.handleUserActivity.bind(this), true);
146
-
147
- document.removeEventListener('keydown', this.handleUserActivity.bind(this), true);
148
- document.removeEventListener('keyup', this.handleUserActivity.bind(this), true);
149
-
150
- document.removeEventListener('input', this.handleUserActivity.bind(this), true);
151
- document.removeEventListener('change', this.handleUserActivity.bind(this), true);
152
- document.removeEventListener('focus', this.handleUserActivity.bind(this), true);
153
- document.removeEventListener('blur', this.handleUserActivity.bind(this), true);
154
-
155
- document.removeEventListener('scroll', this.debounce(this.handleUserActivity, 300).bind(this), true);
156
-
157
- window.removeEventListener('resize', this.handleUserActivity.bind(this), true);
158
- window.removeEventListener('beforeunload', this.handleBeforeUnload.bind(this));
407
+ document.removeEventListener('click', this.handleUserActivity, true);
408
+ document.removeEventListener('dblclick', this.handleUserActivity, true);
409
+ document.removeEventListener('mousedown', this.handleUserActivity, true);
410
+ document.removeEventListener('mouseup', this.handleUserActivity, true);
411
+ document.removeEventListener('mousemove', this.handleMouseMove, true);
412
+ document.removeEventListener('mouseover', this.handleUserActivity, true);
413
+ document.removeEventListener('mouseout', this.handleUserActivity, true);
414
+
415
+ document.removeEventListener('keydown', this.handleUserActivity, true);
416
+ document.removeEventListener('keyup', this.handleUserActivity, true);
417
+
418
+ document.removeEventListener('input', this.handleUserActivity, true);
419
+ document.removeEventListener('change', this.handleUserActivity, true);
420
+ document.removeEventListener('focus', this.handleUserActivity, true);
421
+ document.removeEventListener('blur', this.handleUserActivity, true);
422
+
423
+ document.removeEventListener('scroll', this.debounce(this.handleUserActivity, 300), true);
424
+
425
+ window.removeEventListener('resize', this.handleUserActivity, true);
426
+ window.removeEventListener('beforeunload', this.handleBeforeUnload);
159
427
  },
160
428
 
161
429
  // 处理用户活动
@@ -186,7 +454,7 @@ export default {
186
454
 
187
455
  this.sendUserBehavior(behaviorData);
188
456
  },
189
- handleMouseMove(event) {
457
+ handleMouseMove(event) {
190
458
  if (!this.mouseMoveThrottled) {
191
459
  this.handleUserActivity(event);
192
460
  this.mouseMoveThrottled = true;
@@ -196,20 +464,6 @@ export default {
196
464
  }
197
465
  },
198
466
 
199
- // 处理鼠标移动(降低频率)
200
- // handleMouseMove: function() {
201
- // let isThrottled = false;
202
- // return (event) => {
203
- // if (!isThrottled) {
204
- // this.handleUserActivity(event);
205
- // isThrottled = true;
206
- // setTimeout(() => {
207
- // isThrottled = false;
208
- // }, 500);
209
- // }
210
- // };
211
- // }(),
212
-
213
467
  // 判断是否为自动触发事件
214
468
  isAutomaticEvent(event) {
215
469
  // 自动刷新等非用户主动触发的事件
@@ -234,9 +488,9 @@ export default {
234
488
 
235
489
  // 重置计时器
236
490
  resetTimer() {
237
- console.log('Resetting timer');
238
491
  this.lastActivityTime = Date.now();
239
492
  this.showWarning = false;
493
+ this.reconnectAttempts = 0; // 重置重连尝试次数
240
494
 
241
495
  // 清除警告定时器
242
496
  if (this.warningTimer) {
@@ -245,52 +499,48 @@ export default {
245
499
  }
246
500
 
247
501
  // 重新启动倒计时
248
- this.startCountdown();
502
+ // this.startCountdown();
249
503
 
250
504
  this.$emit('user-active');
251
505
  },
252
506
 
253
507
  // 启动倒计时
254
508
  startCountdown() {
255
- console.log('Starting countdown');
256
509
  // 清除现有定时器
257
510
  if (this.countdownTimer) clearInterval(this.countdownTimer);
258
511
 
259
512
  // 设置新的倒计时
260
513
  this.countdownTimer = setInterval(() => {
261
514
  const now = Date.now();
262
- const elapsedMinutes = (now - this.lastActivityTime) / (1000 * 60);
263
- console.log('Elapsed minutes:', elapsedMinutes);
515
+ const elapsedMinutes = (now - this.lastActivityTime) / (1000 * 50);
264
516
 
265
517
  // 检查是否需要显示警告
266
- if (elapsedMinutes >= (this.timeoutMinutes - this.warningMinutes) && !this.warningTimer) {
267
- console.log('Showing warning');
518
+ if (elapsedMinutes >= (this.currentTimeoutMinutes - this.currentWarningMinutes) && !this.warningTimer) {
268
519
  this.showWarningWarning();
269
520
  }
270
521
 
271
522
  // 检查是否超时
272
- if (elapsedMinutes >= this.timeoutMinutes) {
273
- console.log('Handling timeout');
523
+ if (elapsedMinutes >= this.currentTimeoutMinutes) {
274
524
  this.handleTimeout();
275
525
  }
276
526
  }, 1000);
277
527
  },
278
528
 
279
529
  // 显示超时警告
280
- showWarningWarning() {
530
+ showWarningWarning(timeoutMinutes) {
531
+ let time=timeoutMinutes||50000;
281
532
  console.log('Setting showWarning to true');
282
533
  this.showWarning = true;
283
534
  this.$emit('timeout-warning');
284
-
535
+ if (this.warningTimer) clearTimeout(this.warningTimer);
285
536
  // 设置超时处理
286
537
  this.warningTimer = setTimeout(() => {
287
538
  this.handleTimeout();
288
- }, this.warningMinutes * 60 * 1000);
539
+ }, time);
289
540
  },
290
541
 
291
542
  // 处理超时
292
543
  handleTimeout() {
293
- console.log('Handling timeout');
294
544
  this.showWarning = false;
295
545
  this.$emit('timeout');
296
546
 
@@ -300,9 +550,24 @@ export default {
300
550
 
301
551
  // 登出操作
302
552
  logout() {
303
- // 用于测试:在控制台输出登出信息
304
- console.log('用户超时登出');
553
+ localStorage.clear();
554
+ sessionStorage.clear();
555
+ location.reload();
556
+ // 跳转到登录页
557
+ //this.$router.push('/login');
305
558
 
559
+ // 或者使用window.location
560
+ // window.location.href = '/login';
561
+ // 发送登出消息到后端
562
+ if (this.socket && this.socket.readyState === WebSocket.OPEN) {
563
+ const message = {
564
+ type: 'logout',
565
+ timestamp: Date.now()
566
+ };
567
+ this.socket.send(JSON.stringify(message));
568
+ }
569
+
570
+ // 触发登出事件
306
571
  this.$emit('logout');
307
572
 
308
573
  // 清除定时器
@@ -312,32 +577,48 @@ export default {
312
577
 
313
578
  // 处理页面卸载前的操作
314
579
  handleBeforeUnload(event) {
315
- // 用于测试:在控制台输出页面卸载信息
316
- console.log('页面即将卸载');
580
+ // 发送页面关闭消息
581
+ if (this.socket && this.socket.readyState === WebSocket.OPEN) {
582
+ const message = {
583
+ type: 'page_unload',
584
+ timestamp: Date.now()
585
+ };
586
+ this.socket.send(JSON.stringify(message));
587
+ }
317
588
  },
318
589
 
319
590
  // 手动重置监控
320
591
  reset() {
592
+ // 检查当前路由,如果在登录页面则销毁监控
593
+ if (this.isLoginRoute()) {
594
+ this.destroyMonitor();
595
+ return;
596
+ }
597
+
321
598
  this.resetTimer();
599
+ },
600
+
601
+ // 重新连接WebSocket
602
+ reconnect() {
603
+ // 检查当前路由,如果在登录页面则销毁监控
604
+ if (this.isLoginRoute()) {
605
+ this.destroyMonitor();
606
+ return;
607
+ }
608
+
609
+ // 重置重连尝试次数
610
+ this.reconnectAttempts = 0;
611
+
612
+ if (this.socket) {
613
+ this.socket.close();
614
+ }
615
+ this.initWebSocket();
322
616
  }
323
617
  }
324
618
  };
325
619
  </script>
326
-
327
- <style scoped>
328
- .user-behavior-monitor {
329
- display: none;
330
- }
331
-
332
- /* 使用深度选择器确保样式正确应用 */
333
- .behavior-warning-dialog ::v-deep .el-dialog__body {
334
- text-align: center;
335
- font-size: 16px;
336
- padding: 30px 20px;
337
- }
338
-
339
- /* 确保弹框在最上层 */
340
- .behavior-warning-dialog {
341
- z-index: 9999 !important;
620
+ <style>
621
+ .behavior-warning-dialog.el-dialog.el-dialog--center{
622
+ text-align: left;
342
623
  }
343
624
  </style>