user-behavior-monitor 1.0.0 → 2.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/README.md +2 -29
- package/dist/user-behavior-monitor.common.js +311 -120
- package/dist/user-behavior-monitor.common.js.map +1 -1
- package/dist/user-behavior-monitor.umd.js +311 -120
- package/dist/user-behavior-monitor.umd.js.map +1 -1
- package/dist/user-behavior-monitor.umd.min.js +1 -1
- package/dist/user-behavior-monitor.umd.min.js.map +1 -1
- package/package.json +3 -2
- package/src/components/UserBehaviorMonitor.vue +293 -115
- package/src/components/{UserBehaviorMonitor1.vue → UserBehaviorMonitor2.vue} +130 -60
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
</template>
|
|
16
16
|
|
|
17
17
|
<script>
|
|
18
|
+
import { io } from "socket.io-client";
|
|
19
|
+
|
|
18
20
|
export default {
|
|
19
21
|
name: 'UserBehaviorMonitor',
|
|
20
22
|
props: {
|
|
@@ -36,14 +38,16 @@ export default {
|
|
|
36
38
|
},
|
|
37
39
|
data() {
|
|
38
40
|
return {
|
|
39
|
-
mouseMoveThrottled:false,
|
|
40
|
-
|
|
41
|
+
mouseMoveThrottled: false,
|
|
42
|
+
socket: null,
|
|
41
43
|
countdownTimer: null,
|
|
42
44
|
warningTimer: null,
|
|
43
45
|
showWarning: false,
|
|
44
46
|
warningMessage: `您已${this.timeoutMinutes}分钟未操作,将在${this.warningMinutes}分钟后自动退出`,
|
|
45
47
|
lastActivityTime: null,
|
|
46
|
-
isMonitoring: false
|
|
48
|
+
isMonitoring: false,
|
|
49
|
+
currentTimeoutMinutes: 10,
|
|
50
|
+
currentWarningMinutes: 1
|
|
47
51
|
};
|
|
48
52
|
},
|
|
49
53
|
mounted() {
|
|
@@ -55,17 +59,17 @@ export default {
|
|
|
55
59
|
methods: {
|
|
56
60
|
updateTimeoutSettings(timeoutMinutes, warningMinutes) {
|
|
57
61
|
if (timeoutMinutes !== undefined) {
|
|
58
|
-
this.
|
|
62
|
+
this.currentTimeoutMinutes = timeoutMinutes;
|
|
59
63
|
localStorage.setItem('userBehavior_timeoutMinutes', timeoutMinutes.toString());
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
if (warningMinutes !== undefined) {
|
|
63
|
-
this.
|
|
67
|
+
this.currentWarningMinutes = warningMinutes;
|
|
64
68
|
localStorage.setItem('userBehavior_warningMinutes', warningMinutes.toString());
|
|
65
69
|
}
|
|
66
70
|
|
|
67
71
|
// 更新警告消息
|
|
68
|
-
this.warningMessage = `您已${this.
|
|
72
|
+
this.warningMessage = `您已${this.currentTimeoutMinutes}分钟未操作,将在${this.currentWarningMinutes}分钟后自动退出`;
|
|
69
73
|
|
|
70
74
|
// 重置计时器
|
|
71
75
|
this.resetTimer();
|
|
@@ -77,8 +81,8 @@ export default {
|
|
|
77
81
|
this.isMonitoring = true;
|
|
78
82
|
this.lastActivityTime = Date.now();
|
|
79
83
|
|
|
80
|
-
// 初始化
|
|
81
|
-
this.
|
|
84
|
+
// 初始化Socket.IO连接
|
|
85
|
+
this.initSocketIO();
|
|
82
86
|
|
|
83
87
|
// 启动倒计时
|
|
84
88
|
this.startCountdown();
|
|
@@ -95,56 +99,136 @@ export default {
|
|
|
95
99
|
if (this.countdownTimer) clearInterval(this.countdownTimer);
|
|
96
100
|
if (this.warningTimer) clearTimeout(this.warningTimer);
|
|
97
101
|
|
|
98
|
-
// 关闭
|
|
99
|
-
if (this.
|
|
100
|
-
this.
|
|
101
|
-
this.
|
|
102
|
+
// 关闭Socket.IO连接
|
|
103
|
+
if (this.socket) {
|
|
104
|
+
this.socket.disconnect();
|
|
105
|
+
this.socket = null;
|
|
102
106
|
}
|
|
103
107
|
|
|
104
108
|
// 解绑事件监听器
|
|
105
109
|
this.unbindEventListeners();
|
|
106
110
|
},
|
|
107
111
|
|
|
108
|
-
// 初始化
|
|
109
|
-
|
|
112
|
+
// 初始化Socket.IO连接
|
|
113
|
+
// 初始化Socket.IO连接
|
|
114
|
+
initSocketIO() {
|
|
110
115
|
try {
|
|
111
|
-
|
|
116
|
+
// 使用正确的WebSocket URL和路径配置
|
|
117
|
+
const websocketUrl = 'http://100.150.0.4:30612';
|
|
118
|
+
|
|
119
|
+
// 从localStorage获取timeoutMinutes,如果没有默认为10
|
|
120
|
+
const storedTimeoutMinutes = localStorage.getItem('timeoutMinutes') || 10;
|
|
121
|
+
|
|
122
|
+
// 获取token(假设存储在localStorage中)
|
|
123
|
+
let token = '';
|
|
124
|
+
try {
|
|
125
|
+
const apiHeader = localStorage.getItem('api_header');
|
|
126
|
+
if (apiHeader) {
|
|
127
|
+
token = JSON.parse(apiHeader).Authorization || '';
|
|
128
|
+
}
|
|
129
|
+
} catch (e) {
|
|
130
|
+
token = localStorage.getItem('token') || '';
|
|
131
|
+
}
|
|
112
132
|
|
|
113
|
-
|
|
114
|
-
|
|
133
|
+
// 创建Socket.IO连接,添加自定义headers
|
|
134
|
+
this.socket = io(websocketUrl, {
|
|
135
|
+
transports: ['websocket'],
|
|
136
|
+
path: '/ws/user-activity', // 指定正确的路径
|
|
137
|
+
extraHeaders: {
|
|
138
|
+
'Authorization': token
|
|
139
|
+
},
|
|
140
|
+
query: {
|
|
141
|
+
timeoutMinutes: storedTimeoutMinutes
|
|
142
|
+
},
|
|
143
|
+
reconnection: true,
|
|
144
|
+
reconnectionAttempts: 5,
|
|
145
|
+
reconnectionDelay: 1000,
|
|
146
|
+
timeout: 20000
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
this.socket.on('connect', () => {
|
|
150
|
+
console.log('Socket.IO连接已建立', this.socket.id);
|
|
115
151
|
this.$emit('websocket-open');
|
|
116
|
-
};
|
|
152
|
+
});
|
|
117
153
|
|
|
118
|
-
this.
|
|
119
|
-
console.log('
|
|
120
|
-
this.$emit('websocket-
|
|
121
|
-
};
|
|
154
|
+
this.socket.on('disconnect', (reason) => {
|
|
155
|
+
console.log('Socket.IO连接已断开:', reason);
|
|
156
|
+
this.$emit('websocket-close');
|
|
157
|
+
});
|
|
122
158
|
|
|
123
|
-
|
|
124
|
-
|
|
159
|
+
// 处理后端返回的活动状态信息
|
|
160
|
+
this.socket.on('success', (data) => {
|
|
161
|
+
console.log('收到用户活动状态消息:', data);
|
|
162
|
+
this.handleActivityStatus(data);
|
|
163
|
+
this.$emit('websocket-message', data);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
this.socket.on('connect_error', (error) => {
|
|
167
|
+
console.error('Socket.IO连接错误:', error);
|
|
125
168
|
this.$emit('websocket-error', error);
|
|
126
|
-
};
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
this.socket.on('error', (error) => {
|
|
172
|
+
console.error('Socket.IO错误:', error);
|
|
173
|
+
this.$emit('websocket-error', error);
|
|
174
|
+
});
|
|
127
175
|
|
|
128
|
-
this.websocket.onclose = () => {
|
|
129
|
-
console.log('WebSocket连接已关闭');
|
|
130
|
-
this.$emit('websocket-close');
|
|
131
|
-
};
|
|
132
176
|
} catch (error) {
|
|
133
|
-
console.error('
|
|
177
|
+
console.error('Socket.IO初始化失败:', error);
|
|
134
178
|
this.$emit('websocket-error', error);
|
|
135
179
|
}
|
|
136
180
|
},
|
|
137
181
|
|
|
182
|
+
// 处理活动状态信息
|
|
183
|
+
handleActivityStatus(data) {
|
|
184
|
+
if (data.type === 'ACTIVITY_STATUS' && data.data) {
|
|
185
|
+
const activityData = data.data;
|
|
186
|
+
|
|
187
|
+
// 更新超时和警告时间配置
|
|
188
|
+
this.currentTimeoutMinutes = activityData.timeoutMinutes || 10;
|
|
189
|
+
this.currentWarningMinutes = activityData.reminderMinutes || 1;
|
|
190
|
+
|
|
191
|
+
// 更新警告消息
|
|
192
|
+
this.warningMessage = `您已${this.currentTimeoutMinutes}分钟未操作,将在${this.currentWarningMinutes}分钟后自动退出`;
|
|
193
|
+
|
|
194
|
+
// 检查是否需要显示提醒
|
|
195
|
+
if (activityData.needReminder) {
|
|
196
|
+
this.showWarningWarning();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// 检查是否仍然活跃
|
|
200
|
+
if (!activityData.isActive) {
|
|
201
|
+
this.handleInactiveStatus();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
// 处理非活跃状态
|
|
207
|
+
handleInactiveStatus() {
|
|
208
|
+
// 清空缓存
|
|
209
|
+
localStorage.clear();
|
|
210
|
+
sessionStorage.clear();
|
|
211
|
+
|
|
212
|
+
// 跳转到登录页
|
|
213
|
+
this.$router.push('/login');
|
|
214
|
+
|
|
215
|
+
// 或者使用window.location
|
|
216
|
+
// window.location.href = '/login';
|
|
217
|
+
|
|
218
|
+
// 触发登出事件
|
|
219
|
+
this.$emit('logout');
|
|
220
|
+
},
|
|
221
|
+
|
|
138
222
|
// 发送用户行为数据到后端
|
|
139
223
|
sendUserBehavior(data) {
|
|
140
|
-
|
|
224
|
+
console.log('用户行为监测:')
|
|
225
|
+
if (this.socket && this.socket.connected) {
|
|
141
226
|
const message = {
|
|
142
|
-
type:
|
|
143
|
-
|
|
144
|
-
data: data
|
|
227
|
+
"type": "HEARTBEAT",
|
|
228
|
+
"message": "心跳"
|
|
145
229
|
};
|
|
146
230
|
console.log('用户行为监测:', JSON.stringify(message));
|
|
147
|
-
this.
|
|
231
|
+
this.socket.emit('user_behavior', message);
|
|
148
232
|
}
|
|
149
233
|
},
|
|
150
234
|
|
|
@@ -239,20 +323,6 @@ export default {
|
|
|
239
323
|
}
|
|
240
324
|
},
|
|
241
325
|
|
|
242
|
-
// 处理鼠标移动(降低频率)
|
|
243
|
-
// handleMouseMove: function() {
|
|
244
|
-
// let isThrottled = false;
|
|
245
|
-
// return (event) => {
|
|
246
|
-
// if (!isThrottled) {
|
|
247
|
-
// this.handleUserActivity(event);
|
|
248
|
-
// isThrottled = true;
|
|
249
|
-
// setTimeout(() => {
|
|
250
|
-
// isThrottled = false;
|
|
251
|
-
// }, 500);
|
|
252
|
-
// }
|
|
253
|
-
// };
|
|
254
|
-
// }(),
|
|
255
|
-
|
|
256
326
|
// 判断是否为自动触发事件
|
|
257
327
|
isAutomaticEvent(event) {
|
|
258
328
|
// 自动刷新等非用户主动触发的事件
|
|
@@ -303,12 +373,12 @@ export default {
|
|
|
303
373
|
const elapsedMinutes = (now - this.lastActivityTime) / (1000 * 60);
|
|
304
374
|
|
|
305
375
|
// 检查是否需要显示警告
|
|
306
|
-
if (elapsedMinutes >= (this.
|
|
376
|
+
if (elapsedMinutes >= (this.currentTimeoutMinutes - this.currentWarningMinutes) && !this.warningTimer) {
|
|
307
377
|
this.showWarningWarning();
|
|
308
378
|
}
|
|
309
379
|
|
|
310
380
|
// 检查是否超时
|
|
311
|
-
if (elapsedMinutes >= this.
|
|
381
|
+
if (elapsedMinutes >= this.currentTimeoutMinutes) {
|
|
312
382
|
this.handleTimeout();
|
|
313
383
|
}
|
|
314
384
|
}, 1000);
|
|
@@ -322,7 +392,7 @@ export default {
|
|
|
322
392
|
// 设置超时处理
|
|
323
393
|
this.warningTimer = setTimeout(() => {
|
|
324
394
|
this.handleTimeout();
|
|
325
|
-
}, this.
|
|
395
|
+
}, this.currentWarningMinutes * 60 * 1000);
|
|
326
396
|
},
|
|
327
397
|
|
|
328
398
|
// 处理超时
|
|
@@ -337,12 +407,12 @@ export default {
|
|
|
337
407
|
// 登出操作
|
|
338
408
|
logout() {
|
|
339
409
|
// 发送登出消息到后端
|
|
340
|
-
if (this.
|
|
410
|
+
if (this.socket && this.socket.connected) {
|
|
341
411
|
const message = {
|
|
342
412
|
type: 'logout',
|
|
343
413
|
timestamp: Date.now()
|
|
344
414
|
};
|
|
345
|
-
this.
|
|
415
|
+
this.socket.emit('logout', message);
|
|
346
416
|
}
|
|
347
417
|
|
|
348
418
|
// 触发登出事件
|
|
@@ -356,12 +426,12 @@ export default {
|
|
|
356
426
|
// 处理页面卸载前的操作
|
|
357
427
|
handleBeforeUnload(event) {
|
|
358
428
|
// 发送页面关闭消息
|
|
359
|
-
if (this.
|
|
429
|
+
if (this.socket && this.socket.connected) {
|
|
360
430
|
const message = {
|
|
361
431
|
type: 'page_unload',
|
|
362
432
|
timestamp: Date.now()
|
|
363
433
|
};
|
|
364
|
-
this.
|
|
434
|
+
this.socket.emit('page_unload', message);
|
|
365
435
|
}
|
|
366
436
|
},
|
|
367
437
|
|
|
@@ -370,12 +440,12 @@ export default {
|
|
|
370
440
|
this.resetTimer();
|
|
371
441
|
},
|
|
372
442
|
|
|
373
|
-
// 重新连接
|
|
443
|
+
// 重新连接Socket.IO
|
|
374
444
|
reconnect() {
|
|
375
|
-
if (this.
|
|
376
|
-
this.
|
|
445
|
+
if (this.socket) {
|
|
446
|
+
this.socket.disconnect();
|
|
377
447
|
}
|
|
378
|
-
this.
|
|
448
|
+
this.initSocketIO();
|
|
379
449
|
}
|
|
380
450
|
}
|
|
381
451
|
};
|