customer-chat-sdk 1.1.0 → 1.1.2
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/core/CustomerSDK.d.ts +1 -0
- package/dist/core/CustomerSDK.d.ts.map +1 -1
- package/dist/core/IconManager.d.ts +18 -1
- package/dist/core/IconManager.d.ts.map +1 -1
- package/dist/core/IframeManager.d.ts +3 -2
- package/dist/core/IframeManager.d.ts.map +1 -1
- package/dist/core/ScreenshotManager.d.ts +1 -1
- package/dist/core/ScreenshotManager.d.ts.map +1 -1
- package/dist/customer-sdk.cjs.js +393 -195
- package/dist/customer-sdk.esm.js +393 -195
- package/dist/customer-sdk.min.js +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/customer-sdk.cjs.js
CHANGED
|
@@ -5,7 +5,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
// 直接使用base64字符串,避免打包后路径问题
|
|
6
6
|
const iconImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHgAAAB4CAYAAAA5ZDbSAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAArKSURBVHgB7Z1NbFTXFcf/93mwaGyEabLoIgFTqWRFMZUitSoIu80ial01SFWrKgsGKXYpWdRtFilRK4xUgbKICosmCKIySKCo2UAE6i61XVroDuOoi2YT02RZVFNwQTAzN+e8N88ej9+8j5n3ce5wf5JhPvyY4f3f/9xzz73vXoUeYKish6oljCiFYQ2MaAebVR3D/Jzf1xpDUPTTggIW6feXoOlH0Q9wS9exRK/Pl6qYX6qoJRiOgoEMvqpHWUjlYJ9iQeEJmTb07y6y2CT6HP99/z01C8MwQmB26OMSyuTIH9LTkSA35oLn9FmKCB/WHmP2YUUtQjhiBV4jqsIoJKJJbOC8ZLHFCczhl75UWXvCFuPUDiBXV5TGeWlhXIzALCx9m6Ni3RqfeRL71PJZVYEAChfYdayDc1klSkXBCRr9caxooQsTuIccG4qbiddxsKjQnbvAlDwNVzfgXK8L2wq30ZSMHcs7GctV4IFX9ZTyXGtM8pQq2i2qnCQ3H0NO5CLwk+radnDYrj3CWB5udpAx7FoS96YVdxVy1XCJzgmfG2RMZg5268P9FI6BzP8TRlN3Q/YvkRGZCOyG5H5cApcVLZFkGbJTF3igrEccErfX+rVZk5XIqbbBmyb1y2oDZqy4yfHbZT6HSJHUHDwwqQ/QP1aBpWvIIOXlM+o8UiAVga246ZOWyF0LbMXNjjRE7kpgbi/oS1yCJTNIoP33zqjL6JCOBW5Up24+sWXHvKDyJplojEal5tEBHWXRLG6tHzNW3BxQ7oTBSxvpnKMDOhKYxbVdofxwu1BUW+DqIBKSWODBSf17K24hjFRLbuk3EYkEbhTHbW25KBxMJR2giJ1k2aRKCJR0VR9jd9ySZmwH26RKCKRBicfWYxJLYGp3j9p2VxA0th43VEeGaBuahRIzVEc6uFbCtBVXIDFDdaiDBw/pUdSp7bXIpY6xsCm5oQ5W9fiNuaUgVHjfuK2DN01ovj/IaIE3PwVs/TL9PEOPv7T2vX/fAe7+H/j4c5hPiItLbQ9SyasmRcOCvvJN4OvPAXueB7Y9He+4a/8CFkjoq1TO/9snMA7tuEbcHvReoINNc++eHcCb48De59E1t+94gp+46rncGNq4OFBg6vfehAEzIl/5FnDkB/GdmpQL1w0SWmP2/lk11vryOoFNyJx3Pgu89eN0HBuHP3zkCc1ttmgCXLwui1ZVlCGYw98Brv82P3GZ174L/P031LY/C9FQs3qg9bU1Dm5MWP8UAuEE6v1D+QobxPErnptFQtWt0mNsb14daI2Daxtk3j+09WnPQUWLy7xJbf6RcciEqlu8rknzS2sEpnT7AITB4v759ewSqU6QLHJjJaLV5/4DieFZorjNSA3XpUfY4ofpFQdLDM/vH5YrLsNO5qRPGs1hekVgGu9N9Z6YbuEQKD1rZVjkrcIuwuYw3dwG74MQ+ITxiTMBN7s/DGmsFKlcgd3ihqAxX253TYIjjahQTVq6qxihIbCuyilLcvlRcrvbDo447GYp6IaLXYGVoPUzjhgSmlthcSW52NfUF3gXBGCqe324pCnFxbqhqcO3Q0iZMckCm4w/Hi0Bvt2FtXWqG2W0v5w5SyhFdsv4boiBV8F3pCRYe3egJ+D/h6AwPewoR0b3yPTw3Mzer0EEvN2BQ/m0CAfzPKpeYY+QpoY3J3Eoj96Mgtn5nKw+ZLdI6QmoOraXoIsP0a1TWoPg6TLv/AW4/R/v9zmZSdJuv/MRsPCZd+xrL8avH/PnXryR7NidQqIRtcHbSkpjiy543feoK54nvX3vbW/Gow+LHWdclgXiYxc+T35su8996yfhRQ1JfXmHVUbBRDmCx11v3wl+fSFi4vobHwT/Dh977ZPOPveNP0VPwJPQ5JB5hzJfTjgNrt5q/x7PYQ7j48/av7cQ8l7U5164gVDiNDuZowwROIwoJy096PzYXkCEwFEnOmzgPyrRGt/V+bGhnxvRFZIyWV6GwA/C33+3HNxOc6ITdaIPv9j5se0+15TZJkxf/zempyhWb0SBbHsG+NEL7d8fooRlfGTV6Tu+4g0rvv4SIknz2Be+CvzqJW/UKAxu2//4V4hAbZrQi0Vn0uySfx5Hz8B3Kf70XRQOLzIuIkT79+r2ClHZeV5QN2nJoXqliLnQ1wy8L7cdgu4xvuuQyrchgKj+rClwJBJ0sZKDNURsY37xH+gJrna06G9GKMw7vHU5BCDsyu+YizcgBl1nB/dhEUI4cQVGw8mipIuUzeuUHspwMMMnx6h1MVo4LuwCLVVJYL4LTUqixRyqwEj4whQVnqkPzNo6jSeiXGxiWyzNvWRadyzME1hjFoL4ecWswsfF67Lcy/iaenc2lOQ4mOFB9uNS18FogUOzxJvA/d7RymSdwQn9X2mryp4uy55Oy1Hm278LnvVRKFTbuH9WbeGHq7VoR5aLmXbTbaTw6w8Eiusx5z9YvcO/hg8hjKAJc1LgPOGCsHbXh5LmlZ3SVgTeUJW5/6AvspQSIH8fyeIytb7VpHlFYHdVFiUrm/bhk8rhuujM2p9GK1lcsu/cw9Ory/yvXSdLYJhm/OWUipyKyhGEEyrJOQGjW3aCXSOwG6aFjC75FL1Wlu9anqGxZEDfvDk8M2sEdsuWSo6LixSXmwPu37JrTamsaYVKc3hm1q34rh1ycb34JQ2LEJdF5RB84oqZ5VJVw7rNpIMXBP+ZnqFQPYqCyEtc7sNyCOa7H1hQnmpjQhgORGOeihvr1hcI3rNB4ViRAv/vAfD9t5EpQgsUHUPJ1amg19vvujKpP7Xb2ZkBDw0un1GBm3K0nTZLbfFBWMxAU8RtQ/jOZwW3xZZowtzLhE98V+2vDIsMVD080oYKfP+0mpVavrR4/d6wfQuZyFtXSoquEGHVLQvcMd9ajAgbKfASVUa0DdXy0DjVWrUKIvbyKzbhkkNUYtVM7LsLbagWAodmB2Nxfz22wDZUy0BTnzdOaPZJdH8whYWTHPthKQY698vvkQYJSLwEGq9BXO13N68UvztpL5Gk3W0m8R3+PGZccrCfroxFWHKBxU3S7jbT8SKGAxN6hA6ekTaXuuegpKrah91J2t1mOl6jY/msmlfKDkhkDZ/jTsVlulqE5d4ZdVlpK3JWUMZ8kM8xuiCVdWY3TegydaHOwZIaLC5FyQq6JLWFhK3I6ZGWuExq62Tdoy9EV8t+W+3qAjp3fA7TEpdJfSnwoUN6uFbHjJ3ukwzuCtEfLG6qN+mkvtIdlzT7qM9m+8mJmOd+btriMpksZeiK/Ai7bVkzBnSOSk9hrJuuUBiZ79YwMKmnqCt11BZEWqD2lgcOktaWk5LLdhy2XV7HXNVBOSvXNpPrfiuDk3qartxfPLFuzsm1zeS+oU7DzdPk5sLvf8qZ3FzbTGE7JvG28qqOc09A2J6jVHbanaFaAAVvieVVwOhbHO01oblfqxwcLEpYn8IF9mmUOqfooYjdyLugUMe2IkZgn0boLhvVRnPy5OAyDe2dlyKsjziBfdxkrIZRcnWZnu6DTOZ0HZc3DKKydFKJrMGLFbiZFbGBl8FiF9XN4oEUhVvSRW3GCIFb4TDOW9NTSByln11ZJWi8zDKvxMsLe/J6ntLCbxyMFLiVoSk9VH2IEVd03rKedzX3XD7EO3AG7gvlDc3ddR8qLLpZr6LX6iRoHxZLGzFvgkOj+ALBlx6CtCZy6AAAAABJRU5ErkJggg==';
|
|
7
7
|
class IconManager {
|
|
8
|
-
constructor(position, debug = false) {
|
|
8
|
+
constructor(position, debug = false, target) {
|
|
9
9
|
this.iconElement = null;
|
|
10
10
|
this.badgeElement = null;
|
|
11
11
|
this.onClickCallback = null;
|
|
@@ -17,10 +17,17 @@ class IconManager {
|
|
|
17
17
|
this.iconStartY = 0;
|
|
18
18
|
this.dragMoveHandler = null;
|
|
19
19
|
this.dragEndHandler = null;
|
|
20
|
+
this.checkDragHandler = null; // 临时拖动检测监听器
|
|
21
|
+
this.dragStartHandler = null; // 拖动开始事件监听器
|
|
22
|
+
this.touchStartHandler = null; // 触摸开始事件监听器
|
|
20
23
|
this.iconPosition = null; // 图标位置配置
|
|
21
24
|
this.debug = false; // debug 模式标志
|
|
25
|
+
this.isClickEnabled = true; // 是否允许点击(iframe 打开时禁用)
|
|
26
|
+
this.target = null; // 图标传送目标元素(可以是 HTMLElement 或选择器字符串)
|
|
22
27
|
this.iconPosition = position || null;
|
|
23
28
|
this.debug = debug;
|
|
29
|
+
// 保存 target(可以是 HTMLElement 或字符串选择器)
|
|
30
|
+
this.target = target || null;
|
|
24
31
|
}
|
|
25
32
|
/**
|
|
26
33
|
* 显示悬浮图标
|
|
@@ -34,7 +41,7 @@ class IconManager {
|
|
|
34
41
|
this.iconElement.className = 'customer-sdk-icon';
|
|
35
42
|
// 直接设置样式 - 图标容器
|
|
36
43
|
const defaultStyle = {
|
|
37
|
-
position: '
|
|
44
|
+
position: 'absolute',
|
|
38
45
|
width: '30px',
|
|
39
46
|
height: '30px',
|
|
40
47
|
backgroundColor: 'transparent', // 移除背景色,让图片直接显示
|
|
@@ -43,7 +50,7 @@ class IconManager {
|
|
|
43
50
|
alignItems: 'center',
|
|
44
51
|
justifyContent: 'center',
|
|
45
52
|
cursor: 'pointer',
|
|
46
|
-
zIndex: '
|
|
53
|
+
zIndex: '1000002', // 确保图标始终在最上层(遮罩层 999998,iframe 容器 999999)
|
|
47
54
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
|
|
48
55
|
userSelect: 'none',
|
|
49
56
|
transition: 'transform 0.2s ease',
|
|
@@ -119,8 +126,18 @@ class IconManager {
|
|
|
119
126
|
this.iconElement.appendChild(imgContainer);
|
|
120
127
|
// 添加拖动和点击事件
|
|
121
128
|
this.setupDragEvents();
|
|
122
|
-
//
|
|
123
|
-
|
|
129
|
+
// 添加到目标元素(如果 target 是字符串,需要重新查找,因为可能在初始化时元素还不存在)
|
|
130
|
+
const targetElement = this.getTargetElement();
|
|
131
|
+
if (targetElement) {
|
|
132
|
+
targetElement.appendChild(this.iconElement);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// 如果目标元素不存在,回退到 document.body
|
|
136
|
+
document.body.appendChild(this.iconElement);
|
|
137
|
+
if (this.debug) {
|
|
138
|
+
console.warn('Target element not found, icon added to document.body');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
124
141
|
if (this.debug) {
|
|
125
142
|
console.log('CustomerSDK icon displayed');
|
|
126
143
|
}
|
|
@@ -270,9 +287,11 @@ class IconManager {
|
|
|
270
287
|
// 绑定事件处理器(用于后续清理)
|
|
271
288
|
this.dragMoveHandler = this.handleDragMove.bind(this);
|
|
272
289
|
this.dragEndHandler = this.handleDragEnd.bind(this);
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
this.iconElement.addEventListener('
|
|
290
|
+
this.dragStartHandler = this.handleDragStart.bind(this);
|
|
291
|
+
// 只在图标上监听开始事件(保存引用以便后续移除)
|
|
292
|
+
this.iconElement.addEventListener('mousedown', this.dragStartHandler);
|
|
293
|
+
this.touchStartHandler = this.handleDragStart.bind(this);
|
|
294
|
+
this.iconElement.addEventListener('touchstart', this.touchStartHandler, { passive: false });
|
|
276
295
|
}
|
|
277
296
|
/**
|
|
278
297
|
* 开始拖动
|
|
@@ -295,12 +314,33 @@ class IconManager {
|
|
|
295
314
|
const rect = this.iconElement.getBoundingClientRect();
|
|
296
315
|
this.iconStartX = rect.left;
|
|
297
316
|
this.iconStartY = rect.top;
|
|
298
|
-
//
|
|
299
|
-
|
|
300
|
-
this.iconElement.style.cursor = 'grabbing';
|
|
317
|
+
// 注意:不要在这里立即移除 transition 和设置 cursor
|
|
318
|
+
// 只有在真正开始拖动时才修改样式,避免点击时图标位置跳动
|
|
301
319
|
// 只在真正开始拖动时添加document事件监听
|
|
302
320
|
// 先添加一个临时的move监听器来检测是否真的在拖动
|
|
303
321
|
const checkDrag = (moveEvent) => {
|
|
322
|
+
// 检测事件目标:如果事件发生在iframe或其他元素上,停止检测拖动
|
|
323
|
+
const target = moveEvent.target;
|
|
324
|
+
if (target && target !== this.iconElement && !this.iconElement?.contains(target)) {
|
|
325
|
+
// 检查是否是iframe相关元素
|
|
326
|
+
const isIframeElement = target.tagName === 'IFRAME' ||
|
|
327
|
+
target.closest('iframe') ||
|
|
328
|
+
target.closest('.customer-sdk-container') ||
|
|
329
|
+
target.closest('.customer-sdk-overlay');
|
|
330
|
+
if (isIframeElement) {
|
|
331
|
+
// 如果事件发生在iframe相关元素上,停止检测并清理监听器
|
|
332
|
+
if (this.checkDragHandler) {
|
|
333
|
+
if ('touches' in moveEvent) {
|
|
334
|
+
document.removeEventListener('touchmove', this.checkDragHandler);
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
document.removeEventListener('mousemove', this.checkDragHandler);
|
|
338
|
+
}
|
|
339
|
+
this.checkDragHandler = null;
|
|
340
|
+
}
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
304
344
|
const moveX = 'touches' in moveEvent ? moveEvent.touches[0].clientX : moveEvent.clientX;
|
|
305
345
|
const moveY = 'touches' in moveEvent ? moveEvent.touches[0].clientY : moveEvent.clientY;
|
|
306
346
|
const deltaX = moveX - this.dragStartX;
|
|
@@ -308,12 +348,20 @@ class IconManager {
|
|
|
308
348
|
// 如果移动距离超过5px,开始拖动
|
|
309
349
|
if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
|
|
310
350
|
this.isDragging = true;
|
|
311
|
-
//
|
|
312
|
-
if (
|
|
313
|
-
|
|
351
|
+
// 只有在真正开始拖动时才移除 transition 和设置 cursor
|
|
352
|
+
if (this.iconElement) {
|
|
353
|
+
this.iconElement.style.transition = 'none';
|
|
354
|
+
this.iconElement.style.cursor = 'grabbing';
|
|
314
355
|
}
|
|
315
|
-
|
|
316
|
-
|
|
356
|
+
// 移除临时监听器
|
|
357
|
+
if (this.checkDragHandler) {
|
|
358
|
+
if ('touches' in moveEvent) {
|
|
359
|
+
document.removeEventListener('touchmove', this.checkDragHandler);
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
document.removeEventListener('mousemove', this.checkDragHandler);
|
|
363
|
+
}
|
|
364
|
+
this.checkDragHandler = null;
|
|
317
365
|
}
|
|
318
366
|
// 添加正式的事件监听器
|
|
319
367
|
if (this.dragMoveHandler && this.dragEndHandler) {
|
|
@@ -328,13 +376,15 @@ class IconManager {
|
|
|
328
376
|
}
|
|
329
377
|
}
|
|
330
378
|
};
|
|
379
|
+
// 保存 checkDrag 引用,以便后续清理
|
|
380
|
+
this.checkDragHandler = checkDrag;
|
|
331
381
|
// 添加临时检测监听器
|
|
332
382
|
if ('touches' in e) {
|
|
333
|
-
document.addEventListener('touchmove',
|
|
383
|
+
document.addEventListener('touchmove', this.checkDragHandler, { passive: false });
|
|
334
384
|
document.addEventListener('touchend', this.dragEndHandler);
|
|
335
385
|
}
|
|
336
386
|
else {
|
|
337
|
-
document.addEventListener('mousemove',
|
|
387
|
+
document.addEventListener('mousemove', this.checkDragHandler);
|
|
338
388
|
document.addEventListener('mouseup', this.dragEndHandler);
|
|
339
389
|
}
|
|
340
390
|
}
|
|
@@ -346,20 +396,9 @@ class IconManager {
|
|
|
346
396
|
return;
|
|
347
397
|
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
|
|
348
398
|
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
|
|
349
|
-
//
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
// 检查是否是iframe相关元素
|
|
353
|
-
const isIframeElement = target.tagName === 'IFRAME' ||
|
|
354
|
-
target.closest('iframe') ||
|
|
355
|
-
target.closest('.customer-sdk-container') ||
|
|
356
|
-
target.closest('.customer-sdk-overlay');
|
|
357
|
-
if (isIframeElement) {
|
|
358
|
-
// 如果事件发生在iframe相关元素上,停止拖动
|
|
359
|
-
this.handleDragEnd();
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
399
|
+
// 注意:拖动过程中不检测 iframe 元素,因为用户可能只是想将图标拖动到 iframe 附近或上方
|
|
400
|
+
// 只有在拖动开始时(checkDrag 阶段)才检测 iframe,防止误判为拖动
|
|
401
|
+
// 一旦开始拖动,就应该允许拖动到任何位置,包括 iframe 上方
|
|
363
402
|
// 计算从拖动开始位置到当前位置的距离
|
|
364
403
|
const deltaX = clientX - this.dragStartX;
|
|
365
404
|
const deltaY = clientY - this.dragStartY;
|
|
@@ -394,10 +433,15 @@ class IconManager {
|
|
|
394
433
|
/**
|
|
395
434
|
* 结束拖动
|
|
396
435
|
*/
|
|
397
|
-
handleDragEnd(
|
|
436
|
+
handleDragEnd(_e) {
|
|
398
437
|
if (!this.iconElement)
|
|
399
438
|
return;
|
|
400
|
-
// 清理document
|
|
439
|
+
// 清理document上的所有事件监听器(包括临时检测监听器)
|
|
440
|
+
if (this.checkDragHandler) {
|
|
441
|
+
document.removeEventListener('mousemove', this.checkDragHandler);
|
|
442
|
+
document.removeEventListener('touchmove', this.checkDragHandler);
|
|
443
|
+
this.checkDragHandler = null;
|
|
444
|
+
}
|
|
401
445
|
if (this.dragMoveHandler) {
|
|
402
446
|
document.removeEventListener('mousemove', this.dragMoveHandler);
|
|
403
447
|
document.removeEventListener('touchmove', this.dragMoveHandler);
|
|
@@ -406,6 +450,8 @@ class IconManager {
|
|
|
406
450
|
document.removeEventListener('mouseup', this.dragEndHandler);
|
|
407
451
|
document.removeEventListener('touchend', this.dragEndHandler);
|
|
408
452
|
}
|
|
453
|
+
// 恢复样式(如果之前被修改过)
|
|
454
|
+
// 注意:如果只是点击(没有拖动),这些样式可能没有被修改,但恢复操作是安全的
|
|
409
455
|
this.iconElement.style.transition = 'transform 0.2s ease';
|
|
410
456
|
this.iconElement.style.cursor = 'pointer';
|
|
411
457
|
// 如果只是点击(没有拖动),触发点击事件
|
|
@@ -418,10 +464,90 @@ class IconManager {
|
|
|
418
464
|
* 处理点击事件
|
|
419
465
|
*/
|
|
420
466
|
handleClick() {
|
|
467
|
+
// 如果点击被禁用(iframe 打开时),不执行点击回调
|
|
468
|
+
if (!this.isClickEnabled) {
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
421
471
|
if (this.onClickCallback) {
|
|
422
472
|
this.onClickCallback();
|
|
423
473
|
}
|
|
424
474
|
}
|
|
475
|
+
/**
|
|
476
|
+
* 禁用点击和拖拽(iframe 打开时调用)
|
|
477
|
+
*/
|
|
478
|
+
disableClick() {
|
|
479
|
+
this.isClickEnabled = false;
|
|
480
|
+
// 移除拖动事件监听器
|
|
481
|
+
if (this.iconElement) {
|
|
482
|
+
if (this.dragStartHandler) {
|
|
483
|
+
this.iconElement.removeEventListener('mousedown', this.dragStartHandler);
|
|
484
|
+
}
|
|
485
|
+
if (this.touchStartHandler) {
|
|
486
|
+
this.iconElement.removeEventListener('touchstart', this.touchStartHandler);
|
|
487
|
+
}
|
|
488
|
+
// 禁用所有鼠标事件(包括点击和拖拽)
|
|
489
|
+
this.iconElement.style.pointerEvents = 'none';
|
|
490
|
+
this.iconElement.style.cursor = 'default';
|
|
491
|
+
}
|
|
492
|
+
// 清理可能正在进行的拖动
|
|
493
|
+
this.forceCleanupDragEvents();
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* 启用点击和拖拽(iframe 关闭时调用)
|
|
497
|
+
*/
|
|
498
|
+
enableClick() {
|
|
499
|
+
this.isClickEnabled = true;
|
|
500
|
+
// 重新添加拖动事件监听器
|
|
501
|
+
if (this.iconElement) {
|
|
502
|
+
if (this.dragStartHandler) {
|
|
503
|
+
this.iconElement.addEventListener('mousedown', this.dragStartHandler);
|
|
504
|
+
}
|
|
505
|
+
if (this.touchStartHandler) {
|
|
506
|
+
this.iconElement.addEventListener('touchstart', this.touchStartHandler, { passive: false });
|
|
507
|
+
}
|
|
508
|
+
// 恢复鼠标事件
|
|
509
|
+
this.iconElement.style.pointerEvents = 'auto';
|
|
510
|
+
this.iconElement.style.cursor = 'pointer';
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
/**
|
|
514
|
+
* 获取目标元素(支持动态查找)
|
|
515
|
+
*/
|
|
516
|
+
getTargetElement() {
|
|
517
|
+
if (!this.target) {
|
|
518
|
+
return document.body;
|
|
519
|
+
}
|
|
520
|
+
// 如果 target 是字符串选择器,每次都需要重新查找(支持动态元素)
|
|
521
|
+
if (typeof this.target === 'string') {
|
|
522
|
+
const element = document.querySelector(this.target);
|
|
523
|
+
if (element) {
|
|
524
|
+
return element;
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
// 如果元素不存在,回退到 document.body
|
|
528
|
+
if (this.debug) {
|
|
529
|
+
console.warn(`Target element not found: ${this.target}, falling back to document.body`);
|
|
530
|
+
}
|
|
531
|
+
return document.body;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
// 如果 target 是 HTMLElement
|
|
535
|
+
if (this.target instanceof HTMLElement) {
|
|
536
|
+
// 检查元素是否还在 DOM 中
|
|
537
|
+
if (document.body.contains(this.target)) {
|
|
538
|
+
return this.target;
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
// 如果元素不在 DOM 中,回退到 document.body
|
|
542
|
+
if (this.debug) {
|
|
543
|
+
console.warn('Target element no longer in DOM, falling back to document.body');
|
|
544
|
+
}
|
|
545
|
+
return document.body;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
// 默认回退到 document.body
|
|
549
|
+
return document.body;
|
|
550
|
+
}
|
|
425
551
|
/**
|
|
426
552
|
* 创建消息徽章(简化版)
|
|
427
553
|
*/
|
|
@@ -487,7 +613,7 @@ class IframeManager {
|
|
|
487
613
|
this.config = {
|
|
488
614
|
src: '',
|
|
489
615
|
mode: 'auto', // 默认自动检测设备类型
|
|
490
|
-
width:
|
|
616
|
+
width: 450, // PC 模式默认宽度
|
|
491
617
|
height: 600,
|
|
492
618
|
allowClose: true,
|
|
493
619
|
...config
|
|
@@ -527,9 +653,9 @@ class IframeManager {
|
|
|
527
653
|
try {
|
|
528
654
|
const actualMode = this.getActualMode();
|
|
529
655
|
const isPC = actualMode === 'popup';
|
|
530
|
-
//
|
|
656
|
+
// PC模式:创建或显示遮罩层
|
|
531
657
|
if (isPC) {
|
|
532
|
-
this.createOverlay();
|
|
658
|
+
this.createOverlay(); // createOverlay 内部会检查是否已存在
|
|
533
659
|
}
|
|
534
660
|
// 显示已创建的容器
|
|
535
661
|
if (this.containerElement) {
|
|
@@ -544,8 +670,18 @@ class IframeManager {
|
|
|
544
670
|
opacity: '1',
|
|
545
671
|
display: 'block'
|
|
546
672
|
});
|
|
547
|
-
//
|
|
548
|
-
|
|
673
|
+
// 关键优化:避免重复移动容器导致 iframe 重新加载
|
|
674
|
+
// 只有当容器不在遮罩层内时才移动,且确保遮罩层在 DOM 中
|
|
675
|
+
if (this.overlayElement) {
|
|
676
|
+
// 如果遮罩层不在 DOM 中,先添加到 DOM
|
|
677
|
+
if (!this.overlayElement.parentNode) {
|
|
678
|
+
document.body.appendChild(this.overlayElement);
|
|
679
|
+
}
|
|
680
|
+
// 只有当容器不在遮罩层内时才移动(避免重复移动导致 iframe 重新加载)
|
|
681
|
+
if (this.containerElement.parentNode !== this.overlayElement) {
|
|
682
|
+
this.overlayElement.appendChild(this.containerElement);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
549
685
|
}
|
|
550
686
|
else {
|
|
551
687
|
// 移动端模式:直接全屏显示,不需要遮罩层
|
|
@@ -582,22 +718,27 @@ class IframeManager {
|
|
|
582
718
|
if (!this.isOpen) {
|
|
583
719
|
return;
|
|
584
720
|
}
|
|
585
|
-
// 隐藏容器但保留DOM
|
|
721
|
+
// 隐藏容器但保留DOM元素(不移动容器,避免 iframe 重新加载)
|
|
586
722
|
if (this.containerElement) {
|
|
587
723
|
Object.assign(this.containerElement.style, {
|
|
588
724
|
visibility: 'hidden',
|
|
589
725
|
opacity: '0',
|
|
590
726
|
display: 'none'
|
|
591
727
|
});
|
|
728
|
+
// 注意:不移动容器,保持容器在当前位置(遮罩层或 body),避免 iframe 重新加载
|
|
592
729
|
}
|
|
593
|
-
//
|
|
730
|
+
// 隐藏遮罩层但不移除(仅PC模式,避免重新创建导致 iframe 重新加载)
|
|
594
731
|
if (this.overlayElement) {
|
|
595
|
-
this.overlayElement.
|
|
596
|
-
|
|
732
|
+
Object.assign(this.overlayElement.style, {
|
|
733
|
+
visibility: 'hidden',
|
|
734
|
+
opacity: '0',
|
|
735
|
+
display: 'none'
|
|
736
|
+
});
|
|
737
|
+
// 不移除遮罩层,下次显示时直接显示即可
|
|
597
738
|
}
|
|
598
739
|
// 恢复body滚动(移动端模式)
|
|
599
|
-
const
|
|
600
|
-
if (
|
|
740
|
+
const actualModeForScroll = this.getActualMode();
|
|
741
|
+
if (actualModeForScroll === 'fullscreen') {
|
|
601
742
|
this.preventBodyScroll(false);
|
|
602
743
|
}
|
|
603
744
|
this.isOpen = false;
|
|
@@ -645,9 +786,29 @@ class IframeManager {
|
|
|
645
786
|
}
|
|
646
787
|
}
|
|
647
788
|
/**
|
|
648
|
-
*
|
|
789
|
+
* 创建遮罩层(PC模式使用)
|
|
649
790
|
*/
|
|
650
791
|
createOverlay() {
|
|
792
|
+
// 如果遮罩层已存在,直接显示即可
|
|
793
|
+
if (this.overlayElement && this.overlayElement.parentNode) {
|
|
794
|
+
Object.assign(this.overlayElement.style, {
|
|
795
|
+
visibility: 'visible',
|
|
796
|
+
opacity: '1',
|
|
797
|
+
display: 'flex'
|
|
798
|
+
});
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
// 如果遮罩层存在但不在 DOM 中,重新添加到 DOM
|
|
802
|
+
if (this.overlayElement && !this.overlayElement.parentNode) {
|
|
803
|
+
document.body.appendChild(this.overlayElement);
|
|
804
|
+
Object.assign(this.overlayElement.style, {
|
|
805
|
+
visibility: 'visible',
|
|
806
|
+
opacity: '1',
|
|
807
|
+
display: 'flex'
|
|
808
|
+
});
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
// 创建新的遮罩层
|
|
651
812
|
this.overlayElement = document.createElement('div');
|
|
652
813
|
this.overlayElement.className = 'customer-sdk-overlay';
|
|
653
814
|
Object.assign(this.overlayElement.style, {
|
|
@@ -663,7 +824,7 @@ class IframeManager {
|
|
|
663
824
|
justifyContent: 'center',
|
|
664
825
|
cursor: this.config.allowClose ? 'pointer' : 'default'
|
|
665
826
|
});
|
|
666
|
-
//
|
|
827
|
+
// 点击遮罩层关闭(只添加一次事件监听器)
|
|
667
828
|
if (this.config.allowClose) {
|
|
668
829
|
this.overlayElement.addEventListener('click', (e) => {
|
|
669
830
|
if (e.target === this.overlayElement) {
|
|
@@ -715,45 +876,59 @@ class IframeManager {
|
|
|
715
876
|
'allow-pointer-lock', // 允许指针锁定
|
|
716
877
|
'allow-storage-access-by-user-activation' // 允许用户激活的存储访问
|
|
717
878
|
].join(' '));
|
|
718
|
-
//
|
|
879
|
+
// 根据设备类型设置模式
|
|
719
880
|
const actualMode = this.getActualMode();
|
|
720
881
|
const isPC = actualMode === 'popup';
|
|
721
882
|
this.iframeElement.scrolling = isPC ? 'auto' : 'no'; // PC显示滚动条,移动端禁用
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
883
|
+
// PC 模式:使用配置的宽度和高度
|
|
884
|
+
// 移动端:使用全屏
|
|
885
|
+
const containerStyles = isPC ? {
|
|
886
|
+
// PC 弹窗模式
|
|
887
|
+
width: `${this.config.width || 450}px`,
|
|
888
|
+
height: `${this.config.height || 600}px`,
|
|
889
|
+
maxWidth: '90vw',
|
|
890
|
+
maxHeight: '90vh',
|
|
727
891
|
backgroundColor: '#ffffff',
|
|
728
|
-
borderRadius:
|
|
729
|
-
boxShadow:
|
|
730
|
-
? '0 20px 40px rgba(0, 0, 0, 0.15)'
|
|
731
|
-
: '0 -4px 16px rgba(0, 0, 0, 0.25)',
|
|
892
|
+
borderRadius: '12px',
|
|
893
|
+
boxShadow: '0 20px 40px rgba(0, 0, 0, 0.15)',
|
|
732
894
|
border: 'none',
|
|
733
895
|
position: 'fixed',
|
|
734
896
|
zIndex: '999999',
|
|
735
|
-
// PC
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
897
|
+
// PC模式:居中显示
|
|
898
|
+
top: '50%',
|
|
899
|
+
left: '50%',
|
|
900
|
+
transform: 'translate(-50%, -50%)',
|
|
901
|
+
overflow: 'hidden',
|
|
902
|
+
// 初始隐藏的关键样式
|
|
903
|
+
visibility: 'hidden',
|
|
904
|
+
opacity: '0',
|
|
905
|
+
display: 'none'
|
|
906
|
+
} : {
|
|
907
|
+
// 移动端全屏模式(强制 100% 宽度和高度)
|
|
908
|
+
width: '100%',
|
|
909
|
+
height: '100%',
|
|
910
|
+
maxWidth: '100%',
|
|
911
|
+
maxHeight: '100%',
|
|
912
|
+
backgroundColor: '#ffffff',
|
|
913
|
+
borderRadius: '12px 12px 0 0',
|
|
914
|
+
boxShadow: '0 -4px 16px rgba(0, 0, 0, 0.25)',
|
|
915
|
+
border: 'none',
|
|
916
|
+
position: 'fixed',
|
|
917
|
+
zIndex: '999999',
|
|
918
|
+
// 全屏模式 - 占满整个屏幕
|
|
919
|
+
top: '0',
|
|
920
|
+
left: '0',
|
|
921
|
+
bottom: '0',
|
|
922
|
+
right: '0',
|
|
923
|
+
transform: 'none',
|
|
924
|
+
overflow: 'hidden',
|
|
750
925
|
// 初始隐藏的关键样式
|
|
751
926
|
visibility: 'hidden',
|
|
752
927
|
opacity: '0',
|
|
753
928
|
display: 'none'
|
|
754
929
|
};
|
|
755
930
|
Object.assign(this.containerElement.style, containerStyles);
|
|
756
|
-
// iframe
|
|
931
|
+
// iframe填充整个容器
|
|
757
932
|
const iframeStyles = {
|
|
758
933
|
width: '100%',
|
|
759
934
|
height: '100%',
|
|
@@ -887,6 +1062,7 @@ class IframeManager {
|
|
|
887
1062
|
}
|
|
888
1063
|
/**
|
|
889
1064
|
* 获取当前显示模式
|
|
1065
|
+
* PC 模式使用弹窗,移动端使用全屏
|
|
890
1066
|
*/
|
|
891
1067
|
getActualMode() {
|
|
892
1068
|
if (this.config.mode === 'auto') {
|
|
@@ -939,9 +1115,14 @@ class IframeManager {
|
|
|
939
1115
|
break;
|
|
940
1116
|
case 'resize_iframe':
|
|
941
1117
|
case 'resize':
|
|
942
|
-
|
|
1118
|
+
// PC模式支持 resize,移动端忽略
|
|
1119
|
+
const actualMode = this.getActualMode();
|
|
1120
|
+
if (actualMode === 'popup' && data.width && data.height) {
|
|
943
1121
|
this.resizeIframe(data.width, data.height);
|
|
944
1122
|
}
|
|
1123
|
+
else if (this.debug) {
|
|
1124
|
+
console.log('Resize request ignored (fullscreen mode)');
|
|
1125
|
+
}
|
|
945
1126
|
break;
|
|
946
1127
|
case 'new-message':
|
|
947
1128
|
// 新消息通知 - 触发回调让外层处理
|
|
@@ -960,12 +1141,12 @@ class IframeManager {
|
|
|
960
1141
|
}
|
|
961
1142
|
}
|
|
962
1143
|
/**
|
|
963
|
-
* 调整iframe
|
|
1144
|
+
* 调整iframe大小(PC模式支持)
|
|
964
1145
|
*/
|
|
965
1146
|
resizeIframe(width, height) {
|
|
966
|
-
if (this.
|
|
967
|
-
this.
|
|
968
|
-
this.
|
|
1147
|
+
if (this.containerElement) {
|
|
1148
|
+
this.containerElement.style.width = `${width}px`;
|
|
1149
|
+
this.containerElement.style.height = `${height}px`;
|
|
969
1150
|
}
|
|
970
1151
|
}
|
|
971
1152
|
}
|
|
@@ -14414,7 +14595,7 @@ class ScreenshotManager {
|
|
|
14414
14595
|
useProxy: options.useProxy ?? true, // 默认启用代理(如果配置了proxyUrl)
|
|
14415
14596
|
engine: options.engine ?? 'modern-screenshot',
|
|
14416
14597
|
corsMode: options.corsMode ?? 'canvas-proxy',
|
|
14417
|
-
|
|
14598
|
+
debug: options.debug !== undefined ? options.debug : false, // 默认 false,不输出日志
|
|
14418
14599
|
maxRetries: options.maxRetries ?? 2,
|
|
14419
14600
|
preloadImages: options.preloadImages ?? false, // 默认不预加载,按需加载
|
|
14420
14601
|
maxConcurrentDownloads: options.maxConcurrentDownloads ?? 10, // 增加并发数
|
|
@@ -14434,9 +14615,9 @@ class ScreenshotManager {
|
|
|
14434
14615
|
this.setupMessageListener();
|
|
14435
14616
|
this.setupVisibilityChangeListener();
|
|
14436
14617
|
// 打印初始化信息
|
|
14437
|
-
if (
|
|
14618
|
+
if (this.options.debug) {
|
|
14438
14619
|
console.log('📸 ScreenshotManager 初始化完成');
|
|
14439
|
-
console.log(`📸 配置: interval=${this.options.interval}ms, engine=${this.options.engine},
|
|
14620
|
+
console.log(`📸 配置: interval=${this.options.interval}ms, engine=${this.options.engine}, debug=${this.options.debug}`);
|
|
14440
14621
|
console.log('📸 等待 iframe 发送 checkScreenshot 消息...');
|
|
14441
14622
|
}
|
|
14442
14623
|
// 启动缓存清理定时器
|
|
@@ -14459,7 +14640,7 @@ class ScreenshotManager {
|
|
|
14459
14640
|
if (this.screenshotContext) {
|
|
14460
14641
|
try {
|
|
14461
14642
|
destroyContext(this.screenshotContext);
|
|
14462
|
-
if (
|
|
14643
|
+
if (this.options.debug) {
|
|
14463
14644
|
console.log('📸 目标元素变化,清理 context');
|
|
14464
14645
|
}
|
|
14465
14646
|
}
|
|
@@ -14482,7 +14663,7 @@ class ScreenshotManager {
|
|
|
14482
14663
|
this.handleIframeMessage(event);
|
|
14483
14664
|
};
|
|
14484
14665
|
window.addEventListener('message', this.messageHandler);
|
|
14485
|
-
if (
|
|
14666
|
+
if (this.options.debug) {
|
|
14486
14667
|
console.log('📸 消息监听器已设置,正在监听 iframe 消息...');
|
|
14487
14668
|
}
|
|
14488
14669
|
}
|
|
@@ -14492,12 +14673,12 @@ class ScreenshotManager {
|
|
|
14492
14673
|
setupVisibilityChangeListener() {
|
|
14493
14674
|
document.addEventListener('visibilitychange', () => {
|
|
14494
14675
|
if (document.hidden) {
|
|
14495
|
-
if (
|
|
14676
|
+
if (this.options.debug) {
|
|
14496
14677
|
console.log('📸 页面隐藏,截图轮询已暂停');
|
|
14497
14678
|
}
|
|
14498
14679
|
}
|
|
14499
14680
|
else {
|
|
14500
|
-
if (
|
|
14681
|
+
if (this.options.debug) {
|
|
14501
14682
|
console.log('📸 页面显示,截图轮询已恢复');
|
|
14502
14683
|
}
|
|
14503
14684
|
}
|
|
@@ -14514,7 +14695,7 @@ class ScreenshotManager {
|
|
|
14514
14695
|
}
|
|
14515
14696
|
// 如果提供了发送消息的回调,保存它(用于后续发送二进制数据)
|
|
14516
14697
|
// 注意:消息来源验证在 setupMessageListener 中处理
|
|
14517
|
-
if (
|
|
14698
|
+
if (this.options.debug) {
|
|
14518
14699
|
console.log('📸 [iframe] 收到消息:', event.data);
|
|
14519
14700
|
}
|
|
14520
14701
|
// 尝试解析为二进制配置(新格式)
|
|
@@ -14528,7 +14709,7 @@ class ScreenshotManager {
|
|
|
14528
14709
|
if (isValid) {
|
|
14529
14710
|
// 启用截图功能
|
|
14530
14711
|
if (!this.isEnabled) {
|
|
14531
|
-
if (
|
|
14712
|
+
if (this.options.debug) {
|
|
14532
14713
|
console.log('📸 [iframe] 启用截图功能(二进制模式)');
|
|
14533
14714
|
}
|
|
14534
14715
|
this.isEnabled = true;
|
|
@@ -14538,7 +14719,7 @@ class ScreenshotManager {
|
|
|
14538
14719
|
// 计算剩余有效时间(毫秒)
|
|
14539
14720
|
const remainingTime = binaryConfig.ttl - currentTime;
|
|
14540
14721
|
// 启动或更新截图轮询
|
|
14541
|
-
if (
|
|
14722
|
+
if (this.options.debug) {
|
|
14542
14723
|
const remainingMinutes = Math.ceil(remainingTime / 60000);
|
|
14543
14724
|
console.log(`📸 [iframe] 设置轮询间隔: ${this.dynamicInterval}ms,剩余有效时间: ${remainingMinutes}分钟`);
|
|
14544
14725
|
}
|
|
@@ -14550,7 +14731,7 @@ class ScreenshotManager {
|
|
|
14550
14731
|
this.expirationTimer = null;
|
|
14551
14732
|
}
|
|
14552
14733
|
this.expirationTimer = setTimeout(() => {
|
|
14553
|
-
if (
|
|
14734
|
+
if (this.options.debug) {
|
|
14554
14735
|
console.log('📸 [iframe] 二进制配置已过期,停止截图');
|
|
14555
14736
|
}
|
|
14556
14737
|
this.stopScreenshot();
|
|
@@ -14561,7 +14742,7 @@ class ScreenshotManager {
|
|
|
14561
14742
|
}
|
|
14562
14743
|
else {
|
|
14563
14744
|
// 禁用截图功能(ttl == 0 或已过期)
|
|
14564
|
-
if (
|
|
14745
|
+
if (this.options.debug) {
|
|
14565
14746
|
if (binaryConfig.ttl === 0) {
|
|
14566
14747
|
console.log('📸 [iframe] ttl == 0,禁用截图功能');
|
|
14567
14748
|
}
|
|
@@ -14580,11 +14761,15 @@ class ScreenshotManager {
|
|
|
14580
14761
|
return;
|
|
14581
14762
|
}
|
|
14582
14763
|
// 如果不是二进制配置格式,记录错误
|
|
14583
|
-
|
|
14764
|
+
if (this.options.debug) {
|
|
14765
|
+
console.error('📸 [iframe] 解析配置失败:未识别的配置格式');
|
|
14766
|
+
}
|
|
14584
14767
|
this.uploadError = '解析配置失败:仅支持二进制配置格式';
|
|
14585
14768
|
}
|
|
14586
14769
|
catch (error) {
|
|
14587
|
-
|
|
14770
|
+
if (this.options.debug) {
|
|
14771
|
+
console.error('📸 [iframe] 处理消息失败:', error);
|
|
14772
|
+
}
|
|
14588
14773
|
this.uploadError = error instanceof Error ? error.message : String(error);
|
|
14589
14774
|
}
|
|
14590
14775
|
}
|
|
@@ -14623,17 +14808,19 @@ class ScreenshotManager {
|
|
|
14623
14808
|
*/
|
|
14624
14809
|
startScreenshot(customInterval) {
|
|
14625
14810
|
if (!this.isEnabled) {
|
|
14626
|
-
|
|
14811
|
+
if (this.options.debug) {
|
|
14812
|
+
console.warn('📸 截图功能已禁用,无法启动');
|
|
14813
|
+
}
|
|
14627
14814
|
return;
|
|
14628
14815
|
}
|
|
14629
14816
|
const currentInterval = customInterval || this.dynamicInterval || this.options.interval;
|
|
14630
14817
|
if (this.isRunning) {
|
|
14631
|
-
if (
|
|
14818
|
+
if (this.options.debug) {
|
|
14632
14819
|
console.log(`📸 更新轮询间隔: ${currentInterval}ms`);
|
|
14633
14820
|
}
|
|
14634
14821
|
this.stopScreenshot();
|
|
14635
14822
|
}
|
|
14636
|
-
if (
|
|
14823
|
+
if (this.options.debug) {
|
|
14637
14824
|
console.log(`📸 开始轮询截图,间隔: ${currentInterval}ms`);
|
|
14638
14825
|
}
|
|
14639
14826
|
this.isRunning = true;
|
|
@@ -14646,7 +14833,7 @@ class ScreenshotManager {
|
|
|
14646
14833
|
const scheduleNext = async () => {
|
|
14647
14834
|
// 如果上次任务还没完成,等待完成
|
|
14648
14835
|
if (!this.isCurrentTaskCompleted) {
|
|
14649
|
-
if (
|
|
14836
|
+
if (this.options.debug) {
|
|
14650
14837
|
console.log('📸 [定时] 等待上次任务完成...');
|
|
14651
14838
|
}
|
|
14652
14839
|
// 每100ms检查一次任务是否完成
|
|
@@ -14666,7 +14853,7 @@ class ScreenshotManager {
|
|
|
14666
14853
|
this.isCurrentTaskCompleted = false;
|
|
14667
14854
|
// 记录定时开始时间
|
|
14668
14855
|
const scheduleStartTime = performance.now();
|
|
14669
|
-
if (
|
|
14856
|
+
if (this.options.debug) {
|
|
14670
14857
|
console.log(`📸 [定时开始] 开始新一轮截图任务`);
|
|
14671
14858
|
}
|
|
14672
14859
|
try {
|
|
@@ -14698,7 +14885,7 @@ class ScreenshotManager {
|
|
|
14698
14885
|
const combinedBufferSize = combinedBuffer.byteLength;
|
|
14699
14886
|
const combineTime = performance.now() - combineStartTime;
|
|
14700
14887
|
// 打印大小信息
|
|
14701
|
-
if (
|
|
14888
|
+
if (this.options.debug) {
|
|
14702
14889
|
console.log('📸 [轮询-大小统计]');
|
|
14703
14890
|
console.log(` Base64 大小: ${base64Size} 字符`);
|
|
14704
14891
|
console.log(` 图片字节大小: ${(imageBufferSize / 1024).toFixed(2)} KB (${imageBufferSize} 字节)`);
|
|
@@ -14719,7 +14906,7 @@ class ScreenshotManager {
|
|
|
14719
14906
|
const sendCallbackTime = performance.now() - sendCallbackStartTime;
|
|
14720
14907
|
const totalSendTime = performance.now() - sendStartTime;
|
|
14721
14908
|
const totalTime = performance.now() - scheduleStartTime;
|
|
14722
|
-
if (
|
|
14909
|
+
if (this.options.debug) {
|
|
14723
14910
|
console.log('📸 [轮询] ✅ 二进制数据已发送到 iframe');
|
|
14724
14911
|
console.log(` ⏱️ 发送回调耗时: ${sendCallbackTime.toFixed(2)}ms`);
|
|
14725
14912
|
console.log(` ⏱️ 发送阶段总耗时: ${totalSendTime.toFixed(2)}ms`);
|
|
@@ -14728,7 +14915,9 @@ class ScreenshotManager {
|
|
|
14728
14915
|
}
|
|
14729
14916
|
}
|
|
14730
14917
|
catch (error) {
|
|
14731
|
-
|
|
14918
|
+
if (this.options.debug) {
|
|
14919
|
+
console.error('📸 [轮询] ❌ 处理二进制数据失败:', error);
|
|
14920
|
+
}
|
|
14732
14921
|
}
|
|
14733
14922
|
}
|
|
14734
14923
|
// 任务完成(无压缩模式)
|
|
@@ -14737,7 +14926,7 @@ class ScreenshotManager {
|
|
|
14737
14926
|
else if (this.currentBinaryConfig && this.options.compress) {
|
|
14738
14927
|
// 启用了压缩,等待 Worker 压缩完成后在 onmessage 中发送
|
|
14739
14928
|
// 任务完成标志会在压缩完成的回调中设置
|
|
14740
|
-
if (
|
|
14929
|
+
if (this.options.debug) {
|
|
14741
14930
|
console.log('📸 [轮询] 等待 Worker 压缩完成后发送到 iframe...');
|
|
14742
14931
|
}
|
|
14743
14932
|
}
|
|
@@ -14747,7 +14936,7 @@ class ScreenshotManager {
|
|
|
14747
14936
|
}
|
|
14748
14937
|
}
|
|
14749
14938
|
catch (error) {
|
|
14750
|
-
if (
|
|
14939
|
+
if (this.options.debug) {
|
|
14751
14940
|
console.error('📸 [轮询] 截图失败:', error);
|
|
14752
14941
|
}
|
|
14753
14942
|
// 任务失败,标记为完成
|
|
@@ -14772,7 +14961,7 @@ class ScreenshotManager {
|
|
|
14772
14961
|
if (!this.isRunning) {
|
|
14773
14962
|
return;
|
|
14774
14963
|
}
|
|
14775
|
-
if (
|
|
14964
|
+
if (this.options.debug) {
|
|
14776
14965
|
console.log('📸 停止轮询截图');
|
|
14777
14966
|
}
|
|
14778
14967
|
this.isRunning = false;
|
|
@@ -14789,7 +14978,9 @@ class ScreenshotManager {
|
|
|
14789
14978
|
*/
|
|
14790
14979
|
async captureOnce(force = false) {
|
|
14791
14980
|
if (!this.isEnabled && !force) {
|
|
14792
|
-
|
|
14981
|
+
if (this.options.debug) {
|
|
14982
|
+
console.warn('📸 截图功能已禁用,无法执行截图。如需测试,请先调用 enable(true) 启用截图功能');
|
|
14983
|
+
}
|
|
14793
14984
|
return false;
|
|
14794
14985
|
}
|
|
14795
14986
|
return await this.takeScreenshot();
|
|
@@ -14799,14 +14990,16 @@ class ScreenshotManager {
|
|
|
14799
14990
|
*/
|
|
14800
14991
|
async takeScreenshot(scheduleStartTime) {
|
|
14801
14992
|
if (!this.targetElement) {
|
|
14802
|
-
|
|
14993
|
+
if (this.options.debug) {
|
|
14994
|
+
console.warn('📸 目标元素不存在');
|
|
14995
|
+
}
|
|
14803
14996
|
return false;
|
|
14804
14997
|
}
|
|
14805
14998
|
// 记录截图开始时间
|
|
14806
14999
|
const screenshotStartTime = performance.now();
|
|
14807
15000
|
this.setupGlobalErrorHandlers();
|
|
14808
15001
|
try {
|
|
14809
|
-
if (
|
|
15002
|
+
if (this.options.debug) {
|
|
14810
15003
|
console.log(`📸 开始截图 #${this.screenshotCount + 1}...`);
|
|
14811
15004
|
if (scheduleStartTime) {
|
|
14812
15005
|
const waitTime = screenshotStartTime - scheduleStartTime;
|
|
@@ -14820,7 +15013,7 @@ class ScreenshotManager {
|
|
|
14820
15013
|
this.waitForFonts()
|
|
14821
15014
|
]);
|
|
14822
15015
|
const waitStylesTime = performance.now() - waitStylesStartTime;
|
|
14823
|
-
if (
|
|
15016
|
+
if (this.options.debug) {
|
|
14824
15017
|
console.log(` ⏱️ 等待样式和字体加载耗时: ${waitStylesTime.toFixed(2)}ms`);
|
|
14825
15018
|
}
|
|
14826
15019
|
// 等待元素完全渲染(特别是对于 modern-screenshot)
|
|
@@ -14829,12 +15022,12 @@ class ScreenshotManager {
|
|
|
14829
15022
|
requestAnimationFrame(() => resolve());
|
|
14830
15023
|
}));
|
|
14831
15024
|
const waitRenderTime = performance.now() - waitRenderStartTime;
|
|
14832
|
-
if (
|
|
15025
|
+
if (this.options.debug) {
|
|
14833
15026
|
console.log(` ⏱️ 等待渲染完成耗时: ${waitRenderTime.toFixed(2)}ms`);
|
|
14834
15027
|
}
|
|
14835
15028
|
// 选择截图引擎
|
|
14836
15029
|
const selectedEngine = this.options.engine || 'modern-screenshot';
|
|
14837
|
-
if (
|
|
15030
|
+
if (this.options.debug) {
|
|
14838
15031
|
console.log(`📸 使用截图引擎: ${selectedEngine}`);
|
|
14839
15032
|
}
|
|
14840
15033
|
// 优化:如果启用预加载,才预处理图片;否则让引擎按需加载
|
|
@@ -14874,7 +15067,7 @@ class ScreenshotManager {
|
|
|
14874
15067
|
dataUrl = await this.takeScreenshotWithModernScreenshot(this.targetElement);
|
|
14875
15068
|
}
|
|
14876
15069
|
const engineTime = performance.now() - engineStartTime;
|
|
14877
|
-
if (
|
|
15070
|
+
if (this.options.debug) {
|
|
14878
15071
|
console.log(` ⏱️ 截图引擎执行耗时: ${engineTime.toFixed(2)}ms`);
|
|
14879
15072
|
}
|
|
14880
15073
|
const timestamp = Date.now();
|
|
@@ -14888,7 +15081,7 @@ class ScreenshotManager {
|
|
|
14888
15081
|
const removed = this.screenshotHistory.shift();
|
|
14889
15082
|
// 强制 GC(如果可能)
|
|
14890
15083
|
if (removed && removed.length > 1000000) { // 大于1MB的字符串
|
|
14891
|
-
if (
|
|
15084
|
+
if (this.options.debug) {
|
|
14892
15085
|
console.log(`📸 清理旧截图,释放内存: ${Math.round(removed.length * 0.75 / 1024)} KB`);
|
|
14893
15086
|
}
|
|
14894
15087
|
}
|
|
@@ -14902,7 +15095,7 @@ class ScreenshotManager {
|
|
|
14902
15095
|
if (removed) {
|
|
14903
15096
|
const removedSize = removed.length;
|
|
14904
15097
|
totalSize -= removedSize;
|
|
14905
|
-
if (
|
|
15098
|
+
if (this.options.debug) {
|
|
14906
15099
|
console.warn(`📸 ⚠️ 历史记录总大小超过限制,清理最旧截图: ${Math.round(removedSize * 0.75 / 1024)} KB`);
|
|
14907
15100
|
}
|
|
14908
15101
|
}
|
|
@@ -14914,7 +15107,7 @@ class ScreenshotManager {
|
|
|
14914
15107
|
const screenshotTotalTime = performance.now() - screenshotStartTime;
|
|
14915
15108
|
// 打印基本信息
|
|
14916
15109
|
const base64Data = dataUrl.split(',')[1] || '';
|
|
14917
|
-
if (
|
|
15110
|
+
if (this.options.debug) {
|
|
14918
15111
|
console.log('📸 截图完成:');
|
|
14919
15112
|
console.log(`📸 编号: #${this.screenshotCount}`);
|
|
14920
15113
|
console.log(`📸 时间: ${new Date(timestamp).toLocaleTimeString()}`);
|
|
@@ -14933,7 +15126,7 @@ class ScreenshotManager {
|
|
|
14933
15126
|
// 确保 Worker 已创建
|
|
14934
15127
|
if (!this.worker) {
|
|
14935
15128
|
this.worker = this.createWorker();
|
|
14936
|
-
if (
|
|
15129
|
+
if (this.options.debug) {
|
|
14937
15130
|
if (this.worker) {
|
|
14938
15131
|
console.log('📸 Worker 已创建,准备压缩');
|
|
14939
15132
|
}
|
|
@@ -14945,7 +15138,7 @@ class ScreenshotManager {
|
|
|
14945
15138
|
if (this.worker) {
|
|
14946
15139
|
// 记录压缩开始时间
|
|
14947
15140
|
const compressStartTime = performance.now();
|
|
14948
|
-
if (
|
|
15141
|
+
if (this.options.debug) {
|
|
14949
15142
|
console.log('📸 发送到 WebWorker 进行压缩...');
|
|
14950
15143
|
}
|
|
14951
15144
|
// 保存原始 dataUrl 用于后续对比(在 Worker 压缩完成后)
|
|
@@ -14968,7 +15161,7 @@ class ScreenshotManager {
|
|
|
14968
15161
|
}
|
|
14969
15162
|
else {
|
|
14970
15163
|
// Worker 不可用,如果配置了二进制模式,直接发送原始截图
|
|
14971
|
-
if (
|
|
15164
|
+
if (this.options.debug) {
|
|
14972
15165
|
console.warn('📸 ⚠️ Worker 不可用,跳过压缩(使用原始截图)');
|
|
14973
15166
|
}
|
|
14974
15167
|
// Worker 不可用时,如果配置了二进制模式,立即发送原始截图
|
|
@@ -14984,7 +15177,7 @@ class ScreenshotManager {
|
|
|
14984
15177
|
data: combinedBuffer
|
|
14985
15178
|
};
|
|
14986
15179
|
this.sendToIframeCallback(message);
|
|
14987
|
-
if (
|
|
15180
|
+
if (this.options.debug) {
|
|
14988
15181
|
console.log('📸 [Worker 不可用] ✅ 原始截图已发送到 iframe');
|
|
14989
15182
|
}
|
|
14990
15183
|
}
|
|
@@ -15009,7 +15202,7 @@ class ScreenshotManager {
|
|
|
15009
15202
|
console.error('📸 截图失败:', err);
|
|
15010
15203
|
this.error = errorMessage;
|
|
15011
15204
|
}
|
|
15012
|
-
else if (
|
|
15205
|
+
else if (this.options.debug) {
|
|
15013
15206
|
console.warn('📸 截图遇到跨域问题(已忽略)');
|
|
15014
15207
|
}
|
|
15015
15208
|
return false;
|
|
@@ -15026,7 +15219,7 @@ class ScreenshotManager {
|
|
|
15026
15219
|
* 注意:snapdom 内部使用 worker 进行截图处理,会在后台线程执行,不会阻塞主线程
|
|
15027
15220
|
*/
|
|
15028
15221
|
async takeScreenshotWithSnapdom(element) {
|
|
15029
|
-
if (
|
|
15222
|
+
if (this.options.debug) {
|
|
15030
15223
|
console.log('📸 使用 snapdom 引擎截图...');
|
|
15031
15224
|
}
|
|
15032
15225
|
// 限制只截图可见区域(viewport),避免截图不可见区域
|
|
@@ -15066,7 +15259,7 @@ class ScreenshotManager {
|
|
|
15066
15259
|
// 简化方案:直接使用 body,但添加尺寸限制选项(如果 snapdom 支持)
|
|
15067
15260
|
// 如果不支持,则只能截图整个 body
|
|
15068
15261
|
targetElement = element;
|
|
15069
|
-
if (
|
|
15262
|
+
if (this.options.debug) {
|
|
15070
15263
|
console.log(`📸 注意:snapdom 将截图整个 body,建议使用 html2canvas 或 modern-screenshot 来限制可见区域`);
|
|
15071
15264
|
console.log(`📸 可见区域尺寸: ${window.innerWidth}x${window.innerHeight}`);
|
|
15072
15265
|
}
|
|
@@ -15083,7 +15276,7 @@ class ScreenshotManager {
|
|
|
15083
15276
|
elementRect.left >= 0 &&
|
|
15084
15277
|
elementRect.bottom <= window.innerHeight &&
|
|
15085
15278
|
elementRect.right <= window.innerWidth;
|
|
15086
|
-
if (!isFullyVisible &&
|
|
15279
|
+
if (!isFullyVisible && this.options.debug) {
|
|
15087
15280
|
console.warn(`📸 ⚠️ 元素部分不可见,snapdom 可能会截图整个元素(包括不可见部分)`);
|
|
15088
15281
|
console.warn(`📸 建议:使用 html2canvas 或 modern-screenshot 来限制可见区域`);
|
|
15089
15282
|
}
|
|
@@ -15101,12 +15294,12 @@ class ScreenshotManager {
|
|
|
15101
15294
|
proxyUrl = proxyUrl.endsWith('?') ? proxyUrl + 'url=' : proxyUrl + '?url=';
|
|
15102
15295
|
}
|
|
15103
15296
|
options.useProxy = proxyUrl;
|
|
15104
|
-
if (
|
|
15297
|
+
if (this.options.debug) {
|
|
15105
15298
|
console.log(`📸 使用代理服务器处理跨域图片: ${proxyUrl}`);
|
|
15106
15299
|
}
|
|
15107
15300
|
}
|
|
15108
15301
|
else {
|
|
15109
|
-
if (
|
|
15302
|
+
if (this.options.debug) {
|
|
15110
15303
|
if (!this.options.useProxy) {
|
|
15111
15304
|
console.log('📸 代理功能已禁用(useProxy: false)');
|
|
15112
15305
|
}
|
|
@@ -15148,7 +15341,7 @@ class ScreenshotManager {
|
|
|
15148
15341
|
parent.removeChild(container);
|
|
15149
15342
|
}
|
|
15150
15343
|
}
|
|
15151
|
-
if (
|
|
15344
|
+
if (this.options.debug) {
|
|
15152
15345
|
console.log(`📸 snapdom 截图成功!格式: ${outputFormat}, 尺寸: ${img.width}x${img.height}`);
|
|
15153
15346
|
}
|
|
15154
15347
|
return dataUrl;
|
|
@@ -15166,14 +15359,14 @@ class ScreenshotManager {
|
|
|
15166
15359
|
const errorName = error instanceof Error ? error.name : 'Unknown';
|
|
15167
15360
|
// 针对不同类型的错误给出具体提示
|
|
15168
15361
|
if (errorName === 'EncodingError' || errorMessage.includes('cannot be decoded')) {
|
|
15169
|
-
if (
|
|
15362
|
+
if (this.options.debug) {
|
|
15170
15363
|
console.warn('📸 ⚠️ 图片解码失败 - 这通常是因为跨域图片无法访问');
|
|
15171
15364
|
console.warn('📸 💡 解决方案:配置 proxyUrl 选项');
|
|
15172
15365
|
console.warn('📸 📖 参考: https://snapdom.dev/#cors');
|
|
15173
15366
|
}
|
|
15174
15367
|
}
|
|
15175
15368
|
else if (errorMessage.includes('CORS') || errorMessage.includes('cross-origin')) {
|
|
15176
|
-
if (
|
|
15369
|
+
if (this.options.debug) {
|
|
15177
15370
|
console.warn('📸 ⚠️ 检测到 CORS 错误,建议配置 proxyUrl 选项');
|
|
15178
15371
|
}
|
|
15179
15372
|
}
|
|
@@ -15198,7 +15391,7 @@ class ScreenshotManager {
|
|
|
15198
15391
|
* - 需要快速截图
|
|
15199
15392
|
*/
|
|
15200
15393
|
async takeScreenshotWithHtml2Canvas(element) {
|
|
15201
|
-
if (
|
|
15394
|
+
if (this.options.debug) {
|
|
15202
15395
|
console.log('📸 使用 html2canvas 引擎截图...');
|
|
15203
15396
|
}
|
|
15204
15397
|
try {
|
|
@@ -15252,7 +15445,7 @@ class ScreenshotManager {
|
|
|
15252
15445
|
: (isMobile ? 0.5 : 0.6), // 用户未配置,使用默认值
|
|
15253
15446
|
useCORS: this.options.enableCORS,
|
|
15254
15447
|
allowTaint: !this.options.enableCORS, // 如果启用 CORS,不允许 taint
|
|
15255
|
-
logging:
|
|
15448
|
+
logging: this.options.debug, // 使用配置的静默模式
|
|
15256
15449
|
// foreignObjectRendering: true, // ❌ 移除:可能导致跨域图片不显示
|
|
15257
15450
|
// removeContainer: true, // ❌ 移除:移到后面,避免重复定义
|
|
15258
15451
|
// 不设置 width 和 height,让 html2canvas 自动计算
|
|
@@ -15390,12 +15583,12 @@ class ScreenshotManager {
|
|
|
15390
15583
|
}
|
|
15391
15584
|
catch (e) {
|
|
15392
15585
|
// 忽略内联化错误
|
|
15393
|
-
if (
|
|
15586
|
+
if (this.options.debug) {
|
|
15394
15587
|
console.warn('📸 iOS 样式内联化失败:', e);
|
|
15395
15588
|
}
|
|
15396
15589
|
}
|
|
15397
15590
|
}
|
|
15398
|
-
if (
|
|
15591
|
+
if (this.options.debug) {
|
|
15399
15592
|
const styleLinks = clonedDoc.querySelectorAll('link[rel="stylesheet"]').length;
|
|
15400
15593
|
const styleTags = clonedDoc.querySelectorAll('style').length;
|
|
15401
15594
|
console.log(`📸 onclone: 已复制 ${styleLinks} 个样式表链接和 ${styleTags} 个内联样式标签${isIOS ? ' (iOS 模式:已内联化计算样式)' : ''}`);
|
|
@@ -15444,7 +15637,7 @@ class ScreenshotManager {
|
|
|
15444
15637
|
const cachedDataUrl = this.getCachedImage(originalSrc);
|
|
15445
15638
|
if (cachedDataUrl) {
|
|
15446
15639
|
img.src = cachedDataUrl;
|
|
15447
|
-
if (
|
|
15640
|
+
if (this.options.debug) {
|
|
15448
15641
|
console.log(`📸 使用缓存的图片: ${originalSrc.substring(0, 50)}...`);
|
|
15449
15642
|
}
|
|
15450
15643
|
return;
|
|
@@ -15453,7 +15646,7 @@ class ScreenshotManager {
|
|
|
15453
15646
|
// 构建代理 URL
|
|
15454
15647
|
try {
|
|
15455
15648
|
if (!this.options.proxyUrl) {
|
|
15456
|
-
if (
|
|
15649
|
+
if (this.options.debug) {
|
|
15457
15650
|
console.warn(`📸 ⚠️ 未配置代理 URL,跨域图片可能无法显示: ${originalSrc}`);
|
|
15458
15651
|
}
|
|
15459
15652
|
return;
|
|
@@ -15466,19 +15659,19 @@ class ScreenshotManager {
|
|
|
15466
15659
|
baseUrl = baseUrl.replace(/[?&]$/, '');
|
|
15467
15660
|
const proxyUrl = `${baseUrl}?${params.toString()}`;
|
|
15468
15661
|
img.src = proxyUrl;
|
|
15469
|
-
if (
|
|
15662
|
+
if (this.options.debug) {
|
|
15470
15663
|
console.log(`📸 使用代理 URL 替换跨域图片: ${originalSrc.substring(0, 50)}... -> ${proxyUrl.substring(0, 50)}...`);
|
|
15471
15664
|
}
|
|
15472
15665
|
}
|
|
15473
15666
|
catch (error) {
|
|
15474
|
-
if (
|
|
15667
|
+
if (this.options.debug) {
|
|
15475
15668
|
console.warn(`📸 ⚠️ 处理图片 URL 失败: ${originalSrc}`, error);
|
|
15476
15669
|
}
|
|
15477
15670
|
}
|
|
15478
15671
|
});
|
|
15479
15672
|
};
|
|
15480
15673
|
}
|
|
15481
|
-
if (
|
|
15674
|
+
if (this.options.debug) {
|
|
15482
15675
|
console.log(`📸 html2canvas 配置: 元素尺寸 ${elementWidth}x${elementHeight}, 质量 ${finalQuality.toFixed(2)}, 缩放 ${options.scale}`);
|
|
15483
15676
|
console.log(`📸 html2canvas 将自动计算截图尺寸(不限制 width/height)`);
|
|
15484
15677
|
console.log(`📸 用户配置质量: ${this.options.quality}, 实际使用质量: ${finalQuality.toFixed(2)}`);
|
|
@@ -15523,7 +15716,7 @@ class ScreenshotManager {
|
|
|
15523
15716
|
if (!dataUrl || dataUrl.length < 100) {
|
|
15524
15717
|
throw new Error('生成的截图数据无效或过短');
|
|
15525
15718
|
}
|
|
15526
|
-
if (
|
|
15719
|
+
if (this.options.debug) {
|
|
15527
15720
|
console.log(`📸 html2canvas 截图成功!格式: ${mimeType}, 尺寸: ${canvas.width}x${canvas.height}, 质量: ${finalQualityForExport?.toFixed(2) || 'N/A (PNG)'}`);
|
|
15528
15721
|
console.log(`📸 输出格式: ${mimeType}, 用户配置质量: ${this.options.quality}, 实际使用质量: ${finalQualityForExport?.toFixed(2) || 'N/A (PNG)'}`);
|
|
15529
15722
|
}
|
|
@@ -15531,7 +15724,7 @@ class ScreenshotManager {
|
|
|
15531
15724
|
}
|
|
15532
15725
|
catch (error) {
|
|
15533
15726
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
15534
|
-
if (
|
|
15727
|
+
if (this.options.debug) {
|
|
15535
15728
|
console.error('📸 html2canvas 截图失败:', errorMessage);
|
|
15536
15729
|
if (errorMessage.includes('CORS') || errorMessage.includes('cross-origin')) {
|
|
15537
15730
|
console.warn('📸 💡 建议:配置 proxyUrl 选项处理跨域图片');
|
|
@@ -15564,7 +15757,7 @@ class ScreenshotManager {
|
|
|
15564
15757
|
if (this.isScreenshotInProgress) {
|
|
15565
15758
|
// 队列最多保留 1 个请求,避免积压
|
|
15566
15759
|
if (this.screenshotQueue.length >= 1) {
|
|
15567
|
-
if (
|
|
15760
|
+
if (this.options.debug) {
|
|
15568
15761
|
console.log('📸 截图队列已满,跳过当前请求(等待队列处理)');
|
|
15569
15762
|
}
|
|
15570
15763
|
// 等待队列中的请求完成
|
|
@@ -15593,7 +15786,7 @@ class ScreenshotManager {
|
|
|
15593
15786
|
});
|
|
15594
15787
|
}
|
|
15595
15788
|
this.isScreenshotInProgress = true;
|
|
15596
|
-
if (
|
|
15789
|
+
if (this.options.debug) {
|
|
15597
15790
|
console.log('📸 使用 modern-screenshot 引擎截图(Worker 模式)...');
|
|
15598
15791
|
}
|
|
15599
15792
|
try {
|
|
@@ -15613,7 +15806,7 @@ class ScreenshotManager {
|
|
|
15613
15806
|
elementWidth = element.clientWidth || element.offsetWidth || element.scrollWidth;
|
|
15614
15807
|
elementHeight = element.clientHeight || element.offsetHeight || element.scrollHeight;
|
|
15615
15808
|
}
|
|
15616
|
-
if (
|
|
15809
|
+
if (this.options.debug) {
|
|
15617
15810
|
console.log(`📸 目标元素: ${element.tagName}${element.id ? '#' + element.id : ''}${element.className ? '.' + element.className.split(' ').join('.') : ''}`);
|
|
15618
15811
|
console.log(`📸 元素尺寸: ${elementWidth}x${elementHeight}`);
|
|
15619
15812
|
console.log(`📸 scrollWidth: ${element.scrollWidth}, scrollHeight: ${element.scrollHeight}`);
|
|
@@ -15647,7 +15840,7 @@ class ScreenshotManager {
|
|
|
15647
15840
|
// 检查内存缓存(优先使用缓存,带过期时间检查)
|
|
15648
15841
|
const cachedDataUrl = this.getCachedImage(url);
|
|
15649
15842
|
if (cachedDataUrl) {
|
|
15650
|
-
if (
|
|
15843
|
+
if (this.options.debug) {
|
|
15651
15844
|
console.log(`📸 ✅ 使用内存缓存图片: ${url.substring(0, 50)}...`);
|
|
15652
15845
|
}
|
|
15653
15846
|
return cachedDataUrl;
|
|
@@ -15699,7 +15892,7 @@ class ScreenshotManager {
|
|
|
15699
15892
|
}
|
|
15700
15893
|
}
|
|
15701
15894
|
catch (error) {
|
|
15702
|
-
if (
|
|
15895
|
+
if (this.options.debug) {
|
|
15703
15896
|
console.warn(`📸 代理处理图片失败: ${url.substring(0, 100)}...`, error);
|
|
15704
15897
|
}
|
|
15705
15898
|
// 失败时返回原 URL,让 modern-screenshot 自己处理
|
|
@@ -15748,7 +15941,7 @@ class ScreenshotManager {
|
|
|
15748
15941
|
// 按照 demo 的方式:只在 context 不存在时创建,之后一直复用
|
|
15749
15942
|
// 不进行复杂的检测和重新创建逻辑
|
|
15750
15943
|
if (!this.screenshotContext) {
|
|
15751
|
-
if (
|
|
15944
|
+
if (this.options.debug) {
|
|
15752
15945
|
console.log(`📸 创建截图 Worker 上下文...`);
|
|
15753
15946
|
console.log(`📸 Worker 模式: ${workerNumber} 个 Worker`);
|
|
15754
15947
|
}
|
|
@@ -15769,23 +15962,23 @@ class ScreenshotManager {
|
|
|
15769
15962
|
// 如果用户指定了 workerUrl,使用指定的 URL
|
|
15770
15963
|
if (this.options.workerUrl) {
|
|
15771
15964
|
simpleContextOptions.workerUrl = this.options.workerUrl;
|
|
15772
|
-
if (
|
|
15965
|
+
if (this.options.debug) {
|
|
15773
15966
|
console.log(`📸 使用指定的 Worker URL: ${this.options.workerUrl}`);
|
|
15774
15967
|
}
|
|
15775
15968
|
}
|
|
15776
15969
|
else {
|
|
15777
|
-
if (
|
|
15970
|
+
if (this.options.debug) {
|
|
15778
15971
|
console.log('📸 Worker URL 未指定,modern-screenshot 将自动处理');
|
|
15779
15972
|
}
|
|
15780
15973
|
}
|
|
15781
15974
|
try {
|
|
15782
15975
|
this.screenshotContext = await createContext$1(element, simpleContextOptions);
|
|
15783
|
-
if (
|
|
15976
|
+
if (this.options.debug) {
|
|
15784
15977
|
console.log('📸 Worker 上下文创建成功');
|
|
15785
15978
|
}
|
|
15786
15979
|
}
|
|
15787
15980
|
catch (error) {
|
|
15788
|
-
if (
|
|
15981
|
+
if (this.options.debug) {
|
|
15789
15982
|
console.error('📸 创建 Worker 上下文失败:', error);
|
|
15790
15983
|
}
|
|
15791
15984
|
throw error;
|
|
@@ -15795,7 +15988,7 @@ class ScreenshotManager {
|
|
|
15795
15988
|
// 按照 demo 的方式:使用 domToWebp 时传递配置参数
|
|
15796
15989
|
let dataUrl;
|
|
15797
15990
|
const outputFormat = this.options.outputFormat || 'webp';
|
|
15798
|
-
if (
|
|
15991
|
+
if (this.options.debug) {
|
|
15799
15992
|
console.log(`📸 使用 ${outputFormat.toUpperCase()} 格式截图(直接输出,无需转换)...`);
|
|
15800
15993
|
}
|
|
15801
15994
|
// 构建 domToWebp/domToJpeg/domToPng 的配置参数(和 demo 一致)
|
|
@@ -15830,14 +16023,14 @@ class ScreenshotManager {
|
|
|
15830
16023
|
if (!dataUrl || dataUrl.length < 100) {
|
|
15831
16024
|
throw new Error('生成的截图数据无效或过短');
|
|
15832
16025
|
}
|
|
15833
|
-
if (
|
|
16026
|
+
if (this.options.debug) {
|
|
15834
16027
|
console.log(`📸 ✅ modern-screenshot 截图成功(Worker 模式,${outputFormat.toUpperCase()} 格式)`);
|
|
15835
16028
|
}
|
|
15836
16029
|
return dataUrl;
|
|
15837
16030
|
}
|
|
15838
16031
|
catch (workerError) {
|
|
15839
16032
|
// Worker 模式失败,回退到普通模式(和 demo 一致)
|
|
15840
|
-
if (
|
|
16033
|
+
if (this.options.debug) {
|
|
15841
16034
|
console.warn('📸 Worker 模式失败,回退到普通模式:', workerError);
|
|
15842
16035
|
}
|
|
15843
16036
|
// 销毁失败的 context
|
|
@@ -15880,7 +16073,7 @@ class ScreenshotManager {
|
|
|
15880
16073
|
if (!dataUrl || dataUrl.length < 100) {
|
|
15881
16074
|
throw new Error('生成的截图数据无效或过短');
|
|
15882
16075
|
}
|
|
15883
|
-
if (
|
|
16076
|
+
if (this.options.debug) {
|
|
15884
16077
|
console.log(`📸 ✅ modern-screenshot 截图成功(普通模式,${outputFormat.toUpperCase()} 格式)`);
|
|
15885
16078
|
}
|
|
15886
16079
|
return dataUrl;
|
|
@@ -15888,7 +16081,7 @@ class ScreenshotManager {
|
|
|
15888
16081
|
}
|
|
15889
16082
|
catch (error) {
|
|
15890
16083
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
15891
|
-
if (
|
|
16084
|
+
if (this.options.debug) {
|
|
15892
16085
|
console.error('📸 modern-screenshot 截图失败:', errorMessage);
|
|
15893
16086
|
console.error('📸 元素信息:', {
|
|
15894
16087
|
width: rect.width,
|
|
@@ -15912,7 +16105,7 @@ class ScreenshotManager {
|
|
|
15912
16105
|
catch (error) {
|
|
15913
16106
|
// 外层错误处理:确保即使发生错误也释放锁
|
|
15914
16107
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
15915
|
-
if (
|
|
16108
|
+
if (this.options.debug) {
|
|
15916
16109
|
console.error('📸 modern-screenshot 截图异常:', errorMessage);
|
|
15917
16110
|
}
|
|
15918
16111
|
throw error;
|
|
@@ -15966,7 +16159,7 @@ class ScreenshotManager {
|
|
|
15966
16159
|
link.crossOrigin = 'anonymous';
|
|
15967
16160
|
document.head.appendChild(link);
|
|
15968
16161
|
this.preconnected = true;
|
|
15969
|
-
if (
|
|
16162
|
+
if (this.options.debug) {
|
|
15970
16163
|
console.log(`📸 ✅ 已预连接代理服务器: ${proxyOrigin}`);
|
|
15971
16164
|
}
|
|
15972
16165
|
}
|
|
@@ -16091,7 +16284,7 @@ class ScreenshotManager {
|
|
|
16091
16284
|
if (networkImages.length === 0) {
|
|
16092
16285
|
return;
|
|
16093
16286
|
}
|
|
16094
|
-
if (
|
|
16287
|
+
if (this.options.debug) {
|
|
16095
16288
|
const totalImages = images.length;
|
|
16096
16289
|
console.log(`📸 发现 ${networkImages.length}/${totalImages} 个可视区域内的跨域图片,开始并行预加载...`);
|
|
16097
16290
|
}
|
|
@@ -16099,7 +16292,7 @@ class ScreenshotManager {
|
|
|
16099
16292
|
// 只有当 useProxy 为 true 且 proxyUrl 存在时才使用代理
|
|
16100
16293
|
const shouldUseProxy = this.options.useProxy && this.options.proxyUrl && this.options.proxyUrl.trim() !== '';
|
|
16101
16294
|
if (shouldUseProxy) {
|
|
16102
|
-
if (
|
|
16295
|
+
if (this.options.debug) {
|
|
16103
16296
|
console.log(`📸 使用代理服务器处理跨域图片: ${this.options.proxyUrl}`);
|
|
16104
16297
|
}
|
|
16105
16298
|
// 优化:增加并发数,使用更大的批次
|
|
@@ -16127,13 +16320,13 @@ class ScreenshotManager {
|
|
|
16127
16320
|
}
|
|
16128
16321
|
catch (error) {
|
|
16129
16322
|
// 静默失败,不影响其他图片
|
|
16130
|
-
if (
|
|
16323
|
+
if (this.options.debug) {
|
|
16131
16324
|
console.warn(`📸 ❌ 代理预加载失败: ${originalSrc.substring(0, 50)}...`);
|
|
16132
16325
|
}
|
|
16133
16326
|
}
|
|
16134
16327
|
}));
|
|
16135
16328
|
}));
|
|
16136
|
-
if (
|
|
16329
|
+
if (this.options.debug) {
|
|
16137
16330
|
console.log(`📸 ✅ 预处理完成,缓存了 ${this.imageProxyCache.size} 个代理图片`);
|
|
16138
16331
|
}
|
|
16139
16332
|
}
|
|
@@ -16158,7 +16351,7 @@ class ScreenshotManager {
|
|
|
16158
16351
|
// 移除末尾的 ? 或 &(如果有)
|
|
16159
16352
|
baseUrl = baseUrl.replace(/[?&]$/, '');
|
|
16160
16353
|
const requestUrl = `${baseUrl}?${params.toString()}`;
|
|
16161
|
-
if (
|
|
16354
|
+
if (this.options.debug) {
|
|
16162
16355
|
console.log(`📸 🔄 代理请求 URL: ${requestUrl.substring(0, 200)}...`);
|
|
16163
16356
|
}
|
|
16164
16357
|
// 请求代理服务器
|
|
@@ -16177,7 +16370,7 @@ class ScreenshotManager {
|
|
|
16177
16370
|
const blob = await response.blob();
|
|
16178
16371
|
// 将 blob 转换为 data URL(用于 modern-screenshot 兼容性)
|
|
16179
16372
|
const dataUrl = await this.blobToDataUrl(blob);
|
|
16180
|
-
if (
|
|
16373
|
+
if (this.options.debug) {
|
|
16181
16374
|
console.log(`📸 ✅ 代理模式成功(已转换为 data URL): ${imageUrl.substring(0, 100)}...`);
|
|
16182
16375
|
}
|
|
16183
16376
|
return dataUrl;
|
|
@@ -16208,7 +16401,7 @@ class ScreenshotManager {
|
|
|
16208
16401
|
if (sizeMB > maxSizeMB) {
|
|
16209
16402
|
if (this.options.skipLargeImages) {
|
|
16210
16403
|
// 跳过过大的图片,返回占位符
|
|
16211
|
-
if (
|
|
16404
|
+
if (this.options.debug) {
|
|
16212
16405
|
console.warn(`📸 ⚠️ 跳过过大图片(${sizeMB.toFixed(2)}MB > ${maxSizeMB}MB): ${url.substring(0, 100)}...`);
|
|
16213
16406
|
}
|
|
16214
16407
|
// 返回一个 1x1 的透明占位符,避免截图失败
|
|
@@ -16216,7 +16409,7 @@ class ScreenshotManager {
|
|
|
16216
16409
|
}
|
|
16217
16410
|
else {
|
|
16218
16411
|
// 不跳过,但添加警告
|
|
16219
|
-
if (
|
|
16412
|
+
if (this.options.debug) {
|
|
16220
16413
|
console.warn(`📸 ⚠️ 图片较大(${sizeMB.toFixed(2)}MB),可能导致内存问题: ${url.substring(0, 100)}...`);
|
|
16221
16414
|
}
|
|
16222
16415
|
}
|
|
@@ -16230,14 +16423,14 @@ class ScreenshotManager {
|
|
|
16230
16423
|
if (blobSizeMB > maxSizeMB) {
|
|
16231
16424
|
if (this.options.skipLargeImages) {
|
|
16232
16425
|
// 跳过过大的图片,返回占位符
|
|
16233
|
-
if (
|
|
16426
|
+
if (this.options.debug) {
|
|
16234
16427
|
console.warn(`📸 ⚠️ 跳过过大图片(实际大小 ${blobSizeMB.toFixed(2)}MB > ${maxSizeMB}MB): ${url.substring(0, 100)}...`);
|
|
16235
16428
|
}
|
|
16236
16429
|
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
|
|
16237
16430
|
}
|
|
16238
16431
|
else {
|
|
16239
16432
|
// 不跳过,但添加警告
|
|
16240
|
-
if (
|
|
16433
|
+
if (this.options.debug) {
|
|
16241
16434
|
console.warn(`📸 ⚠️ 图片较大(实际大小 ${blobSizeMB.toFixed(2)}MB),可能导致内存问题: ${url.substring(0, 100)}...`);
|
|
16242
16435
|
}
|
|
16243
16436
|
}
|
|
@@ -16253,7 +16446,7 @@ class ScreenshotManager {
|
|
|
16253
16446
|
}
|
|
16254
16447
|
catch (error) {
|
|
16255
16448
|
// 下载失败,返回原 URL,让 modern-screenshot 自己处理
|
|
16256
|
-
if (
|
|
16449
|
+
if (this.options.debug) {
|
|
16257
16450
|
console.warn(`📸 ⚠️ 下载图片失败: ${url.substring(0, 100)}...`, error);
|
|
16258
16451
|
}
|
|
16259
16452
|
return url;
|
|
@@ -16684,7 +16877,7 @@ class ScreenshotManager {
|
|
|
16684
16877
|
// 更新历史记录为压缩后的数据
|
|
16685
16878
|
this.screenshotHistory[this.screenshotHistory.length - 1] = compressed.dataUrl;
|
|
16686
16879
|
// 打印压缩统计信息和 base64 对比
|
|
16687
|
-
if (
|
|
16880
|
+
if (this.options.debug) {
|
|
16688
16881
|
if (compressed.error) {
|
|
16689
16882
|
// 压缩失败,使用原始数据
|
|
16690
16883
|
console.warn('📸 [Worker 压缩] ⚠️ 压缩失败,使用原始截图');
|
|
@@ -16738,7 +16931,7 @@ class ScreenshotManager {
|
|
|
16738
16931
|
};
|
|
16739
16932
|
newWorker.onerror = (e) => {
|
|
16740
16933
|
console.error('📸 WebWorker 错误:', e);
|
|
16741
|
-
if (
|
|
16934
|
+
if (this.options.debug) {
|
|
16742
16935
|
console.warn('📸 Worker 压缩失败,使用原始截图');
|
|
16743
16936
|
}
|
|
16744
16937
|
// Worker 发生错误时,如果配置了二进制模式,发送原始截图
|
|
@@ -16754,7 +16947,7 @@ class ScreenshotManager {
|
|
|
16754
16947
|
data: combinedBuffer
|
|
16755
16948
|
};
|
|
16756
16949
|
this.sendToIframeCallback(message);
|
|
16757
|
-
if (
|
|
16950
|
+
if (this.options.debug) {
|
|
16758
16951
|
console.log('📸 [Worker 错误] ✅ 原始截图已发送到 iframe');
|
|
16759
16952
|
}
|
|
16760
16953
|
}
|
|
@@ -16919,7 +17112,7 @@ class ScreenshotManager {
|
|
|
16919
17112
|
async takeScreenshotAndSendBinary(config) {
|
|
16920
17113
|
// 如果已经在运行,先停止再重新开始
|
|
16921
17114
|
if (this.isRunning) {
|
|
16922
|
-
if (
|
|
17115
|
+
if (this.options.debug) {
|
|
16923
17116
|
console.log(`📸 更新轮询间隔: ${this.dynamicInterval || this.options.interval}ms`);
|
|
16924
17117
|
}
|
|
16925
17118
|
this.stopScreenshot();
|
|
@@ -16950,7 +17143,7 @@ class ScreenshotManager {
|
|
|
16950
17143
|
// 验证:base64Data(从 latestScreenshot 提取)和 imageBufferBase64(从 imageBuffer 转换)应该一致
|
|
16951
17144
|
const isBase64Same = base64Data === imageBufferBase64;
|
|
16952
17145
|
// 打印 imageBuffer 的 base64 编码(用于和接收端对比)
|
|
16953
|
-
if (
|
|
17146
|
+
if (this.options.debug) {
|
|
16954
17147
|
console.log('📸 [发送前] 数据流程分析:');
|
|
16955
17148
|
console.log(` latestScreenshot: ${latestScreenshot.substring(0, 50)}... (原始 data URL)`);
|
|
16956
17149
|
console.log(` base64Data (从 latestScreenshot 提取): 长度 ${base64Data.length} 字符`);
|
|
@@ -16976,7 +17169,7 @@ class ScreenshotManager {
|
|
|
16976
17169
|
const combinedBuffer = this.combineBinaryData(configBuffer, imageBuffer);
|
|
16977
17170
|
const combinedBufferSize = combinedBuffer.byteLength;
|
|
16978
17171
|
// 打印大小信息
|
|
16979
|
-
if (
|
|
17172
|
+
if (this.options.debug) {
|
|
16980
17173
|
console.log('📸 [大小统计]');
|
|
16981
17174
|
console.log(` Base64 大小: ${base64Size} 字符`);
|
|
16982
17175
|
console.log(` 图片字节大小: ${(imageBufferSize / 1024).toFixed(2)} KB (${imageBufferSize} 字节)`);
|
|
@@ -16990,7 +17183,7 @@ class ScreenshotManager {
|
|
|
16990
17183
|
data: combinedBuffer
|
|
16991
17184
|
};
|
|
16992
17185
|
this.sendToIframeCallback(message);
|
|
16993
|
-
if (
|
|
17186
|
+
if (this.options.debug) {
|
|
16994
17187
|
console.log('📸 [iframe] ✅ 二进制数据已发送到 iframe');
|
|
16995
17188
|
}
|
|
16996
17189
|
}
|
|
@@ -17004,14 +17197,14 @@ class ScreenshotManager {
|
|
|
17004
17197
|
}
|
|
17005
17198
|
}
|
|
17006
17199
|
else {
|
|
17007
|
-
if (
|
|
17200
|
+
if (this.options.debug) {
|
|
17008
17201
|
console.warn('📸 [iframe] 截图完成但未找到截图数据');
|
|
17009
17202
|
}
|
|
17010
17203
|
}
|
|
17011
17204
|
}
|
|
17012
17205
|
else {
|
|
17013
17206
|
// 启用了压缩,等待 Worker 压缩完成后在 onmessage 中自动发送
|
|
17014
|
-
if (
|
|
17207
|
+
if (this.options.debug) {
|
|
17015
17208
|
console.log('📸 [iframe] 等待 Worker 压缩完成后发送到 iframe...');
|
|
17016
17209
|
}
|
|
17017
17210
|
}
|
|
@@ -17041,7 +17234,7 @@ class ScreenshotManager {
|
|
|
17041
17234
|
const base64Data = dataUrl.split(',')[1] || '';
|
|
17042
17235
|
const base64Size = base64Data.length;
|
|
17043
17236
|
// 完整打印发送前的 base64 信息(用于调试)
|
|
17044
|
-
if (
|
|
17237
|
+
if (this.options.debug) {
|
|
17045
17238
|
console.log('📸 [发送前] Base64 信息:');
|
|
17046
17239
|
console.log(` Base64 长度: ${base64Size} 字符`);
|
|
17047
17240
|
console.log(` 📸 [发送前] 完整 Base64:`);
|
|
@@ -17065,7 +17258,7 @@ class ScreenshotManager {
|
|
|
17065
17258
|
const combinedBufferSize = combinedBuffer.byteLength;
|
|
17066
17259
|
const combineTime = performance.now() - combineStartTime;
|
|
17067
17260
|
// 打印大小信息
|
|
17068
|
-
if (
|
|
17261
|
+
if (this.options.debug) {
|
|
17069
17262
|
console.log('📸 [压缩后-大小统计]');
|
|
17070
17263
|
console.log(` Base64 大小: ${base64Size} 字符`);
|
|
17071
17264
|
console.log(` 图片字节大小: ${(imageBufferSize / 1024).toFixed(2)} KB (${imageBufferSize} 字节)`);
|
|
@@ -17088,7 +17281,7 @@ class ScreenshotManager {
|
|
|
17088
17281
|
if (scheduleStartTime) {
|
|
17089
17282
|
totalTime = performance.now() - scheduleStartTime;
|
|
17090
17283
|
}
|
|
17091
|
-
if (
|
|
17284
|
+
if (this.options.debug) {
|
|
17092
17285
|
console.log('📸 [压缩后] ✅ 二进制数据已发送到 iframe');
|
|
17093
17286
|
console.log(` ⏱️ 发送回调耗时: ${sendCallbackTime.toFixed(2)}ms`);
|
|
17094
17287
|
console.log(` ⏱️ 发送阶段总耗时: ${totalSendTime.toFixed(2)}ms`);
|
|
@@ -17191,8 +17384,8 @@ class ScreenshotManager {
|
|
|
17191
17384
|
if (newOptions.compress !== undefined) {
|
|
17192
17385
|
this.options.compress = newOptions.compress;
|
|
17193
17386
|
}
|
|
17194
|
-
if (newOptions.
|
|
17195
|
-
this.options.
|
|
17387
|
+
if (newOptions.debug !== undefined) {
|
|
17388
|
+
this.options.debug = newOptions.debug;
|
|
17196
17389
|
}
|
|
17197
17390
|
if (newOptions.proxyUrl !== undefined) {
|
|
17198
17391
|
this.options.proxyUrl = newOptions.proxyUrl;
|
|
@@ -17205,7 +17398,7 @@ class ScreenshotManager {
|
|
|
17205
17398
|
if (this.screenshotContext) {
|
|
17206
17399
|
try {
|
|
17207
17400
|
destroyContext(this.screenshotContext);
|
|
17208
|
-
if (
|
|
17401
|
+
if (this.options.debug) {
|
|
17209
17402
|
console.log(`📸 引擎已从 ${oldEngine} 切换到 ${newEngine},已清理旧 context`);
|
|
17210
17403
|
}
|
|
17211
17404
|
}
|
|
@@ -17214,12 +17407,12 @@ class ScreenshotManager {
|
|
|
17214
17407
|
}
|
|
17215
17408
|
this.screenshotContext = null;
|
|
17216
17409
|
}
|
|
17217
|
-
if (
|
|
17410
|
+
if (this.options.debug) {
|
|
17218
17411
|
console.log(`📸 截图引擎已更新: ${oldEngine} → ${newEngine}`);
|
|
17219
17412
|
console.log(`📸 下次截图时将使用新引擎: ${newEngine}`);
|
|
17220
17413
|
}
|
|
17221
17414
|
}
|
|
17222
|
-
else if (
|
|
17415
|
+
else if (this.options.debug) {
|
|
17223
17416
|
console.log('📸 截图配置已更新');
|
|
17224
17417
|
}
|
|
17225
17418
|
}
|
|
@@ -17319,7 +17512,7 @@ class ScreenshotManager {
|
|
|
17319
17512
|
const itemSizeMB = value.dataUrl.length * 0.75 / (1024 * 1024);
|
|
17320
17513
|
this.imageProxyCache.delete(key);
|
|
17321
17514
|
currentSizeMB -= itemSizeMB;
|
|
17322
|
-
if (
|
|
17515
|
+
if (this.options.debug) {
|
|
17323
17516
|
console.log(`📸 清理内存缓存(超过限制): ${key.substring(0, 50)}...`);
|
|
17324
17517
|
}
|
|
17325
17518
|
}
|
|
@@ -17344,7 +17537,7 @@ class ScreenshotManager {
|
|
|
17344
17537
|
expiredUrls.forEach(url => {
|
|
17345
17538
|
this.imageProxyCache.delete(url);
|
|
17346
17539
|
});
|
|
17347
|
-
if (expiredUrls.length > 0 &&
|
|
17540
|
+
if (expiredUrls.length > 0 && this.options.debug) {
|
|
17348
17541
|
console.log(`📸 清理了 ${expiredUrls.length} 个过期缓存`);
|
|
17349
17542
|
}
|
|
17350
17543
|
}
|
|
@@ -17356,7 +17549,7 @@ class ScreenshotManager {
|
|
|
17356
17549
|
// 每2分钟清理一次过期缓存(从5分钟改为2分钟,更频繁)
|
|
17357
17550
|
setInterval(() => {
|
|
17358
17551
|
this.cleanExpiredCache();
|
|
17359
|
-
if (
|
|
17552
|
+
if (this.options.debug) {
|
|
17360
17553
|
const memoryCacheSize = this.imageProxyCache.size;
|
|
17361
17554
|
const memoryCacheSizeMB = Array.from(this.imageProxyCache.values())
|
|
17362
17555
|
.reduce((sum, cached) => sum + cached.dataUrl.length * 0.75 / (1024 * 1024), 0);
|
|
@@ -20696,16 +20889,17 @@ class CustomerServiceSDK {
|
|
|
20696
20889
|
agent: config.agent,
|
|
20697
20890
|
timestamp: Date.now()
|
|
20698
20891
|
};
|
|
20699
|
-
//
|
|
20892
|
+
// 创建悬浮图标管理器(支持自定义位置和传送目标)
|
|
20700
20893
|
const iconPosition = options?.iconPosition || undefined;
|
|
20701
|
-
|
|
20894
|
+
const iconTarget = options?.target || undefined;
|
|
20895
|
+
this.iconManager = new IconManager(iconPosition, this.debug, iconTarget);
|
|
20702
20896
|
await this.iconManager.show();
|
|
20703
20897
|
// 创建iframe管理器(自动检测设备类型)
|
|
20704
20898
|
this.iframeManager = new IframeManager({
|
|
20705
20899
|
src: iframeUrl,
|
|
20706
|
-
mode: 'auto', //
|
|
20707
|
-
width:
|
|
20708
|
-
height: 600,
|
|
20900
|
+
mode: 'auto', // 自动根据设备类型选择模式(PC弹窗,移动端全屏)
|
|
20901
|
+
width: options?.width || 450, // PC模式宽度(像素,默认450px),移动端不使用
|
|
20902
|
+
height: options?.height || 600, // PC模式高度(像素),移动端不使用(强制全屏)
|
|
20709
20903
|
allowClose: true,
|
|
20710
20904
|
debug: this.debug, // 传递 debug 标志
|
|
20711
20905
|
onMessage: (messageType, _data) => {
|
|
@@ -20717,8 +20911,9 @@ class CustomerServiceSDK {
|
|
|
20717
20911
|
// checkScreenshot 消息由 ScreenshotManager 处理,不需要在这里处理
|
|
20718
20912
|
},
|
|
20719
20913
|
onClose: () => {
|
|
20720
|
-
// iframe
|
|
20914
|
+
// iframe关闭时,清理图标拖动事件监听器,并重新启用图标点击
|
|
20721
20915
|
this.iconManager?.forceCleanupDragEvents();
|
|
20916
|
+
this.iconManager?.enableClick();
|
|
20722
20917
|
},
|
|
20723
20918
|
...options
|
|
20724
20919
|
});
|
|
@@ -20729,16 +20924,18 @@ class CustomerServiceSDK {
|
|
|
20729
20924
|
// 打开iframe时清除红点通知
|
|
20730
20925
|
this.clearNotification();
|
|
20731
20926
|
this.iframeManager?.show();
|
|
20927
|
+
// iframe 打开后,禁用图标点击(防止重复打开)
|
|
20928
|
+
this.iconManager?.disableClick();
|
|
20732
20929
|
});
|
|
20733
20930
|
// 初始化截图管理器(如果启用了截图功能)
|
|
20734
20931
|
if (config.screenshot) {
|
|
20735
20932
|
// 默认截图目标为 document.body,可以通过配置自定义
|
|
20736
20933
|
const targetElement = document.body;
|
|
20737
20934
|
// 传入发送消息到 iframe 的回调函数
|
|
20738
|
-
// 将 debug
|
|
20935
|
+
// 将 debug 配置传递给截图管理器
|
|
20739
20936
|
const screenshotOptions = {
|
|
20740
20937
|
...config.screenshot,
|
|
20741
|
-
|
|
20938
|
+
debug: this.debug // 直接传递 debug 标志
|
|
20742
20939
|
};
|
|
20743
20940
|
this.screenshotManager = new ScreenshotManager(targetElement, screenshotOptions, (data) => {
|
|
20744
20941
|
// 通过 IframeManager 发送消息到 iframe
|
|
@@ -20953,8 +21150,9 @@ class CustomerServiceSDK {
|
|
|
20953
21150
|
return result.visitorId;
|
|
20954
21151
|
}
|
|
20955
21152
|
catch (error) {
|
|
20956
|
-
|
|
20957
|
-
|
|
21153
|
+
if (this.debug) {
|
|
21154
|
+
console.warn('❌ Failed to get device fingerprint, using fallback:', error);
|
|
21155
|
+
}
|
|
20958
21156
|
const fallbackId = 'device_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
|
20959
21157
|
if (this.debug) {
|
|
20960
21158
|
console.log('🆔 Fallback Device ID:', fallbackId);
|