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.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// 直接使用base64字符串,避免打包后路径问题
|
|
2
2
|
const iconImage = 'data:image/png;base64,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';
|
|
3
3
|
class IconManager {
|
|
4
|
-
constructor(position, debug = false) {
|
|
4
|
+
constructor(position, debug = false, target) {
|
|
5
5
|
this.iconElement = null;
|
|
6
6
|
this.badgeElement = null;
|
|
7
7
|
this.onClickCallback = null;
|
|
@@ -13,10 +13,17 @@ class IconManager {
|
|
|
13
13
|
this.iconStartY = 0;
|
|
14
14
|
this.dragMoveHandler = null;
|
|
15
15
|
this.dragEndHandler = null;
|
|
16
|
+
this.checkDragHandler = null; // 临时拖动检测监听器
|
|
17
|
+
this.dragStartHandler = null; // 拖动开始事件监听器
|
|
18
|
+
this.touchStartHandler = null; // 触摸开始事件监听器
|
|
16
19
|
this.iconPosition = null; // 图标位置配置
|
|
17
20
|
this.debug = false; // debug 模式标志
|
|
21
|
+
this.isClickEnabled = true; // 是否允许点击(iframe 打开时禁用)
|
|
22
|
+
this.target = null; // 图标传送目标元素(可以是 HTMLElement 或选择器字符串)
|
|
18
23
|
this.iconPosition = position || null;
|
|
19
24
|
this.debug = debug;
|
|
25
|
+
// 保存 target(可以是 HTMLElement 或字符串选择器)
|
|
26
|
+
this.target = target || null;
|
|
20
27
|
}
|
|
21
28
|
/**
|
|
22
29
|
* 显示悬浮图标
|
|
@@ -30,7 +37,7 @@ class IconManager {
|
|
|
30
37
|
this.iconElement.className = 'customer-sdk-icon';
|
|
31
38
|
// 直接设置样式 - 图标容器
|
|
32
39
|
const defaultStyle = {
|
|
33
|
-
position: '
|
|
40
|
+
position: 'absolute',
|
|
34
41
|
width: '30px',
|
|
35
42
|
height: '30px',
|
|
36
43
|
backgroundColor: 'transparent', // 移除背景色,让图片直接显示
|
|
@@ -39,7 +46,7 @@ class IconManager {
|
|
|
39
46
|
alignItems: 'center',
|
|
40
47
|
justifyContent: 'center',
|
|
41
48
|
cursor: 'pointer',
|
|
42
|
-
zIndex: '
|
|
49
|
+
zIndex: '1000002', // 确保图标始终在最上层(遮罩层 999998,iframe 容器 999999)
|
|
43
50
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
|
|
44
51
|
userSelect: 'none',
|
|
45
52
|
transition: 'transform 0.2s ease',
|
|
@@ -115,8 +122,18 @@ class IconManager {
|
|
|
115
122
|
this.iconElement.appendChild(imgContainer);
|
|
116
123
|
// 添加拖动和点击事件
|
|
117
124
|
this.setupDragEvents();
|
|
118
|
-
//
|
|
119
|
-
|
|
125
|
+
// 添加到目标元素(如果 target 是字符串,需要重新查找,因为可能在初始化时元素还不存在)
|
|
126
|
+
const targetElement = this.getTargetElement();
|
|
127
|
+
if (targetElement) {
|
|
128
|
+
targetElement.appendChild(this.iconElement);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
// 如果目标元素不存在,回退到 document.body
|
|
132
|
+
document.body.appendChild(this.iconElement);
|
|
133
|
+
if (this.debug) {
|
|
134
|
+
console.warn('Target element not found, icon added to document.body');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
120
137
|
if (this.debug) {
|
|
121
138
|
console.log('CustomerSDK icon displayed');
|
|
122
139
|
}
|
|
@@ -266,9 +283,11 @@ class IconManager {
|
|
|
266
283
|
// 绑定事件处理器(用于后续清理)
|
|
267
284
|
this.dragMoveHandler = this.handleDragMove.bind(this);
|
|
268
285
|
this.dragEndHandler = this.handleDragEnd.bind(this);
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
this.iconElement.addEventListener('
|
|
286
|
+
this.dragStartHandler = this.handleDragStart.bind(this);
|
|
287
|
+
// 只在图标上监听开始事件(保存引用以便后续移除)
|
|
288
|
+
this.iconElement.addEventListener('mousedown', this.dragStartHandler);
|
|
289
|
+
this.touchStartHandler = this.handleDragStart.bind(this);
|
|
290
|
+
this.iconElement.addEventListener('touchstart', this.touchStartHandler, { passive: false });
|
|
272
291
|
}
|
|
273
292
|
/**
|
|
274
293
|
* 开始拖动
|
|
@@ -291,12 +310,33 @@ class IconManager {
|
|
|
291
310
|
const rect = this.iconElement.getBoundingClientRect();
|
|
292
311
|
this.iconStartX = rect.left;
|
|
293
312
|
this.iconStartY = rect.top;
|
|
294
|
-
//
|
|
295
|
-
|
|
296
|
-
this.iconElement.style.cursor = 'grabbing';
|
|
313
|
+
// 注意:不要在这里立即移除 transition 和设置 cursor
|
|
314
|
+
// 只有在真正开始拖动时才修改样式,避免点击时图标位置跳动
|
|
297
315
|
// 只在真正开始拖动时添加document事件监听
|
|
298
316
|
// 先添加一个临时的move监听器来检测是否真的在拖动
|
|
299
317
|
const checkDrag = (moveEvent) => {
|
|
318
|
+
// 检测事件目标:如果事件发生在iframe或其他元素上,停止检测拖动
|
|
319
|
+
const target = moveEvent.target;
|
|
320
|
+
if (target && target !== this.iconElement && !this.iconElement?.contains(target)) {
|
|
321
|
+
// 检查是否是iframe相关元素
|
|
322
|
+
const isIframeElement = target.tagName === 'IFRAME' ||
|
|
323
|
+
target.closest('iframe') ||
|
|
324
|
+
target.closest('.customer-sdk-container') ||
|
|
325
|
+
target.closest('.customer-sdk-overlay');
|
|
326
|
+
if (isIframeElement) {
|
|
327
|
+
// 如果事件发生在iframe相关元素上,停止检测并清理监听器
|
|
328
|
+
if (this.checkDragHandler) {
|
|
329
|
+
if ('touches' in moveEvent) {
|
|
330
|
+
document.removeEventListener('touchmove', this.checkDragHandler);
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
document.removeEventListener('mousemove', this.checkDragHandler);
|
|
334
|
+
}
|
|
335
|
+
this.checkDragHandler = null;
|
|
336
|
+
}
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
300
340
|
const moveX = 'touches' in moveEvent ? moveEvent.touches[0].clientX : moveEvent.clientX;
|
|
301
341
|
const moveY = 'touches' in moveEvent ? moveEvent.touches[0].clientY : moveEvent.clientY;
|
|
302
342
|
const deltaX = moveX - this.dragStartX;
|
|
@@ -304,12 +344,20 @@ class IconManager {
|
|
|
304
344
|
// 如果移动距离超过5px,开始拖动
|
|
305
345
|
if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
|
|
306
346
|
this.isDragging = true;
|
|
307
|
-
//
|
|
308
|
-
if (
|
|
309
|
-
|
|
347
|
+
// 只有在真正开始拖动时才移除 transition 和设置 cursor
|
|
348
|
+
if (this.iconElement) {
|
|
349
|
+
this.iconElement.style.transition = 'none';
|
|
350
|
+
this.iconElement.style.cursor = 'grabbing';
|
|
310
351
|
}
|
|
311
|
-
|
|
312
|
-
|
|
352
|
+
// 移除临时监听器
|
|
353
|
+
if (this.checkDragHandler) {
|
|
354
|
+
if ('touches' in moveEvent) {
|
|
355
|
+
document.removeEventListener('touchmove', this.checkDragHandler);
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
document.removeEventListener('mousemove', this.checkDragHandler);
|
|
359
|
+
}
|
|
360
|
+
this.checkDragHandler = null;
|
|
313
361
|
}
|
|
314
362
|
// 添加正式的事件监听器
|
|
315
363
|
if (this.dragMoveHandler && this.dragEndHandler) {
|
|
@@ -324,13 +372,15 @@ class IconManager {
|
|
|
324
372
|
}
|
|
325
373
|
}
|
|
326
374
|
};
|
|
375
|
+
// 保存 checkDrag 引用,以便后续清理
|
|
376
|
+
this.checkDragHandler = checkDrag;
|
|
327
377
|
// 添加临时检测监听器
|
|
328
378
|
if ('touches' in e) {
|
|
329
|
-
document.addEventListener('touchmove',
|
|
379
|
+
document.addEventListener('touchmove', this.checkDragHandler, { passive: false });
|
|
330
380
|
document.addEventListener('touchend', this.dragEndHandler);
|
|
331
381
|
}
|
|
332
382
|
else {
|
|
333
|
-
document.addEventListener('mousemove',
|
|
383
|
+
document.addEventListener('mousemove', this.checkDragHandler);
|
|
334
384
|
document.addEventListener('mouseup', this.dragEndHandler);
|
|
335
385
|
}
|
|
336
386
|
}
|
|
@@ -342,20 +392,9 @@ class IconManager {
|
|
|
342
392
|
return;
|
|
343
393
|
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
|
|
344
394
|
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
|
|
345
|
-
//
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
// 检查是否是iframe相关元素
|
|
349
|
-
const isIframeElement = target.tagName === 'IFRAME' ||
|
|
350
|
-
target.closest('iframe') ||
|
|
351
|
-
target.closest('.customer-sdk-container') ||
|
|
352
|
-
target.closest('.customer-sdk-overlay');
|
|
353
|
-
if (isIframeElement) {
|
|
354
|
-
// 如果事件发生在iframe相关元素上,停止拖动
|
|
355
|
-
this.handleDragEnd();
|
|
356
|
-
return;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
395
|
+
// 注意:拖动过程中不检测 iframe 元素,因为用户可能只是想将图标拖动到 iframe 附近或上方
|
|
396
|
+
// 只有在拖动开始时(checkDrag 阶段)才检测 iframe,防止误判为拖动
|
|
397
|
+
// 一旦开始拖动,就应该允许拖动到任何位置,包括 iframe 上方
|
|
359
398
|
// 计算从拖动开始位置到当前位置的距离
|
|
360
399
|
const deltaX = clientX - this.dragStartX;
|
|
361
400
|
const deltaY = clientY - this.dragStartY;
|
|
@@ -390,10 +429,15 @@ class IconManager {
|
|
|
390
429
|
/**
|
|
391
430
|
* 结束拖动
|
|
392
431
|
*/
|
|
393
|
-
handleDragEnd(
|
|
432
|
+
handleDragEnd(_e) {
|
|
394
433
|
if (!this.iconElement)
|
|
395
434
|
return;
|
|
396
|
-
// 清理document
|
|
435
|
+
// 清理document上的所有事件监听器(包括临时检测监听器)
|
|
436
|
+
if (this.checkDragHandler) {
|
|
437
|
+
document.removeEventListener('mousemove', this.checkDragHandler);
|
|
438
|
+
document.removeEventListener('touchmove', this.checkDragHandler);
|
|
439
|
+
this.checkDragHandler = null;
|
|
440
|
+
}
|
|
397
441
|
if (this.dragMoveHandler) {
|
|
398
442
|
document.removeEventListener('mousemove', this.dragMoveHandler);
|
|
399
443
|
document.removeEventListener('touchmove', this.dragMoveHandler);
|
|
@@ -402,6 +446,8 @@ class IconManager {
|
|
|
402
446
|
document.removeEventListener('mouseup', this.dragEndHandler);
|
|
403
447
|
document.removeEventListener('touchend', this.dragEndHandler);
|
|
404
448
|
}
|
|
449
|
+
// 恢复样式(如果之前被修改过)
|
|
450
|
+
// 注意:如果只是点击(没有拖动),这些样式可能没有被修改,但恢复操作是安全的
|
|
405
451
|
this.iconElement.style.transition = 'transform 0.2s ease';
|
|
406
452
|
this.iconElement.style.cursor = 'pointer';
|
|
407
453
|
// 如果只是点击(没有拖动),触发点击事件
|
|
@@ -414,10 +460,90 @@ class IconManager {
|
|
|
414
460
|
* 处理点击事件
|
|
415
461
|
*/
|
|
416
462
|
handleClick() {
|
|
463
|
+
// 如果点击被禁用(iframe 打开时),不执行点击回调
|
|
464
|
+
if (!this.isClickEnabled) {
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
417
467
|
if (this.onClickCallback) {
|
|
418
468
|
this.onClickCallback();
|
|
419
469
|
}
|
|
420
470
|
}
|
|
471
|
+
/**
|
|
472
|
+
* 禁用点击和拖拽(iframe 打开时调用)
|
|
473
|
+
*/
|
|
474
|
+
disableClick() {
|
|
475
|
+
this.isClickEnabled = false;
|
|
476
|
+
// 移除拖动事件监听器
|
|
477
|
+
if (this.iconElement) {
|
|
478
|
+
if (this.dragStartHandler) {
|
|
479
|
+
this.iconElement.removeEventListener('mousedown', this.dragStartHandler);
|
|
480
|
+
}
|
|
481
|
+
if (this.touchStartHandler) {
|
|
482
|
+
this.iconElement.removeEventListener('touchstart', this.touchStartHandler);
|
|
483
|
+
}
|
|
484
|
+
// 禁用所有鼠标事件(包括点击和拖拽)
|
|
485
|
+
this.iconElement.style.pointerEvents = 'none';
|
|
486
|
+
this.iconElement.style.cursor = 'default';
|
|
487
|
+
}
|
|
488
|
+
// 清理可能正在进行的拖动
|
|
489
|
+
this.forceCleanupDragEvents();
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* 启用点击和拖拽(iframe 关闭时调用)
|
|
493
|
+
*/
|
|
494
|
+
enableClick() {
|
|
495
|
+
this.isClickEnabled = true;
|
|
496
|
+
// 重新添加拖动事件监听器
|
|
497
|
+
if (this.iconElement) {
|
|
498
|
+
if (this.dragStartHandler) {
|
|
499
|
+
this.iconElement.addEventListener('mousedown', this.dragStartHandler);
|
|
500
|
+
}
|
|
501
|
+
if (this.touchStartHandler) {
|
|
502
|
+
this.iconElement.addEventListener('touchstart', this.touchStartHandler, { passive: false });
|
|
503
|
+
}
|
|
504
|
+
// 恢复鼠标事件
|
|
505
|
+
this.iconElement.style.pointerEvents = 'auto';
|
|
506
|
+
this.iconElement.style.cursor = 'pointer';
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
/**
|
|
510
|
+
* 获取目标元素(支持动态查找)
|
|
511
|
+
*/
|
|
512
|
+
getTargetElement() {
|
|
513
|
+
if (!this.target) {
|
|
514
|
+
return document.body;
|
|
515
|
+
}
|
|
516
|
+
// 如果 target 是字符串选择器,每次都需要重新查找(支持动态元素)
|
|
517
|
+
if (typeof this.target === 'string') {
|
|
518
|
+
const element = document.querySelector(this.target);
|
|
519
|
+
if (element) {
|
|
520
|
+
return element;
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
// 如果元素不存在,回退到 document.body
|
|
524
|
+
if (this.debug) {
|
|
525
|
+
console.warn(`Target element not found: ${this.target}, falling back to document.body`);
|
|
526
|
+
}
|
|
527
|
+
return document.body;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
// 如果 target 是 HTMLElement
|
|
531
|
+
if (this.target instanceof HTMLElement) {
|
|
532
|
+
// 检查元素是否还在 DOM 中
|
|
533
|
+
if (document.body.contains(this.target)) {
|
|
534
|
+
return this.target;
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
// 如果元素不在 DOM 中,回退到 document.body
|
|
538
|
+
if (this.debug) {
|
|
539
|
+
console.warn('Target element no longer in DOM, falling back to document.body');
|
|
540
|
+
}
|
|
541
|
+
return document.body;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
// 默认回退到 document.body
|
|
545
|
+
return document.body;
|
|
546
|
+
}
|
|
421
547
|
/**
|
|
422
548
|
* 创建消息徽章(简化版)
|
|
423
549
|
*/
|
|
@@ -483,7 +609,7 @@ class IframeManager {
|
|
|
483
609
|
this.config = {
|
|
484
610
|
src: '',
|
|
485
611
|
mode: 'auto', // 默认自动检测设备类型
|
|
486
|
-
width:
|
|
612
|
+
width: 450, // PC 模式默认宽度
|
|
487
613
|
height: 600,
|
|
488
614
|
allowClose: true,
|
|
489
615
|
...config
|
|
@@ -523,9 +649,9 @@ class IframeManager {
|
|
|
523
649
|
try {
|
|
524
650
|
const actualMode = this.getActualMode();
|
|
525
651
|
const isPC = actualMode === 'popup';
|
|
526
|
-
//
|
|
652
|
+
// PC模式:创建或显示遮罩层
|
|
527
653
|
if (isPC) {
|
|
528
|
-
this.createOverlay();
|
|
654
|
+
this.createOverlay(); // createOverlay 内部会检查是否已存在
|
|
529
655
|
}
|
|
530
656
|
// 显示已创建的容器
|
|
531
657
|
if (this.containerElement) {
|
|
@@ -540,8 +666,18 @@ class IframeManager {
|
|
|
540
666
|
opacity: '1',
|
|
541
667
|
display: 'block'
|
|
542
668
|
});
|
|
543
|
-
//
|
|
544
|
-
|
|
669
|
+
// 关键优化:避免重复移动容器导致 iframe 重新加载
|
|
670
|
+
// 只有当容器不在遮罩层内时才移动,且确保遮罩层在 DOM 中
|
|
671
|
+
if (this.overlayElement) {
|
|
672
|
+
// 如果遮罩层不在 DOM 中,先添加到 DOM
|
|
673
|
+
if (!this.overlayElement.parentNode) {
|
|
674
|
+
document.body.appendChild(this.overlayElement);
|
|
675
|
+
}
|
|
676
|
+
// 只有当容器不在遮罩层内时才移动(避免重复移动导致 iframe 重新加载)
|
|
677
|
+
if (this.containerElement.parentNode !== this.overlayElement) {
|
|
678
|
+
this.overlayElement.appendChild(this.containerElement);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
545
681
|
}
|
|
546
682
|
else {
|
|
547
683
|
// 移动端模式:直接全屏显示,不需要遮罩层
|
|
@@ -578,22 +714,27 @@ class IframeManager {
|
|
|
578
714
|
if (!this.isOpen) {
|
|
579
715
|
return;
|
|
580
716
|
}
|
|
581
|
-
// 隐藏容器但保留DOM
|
|
717
|
+
// 隐藏容器但保留DOM元素(不移动容器,避免 iframe 重新加载)
|
|
582
718
|
if (this.containerElement) {
|
|
583
719
|
Object.assign(this.containerElement.style, {
|
|
584
720
|
visibility: 'hidden',
|
|
585
721
|
opacity: '0',
|
|
586
722
|
display: 'none'
|
|
587
723
|
});
|
|
724
|
+
// 注意:不移动容器,保持容器在当前位置(遮罩层或 body),避免 iframe 重新加载
|
|
588
725
|
}
|
|
589
|
-
//
|
|
726
|
+
// 隐藏遮罩层但不移除(仅PC模式,避免重新创建导致 iframe 重新加载)
|
|
590
727
|
if (this.overlayElement) {
|
|
591
|
-
this.overlayElement.
|
|
592
|
-
|
|
728
|
+
Object.assign(this.overlayElement.style, {
|
|
729
|
+
visibility: 'hidden',
|
|
730
|
+
opacity: '0',
|
|
731
|
+
display: 'none'
|
|
732
|
+
});
|
|
733
|
+
// 不移除遮罩层,下次显示时直接显示即可
|
|
593
734
|
}
|
|
594
735
|
// 恢复body滚动(移动端模式)
|
|
595
|
-
const
|
|
596
|
-
if (
|
|
736
|
+
const actualModeForScroll = this.getActualMode();
|
|
737
|
+
if (actualModeForScroll === 'fullscreen') {
|
|
597
738
|
this.preventBodyScroll(false);
|
|
598
739
|
}
|
|
599
740
|
this.isOpen = false;
|
|
@@ -641,9 +782,29 @@ class IframeManager {
|
|
|
641
782
|
}
|
|
642
783
|
}
|
|
643
784
|
/**
|
|
644
|
-
*
|
|
785
|
+
* 创建遮罩层(PC模式使用)
|
|
645
786
|
*/
|
|
646
787
|
createOverlay() {
|
|
788
|
+
// 如果遮罩层已存在,直接显示即可
|
|
789
|
+
if (this.overlayElement && this.overlayElement.parentNode) {
|
|
790
|
+
Object.assign(this.overlayElement.style, {
|
|
791
|
+
visibility: 'visible',
|
|
792
|
+
opacity: '1',
|
|
793
|
+
display: 'flex'
|
|
794
|
+
});
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
// 如果遮罩层存在但不在 DOM 中,重新添加到 DOM
|
|
798
|
+
if (this.overlayElement && !this.overlayElement.parentNode) {
|
|
799
|
+
document.body.appendChild(this.overlayElement);
|
|
800
|
+
Object.assign(this.overlayElement.style, {
|
|
801
|
+
visibility: 'visible',
|
|
802
|
+
opacity: '1',
|
|
803
|
+
display: 'flex'
|
|
804
|
+
});
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
// 创建新的遮罩层
|
|
647
808
|
this.overlayElement = document.createElement('div');
|
|
648
809
|
this.overlayElement.className = 'customer-sdk-overlay';
|
|
649
810
|
Object.assign(this.overlayElement.style, {
|
|
@@ -659,7 +820,7 @@ class IframeManager {
|
|
|
659
820
|
justifyContent: 'center',
|
|
660
821
|
cursor: this.config.allowClose ? 'pointer' : 'default'
|
|
661
822
|
});
|
|
662
|
-
//
|
|
823
|
+
// 点击遮罩层关闭(只添加一次事件监听器)
|
|
663
824
|
if (this.config.allowClose) {
|
|
664
825
|
this.overlayElement.addEventListener('click', (e) => {
|
|
665
826
|
if (e.target === this.overlayElement) {
|
|
@@ -711,45 +872,59 @@ class IframeManager {
|
|
|
711
872
|
'allow-pointer-lock', // 允许指针锁定
|
|
712
873
|
'allow-storage-access-by-user-activation' // 允许用户激活的存储访问
|
|
713
874
|
].join(' '));
|
|
714
|
-
//
|
|
875
|
+
// 根据设备类型设置模式
|
|
715
876
|
const actualMode = this.getActualMode();
|
|
716
877
|
const isPC = actualMode === 'popup';
|
|
717
878
|
this.iframeElement.scrolling = isPC ? 'auto' : 'no'; // PC显示滚动条,移动端禁用
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
879
|
+
// PC 模式:使用配置的宽度和高度
|
|
880
|
+
// 移动端:使用全屏
|
|
881
|
+
const containerStyles = isPC ? {
|
|
882
|
+
// PC 弹窗模式
|
|
883
|
+
width: `${this.config.width || 450}px`,
|
|
884
|
+
height: `${this.config.height || 600}px`,
|
|
885
|
+
maxWidth: '90vw',
|
|
886
|
+
maxHeight: '90vh',
|
|
723
887
|
backgroundColor: '#ffffff',
|
|
724
|
-
borderRadius:
|
|
725
|
-
boxShadow:
|
|
726
|
-
? '0 20px 40px rgba(0, 0, 0, 0.15)'
|
|
727
|
-
: '0 -4px 16px rgba(0, 0, 0, 0.25)',
|
|
888
|
+
borderRadius: '12px',
|
|
889
|
+
boxShadow: '0 20px 40px rgba(0, 0, 0, 0.15)',
|
|
728
890
|
border: 'none',
|
|
729
891
|
position: 'fixed',
|
|
730
892
|
zIndex: '999999',
|
|
731
|
-
// PC
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
893
|
+
// PC模式:居中显示
|
|
894
|
+
top: '50%',
|
|
895
|
+
left: '50%',
|
|
896
|
+
transform: 'translate(-50%, -50%)',
|
|
897
|
+
overflow: 'hidden',
|
|
898
|
+
// 初始隐藏的关键样式
|
|
899
|
+
visibility: 'hidden',
|
|
900
|
+
opacity: '0',
|
|
901
|
+
display: 'none'
|
|
902
|
+
} : {
|
|
903
|
+
// 移动端全屏模式(强制 100% 宽度和高度)
|
|
904
|
+
width: '100%',
|
|
905
|
+
height: '100%',
|
|
906
|
+
maxWidth: '100%',
|
|
907
|
+
maxHeight: '100%',
|
|
908
|
+
backgroundColor: '#ffffff',
|
|
909
|
+
borderRadius: '12px 12px 0 0',
|
|
910
|
+
boxShadow: '0 -4px 16px rgba(0, 0, 0, 0.25)',
|
|
911
|
+
border: 'none',
|
|
912
|
+
position: 'fixed',
|
|
913
|
+
zIndex: '999999',
|
|
914
|
+
// 全屏模式 - 占满整个屏幕
|
|
915
|
+
top: '0',
|
|
916
|
+
left: '0',
|
|
917
|
+
bottom: '0',
|
|
918
|
+
right: '0',
|
|
919
|
+
transform: 'none',
|
|
920
|
+
overflow: 'hidden',
|
|
746
921
|
// 初始隐藏的关键样式
|
|
747
922
|
visibility: 'hidden',
|
|
748
923
|
opacity: '0',
|
|
749
924
|
display: 'none'
|
|
750
925
|
};
|
|
751
926
|
Object.assign(this.containerElement.style, containerStyles);
|
|
752
|
-
// iframe
|
|
927
|
+
// iframe填充整个容器
|
|
753
928
|
const iframeStyles = {
|
|
754
929
|
width: '100%',
|
|
755
930
|
height: '100%',
|
|
@@ -883,6 +1058,7 @@ class IframeManager {
|
|
|
883
1058
|
}
|
|
884
1059
|
/**
|
|
885
1060
|
* 获取当前显示模式
|
|
1061
|
+
* PC 模式使用弹窗,移动端使用全屏
|
|
886
1062
|
*/
|
|
887
1063
|
getActualMode() {
|
|
888
1064
|
if (this.config.mode === 'auto') {
|
|
@@ -935,9 +1111,14 @@ class IframeManager {
|
|
|
935
1111
|
break;
|
|
936
1112
|
case 'resize_iframe':
|
|
937
1113
|
case 'resize':
|
|
938
|
-
|
|
1114
|
+
// PC模式支持 resize,移动端忽略
|
|
1115
|
+
const actualMode = this.getActualMode();
|
|
1116
|
+
if (actualMode === 'popup' && data.width && data.height) {
|
|
939
1117
|
this.resizeIframe(data.width, data.height);
|
|
940
1118
|
}
|
|
1119
|
+
else if (this.debug) {
|
|
1120
|
+
console.log('Resize request ignored (fullscreen mode)');
|
|
1121
|
+
}
|
|
941
1122
|
break;
|
|
942
1123
|
case 'new-message':
|
|
943
1124
|
// 新消息通知 - 触发回调让外层处理
|
|
@@ -956,12 +1137,12 @@ class IframeManager {
|
|
|
956
1137
|
}
|
|
957
1138
|
}
|
|
958
1139
|
/**
|
|
959
|
-
* 调整iframe
|
|
1140
|
+
* 调整iframe大小(PC模式支持)
|
|
960
1141
|
*/
|
|
961
1142
|
resizeIframe(width, height) {
|
|
962
|
-
if (this.
|
|
963
|
-
this.
|
|
964
|
-
this.
|
|
1143
|
+
if (this.containerElement) {
|
|
1144
|
+
this.containerElement.style.width = `${width}px`;
|
|
1145
|
+
this.containerElement.style.height = `${height}px`;
|
|
965
1146
|
}
|
|
966
1147
|
}
|
|
967
1148
|
}
|
|
@@ -14410,7 +14591,7 @@ class ScreenshotManager {
|
|
|
14410
14591
|
useProxy: options.useProxy ?? true, // 默认启用代理(如果配置了proxyUrl)
|
|
14411
14592
|
engine: options.engine ?? 'modern-screenshot',
|
|
14412
14593
|
corsMode: options.corsMode ?? 'canvas-proxy',
|
|
14413
|
-
|
|
14594
|
+
debug: options.debug !== undefined ? options.debug : false, // 默认 false,不输出日志
|
|
14414
14595
|
maxRetries: options.maxRetries ?? 2,
|
|
14415
14596
|
preloadImages: options.preloadImages ?? false, // 默认不预加载,按需加载
|
|
14416
14597
|
maxConcurrentDownloads: options.maxConcurrentDownloads ?? 10, // 增加并发数
|
|
@@ -14430,9 +14611,9 @@ class ScreenshotManager {
|
|
|
14430
14611
|
this.setupMessageListener();
|
|
14431
14612
|
this.setupVisibilityChangeListener();
|
|
14432
14613
|
// 打印初始化信息
|
|
14433
|
-
if (
|
|
14614
|
+
if (this.options.debug) {
|
|
14434
14615
|
console.log('📸 ScreenshotManager 初始化完成');
|
|
14435
|
-
console.log(`📸 配置: interval=${this.options.interval}ms, engine=${this.options.engine},
|
|
14616
|
+
console.log(`📸 配置: interval=${this.options.interval}ms, engine=${this.options.engine}, debug=${this.options.debug}`);
|
|
14436
14617
|
console.log('📸 等待 iframe 发送 checkScreenshot 消息...');
|
|
14437
14618
|
}
|
|
14438
14619
|
// 启动缓存清理定时器
|
|
@@ -14455,7 +14636,7 @@ class ScreenshotManager {
|
|
|
14455
14636
|
if (this.screenshotContext) {
|
|
14456
14637
|
try {
|
|
14457
14638
|
destroyContext(this.screenshotContext);
|
|
14458
|
-
if (
|
|
14639
|
+
if (this.options.debug) {
|
|
14459
14640
|
console.log('📸 目标元素变化,清理 context');
|
|
14460
14641
|
}
|
|
14461
14642
|
}
|
|
@@ -14478,7 +14659,7 @@ class ScreenshotManager {
|
|
|
14478
14659
|
this.handleIframeMessage(event);
|
|
14479
14660
|
};
|
|
14480
14661
|
window.addEventListener('message', this.messageHandler);
|
|
14481
|
-
if (
|
|
14662
|
+
if (this.options.debug) {
|
|
14482
14663
|
console.log('📸 消息监听器已设置,正在监听 iframe 消息...');
|
|
14483
14664
|
}
|
|
14484
14665
|
}
|
|
@@ -14488,12 +14669,12 @@ class ScreenshotManager {
|
|
|
14488
14669
|
setupVisibilityChangeListener() {
|
|
14489
14670
|
document.addEventListener('visibilitychange', () => {
|
|
14490
14671
|
if (document.hidden) {
|
|
14491
|
-
if (
|
|
14672
|
+
if (this.options.debug) {
|
|
14492
14673
|
console.log('📸 页面隐藏,截图轮询已暂停');
|
|
14493
14674
|
}
|
|
14494
14675
|
}
|
|
14495
14676
|
else {
|
|
14496
|
-
if (
|
|
14677
|
+
if (this.options.debug) {
|
|
14497
14678
|
console.log('📸 页面显示,截图轮询已恢复');
|
|
14498
14679
|
}
|
|
14499
14680
|
}
|
|
@@ -14510,7 +14691,7 @@ class ScreenshotManager {
|
|
|
14510
14691
|
}
|
|
14511
14692
|
// 如果提供了发送消息的回调,保存它(用于后续发送二进制数据)
|
|
14512
14693
|
// 注意:消息来源验证在 setupMessageListener 中处理
|
|
14513
|
-
if (
|
|
14694
|
+
if (this.options.debug) {
|
|
14514
14695
|
console.log('📸 [iframe] 收到消息:', event.data);
|
|
14515
14696
|
}
|
|
14516
14697
|
// 尝试解析为二进制配置(新格式)
|
|
@@ -14524,7 +14705,7 @@ class ScreenshotManager {
|
|
|
14524
14705
|
if (isValid) {
|
|
14525
14706
|
// 启用截图功能
|
|
14526
14707
|
if (!this.isEnabled) {
|
|
14527
|
-
if (
|
|
14708
|
+
if (this.options.debug) {
|
|
14528
14709
|
console.log('📸 [iframe] 启用截图功能(二进制模式)');
|
|
14529
14710
|
}
|
|
14530
14711
|
this.isEnabled = true;
|
|
@@ -14534,7 +14715,7 @@ class ScreenshotManager {
|
|
|
14534
14715
|
// 计算剩余有效时间(毫秒)
|
|
14535
14716
|
const remainingTime = binaryConfig.ttl - currentTime;
|
|
14536
14717
|
// 启动或更新截图轮询
|
|
14537
|
-
if (
|
|
14718
|
+
if (this.options.debug) {
|
|
14538
14719
|
const remainingMinutes = Math.ceil(remainingTime / 60000);
|
|
14539
14720
|
console.log(`📸 [iframe] 设置轮询间隔: ${this.dynamicInterval}ms,剩余有效时间: ${remainingMinutes}分钟`);
|
|
14540
14721
|
}
|
|
@@ -14546,7 +14727,7 @@ class ScreenshotManager {
|
|
|
14546
14727
|
this.expirationTimer = null;
|
|
14547
14728
|
}
|
|
14548
14729
|
this.expirationTimer = setTimeout(() => {
|
|
14549
|
-
if (
|
|
14730
|
+
if (this.options.debug) {
|
|
14550
14731
|
console.log('📸 [iframe] 二进制配置已过期,停止截图');
|
|
14551
14732
|
}
|
|
14552
14733
|
this.stopScreenshot();
|
|
@@ -14557,7 +14738,7 @@ class ScreenshotManager {
|
|
|
14557
14738
|
}
|
|
14558
14739
|
else {
|
|
14559
14740
|
// 禁用截图功能(ttl == 0 或已过期)
|
|
14560
|
-
if (
|
|
14741
|
+
if (this.options.debug) {
|
|
14561
14742
|
if (binaryConfig.ttl === 0) {
|
|
14562
14743
|
console.log('📸 [iframe] ttl == 0,禁用截图功能');
|
|
14563
14744
|
}
|
|
@@ -14576,11 +14757,15 @@ class ScreenshotManager {
|
|
|
14576
14757
|
return;
|
|
14577
14758
|
}
|
|
14578
14759
|
// 如果不是二进制配置格式,记录错误
|
|
14579
|
-
|
|
14760
|
+
if (this.options.debug) {
|
|
14761
|
+
console.error('📸 [iframe] 解析配置失败:未识别的配置格式');
|
|
14762
|
+
}
|
|
14580
14763
|
this.uploadError = '解析配置失败:仅支持二进制配置格式';
|
|
14581
14764
|
}
|
|
14582
14765
|
catch (error) {
|
|
14583
|
-
|
|
14766
|
+
if (this.options.debug) {
|
|
14767
|
+
console.error('📸 [iframe] 处理消息失败:', error);
|
|
14768
|
+
}
|
|
14584
14769
|
this.uploadError = error instanceof Error ? error.message : String(error);
|
|
14585
14770
|
}
|
|
14586
14771
|
}
|
|
@@ -14619,17 +14804,19 @@ class ScreenshotManager {
|
|
|
14619
14804
|
*/
|
|
14620
14805
|
startScreenshot(customInterval) {
|
|
14621
14806
|
if (!this.isEnabled) {
|
|
14622
|
-
|
|
14807
|
+
if (this.options.debug) {
|
|
14808
|
+
console.warn('📸 截图功能已禁用,无法启动');
|
|
14809
|
+
}
|
|
14623
14810
|
return;
|
|
14624
14811
|
}
|
|
14625
14812
|
const currentInterval = customInterval || this.dynamicInterval || this.options.interval;
|
|
14626
14813
|
if (this.isRunning) {
|
|
14627
|
-
if (
|
|
14814
|
+
if (this.options.debug) {
|
|
14628
14815
|
console.log(`📸 更新轮询间隔: ${currentInterval}ms`);
|
|
14629
14816
|
}
|
|
14630
14817
|
this.stopScreenshot();
|
|
14631
14818
|
}
|
|
14632
|
-
if (
|
|
14819
|
+
if (this.options.debug) {
|
|
14633
14820
|
console.log(`📸 开始轮询截图,间隔: ${currentInterval}ms`);
|
|
14634
14821
|
}
|
|
14635
14822
|
this.isRunning = true;
|
|
@@ -14642,7 +14829,7 @@ class ScreenshotManager {
|
|
|
14642
14829
|
const scheduleNext = async () => {
|
|
14643
14830
|
// 如果上次任务还没完成,等待完成
|
|
14644
14831
|
if (!this.isCurrentTaskCompleted) {
|
|
14645
|
-
if (
|
|
14832
|
+
if (this.options.debug) {
|
|
14646
14833
|
console.log('📸 [定时] 等待上次任务完成...');
|
|
14647
14834
|
}
|
|
14648
14835
|
// 每100ms检查一次任务是否完成
|
|
@@ -14662,7 +14849,7 @@ class ScreenshotManager {
|
|
|
14662
14849
|
this.isCurrentTaskCompleted = false;
|
|
14663
14850
|
// 记录定时开始时间
|
|
14664
14851
|
const scheduleStartTime = performance.now();
|
|
14665
|
-
if (
|
|
14852
|
+
if (this.options.debug) {
|
|
14666
14853
|
console.log(`📸 [定时开始] 开始新一轮截图任务`);
|
|
14667
14854
|
}
|
|
14668
14855
|
try {
|
|
@@ -14694,7 +14881,7 @@ class ScreenshotManager {
|
|
|
14694
14881
|
const combinedBufferSize = combinedBuffer.byteLength;
|
|
14695
14882
|
const combineTime = performance.now() - combineStartTime;
|
|
14696
14883
|
// 打印大小信息
|
|
14697
|
-
if (
|
|
14884
|
+
if (this.options.debug) {
|
|
14698
14885
|
console.log('📸 [轮询-大小统计]');
|
|
14699
14886
|
console.log(` Base64 大小: ${base64Size} 字符`);
|
|
14700
14887
|
console.log(` 图片字节大小: ${(imageBufferSize / 1024).toFixed(2)} KB (${imageBufferSize} 字节)`);
|
|
@@ -14715,7 +14902,7 @@ class ScreenshotManager {
|
|
|
14715
14902
|
const sendCallbackTime = performance.now() - sendCallbackStartTime;
|
|
14716
14903
|
const totalSendTime = performance.now() - sendStartTime;
|
|
14717
14904
|
const totalTime = performance.now() - scheduleStartTime;
|
|
14718
|
-
if (
|
|
14905
|
+
if (this.options.debug) {
|
|
14719
14906
|
console.log('📸 [轮询] ✅ 二进制数据已发送到 iframe');
|
|
14720
14907
|
console.log(` ⏱️ 发送回调耗时: ${sendCallbackTime.toFixed(2)}ms`);
|
|
14721
14908
|
console.log(` ⏱️ 发送阶段总耗时: ${totalSendTime.toFixed(2)}ms`);
|
|
@@ -14724,7 +14911,9 @@ class ScreenshotManager {
|
|
|
14724
14911
|
}
|
|
14725
14912
|
}
|
|
14726
14913
|
catch (error) {
|
|
14727
|
-
|
|
14914
|
+
if (this.options.debug) {
|
|
14915
|
+
console.error('📸 [轮询] ❌ 处理二进制数据失败:', error);
|
|
14916
|
+
}
|
|
14728
14917
|
}
|
|
14729
14918
|
}
|
|
14730
14919
|
// 任务完成(无压缩模式)
|
|
@@ -14733,7 +14922,7 @@ class ScreenshotManager {
|
|
|
14733
14922
|
else if (this.currentBinaryConfig && this.options.compress) {
|
|
14734
14923
|
// 启用了压缩,等待 Worker 压缩完成后在 onmessage 中发送
|
|
14735
14924
|
// 任务完成标志会在压缩完成的回调中设置
|
|
14736
|
-
if (
|
|
14925
|
+
if (this.options.debug) {
|
|
14737
14926
|
console.log('📸 [轮询] 等待 Worker 压缩完成后发送到 iframe...');
|
|
14738
14927
|
}
|
|
14739
14928
|
}
|
|
@@ -14743,7 +14932,7 @@ class ScreenshotManager {
|
|
|
14743
14932
|
}
|
|
14744
14933
|
}
|
|
14745
14934
|
catch (error) {
|
|
14746
|
-
if (
|
|
14935
|
+
if (this.options.debug) {
|
|
14747
14936
|
console.error('📸 [轮询] 截图失败:', error);
|
|
14748
14937
|
}
|
|
14749
14938
|
// 任务失败,标记为完成
|
|
@@ -14768,7 +14957,7 @@ class ScreenshotManager {
|
|
|
14768
14957
|
if (!this.isRunning) {
|
|
14769
14958
|
return;
|
|
14770
14959
|
}
|
|
14771
|
-
if (
|
|
14960
|
+
if (this.options.debug) {
|
|
14772
14961
|
console.log('📸 停止轮询截图');
|
|
14773
14962
|
}
|
|
14774
14963
|
this.isRunning = false;
|
|
@@ -14785,7 +14974,9 @@ class ScreenshotManager {
|
|
|
14785
14974
|
*/
|
|
14786
14975
|
async captureOnce(force = false) {
|
|
14787
14976
|
if (!this.isEnabled && !force) {
|
|
14788
|
-
|
|
14977
|
+
if (this.options.debug) {
|
|
14978
|
+
console.warn('📸 截图功能已禁用,无法执行截图。如需测试,请先调用 enable(true) 启用截图功能');
|
|
14979
|
+
}
|
|
14789
14980
|
return false;
|
|
14790
14981
|
}
|
|
14791
14982
|
return await this.takeScreenshot();
|
|
@@ -14795,14 +14986,16 @@ class ScreenshotManager {
|
|
|
14795
14986
|
*/
|
|
14796
14987
|
async takeScreenshot(scheduleStartTime) {
|
|
14797
14988
|
if (!this.targetElement) {
|
|
14798
|
-
|
|
14989
|
+
if (this.options.debug) {
|
|
14990
|
+
console.warn('📸 目标元素不存在');
|
|
14991
|
+
}
|
|
14799
14992
|
return false;
|
|
14800
14993
|
}
|
|
14801
14994
|
// 记录截图开始时间
|
|
14802
14995
|
const screenshotStartTime = performance.now();
|
|
14803
14996
|
this.setupGlobalErrorHandlers();
|
|
14804
14997
|
try {
|
|
14805
|
-
if (
|
|
14998
|
+
if (this.options.debug) {
|
|
14806
14999
|
console.log(`📸 开始截图 #${this.screenshotCount + 1}...`);
|
|
14807
15000
|
if (scheduleStartTime) {
|
|
14808
15001
|
const waitTime = screenshotStartTime - scheduleStartTime;
|
|
@@ -14816,7 +15009,7 @@ class ScreenshotManager {
|
|
|
14816
15009
|
this.waitForFonts()
|
|
14817
15010
|
]);
|
|
14818
15011
|
const waitStylesTime = performance.now() - waitStylesStartTime;
|
|
14819
|
-
if (
|
|
15012
|
+
if (this.options.debug) {
|
|
14820
15013
|
console.log(` ⏱️ 等待样式和字体加载耗时: ${waitStylesTime.toFixed(2)}ms`);
|
|
14821
15014
|
}
|
|
14822
15015
|
// 等待元素完全渲染(特别是对于 modern-screenshot)
|
|
@@ -14825,12 +15018,12 @@ class ScreenshotManager {
|
|
|
14825
15018
|
requestAnimationFrame(() => resolve());
|
|
14826
15019
|
}));
|
|
14827
15020
|
const waitRenderTime = performance.now() - waitRenderStartTime;
|
|
14828
|
-
if (
|
|
15021
|
+
if (this.options.debug) {
|
|
14829
15022
|
console.log(` ⏱️ 等待渲染完成耗时: ${waitRenderTime.toFixed(2)}ms`);
|
|
14830
15023
|
}
|
|
14831
15024
|
// 选择截图引擎
|
|
14832
15025
|
const selectedEngine = this.options.engine || 'modern-screenshot';
|
|
14833
|
-
if (
|
|
15026
|
+
if (this.options.debug) {
|
|
14834
15027
|
console.log(`📸 使用截图引擎: ${selectedEngine}`);
|
|
14835
15028
|
}
|
|
14836
15029
|
// 优化:如果启用预加载,才预处理图片;否则让引擎按需加载
|
|
@@ -14870,7 +15063,7 @@ class ScreenshotManager {
|
|
|
14870
15063
|
dataUrl = await this.takeScreenshotWithModernScreenshot(this.targetElement);
|
|
14871
15064
|
}
|
|
14872
15065
|
const engineTime = performance.now() - engineStartTime;
|
|
14873
|
-
if (
|
|
15066
|
+
if (this.options.debug) {
|
|
14874
15067
|
console.log(` ⏱️ 截图引擎执行耗时: ${engineTime.toFixed(2)}ms`);
|
|
14875
15068
|
}
|
|
14876
15069
|
const timestamp = Date.now();
|
|
@@ -14884,7 +15077,7 @@ class ScreenshotManager {
|
|
|
14884
15077
|
const removed = this.screenshotHistory.shift();
|
|
14885
15078
|
// 强制 GC(如果可能)
|
|
14886
15079
|
if (removed && removed.length > 1000000) { // 大于1MB的字符串
|
|
14887
|
-
if (
|
|
15080
|
+
if (this.options.debug) {
|
|
14888
15081
|
console.log(`📸 清理旧截图,释放内存: ${Math.round(removed.length * 0.75 / 1024)} KB`);
|
|
14889
15082
|
}
|
|
14890
15083
|
}
|
|
@@ -14898,7 +15091,7 @@ class ScreenshotManager {
|
|
|
14898
15091
|
if (removed) {
|
|
14899
15092
|
const removedSize = removed.length;
|
|
14900
15093
|
totalSize -= removedSize;
|
|
14901
|
-
if (
|
|
15094
|
+
if (this.options.debug) {
|
|
14902
15095
|
console.warn(`📸 ⚠️ 历史记录总大小超过限制,清理最旧截图: ${Math.round(removedSize * 0.75 / 1024)} KB`);
|
|
14903
15096
|
}
|
|
14904
15097
|
}
|
|
@@ -14910,7 +15103,7 @@ class ScreenshotManager {
|
|
|
14910
15103
|
const screenshotTotalTime = performance.now() - screenshotStartTime;
|
|
14911
15104
|
// 打印基本信息
|
|
14912
15105
|
const base64Data = dataUrl.split(',')[1] || '';
|
|
14913
|
-
if (
|
|
15106
|
+
if (this.options.debug) {
|
|
14914
15107
|
console.log('📸 截图完成:');
|
|
14915
15108
|
console.log(`📸 编号: #${this.screenshotCount}`);
|
|
14916
15109
|
console.log(`📸 时间: ${new Date(timestamp).toLocaleTimeString()}`);
|
|
@@ -14929,7 +15122,7 @@ class ScreenshotManager {
|
|
|
14929
15122
|
// 确保 Worker 已创建
|
|
14930
15123
|
if (!this.worker) {
|
|
14931
15124
|
this.worker = this.createWorker();
|
|
14932
|
-
if (
|
|
15125
|
+
if (this.options.debug) {
|
|
14933
15126
|
if (this.worker) {
|
|
14934
15127
|
console.log('📸 Worker 已创建,准备压缩');
|
|
14935
15128
|
}
|
|
@@ -14941,7 +15134,7 @@ class ScreenshotManager {
|
|
|
14941
15134
|
if (this.worker) {
|
|
14942
15135
|
// 记录压缩开始时间
|
|
14943
15136
|
const compressStartTime = performance.now();
|
|
14944
|
-
if (
|
|
15137
|
+
if (this.options.debug) {
|
|
14945
15138
|
console.log('📸 发送到 WebWorker 进行压缩...');
|
|
14946
15139
|
}
|
|
14947
15140
|
// 保存原始 dataUrl 用于后续对比(在 Worker 压缩完成后)
|
|
@@ -14964,7 +15157,7 @@ class ScreenshotManager {
|
|
|
14964
15157
|
}
|
|
14965
15158
|
else {
|
|
14966
15159
|
// Worker 不可用,如果配置了二进制模式,直接发送原始截图
|
|
14967
|
-
if (
|
|
15160
|
+
if (this.options.debug) {
|
|
14968
15161
|
console.warn('📸 ⚠️ Worker 不可用,跳过压缩(使用原始截图)');
|
|
14969
15162
|
}
|
|
14970
15163
|
// Worker 不可用时,如果配置了二进制模式,立即发送原始截图
|
|
@@ -14980,7 +15173,7 @@ class ScreenshotManager {
|
|
|
14980
15173
|
data: combinedBuffer
|
|
14981
15174
|
};
|
|
14982
15175
|
this.sendToIframeCallback(message);
|
|
14983
|
-
if (
|
|
15176
|
+
if (this.options.debug) {
|
|
14984
15177
|
console.log('📸 [Worker 不可用] ✅ 原始截图已发送到 iframe');
|
|
14985
15178
|
}
|
|
14986
15179
|
}
|
|
@@ -15005,7 +15198,7 @@ class ScreenshotManager {
|
|
|
15005
15198
|
console.error('📸 截图失败:', err);
|
|
15006
15199
|
this.error = errorMessage;
|
|
15007
15200
|
}
|
|
15008
|
-
else if (
|
|
15201
|
+
else if (this.options.debug) {
|
|
15009
15202
|
console.warn('📸 截图遇到跨域问题(已忽略)');
|
|
15010
15203
|
}
|
|
15011
15204
|
return false;
|
|
@@ -15022,7 +15215,7 @@ class ScreenshotManager {
|
|
|
15022
15215
|
* 注意:snapdom 内部使用 worker 进行截图处理,会在后台线程执行,不会阻塞主线程
|
|
15023
15216
|
*/
|
|
15024
15217
|
async takeScreenshotWithSnapdom(element) {
|
|
15025
|
-
if (
|
|
15218
|
+
if (this.options.debug) {
|
|
15026
15219
|
console.log('📸 使用 snapdom 引擎截图...');
|
|
15027
15220
|
}
|
|
15028
15221
|
// 限制只截图可见区域(viewport),避免截图不可见区域
|
|
@@ -15062,7 +15255,7 @@ class ScreenshotManager {
|
|
|
15062
15255
|
// 简化方案:直接使用 body,但添加尺寸限制选项(如果 snapdom 支持)
|
|
15063
15256
|
// 如果不支持,则只能截图整个 body
|
|
15064
15257
|
targetElement = element;
|
|
15065
|
-
if (
|
|
15258
|
+
if (this.options.debug) {
|
|
15066
15259
|
console.log(`📸 注意:snapdom 将截图整个 body,建议使用 html2canvas 或 modern-screenshot 来限制可见区域`);
|
|
15067
15260
|
console.log(`📸 可见区域尺寸: ${window.innerWidth}x${window.innerHeight}`);
|
|
15068
15261
|
}
|
|
@@ -15079,7 +15272,7 @@ class ScreenshotManager {
|
|
|
15079
15272
|
elementRect.left >= 0 &&
|
|
15080
15273
|
elementRect.bottom <= window.innerHeight &&
|
|
15081
15274
|
elementRect.right <= window.innerWidth;
|
|
15082
|
-
if (!isFullyVisible &&
|
|
15275
|
+
if (!isFullyVisible && this.options.debug) {
|
|
15083
15276
|
console.warn(`📸 ⚠️ 元素部分不可见,snapdom 可能会截图整个元素(包括不可见部分)`);
|
|
15084
15277
|
console.warn(`📸 建议:使用 html2canvas 或 modern-screenshot 来限制可见区域`);
|
|
15085
15278
|
}
|
|
@@ -15097,12 +15290,12 @@ class ScreenshotManager {
|
|
|
15097
15290
|
proxyUrl = proxyUrl.endsWith('?') ? proxyUrl + 'url=' : proxyUrl + '?url=';
|
|
15098
15291
|
}
|
|
15099
15292
|
options.useProxy = proxyUrl;
|
|
15100
|
-
if (
|
|
15293
|
+
if (this.options.debug) {
|
|
15101
15294
|
console.log(`📸 使用代理服务器处理跨域图片: ${proxyUrl}`);
|
|
15102
15295
|
}
|
|
15103
15296
|
}
|
|
15104
15297
|
else {
|
|
15105
|
-
if (
|
|
15298
|
+
if (this.options.debug) {
|
|
15106
15299
|
if (!this.options.useProxy) {
|
|
15107
15300
|
console.log('📸 代理功能已禁用(useProxy: false)');
|
|
15108
15301
|
}
|
|
@@ -15144,7 +15337,7 @@ class ScreenshotManager {
|
|
|
15144
15337
|
parent.removeChild(container);
|
|
15145
15338
|
}
|
|
15146
15339
|
}
|
|
15147
|
-
if (
|
|
15340
|
+
if (this.options.debug) {
|
|
15148
15341
|
console.log(`📸 snapdom 截图成功!格式: ${outputFormat}, 尺寸: ${img.width}x${img.height}`);
|
|
15149
15342
|
}
|
|
15150
15343
|
return dataUrl;
|
|
@@ -15162,14 +15355,14 @@ class ScreenshotManager {
|
|
|
15162
15355
|
const errorName = error instanceof Error ? error.name : 'Unknown';
|
|
15163
15356
|
// 针对不同类型的错误给出具体提示
|
|
15164
15357
|
if (errorName === 'EncodingError' || errorMessage.includes('cannot be decoded')) {
|
|
15165
|
-
if (
|
|
15358
|
+
if (this.options.debug) {
|
|
15166
15359
|
console.warn('📸 ⚠️ 图片解码失败 - 这通常是因为跨域图片无法访问');
|
|
15167
15360
|
console.warn('📸 💡 解决方案:配置 proxyUrl 选项');
|
|
15168
15361
|
console.warn('📸 📖 参考: https://snapdom.dev/#cors');
|
|
15169
15362
|
}
|
|
15170
15363
|
}
|
|
15171
15364
|
else if (errorMessage.includes('CORS') || errorMessage.includes('cross-origin')) {
|
|
15172
|
-
if (
|
|
15365
|
+
if (this.options.debug) {
|
|
15173
15366
|
console.warn('📸 ⚠️ 检测到 CORS 错误,建议配置 proxyUrl 选项');
|
|
15174
15367
|
}
|
|
15175
15368
|
}
|
|
@@ -15194,7 +15387,7 @@ class ScreenshotManager {
|
|
|
15194
15387
|
* - 需要快速截图
|
|
15195
15388
|
*/
|
|
15196
15389
|
async takeScreenshotWithHtml2Canvas(element) {
|
|
15197
|
-
if (
|
|
15390
|
+
if (this.options.debug) {
|
|
15198
15391
|
console.log('📸 使用 html2canvas 引擎截图...');
|
|
15199
15392
|
}
|
|
15200
15393
|
try {
|
|
@@ -15248,7 +15441,7 @@ class ScreenshotManager {
|
|
|
15248
15441
|
: (isMobile ? 0.5 : 0.6), // 用户未配置,使用默认值
|
|
15249
15442
|
useCORS: this.options.enableCORS,
|
|
15250
15443
|
allowTaint: !this.options.enableCORS, // 如果启用 CORS,不允许 taint
|
|
15251
|
-
logging:
|
|
15444
|
+
logging: this.options.debug, // 使用配置的静默模式
|
|
15252
15445
|
// foreignObjectRendering: true, // ❌ 移除:可能导致跨域图片不显示
|
|
15253
15446
|
// removeContainer: true, // ❌ 移除:移到后面,避免重复定义
|
|
15254
15447
|
// 不设置 width 和 height,让 html2canvas 自动计算
|
|
@@ -15386,12 +15579,12 @@ class ScreenshotManager {
|
|
|
15386
15579
|
}
|
|
15387
15580
|
catch (e) {
|
|
15388
15581
|
// 忽略内联化错误
|
|
15389
|
-
if (
|
|
15582
|
+
if (this.options.debug) {
|
|
15390
15583
|
console.warn('📸 iOS 样式内联化失败:', e);
|
|
15391
15584
|
}
|
|
15392
15585
|
}
|
|
15393
15586
|
}
|
|
15394
|
-
if (
|
|
15587
|
+
if (this.options.debug) {
|
|
15395
15588
|
const styleLinks = clonedDoc.querySelectorAll('link[rel="stylesheet"]').length;
|
|
15396
15589
|
const styleTags = clonedDoc.querySelectorAll('style').length;
|
|
15397
15590
|
console.log(`📸 onclone: 已复制 ${styleLinks} 个样式表链接和 ${styleTags} 个内联样式标签${isIOS ? ' (iOS 模式:已内联化计算样式)' : ''}`);
|
|
@@ -15440,7 +15633,7 @@ class ScreenshotManager {
|
|
|
15440
15633
|
const cachedDataUrl = this.getCachedImage(originalSrc);
|
|
15441
15634
|
if (cachedDataUrl) {
|
|
15442
15635
|
img.src = cachedDataUrl;
|
|
15443
|
-
if (
|
|
15636
|
+
if (this.options.debug) {
|
|
15444
15637
|
console.log(`📸 使用缓存的图片: ${originalSrc.substring(0, 50)}...`);
|
|
15445
15638
|
}
|
|
15446
15639
|
return;
|
|
@@ -15449,7 +15642,7 @@ class ScreenshotManager {
|
|
|
15449
15642
|
// 构建代理 URL
|
|
15450
15643
|
try {
|
|
15451
15644
|
if (!this.options.proxyUrl) {
|
|
15452
|
-
if (
|
|
15645
|
+
if (this.options.debug) {
|
|
15453
15646
|
console.warn(`📸 ⚠️ 未配置代理 URL,跨域图片可能无法显示: ${originalSrc}`);
|
|
15454
15647
|
}
|
|
15455
15648
|
return;
|
|
@@ -15462,19 +15655,19 @@ class ScreenshotManager {
|
|
|
15462
15655
|
baseUrl = baseUrl.replace(/[?&]$/, '');
|
|
15463
15656
|
const proxyUrl = `${baseUrl}?${params.toString()}`;
|
|
15464
15657
|
img.src = proxyUrl;
|
|
15465
|
-
if (
|
|
15658
|
+
if (this.options.debug) {
|
|
15466
15659
|
console.log(`📸 使用代理 URL 替换跨域图片: ${originalSrc.substring(0, 50)}... -> ${proxyUrl.substring(0, 50)}...`);
|
|
15467
15660
|
}
|
|
15468
15661
|
}
|
|
15469
15662
|
catch (error) {
|
|
15470
|
-
if (
|
|
15663
|
+
if (this.options.debug) {
|
|
15471
15664
|
console.warn(`📸 ⚠️ 处理图片 URL 失败: ${originalSrc}`, error);
|
|
15472
15665
|
}
|
|
15473
15666
|
}
|
|
15474
15667
|
});
|
|
15475
15668
|
};
|
|
15476
15669
|
}
|
|
15477
|
-
if (
|
|
15670
|
+
if (this.options.debug) {
|
|
15478
15671
|
console.log(`📸 html2canvas 配置: 元素尺寸 ${elementWidth}x${elementHeight}, 质量 ${finalQuality.toFixed(2)}, 缩放 ${options.scale}`);
|
|
15479
15672
|
console.log(`📸 html2canvas 将自动计算截图尺寸(不限制 width/height)`);
|
|
15480
15673
|
console.log(`📸 用户配置质量: ${this.options.quality}, 实际使用质量: ${finalQuality.toFixed(2)}`);
|
|
@@ -15519,7 +15712,7 @@ class ScreenshotManager {
|
|
|
15519
15712
|
if (!dataUrl || dataUrl.length < 100) {
|
|
15520
15713
|
throw new Error('生成的截图数据无效或过短');
|
|
15521
15714
|
}
|
|
15522
|
-
if (
|
|
15715
|
+
if (this.options.debug) {
|
|
15523
15716
|
console.log(`📸 html2canvas 截图成功!格式: ${mimeType}, 尺寸: ${canvas.width}x${canvas.height}, 质量: ${finalQualityForExport?.toFixed(2) || 'N/A (PNG)'}`);
|
|
15524
15717
|
console.log(`📸 输出格式: ${mimeType}, 用户配置质量: ${this.options.quality}, 实际使用质量: ${finalQualityForExport?.toFixed(2) || 'N/A (PNG)'}`);
|
|
15525
15718
|
}
|
|
@@ -15527,7 +15720,7 @@ class ScreenshotManager {
|
|
|
15527
15720
|
}
|
|
15528
15721
|
catch (error) {
|
|
15529
15722
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
15530
|
-
if (
|
|
15723
|
+
if (this.options.debug) {
|
|
15531
15724
|
console.error('📸 html2canvas 截图失败:', errorMessage);
|
|
15532
15725
|
if (errorMessage.includes('CORS') || errorMessage.includes('cross-origin')) {
|
|
15533
15726
|
console.warn('📸 💡 建议:配置 proxyUrl 选项处理跨域图片');
|
|
@@ -15560,7 +15753,7 @@ class ScreenshotManager {
|
|
|
15560
15753
|
if (this.isScreenshotInProgress) {
|
|
15561
15754
|
// 队列最多保留 1 个请求,避免积压
|
|
15562
15755
|
if (this.screenshotQueue.length >= 1) {
|
|
15563
|
-
if (
|
|
15756
|
+
if (this.options.debug) {
|
|
15564
15757
|
console.log('📸 截图队列已满,跳过当前请求(等待队列处理)');
|
|
15565
15758
|
}
|
|
15566
15759
|
// 等待队列中的请求完成
|
|
@@ -15589,7 +15782,7 @@ class ScreenshotManager {
|
|
|
15589
15782
|
});
|
|
15590
15783
|
}
|
|
15591
15784
|
this.isScreenshotInProgress = true;
|
|
15592
|
-
if (
|
|
15785
|
+
if (this.options.debug) {
|
|
15593
15786
|
console.log('📸 使用 modern-screenshot 引擎截图(Worker 模式)...');
|
|
15594
15787
|
}
|
|
15595
15788
|
try {
|
|
@@ -15609,7 +15802,7 @@ class ScreenshotManager {
|
|
|
15609
15802
|
elementWidth = element.clientWidth || element.offsetWidth || element.scrollWidth;
|
|
15610
15803
|
elementHeight = element.clientHeight || element.offsetHeight || element.scrollHeight;
|
|
15611
15804
|
}
|
|
15612
|
-
if (
|
|
15805
|
+
if (this.options.debug) {
|
|
15613
15806
|
console.log(`📸 目标元素: ${element.tagName}${element.id ? '#' + element.id : ''}${element.className ? '.' + element.className.split(' ').join('.') : ''}`);
|
|
15614
15807
|
console.log(`📸 元素尺寸: ${elementWidth}x${elementHeight}`);
|
|
15615
15808
|
console.log(`📸 scrollWidth: ${element.scrollWidth}, scrollHeight: ${element.scrollHeight}`);
|
|
@@ -15643,7 +15836,7 @@ class ScreenshotManager {
|
|
|
15643
15836
|
// 检查内存缓存(优先使用缓存,带过期时间检查)
|
|
15644
15837
|
const cachedDataUrl = this.getCachedImage(url);
|
|
15645
15838
|
if (cachedDataUrl) {
|
|
15646
|
-
if (
|
|
15839
|
+
if (this.options.debug) {
|
|
15647
15840
|
console.log(`📸 ✅ 使用内存缓存图片: ${url.substring(0, 50)}...`);
|
|
15648
15841
|
}
|
|
15649
15842
|
return cachedDataUrl;
|
|
@@ -15695,7 +15888,7 @@ class ScreenshotManager {
|
|
|
15695
15888
|
}
|
|
15696
15889
|
}
|
|
15697
15890
|
catch (error) {
|
|
15698
|
-
if (
|
|
15891
|
+
if (this.options.debug) {
|
|
15699
15892
|
console.warn(`📸 代理处理图片失败: ${url.substring(0, 100)}...`, error);
|
|
15700
15893
|
}
|
|
15701
15894
|
// 失败时返回原 URL,让 modern-screenshot 自己处理
|
|
@@ -15744,7 +15937,7 @@ class ScreenshotManager {
|
|
|
15744
15937
|
// 按照 demo 的方式:只在 context 不存在时创建,之后一直复用
|
|
15745
15938
|
// 不进行复杂的检测和重新创建逻辑
|
|
15746
15939
|
if (!this.screenshotContext) {
|
|
15747
|
-
if (
|
|
15940
|
+
if (this.options.debug) {
|
|
15748
15941
|
console.log(`📸 创建截图 Worker 上下文...`);
|
|
15749
15942
|
console.log(`📸 Worker 模式: ${workerNumber} 个 Worker`);
|
|
15750
15943
|
}
|
|
@@ -15765,23 +15958,23 @@ class ScreenshotManager {
|
|
|
15765
15958
|
// 如果用户指定了 workerUrl,使用指定的 URL
|
|
15766
15959
|
if (this.options.workerUrl) {
|
|
15767
15960
|
simpleContextOptions.workerUrl = this.options.workerUrl;
|
|
15768
|
-
if (
|
|
15961
|
+
if (this.options.debug) {
|
|
15769
15962
|
console.log(`📸 使用指定的 Worker URL: ${this.options.workerUrl}`);
|
|
15770
15963
|
}
|
|
15771
15964
|
}
|
|
15772
15965
|
else {
|
|
15773
|
-
if (
|
|
15966
|
+
if (this.options.debug) {
|
|
15774
15967
|
console.log('📸 Worker URL 未指定,modern-screenshot 将自动处理');
|
|
15775
15968
|
}
|
|
15776
15969
|
}
|
|
15777
15970
|
try {
|
|
15778
15971
|
this.screenshotContext = await createContext$1(element, simpleContextOptions);
|
|
15779
|
-
if (
|
|
15972
|
+
if (this.options.debug) {
|
|
15780
15973
|
console.log('📸 Worker 上下文创建成功');
|
|
15781
15974
|
}
|
|
15782
15975
|
}
|
|
15783
15976
|
catch (error) {
|
|
15784
|
-
if (
|
|
15977
|
+
if (this.options.debug) {
|
|
15785
15978
|
console.error('📸 创建 Worker 上下文失败:', error);
|
|
15786
15979
|
}
|
|
15787
15980
|
throw error;
|
|
@@ -15791,7 +15984,7 @@ class ScreenshotManager {
|
|
|
15791
15984
|
// 按照 demo 的方式:使用 domToWebp 时传递配置参数
|
|
15792
15985
|
let dataUrl;
|
|
15793
15986
|
const outputFormat = this.options.outputFormat || 'webp';
|
|
15794
|
-
if (
|
|
15987
|
+
if (this.options.debug) {
|
|
15795
15988
|
console.log(`📸 使用 ${outputFormat.toUpperCase()} 格式截图(直接输出,无需转换)...`);
|
|
15796
15989
|
}
|
|
15797
15990
|
// 构建 domToWebp/domToJpeg/domToPng 的配置参数(和 demo 一致)
|
|
@@ -15826,14 +16019,14 @@ class ScreenshotManager {
|
|
|
15826
16019
|
if (!dataUrl || dataUrl.length < 100) {
|
|
15827
16020
|
throw new Error('生成的截图数据无效或过短');
|
|
15828
16021
|
}
|
|
15829
|
-
if (
|
|
16022
|
+
if (this.options.debug) {
|
|
15830
16023
|
console.log(`📸 ✅ modern-screenshot 截图成功(Worker 模式,${outputFormat.toUpperCase()} 格式)`);
|
|
15831
16024
|
}
|
|
15832
16025
|
return dataUrl;
|
|
15833
16026
|
}
|
|
15834
16027
|
catch (workerError) {
|
|
15835
16028
|
// Worker 模式失败,回退到普通模式(和 demo 一致)
|
|
15836
|
-
if (
|
|
16029
|
+
if (this.options.debug) {
|
|
15837
16030
|
console.warn('📸 Worker 模式失败,回退到普通模式:', workerError);
|
|
15838
16031
|
}
|
|
15839
16032
|
// 销毁失败的 context
|
|
@@ -15876,7 +16069,7 @@ class ScreenshotManager {
|
|
|
15876
16069
|
if (!dataUrl || dataUrl.length < 100) {
|
|
15877
16070
|
throw new Error('生成的截图数据无效或过短');
|
|
15878
16071
|
}
|
|
15879
|
-
if (
|
|
16072
|
+
if (this.options.debug) {
|
|
15880
16073
|
console.log(`📸 ✅ modern-screenshot 截图成功(普通模式,${outputFormat.toUpperCase()} 格式)`);
|
|
15881
16074
|
}
|
|
15882
16075
|
return dataUrl;
|
|
@@ -15884,7 +16077,7 @@ class ScreenshotManager {
|
|
|
15884
16077
|
}
|
|
15885
16078
|
catch (error) {
|
|
15886
16079
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
15887
|
-
if (
|
|
16080
|
+
if (this.options.debug) {
|
|
15888
16081
|
console.error('📸 modern-screenshot 截图失败:', errorMessage);
|
|
15889
16082
|
console.error('📸 元素信息:', {
|
|
15890
16083
|
width: rect.width,
|
|
@@ -15908,7 +16101,7 @@ class ScreenshotManager {
|
|
|
15908
16101
|
catch (error) {
|
|
15909
16102
|
// 外层错误处理:确保即使发生错误也释放锁
|
|
15910
16103
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
15911
|
-
if (
|
|
16104
|
+
if (this.options.debug) {
|
|
15912
16105
|
console.error('📸 modern-screenshot 截图异常:', errorMessage);
|
|
15913
16106
|
}
|
|
15914
16107
|
throw error;
|
|
@@ -15962,7 +16155,7 @@ class ScreenshotManager {
|
|
|
15962
16155
|
link.crossOrigin = 'anonymous';
|
|
15963
16156
|
document.head.appendChild(link);
|
|
15964
16157
|
this.preconnected = true;
|
|
15965
|
-
if (
|
|
16158
|
+
if (this.options.debug) {
|
|
15966
16159
|
console.log(`📸 ✅ 已预连接代理服务器: ${proxyOrigin}`);
|
|
15967
16160
|
}
|
|
15968
16161
|
}
|
|
@@ -16087,7 +16280,7 @@ class ScreenshotManager {
|
|
|
16087
16280
|
if (networkImages.length === 0) {
|
|
16088
16281
|
return;
|
|
16089
16282
|
}
|
|
16090
|
-
if (
|
|
16283
|
+
if (this.options.debug) {
|
|
16091
16284
|
const totalImages = images.length;
|
|
16092
16285
|
console.log(`📸 发现 ${networkImages.length}/${totalImages} 个可视区域内的跨域图片,开始并行预加载...`);
|
|
16093
16286
|
}
|
|
@@ -16095,7 +16288,7 @@ class ScreenshotManager {
|
|
|
16095
16288
|
// 只有当 useProxy 为 true 且 proxyUrl 存在时才使用代理
|
|
16096
16289
|
const shouldUseProxy = this.options.useProxy && this.options.proxyUrl && this.options.proxyUrl.trim() !== '';
|
|
16097
16290
|
if (shouldUseProxy) {
|
|
16098
|
-
if (
|
|
16291
|
+
if (this.options.debug) {
|
|
16099
16292
|
console.log(`📸 使用代理服务器处理跨域图片: ${this.options.proxyUrl}`);
|
|
16100
16293
|
}
|
|
16101
16294
|
// 优化:增加并发数,使用更大的批次
|
|
@@ -16123,13 +16316,13 @@ class ScreenshotManager {
|
|
|
16123
16316
|
}
|
|
16124
16317
|
catch (error) {
|
|
16125
16318
|
// 静默失败,不影响其他图片
|
|
16126
|
-
if (
|
|
16319
|
+
if (this.options.debug) {
|
|
16127
16320
|
console.warn(`📸 ❌ 代理预加载失败: ${originalSrc.substring(0, 50)}...`);
|
|
16128
16321
|
}
|
|
16129
16322
|
}
|
|
16130
16323
|
}));
|
|
16131
16324
|
}));
|
|
16132
|
-
if (
|
|
16325
|
+
if (this.options.debug) {
|
|
16133
16326
|
console.log(`📸 ✅ 预处理完成,缓存了 ${this.imageProxyCache.size} 个代理图片`);
|
|
16134
16327
|
}
|
|
16135
16328
|
}
|
|
@@ -16154,7 +16347,7 @@ class ScreenshotManager {
|
|
|
16154
16347
|
// 移除末尾的 ? 或 &(如果有)
|
|
16155
16348
|
baseUrl = baseUrl.replace(/[?&]$/, '');
|
|
16156
16349
|
const requestUrl = `${baseUrl}?${params.toString()}`;
|
|
16157
|
-
if (
|
|
16350
|
+
if (this.options.debug) {
|
|
16158
16351
|
console.log(`📸 🔄 代理请求 URL: ${requestUrl.substring(0, 200)}...`);
|
|
16159
16352
|
}
|
|
16160
16353
|
// 请求代理服务器
|
|
@@ -16173,7 +16366,7 @@ class ScreenshotManager {
|
|
|
16173
16366
|
const blob = await response.blob();
|
|
16174
16367
|
// 将 blob 转换为 data URL(用于 modern-screenshot 兼容性)
|
|
16175
16368
|
const dataUrl = await this.blobToDataUrl(blob);
|
|
16176
|
-
if (
|
|
16369
|
+
if (this.options.debug) {
|
|
16177
16370
|
console.log(`📸 ✅ 代理模式成功(已转换为 data URL): ${imageUrl.substring(0, 100)}...`);
|
|
16178
16371
|
}
|
|
16179
16372
|
return dataUrl;
|
|
@@ -16204,7 +16397,7 @@ class ScreenshotManager {
|
|
|
16204
16397
|
if (sizeMB > maxSizeMB) {
|
|
16205
16398
|
if (this.options.skipLargeImages) {
|
|
16206
16399
|
// 跳过过大的图片,返回占位符
|
|
16207
|
-
if (
|
|
16400
|
+
if (this.options.debug) {
|
|
16208
16401
|
console.warn(`📸 ⚠️ 跳过过大图片(${sizeMB.toFixed(2)}MB > ${maxSizeMB}MB): ${url.substring(0, 100)}...`);
|
|
16209
16402
|
}
|
|
16210
16403
|
// 返回一个 1x1 的透明占位符,避免截图失败
|
|
@@ -16212,7 +16405,7 @@ class ScreenshotManager {
|
|
|
16212
16405
|
}
|
|
16213
16406
|
else {
|
|
16214
16407
|
// 不跳过,但添加警告
|
|
16215
|
-
if (
|
|
16408
|
+
if (this.options.debug) {
|
|
16216
16409
|
console.warn(`📸 ⚠️ 图片较大(${sizeMB.toFixed(2)}MB),可能导致内存问题: ${url.substring(0, 100)}...`);
|
|
16217
16410
|
}
|
|
16218
16411
|
}
|
|
@@ -16226,14 +16419,14 @@ class ScreenshotManager {
|
|
|
16226
16419
|
if (blobSizeMB > maxSizeMB) {
|
|
16227
16420
|
if (this.options.skipLargeImages) {
|
|
16228
16421
|
// 跳过过大的图片,返回占位符
|
|
16229
|
-
if (
|
|
16422
|
+
if (this.options.debug) {
|
|
16230
16423
|
console.warn(`📸 ⚠️ 跳过过大图片(实际大小 ${blobSizeMB.toFixed(2)}MB > ${maxSizeMB}MB): ${url.substring(0, 100)}...`);
|
|
16231
16424
|
}
|
|
16232
16425
|
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==';
|
|
16233
16426
|
}
|
|
16234
16427
|
else {
|
|
16235
16428
|
// 不跳过,但添加警告
|
|
16236
|
-
if (
|
|
16429
|
+
if (this.options.debug) {
|
|
16237
16430
|
console.warn(`📸 ⚠️ 图片较大(实际大小 ${blobSizeMB.toFixed(2)}MB),可能导致内存问题: ${url.substring(0, 100)}...`);
|
|
16238
16431
|
}
|
|
16239
16432
|
}
|
|
@@ -16249,7 +16442,7 @@ class ScreenshotManager {
|
|
|
16249
16442
|
}
|
|
16250
16443
|
catch (error) {
|
|
16251
16444
|
// 下载失败,返回原 URL,让 modern-screenshot 自己处理
|
|
16252
|
-
if (
|
|
16445
|
+
if (this.options.debug) {
|
|
16253
16446
|
console.warn(`📸 ⚠️ 下载图片失败: ${url.substring(0, 100)}...`, error);
|
|
16254
16447
|
}
|
|
16255
16448
|
return url;
|
|
@@ -16680,7 +16873,7 @@ class ScreenshotManager {
|
|
|
16680
16873
|
// 更新历史记录为压缩后的数据
|
|
16681
16874
|
this.screenshotHistory[this.screenshotHistory.length - 1] = compressed.dataUrl;
|
|
16682
16875
|
// 打印压缩统计信息和 base64 对比
|
|
16683
|
-
if (
|
|
16876
|
+
if (this.options.debug) {
|
|
16684
16877
|
if (compressed.error) {
|
|
16685
16878
|
// 压缩失败,使用原始数据
|
|
16686
16879
|
console.warn('📸 [Worker 压缩] ⚠️ 压缩失败,使用原始截图');
|
|
@@ -16734,7 +16927,7 @@ class ScreenshotManager {
|
|
|
16734
16927
|
};
|
|
16735
16928
|
newWorker.onerror = (e) => {
|
|
16736
16929
|
console.error('📸 WebWorker 错误:', e);
|
|
16737
|
-
if (
|
|
16930
|
+
if (this.options.debug) {
|
|
16738
16931
|
console.warn('📸 Worker 压缩失败,使用原始截图');
|
|
16739
16932
|
}
|
|
16740
16933
|
// Worker 发生错误时,如果配置了二进制模式,发送原始截图
|
|
@@ -16750,7 +16943,7 @@ class ScreenshotManager {
|
|
|
16750
16943
|
data: combinedBuffer
|
|
16751
16944
|
};
|
|
16752
16945
|
this.sendToIframeCallback(message);
|
|
16753
|
-
if (
|
|
16946
|
+
if (this.options.debug) {
|
|
16754
16947
|
console.log('📸 [Worker 错误] ✅ 原始截图已发送到 iframe');
|
|
16755
16948
|
}
|
|
16756
16949
|
}
|
|
@@ -16915,7 +17108,7 @@ class ScreenshotManager {
|
|
|
16915
17108
|
async takeScreenshotAndSendBinary(config) {
|
|
16916
17109
|
// 如果已经在运行,先停止再重新开始
|
|
16917
17110
|
if (this.isRunning) {
|
|
16918
|
-
if (
|
|
17111
|
+
if (this.options.debug) {
|
|
16919
17112
|
console.log(`📸 更新轮询间隔: ${this.dynamicInterval || this.options.interval}ms`);
|
|
16920
17113
|
}
|
|
16921
17114
|
this.stopScreenshot();
|
|
@@ -16946,7 +17139,7 @@ class ScreenshotManager {
|
|
|
16946
17139
|
// 验证:base64Data(从 latestScreenshot 提取)和 imageBufferBase64(从 imageBuffer 转换)应该一致
|
|
16947
17140
|
const isBase64Same = base64Data === imageBufferBase64;
|
|
16948
17141
|
// 打印 imageBuffer 的 base64 编码(用于和接收端对比)
|
|
16949
|
-
if (
|
|
17142
|
+
if (this.options.debug) {
|
|
16950
17143
|
console.log('📸 [发送前] 数据流程分析:');
|
|
16951
17144
|
console.log(` latestScreenshot: ${latestScreenshot.substring(0, 50)}... (原始 data URL)`);
|
|
16952
17145
|
console.log(` base64Data (从 latestScreenshot 提取): 长度 ${base64Data.length} 字符`);
|
|
@@ -16972,7 +17165,7 @@ class ScreenshotManager {
|
|
|
16972
17165
|
const combinedBuffer = this.combineBinaryData(configBuffer, imageBuffer);
|
|
16973
17166
|
const combinedBufferSize = combinedBuffer.byteLength;
|
|
16974
17167
|
// 打印大小信息
|
|
16975
|
-
if (
|
|
17168
|
+
if (this.options.debug) {
|
|
16976
17169
|
console.log('📸 [大小统计]');
|
|
16977
17170
|
console.log(` Base64 大小: ${base64Size} 字符`);
|
|
16978
17171
|
console.log(` 图片字节大小: ${(imageBufferSize / 1024).toFixed(2)} KB (${imageBufferSize} 字节)`);
|
|
@@ -16986,7 +17179,7 @@ class ScreenshotManager {
|
|
|
16986
17179
|
data: combinedBuffer
|
|
16987
17180
|
};
|
|
16988
17181
|
this.sendToIframeCallback(message);
|
|
16989
|
-
if (
|
|
17182
|
+
if (this.options.debug) {
|
|
16990
17183
|
console.log('📸 [iframe] ✅ 二进制数据已发送到 iframe');
|
|
16991
17184
|
}
|
|
16992
17185
|
}
|
|
@@ -17000,14 +17193,14 @@ class ScreenshotManager {
|
|
|
17000
17193
|
}
|
|
17001
17194
|
}
|
|
17002
17195
|
else {
|
|
17003
|
-
if (
|
|
17196
|
+
if (this.options.debug) {
|
|
17004
17197
|
console.warn('📸 [iframe] 截图完成但未找到截图数据');
|
|
17005
17198
|
}
|
|
17006
17199
|
}
|
|
17007
17200
|
}
|
|
17008
17201
|
else {
|
|
17009
17202
|
// 启用了压缩,等待 Worker 压缩完成后在 onmessage 中自动发送
|
|
17010
|
-
if (
|
|
17203
|
+
if (this.options.debug) {
|
|
17011
17204
|
console.log('📸 [iframe] 等待 Worker 压缩完成后发送到 iframe...');
|
|
17012
17205
|
}
|
|
17013
17206
|
}
|
|
@@ -17037,7 +17230,7 @@ class ScreenshotManager {
|
|
|
17037
17230
|
const base64Data = dataUrl.split(',')[1] || '';
|
|
17038
17231
|
const base64Size = base64Data.length;
|
|
17039
17232
|
// 完整打印发送前的 base64 信息(用于调试)
|
|
17040
|
-
if (
|
|
17233
|
+
if (this.options.debug) {
|
|
17041
17234
|
console.log('📸 [发送前] Base64 信息:');
|
|
17042
17235
|
console.log(` Base64 长度: ${base64Size} 字符`);
|
|
17043
17236
|
console.log(` 📸 [发送前] 完整 Base64:`);
|
|
@@ -17061,7 +17254,7 @@ class ScreenshotManager {
|
|
|
17061
17254
|
const combinedBufferSize = combinedBuffer.byteLength;
|
|
17062
17255
|
const combineTime = performance.now() - combineStartTime;
|
|
17063
17256
|
// 打印大小信息
|
|
17064
|
-
if (
|
|
17257
|
+
if (this.options.debug) {
|
|
17065
17258
|
console.log('📸 [压缩后-大小统计]');
|
|
17066
17259
|
console.log(` Base64 大小: ${base64Size} 字符`);
|
|
17067
17260
|
console.log(` 图片字节大小: ${(imageBufferSize / 1024).toFixed(2)} KB (${imageBufferSize} 字节)`);
|
|
@@ -17084,7 +17277,7 @@ class ScreenshotManager {
|
|
|
17084
17277
|
if (scheduleStartTime) {
|
|
17085
17278
|
totalTime = performance.now() - scheduleStartTime;
|
|
17086
17279
|
}
|
|
17087
|
-
if (
|
|
17280
|
+
if (this.options.debug) {
|
|
17088
17281
|
console.log('📸 [压缩后] ✅ 二进制数据已发送到 iframe');
|
|
17089
17282
|
console.log(` ⏱️ 发送回调耗时: ${sendCallbackTime.toFixed(2)}ms`);
|
|
17090
17283
|
console.log(` ⏱️ 发送阶段总耗时: ${totalSendTime.toFixed(2)}ms`);
|
|
@@ -17187,8 +17380,8 @@ class ScreenshotManager {
|
|
|
17187
17380
|
if (newOptions.compress !== undefined) {
|
|
17188
17381
|
this.options.compress = newOptions.compress;
|
|
17189
17382
|
}
|
|
17190
|
-
if (newOptions.
|
|
17191
|
-
this.options.
|
|
17383
|
+
if (newOptions.debug !== undefined) {
|
|
17384
|
+
this.options.debug = newOptions.debug;
|
|
17192
17385
|
}
|
|
17193
17386
|
if (newOptions.proxyUrl !== undefined) {
|
|
17194
17387
|
this.options.proxyUrl = newOptions.proxyUrl;
|
|
@@ -17201,7 +17394,7 @@ class ScreenshotManager {
|
|
|
17201
17394
|
if (this.screenshotContext) {
|
|
17202
17395
|
try {
|
|
17203
17396
|
destroyContext(this.screenshotContext);
|
|
17204
|
-
if (
|
|
17397
|
+
if (this.options.debug) {
|
|
17205
17398
|
console.log(`📸 引擎已从 ${oldEngine} 切换到 ${newEngine},已清理旧 context`);
|
|
17206
17399
|
}
|
|
17207
17400
|
}
|
|
@@ -17210,12 +17403,12 @@ class ScreenshotManager {
|
|
|
17210
17403
|
}
|
|
17211
17404
|
this.screenshotContext = null;
|
|
17212
17405
|
}
|
|
17213
|
-
if (
|
|
17406
|
+
if (this.options.debug) {
|
|
17214
17407
|
console.log(`📸 截图引擎已更新: ${oldEngine} → ${newEngine}`);
|
|
17215
17408
|
console.log(`📸 下次截图时将使用新引擎: ${newEngine}`);
|
|
17216
17409
|
}
|
|
17217
17410
|
}
|
|
17218
|
-
else if (
|
|
17411
|
+
else if (this.options.debug) {
|
|
17219
17412
|
console.log('📸 截图配置已更新');
|
|
17220
17413
|
}
|
|
17221
17414
|
}
|
|
@@ -17315,7 +17508,7 @@ class ScreenshotManager {
|
|
|
17315
17508
|
const itemSizeMB = value.dataUrl.length * 0.75 / (1024 * 1024);
|
|
17316
17509
|
this.imageProxyCache.delete(key);
|
|
17317
17510
|
currentSizeMB -= itemSizeMB;
|
|
17318
|
-
if (
|
|
17511
|
+
if (this.options.debug) {
|
|
17319
17512
|
console.log(`📸 清理内存缓存(超过限制): ${key.substring(0, 50)}...`);
|
|
17320
17513
|
}
|
|
17321
17514
|
}
|
|
@@ -17340,7 +17533,7 @@ class ScreenshotManager {
|
|
|
17340
17533
|
expiredUrls.forEach(url => {
|
|
17341
17534
|
this.imageProxyCache.delete(url);
|
|
17342
17535
|
});
|
|
17343
|
-
if (expiredUrls.length > 0 &&
|
|
17536
|
+
if (expiredUrls.length > 0 && this.options.debug) {
|
|
17344
17537
|
console.log(`📸 清理了 ${expiredUrls.length} 个过期缓存`);
|
|
17345
17538
|
}
|
|
17346
17539
|
}
|
|
@@ -17352,7 +17545,7 @@ class ScreenshotManager {
|
|
|
17352
17545
|
// 每2分钟清理一次过期缓存(从5分钟改为2分钟,更频繁)
|
|
17353
17546
|
setInterval(() => {
|
|
17354
17547
|
this.cleanExpiredCache();
|
|
17355
|
-
if (
|
|
17548
|
+
if (this.options.debug) {
|
|
17356
17549
|
const memoryCacheSize = this.imageProxyCache.size;
|
|
17357
17550
|
const memoryCacheSizeMB = Array.from(this.imageProxyCache.values())
|
|
17358
17551
|
.reduce((sum, cached) => sum + cached.dataUrl.length * 0.75 / (1024 * 1024), 0);
|
|
@@ -20692,16 +20885,17 @@ class CustomerServiceSDK {
|
|
|
20692
20885
|
agent: config.agent,
|
|
20693
20886
|
timestamp: Date.now()
|
|
20694
20887
|
};
|
|
20695
|
-
//
|
|
20888
|
+
// 创建悬浮图标管理器(支持自定义位置和传送目标)
|
|
20696
20889
|
const iconPosition = options?.iconPosition || undefined;
|
|
20697
|
-
|
|
20890
|
+
const iconTarget = options?.target || undefined;
|
|
20891
|
+
this.iconManager = new IconManager(iconPosition, this.debug, iconTarget);
|
|
20698
20892
|
await this.iconManager.show();
|
|
20699
20893
|
// 创建iframe管理器(自动检测设备类型)
|
|
20700
20894
|
this.iframeManager = new IframeManager({
|
|
20701
20895
|
src: iframeUrl,
|
|
20702
|
-
mode: 'auto', //
|
|
20703
|
-
width:
|
|
20704
|
-
height: 600,
|
|
20896
|
+
mode: 'auto', // 自动根据设备类型选择模式(PC弹窗,移动端全屏)
|
|
20897
|
+
width: options?.width || 450, // PC模式宽度(像素,默认450px),移动端不使用
|
|
20898
|
+
height: options?.height || 600, // PC模式高度(像素),移动端不使用(强制全屏)
|
|
20705
20899
|
allowClose: true,
|
|
20706
20900
|
debug: this.debug, // 传递 debug 标志
|
|
20707
20901
|
onMessage: (messageType, _data) => {
|
|
@@ -20713,8 +20907,9 @@ class CustomerServiceSDK {
|
|
|
20713
20907
|
// checkScreenshot 消息由 ScreenshotManager 处理,不需要在这里处理
|
|
20714
20908
|
},
|
|
20715
20909
|
onClose: () => {
|
|
20716
|
-
// iframe
|
|
20910
|
+
// iframe关闭时,清理图标拖动事件监听器,并重新启用图标点击
|
|
20717
20911
|
this.iconManager?.forceCleanupDragEvents();
|
|
20912
|
+
this.iconManager?.enableClick();
|
|
20718
20913
|
},
|
|
20719
20914
|
...options
|
|
20720
20915
|
});
|
|
@@ -20725,16 +20920,18 @@ class CustomerServiceSDK {
|
|
|
20725
20920
|
// 打开iframe时清除红点通知
|
|
20726
20921
|
this.clearNotification();
|
|
20727
20922
|
this.iframeManager?.show();
|
|
20923
|
+
// iframe 打开后,禁用图标点击(防止重复打开)
|
|
20924
|
+
this.iconManager?.disableClick();
|
|
20728
20925
|
});
|
|
20729
20926
|
// 初始化截图管理器(如果启用了截图功能)
|
|
20730
20927
|
if (config.screenshot) {
|
|
20731
20928
|
// 默认截图目标为 document.body,可以通过配置自定义
|
|
20732
20929
|
const targetElement = document.body;
|
|
20733
20930
|
// 传入发送消息到 iframe 的回调函数
|
|
20734
|
-
// 将 debug
|
|
20931
|
+
// 将 debug 配置传递给截图管理器
|
|
20735
20932
|
const screenshotOptions = {
|
|
20736
20933
|
...config.screenshot,
|
|
20737
|
-
|
|
20934
|
+
debug: this.debug // 直接传递 debug 标志
|
|
20738
20935
|
};
|
|
20739
20936
|
this.screenshotManager = new ScreenshotManager(targetElement, screenshotOptions, (data) => {
|
|
20740
20937
|
// 通过 IframeManager 发送消息到 iframe
|
|
@@ -20949,8 +21146,9 @@ class CustomerServiceSDK {
|
|
|
20949
21146
|
return result.visitorId;
|
|
20950
21147
|
}
|
|
20951
21148
|
catch (error) {
|
|
20952
|
-
|
|
20953
|
-
|
|
21149
|
+
if (this.debug) {
|
|
21150
|
+
console.warn('❌ Failed to get device fingerprint, using fallback:', error);
|
|
21151
|
+
}
|
|
20954
21152
|
const fallbackId = 'device_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
|
20955
21153
|
if (this.debug) {
|
|
20956
21154
|
console.log('🆔 Fallback Device ID:', fallbackId);
|