customer-chat-sdk 1.0.75 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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) {
4
+ constructor(position, debug = false) {
5
5
  this.iconElement = null;
6
6
  this.badgeElement = null;
7
7
  this.onClickCallback = null;
@@ -13,8 +13,14 @@ 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; // 图标位置配置
20
+ this.debug = false; // debug 模式标志
21
+ this.isClickEnabled = true; // 是否允许点击(iframe 打开时禁用)
17
22
  this.iconPosition = position || null;
23
+ this.debug = debug;
18
24
  }
19
25
  /**
20
26
  * 显示悬浮图标
@@ -28,7 +34,7 @@ class IconManager {
28
34
  this.iconElement.className = 'customer-sdk-icon';
29
35
  // 直接设置样式 - 图标容器
30
36
  const defaultStyle = {
31
- position: 'fixed',
37
+ position: 'absolute',
32
38
  width: '30px',
33
39
  height: '30px',
34
40
  backgroundColor: 'transparent', // 移除背景色,让图片直接显示
@@ -37,7 +43,7 @@ class IconManager {
37
43
  alignItems: 'center',
38
44
  justifyContent: 'center',
39
45
  cursor: 'pointer',
40
- zIndex: '999999',
46
+ zIndex: '1000002', // 确保图标始终在最上层(遮罩层 999998,iframe 容器 999999)
41
47
  boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
42
48
  userSelect: 'none',
43
49
  transition: 'transform 0.2s ease',
@@ -82,7 +88,9 @@ class IconManager {
82
88
  iconImg.alt = 'Customer Service';
83
89
  // 添加图片加载成功处理
84
90
  iconImg.onload = () => {
85
- console.log('Icon image loaded successfully');
91
+ if (this.debug) {
92
+ console.log('Icon image loaded successfully');
93
+ }
86
94
  if (this.iconElement) {
87
95
  // 确保图片可见
88
96
  iconImg.style.opacity = '1';
@@ -91,6 +99,7 @@ class IconManager {
91
99
  };
92
100
  // 添加图片加载错误处理
93
101
  iconImg.onerror = (e) => {
102
+ // 错误始终输出
94
103
  console.error('Failed to load icon image', e);
95
104
  // 只记录错误,不使用默认样式,必须使用外部提供的图片
96
105
  };
@@ -112,7 +121,9 @@ class IconManager {
112
121
  this.setupDragEvents();
113
122
  // 添加到页面
114
123
  document.body.appendChild(this.iconElement);
115
- console.log('CustomerSDK icon displayed');
124
+ if (this.debug) {
125
+ console.log('CustomerSDK icon displayed');
126
+ }
116
127
  }
117
128
  /**
118
129
  * 强制清理所有拖动事件监听器
@@ -125,7 +136,9 @@ class IconManager {
125
136
  document.removeEventListener('touchmove', this.dragMoveHandler);
126
137
  }
127
138
  catch (e) {
128
- console.warn('Error removing drag move listeners:', e);
139
+ if (this.debug) {
140
+ console.warn('Error removing drag move listeners:', e);
141
+ }
129
142
  }
130
143
  }
131
144
  if (this.dragEndHandler) {
@@ -134,7 +147,9 @@ class IconManager {
134
147
  document.removeEventListener('touchend', this.dragEndHandler);
135
148
  }
136
149
  catch (e) {
137
- console.warn('Error removing drag end listeners:', e);
150
+ if (this.debug) {
151
+ console.warn('Error removing drag end listeners:', e);
152
+ }
138
153
  }
139
154
  }
140
155
  // 重置拖动状态
@@ -157,7 +172,9 @@ class IconManager {
157
172
  this.onClickCallback = null;
158
173
  this.dragMoveHandler = null;
159
174
  this.dragEndHandler = null;
160
- console.log('CustomerSDK icon hidden');
175
+ if (this.debug) {
176
+ console.log('CustomerSDK icon hidden');
177
+ }
161
178
  }
162
179
  }
163
180
  /**
@@ -214,7 +231,9 @@ class IconManager {
214
231
  */
215
232
  showNotification(options) {
216
233
  if (!this.iconElement) {
217
- console.warn('Icon not displayed, cannot show notification');
234
+ if (this.debug) {
235
+ console.warn('Icon not displayed, cannot show notification');
236
+ }
218
237
  return;
219
238
  }
220
239
  const { showBadge = true, badgeCount = 0, badgeText = '', pulse = false } = options;
@@ -226,7 +245,9 @@ class IconManager {
226
245
  if (this.notificationCallback) {
227
246
  this.notificationCallback({ badgeCount, badgeText, pulse });
228
247
  }
229
- console.log('Notification shown:', { badgeCount, badgeText });
248
+ if (this.debug) {
249
+ console.log('Notification shown:', { badgeCount, badgeText });
250
+ }
230
251
  }
231
252
  /**
232
253
  * 清除消息订阅
@@ -236,7 +257,9 @@ class IconManager {
236
257
  this.badgeElement.remove();
237
258
  this.badgeElement = null;
238
259
  }
239
- console.log('Notification cleared');
260
+ if (this.debug) {
261
+ console.log('Notification cleared');
262
+ }
240
263
  }
241
264
  /**
242
265
  * 设置拖动和点击事件
@@ -247,9 +270,11 @@ class IconManager {
247
270
  // 绑定事件处理器(用于后续清理)
248
271
  this.dragMoveHandler = this.handleDragMove.bind(this);
249
272
  this.dragEndHandler = this.handleDragEnd.bind(this);
250
- // 只在图标上监听开始事件
251
- this.iconElement.addEventListener('mousedown', this.handleDragStart.bind(this));
252
- this.iconElement.addEventListener('touchstart', this.handleDragStart.bind(this), { passive: false });
273
+ this.dragStartHandler = this.handleDragStart.bind(this);
274
+ // 只在图标上监听开始事件(保存引用以便后续移除)
275
+ this.iconElement.addEventListener('mousedown', this.dragStartHandler);
276
+ this.touchStartHandler = this.handleDragStart.bind(this);
277
+ this.iconElement.addEventListener('touchstart', this.touchStartHandler, { passive: false });
253
278
  }
254
279
  /**
255
280
  * 开始拖动
@@ -272,12 +297,33 @@ class IconManager {
272
297
  const rect = this.iconElement.getBoundingClientRect();
273
298
  this.iconStartX = rect.left;
274
299
  this.iconStartY = rect.top;
275
- // 添加拖动样式
276
- this.iconElement.style.transition = 'none';
277
- this.iconElement.style.cursor = 'grabbing';
300
+ // 注意:不要在这里立即移除 transition 和设置 cursor
301
+ // 只有在真正开始拖动时才修改样式,避免点击时图标位置跳动
278
302
  // 只在真正开始拖动时添加document事件监听
279
303
  // 先添加一个临时的move监听器来检测是否真的在拖动
280
304
  const checkDrag = (moveEvent) => {
305
+ // 检测事件目标:如果事件发生在iframe或其他元素上,停止检测拖动
306
+ const target = moveEvent.target;
307
+ if (target && target !== this.iconElement && !this.iconElement?.contains(target)) {
308
+ // 检查是否是iframe相关元素
309
+ const isIframeElement = target.tagName === 'IFRAME' ||
310
+ target.closest('iframe') ||
311
+ target.closest('.customer-sdk-container') ||
312
+ target.closest('.customer-sdk-overlay');
313
+ if (isIframeElement) {
314
+ // 如果事件发生在iframe相关元素上,停止检测并清理监听器
315
+ if (this.checkDragHandler) {
316
+ if ('touches' in moveEvent) {
317
+ document.removeEventListener('touchmove', this.checkDragHandler);
318
+ }
319
+ else {
320
+ document.removeEventListener('mousemove', this.checkDragHandler);
321
+ }
322
+ this.checkDragHandler = null;
323
+ }
324
+ return;
325
+ }
326
+ }
281
327
  const moveX = 'touches' in moveEvent ? moveEvent.touches[0].clientX : moveEvent.clientX;
282
328
  const moveY = 'touches' in moveEvent ? moveEvent.touches[0].clientY : moveEvent.clientY;
283
329
  const deltaX = moveX - this.dragStartX;
@@ -285,12 +331,20 @@ class IconManager {
285
331
  // 如果移动距离超过5px,开始拖动
286
332
  if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
287
333
  this.isDragging = true;
288
- // 移除临时监听器
289
- if ('touches' in moveEvent) {
290
- document.removeEventListener('touchmove', checkDrag);
334
+ // 只有在真正开始拖动时才移除 transition 和设置 cursor
335
+ if (this.iconElement) {
336
+ this.iconElement.style.transition = 'none';
337
+ this.iconElement.style.cursor = 'grabbing';
291
338
  }
292
- else {
293
- document.removeEventListener('mousemove', checkDrag);
339
+ // 移除临时监听器
340
+ if (this.checkDragHandler) {
341
+ if ('touches' in moveEvent) {
342
+ document.removeEventListener('touchmove', this.checkDragHandler);
343
+ }
344
+ else {
345
+ document.removeEventListener('mousemove', this.checkDragHandler);
346
+ }
347
+ this.checkDragHandler = null;
294
348
  }
295
349
  // 添加正式的事件监听器
296
350
  if (this.dragMoveHandler && this.dragEndHandler) {
@@ -305,13 +359,15 @@ class IconManager {
305
359
  }
306
360
  }
307
361
  };
362
+ // 保存 checkDrag 引用,以便后续清理
363
+ this.checkDragHandler = checkDrag;
308
364
  // 添加临时检测监听器
309
365
  if ('touches' in e) {
310
- document.addEventListener('touchmove', checkDrag, { passive: false });
366
+ document.addEventListener('touchmove', this.checkDragHandler, { passive: false });
311
367
  document.addEventListener('touchend', this.dragEndHandler);
312
368
  }
313
369
  else {
314
- document.addEventListener('mousemove', checkDrag);
370
+ document.addEventListener('mousemove', this.checkDragHandler);
315
371
  document.addEventListener('mouseup', this.dragEndHandler);
316
372
  }
317
373
  }
@@ -323,20 +379,9 @@ class IconManager {
323
379
  return;
324
380
  const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
325
381
  const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
326
- // 检测事件目标:如果事件发生在iframe或其他元素上,停止拖动
327
- const target = e.target;
328
- if (target && target !== this.iconElement && !this.iconElement.contains(target)) {
329
- // 检查是否是iframe相关元素
330
- const isIframeElement = target.tagName === 'IFRAME' ||
331
- target.closest('iframe') ||
332
- target.closest('.customer-sdk-container') ||
333
- target.closest('.customer-sdk-overlay');
334
- if (isIframeElement) {
335
- // 如果事件发生在iframe相关元素上,停止拖动
336
- this.handleDragEnd();
337
- return;
338
- }
339
- }
382
+ // 注意:拖动过程中不检测 iframe 元素,因为用户可能只是想将图标拖动到 iframe 附近或上方
383
+ // 只有在拖动开始时(checkDrag 阶段)才检测 iframe,防止误判为拖动
384
+ // 一旦开始拖动,就应该允许拖动到任何位置,包括 iframe 上方
340
385
  // 计算从拖动开始位置到当前位置的距离
341
386
  const deltaX = clientX - this.dragStartX;
342
387
  const deltaY = clientY - this.dragStartY;
@@ -371,10 +416,15 @@ class IconManager {
371
416
  /**
372
417
  * 结束拖动
373
418
  */
374
- handleDragEnd(e) {
419
+ handleDragEnd(_e) {
375
420
  if (!this.iconElement)
376
421
  return;
377
- // 清理document上的事件监听器
422
+ // 清理document上的所有事件监听器(包括临时检测监听器)
423
+ if (this.checkDragHandler) {
424
+ document.removeEventListener('mousemove', this.checkDragHandler);
425
+ document.removeEventListener('touchmove', this.checkDragHandler);
426
+ this.checkDragHandler = null;
427
+ }
378
428
  if (this.dragMoveHandler) {
379
429
  document.removeEventListener('mousemove', this.dragMoveHandler);
380
430
  document.removeEventListener('touchmove', this.dragMoveHandler);
@@ -383,6 +433,8 @@ class IconManager {
383
433
  document.removeEventListener('mouseup', this.dragEndHandler);
384
434
  document.removeEventListener('touchend', this.dragEndHandler);
385
435
  }
436
+ // 恢复样式(如果之前被修改过)
437
+ // 注意:如果只是点击(没有拖动),这些样式可能没有被修改,但恢复操作是安全的
386
438
  this.iconElement.style.transition = 'transform 0.2s ease';
387
439
  this.iconElement.style.cursor = 'pointer';
388
440
  // 如果只是点击(没有拖动),触发点击事件
@@ -395,10 +447,52 @@ class IconManager {
395
447
  * 处理点击事件
396
448
  */
397
449
  handleClick() {
450
+ // 如果点击被禁用(iframe 打开时),不执行点击回调
451
+ if (!this.isClickEnabled) {
452
+ return;
453
+ }
398
454
  if (this.onClickCallback) {
399
455
  this.onClickCallback();
400
456
  }
401
457
  }
458
+ /**
459
+ * 禁用点击和拖拽(iframe 打开时调用)
460
+ */
461
+ disableClick() {
462
+ this.isClickEnabled = false;
463
+ // 移除拖动事件监听器
464
+ if (this.iconElement) {
465
+ if (this.dragStartHandler) {
466
+ this.iconElement.removeEventListener('mousedown', this.dragStartHandler);
467
+ }
468
+ if (this.touchStartHandler) {
469
+ this.iconElement.removeEventListener('touchstart', this.touchStartHandler);
470
+ }
471
+ // 禁用所有鼠标事件(包括点击和拖拽)
472
+ this.iconElement.style.pointerEvents = 'none';
473
+ this.iconElement.style.cursor = 'default';
474
+ }
475
+ // 清理可能正在进行的拖动
476
+ this.forceCleanupDragEvents();
477
+ }
478
+ /**
479
+ * 启用点击和拖拽(iframe 关闭时调用)
480
+ */
481
+ enableClick() {
482
+ this.isClickEnabled = true;
483
+ // 重新添加拖动事件监听器
484
+ if (this.iconElement) {
485
+ if (this.dragStartHandler) {
486
+ this.iconElement.addEventListener('mousedown', this.dragStartHandler);
487
+ }
488
+ if (this.touchStartHandler) {
489
+ this.iconElement.addEventListener('touchstart', this.touchStartHandler, { passive: false });
490
+ }
491
+ // 恢复鼠标事件
492
+ this.iconElement.style.pointerEvents = 'auto';
493
+ this.iconElement.style.cursor = 'pointer';
494
+ }
495
+ }
402
496
  /**
403
497
  * 创建消息徽章(简化版)
404
498
  */
@@ -460,14 +554,16 @@ class IframeManager {
460
554
  this.containerElement = null; // 包装容器,包含iframe和关闭按钮
461
555
  this.isOpen = false;
462
556
  this.isCreated = false;
557
+ this.debug = false; // debug 模式标志
463
558
  this.config = {
464
559
  src: '',
465
560
  mode: 'auto', // 默认自动检测设备类型
466
- width: 400,
561
+ width: 450, // PC 模式默认宽度
467
562
  height: 600,
468
563
  allowClose: true,
469
564
  ...config
470
565
  };
566
+ this.debug = config.debug ?? false;
471
567
  this.setupMessageListener();
472
568
  }
473
569
  /**
@@ -479,9 +575,12 @@ class IframeManager {
479
575
  // 创建隐藏的iframe(预连接到SSE)
480
576
  this.createIframe();
481
577
  this.isCreated = true;
482
- console.log('CustomerSDK iframe initialized (hidden, SSE connected)');
578
+ if (this.debug) {
579
+ console.log('CustomerSDK iframe initialized (hidden, SSE connected)');
580
+ }
483
581
  }
484
582
  catch (error) {
583
+ // 错误始终输出
485
584
  console.error('Failed to initialize iframe:', error);
486
585
  throw error;
487
586
  }
@@ -499,9 +598,9 @@ class IframeManager {
499
598
  try {
500
599
  const actualMode = this.getActualMode();
501
600
  const isPC = actualMode === 'popup';
502
- // PC模式下创建遮罩层
601
+ // PC模式:创建或显示遮罩层
503
602
  if (isPC) {
504
- this.createOverlay();
603
+ this.createOverlay(); // createOverlay 内部会检查是否已存在
505
604
  }
506
605
  // 显示已创建的容器
507
606
  if (this.containerElement) {
@@ -516,8 +615,18 @@ class IframeManager {
516
615
  opacity: '1',
517
616
  display: 'block'
518
617
  });
519
- // 将容器移到遮罩层内
520
- this.overlayElement?.appendChild(this.containerElement);
618
+ // 关键优化:避免重复移动容器导致 iframe 重新加载
619
+ // 只有当容器不在遮罩层内时才移动,且确保遮罩层在 DOM 中
620
+ if (this.overlayElement) {
621
+ // 如果遮罩层不在 DOM 中,先添加到 DOM
622
+ if (!this.overlayElement.parentNode) {
623
+ document.body.appendChild(this.overlayElement);
624
+ }
625
+ // 只有当容器不在遮罩层内时才移动(避免重复移动导致 iframe 重新加载)
626
+ if (this.containerElement.parentNode !== this.overlayElement) {
627
+ this.overlayElement.appendChild(this.containerElement);
628
+ }
629
+ }
521
630
  }
522
631
  else {
523
632
  // 移动端模式:直接全屏显示,不需要遮罩层
@@ -532,9 +641,12 @@ class IframeManager {
532
641
  }
533
642
  }
534
643
  this.isOpen = true;
535
- console.log('CustomerSDK iframe shown');
644
+ if (this.debug) {
645
+ console.log('CustomerSDK iframe shown');
646
+ }
536
647
  }
537
648
  catch (error) {
649
+ // 错误始终输出
538
650
  console.error('Failed to show iframe:', error);
539
651
  }
540
652
  }
@@ -551,26 +663,33 @@ class IframeManager {
551
663
  if (!this.isOpen) {
552
664
  return;
553
665
  }
554
- // 隐藏容器但保留DOM元素
666
+ // 隐藏容器但保留DOM元素(不移动容器,避免 iframe 重新加载)
555
667
  if (this.containerElement) {
556
668
  Object.assign(this.containerElement.style, {
557
669
  visibility: 'hidden',
558
670
  opacity: '0',
559
671
  display: 'none'
560
672
  });
673
+ // 注意:不移动容器,保持容器在当前位置(遮罩层或 body),避免 iframe 重新加载
561
674
  }
562
- // 移除遮罩层(仅PC模式)
675
+ // 隐藏遮罩层但不移除(仅PC模式,避免重新创建导致 iframe 重新加载)
563
676
  if (this.overlayElement) {
564
- this.overlayElement.remove();
565
- this.overlayElement = null;
677
+ Object.assign(this.overlayElement.style, {
678
+ visibility: 'hidden',
679
+ opacity: '0',
680
+ display: 'none'
681
+ });
682
+ // 不移除遮罩层,下次显示时直接显示即可
566
683
  }
567
684
  // 恢复body滚动(移动端模式)
568
- const actualMode = this.getActualMode();
569
- if (actualMode === 'fullscreen') {
685
+ const actualModeForScroll = this.getActualMode();
686
+ if (actualModeForScroll === 'fullscreen') {
570
687
  this.preventBodyScroll(false);
571
688
  }
572
689
  this.isOpen = false;
573
- console.log('CustomerSDK iframe hidden (SSE still connected)');
690
+ if (this.debug) {
691
+ console.log('CustomerSDK iframe hidden (SSE still connected)');
692
+ }
574
693
  // 触发关闭回调
575
694
  if (this.config.onClose) {
576
695
  this.config.onClose();
@@ -593,7 +712,9 @@ class IframeManager {
593
712
  this.iframeElement = null;
594
713
  }
595
714
  this.isCreated = false;
596
- console.log('CustomerSDK container destroyed');
715
+ if (this.debug) {
716
+ console.log('CustomerSDK container destroyed');
717
+ }
597
718
  }
598
719
  /**
599
720
  * 检查是否已打开
@@ -610,9 +731,29 @@ class IframeManager {
610
731
  }
611
732
  }
612
733
  /**
613
- * 创建遮罩层
734
+ * 创建遮罩层(PC模式使用)
614
735
  */
615
736
  createOverlay() {
737
+ // 如果遮罩层已存在,直接显示即可
738
+ if (this.overlayElement && this.overlayElement.parentNode) {
739
+ Object.assign(this.overlayElement.style, {
740
+ visibility: 'visible',
741
+ opacity: '1',
742
+ display: 'flex'
743
+ });
744
+ return;
745
+ }
746
+ // 如果遮罩层存在但不在 DOM 中,重新添加到 DOM
747
+ if (this.overlayElement && !this.overlayElement.parentNode) {
748
+ document.body.appendChild(this.overlayElement);
749
+ Object.assign(this.overlayElement.style, {
750
+ visibility: 'visible',
751
+ opacity: '1',
752
+ display: 'flex'
753
+ });
754
+ return;
755
+ }
756
+ // 创建新的遮罩层
616
757
  this.overlayElement = document.createElement('div');
617
758
  this.overlayElement.className = 'customer-sdk-overlay';
618
759
  Object.assign(this.overlayElement.style, {
@@ -628,7 +769,7 @@ class IframeManager {
628
769
  justifyContent: 'center',
629
770
  cursor: this.config.allowClose ? 'pointer' : 'default'
630
771
  });
631
- // 点击遮罩层关闭
772
+ // 点击遮罩层关闭(只添加一次事件监听器)
632
773
  if (this.config.allowClose) {
633
774
  this.overlayElement.addEventListener('click', (e) => {
634
775
  if (e.target === this.overlayElement) {
@@ -680,45 +821,59 @@ class IframeManager {
680
821
  'allow-pointer-lock', // 允许指针锁定
681
822
  'allow-storage-access-by-user-activation' // 允许用户激活的存储访问
682
823
  ].join(' '));
683
- // 根据设备类型设置滚动行为
824
+ // 根据设备类型设置模式
684
825
  const actualMode = this.getActualMode();
685
826
  const isPC = actualMode === 'popup';
686
827
  this.iframeElement.scrolling = isPC ? 'auto' : 'no'; // PC显示滚动条,移动端禁用
687
- const containerStyles = {
688
- width: isPC ? `${this.config.width}px` : '100%',
689
- height: isPC ? `${this.config.height}px` : '100%',
690
- maxWidth: isPC ? '450px' : '100%',
691
- maxHeight: isPC ? '700px' : '100%',
828
+ // PC 模式:使用配置的宽度和高度
829
+ // 移动端:使用全屏
830
+ const containerStyles = isPC ? {
831
+ // PC 弹窗模式
832
+ width: `${this.config.width || 450}px`,
833
+ height: `${this.config.height || 600}px`,
834
+ maxWidth: '90vw',
835
+ maxHeight: '90vh',
692
836
  backgroundColor: '#ffffff',
693
- borderRadius: isPC ? '12px' : '12px 12px 0 0',
694
- boxShadow: isPC
695
- ? '0 20px 40px rgba(0, 0, 0, 0.15)'
696
- : '0 -4px 16px rgba(0, 0, 0, 0.25)',
837
+ borderRadius: '12px',
838
+ boxShadow: '0 20px 40px rgba(0, 0, 0, 0.15)',
697
839
  border: 'none',
698
840
  position: 'fixed',
699
841
  zIndex: '999999',
700
- // PC模式下的定位
701
- ...(isPC ? {
702
- top: '50%',
703
- left: '50%',
704
- transform: 'translate(-50%, -50%)'
705
- } : {
706
- // 移动端全屏模式 - 确保占满屏幕且不滚动
707
- top: '0',
708
- left: '0',
709
- bottom: '0',
710
- right: '0',
711
- transform: 'none',
712
- overflow: 'hidden', // 防止容器本身滚动
713
- position: 'fixed' // 确保固定定位
714
- }),
842
+ // PC模式:居中显示
843
+ top: '50%',
844
+ left: '50%',
845
+ transform: 'translate(-50%, -50%)',
846
+ overflow: 'hidden',
847
+ // 初始隐藏的关键样式
848
+ visibility: 'hidden',
849
+ opacity: '0',
850
+ display: 'none'
851
+ } : {
852
+ // 移动端全屏模式(强制 100% 宽度和高度)
853
+ width: '100%',
854
+ height: '100%',
855
+ maxWidth: '100%',
856
+ maxHeight: '100%',
857
+ backgroundColor: '#ffffff',
858
+ borderRadius: '12px 12px 0 0',
859
+ boxShadow: '0 -4px 16px rgba(0, 0, 0, 0.25)',
860
+ border: 'none',
861
+ position: 'fixed',
862
+ zIndex: '999999',
863
+ // 全屏模式 - 占满整个屏幕
864
+ top: '0',
865
+ left: '0',
866
+ bottom: '0',
867
+ right: '0',
868
+ transform: 'none',
869
+ overflow: 'hidden',
715
870
  // 初始隐藏的关键样式
716
871
  visibility: 'hidden',
717
872
  opacity: '0',
718
873
  display: 'none'
719
874
  };
720
875
  Object.assign(this.containerElement.style, containerStyles);
721
- // iframe填充整个容器,根据设备类型设置滚动样式
876
+ // iframe填充整个容器
722
877
  const iframeStyles = {
723
878
  width: '100%',
724
879
  height: '100%',
@@ -744,7 +899,9 @@ class IframeManager {
744
899
  });
745
900
  // 添加到body(预连接SSE,但不显示)
746
901
  document.body.appendChild(this.containerElement);
747
- console.log('CustomerSDK container created (hidden, ready for SSE)');
902
+ if (this.debug) {
903
+ console.log('CustomerSDK container created (hidden, ready for SSE)');
904
+ }
748
905
  }
749
906
  /**
750
907
  * 向iframe注入移动端优化样式(隐藏滚动条)
@@ -796,12 +953,16 @@ class IframeManager {
796
953
  `;
797
954
  // 注入样式
798
955
  iframeDoc.head?.appendChild(style);
799
- console.log('CustomerSDK mobile styles injected successfully');
956
+ if (this.debug) {
957
+ console.log('CustomerSDK mobile styles injected successfully');
958
+ }
800
959
  }
801
960
  }
802
961
  catch (error) {
803
962
  // 跨域限制时静默忽略
804
- console.log('Cannot inject styles due to cross-origin restrictions:', error);
963
+ if (this.debug) {
964
+ console.log('Cannot inject styles due to cross-origin restrictions:', error);
965
+ }
805
966
  }
806
967
  }
807
968
  /**
@@ -846,6 +1007,7 @@ class IframeManager {
846
1007
  }
847
1008
  /**
848
1009
  * 获取当前显示模式
1010
+ * PC 模式使用弹窗,移动端使用全屏
849
1011
  */
850
1012
  getActualMode() {
851
1013
  if (this.config.mode === 'auto') {
@@ -868,7 +1030,9 @@ class IframeManager {
868
1030
  * 处理来自iframe的消息
869
1031
  */
870
1032
  handleIframeMessage(data) {
871
- console.log('Message from iframe:', data);
1033
+ if (this.debug) {
1034
+ console.log('Message from iframe:', data);
1035
+ }
872
1036
  // 判断data是字符串还是对象,兼容两种格式
873
1037
  let messageType;
874
1038
  if (typeof data === 'string') {
@@ -878,13 +1042,17 @@ class IframeManager {
878
1042
  messageType = data.type;
879
1043
  }
880
1044
  else {
881
- console.log('Unknown message format:', data);
1045
+ if (this.debug) {
1046
+ console.log('Unknown message format:', data);
1047
+ }
882
1048
  return;
883
1049
  }
884
1050
  // 根据消息类型处理不同的操作
885
1051
  switch (messageType) {
886
1052
  case 'iframe_ready':
887
- console.log('Iframe is ready');
1053
+ if (this.debug) {
1054
+ console.log('Iframe is ready');
1055
+ }
888
1056
  break;
889
1057
  case 'close_iframe':
890
1058
  case 'close':
@@ -892,29 +1060,38 @@ class IframeManager {
892
1060
  break;
893
1061
  case 'resize_iframe':
894
1062
  case 'resize':
895
- if (data.width && data.height) {
1063
+ // PC模式支持 resize,移动端忽略
1064
+ const actualMode = this.getActualMode();
1065
+ if (actualMode === 'popup' && data.width && data.height) {
896
1066
  this.resizeIframe(data.width, data.height);
897
1067
  }
1068
+ else if (this.debug) {
1069
+ console.log('Resize request ignored (fullscreen mode)');
1070
+ }
898
1071
  break;
899
1072
  case 'new-message':
900
1073
  // 新消息通知 - 触发回调让外层处理
901
- console.log('Received new message notification');
1074
+ if (this.debug) {
1075
+ console.log('Received new message notification');
1076
+ }
902
1077
  if (this.config.onMessage) {
903
1078
  this.config.onMessage(messageType, data);
904
1079
  }
905
1080
  break;
906
1081
  default:
907
1082
  // 可以在这里添加自定义消息处理
908
- console.log('Custom message:', data);
1083
+ if (this.debug) {
1084
+ console.log('Custom message:', data);
1085
+ }
909
1086
  }
910
1087
  }
911
1088
  /**
912
- * 调整iframe大小
1089
+ * 调整iframe大小(PC模式支持)
913
1090
  */
914
1091
  resizeIframe(width, height) {
915
- if (this.iframeElement) {
916
- this.iframeElement.style.width = `${width}px`;
917
- this.iframeElement.style.height = `${height}px`;
1092
+ if (this.containerElement) {
1093
+ this.containerElement.style.width = `${width}px`;
1094
+ this.containerElement.style.height = `${height}px`;
918
1095
  }
919
1096
  }
920
1097
  }
@@ -20607,35 +20784,56 @@ class CustomerServiceSDK {
20607
20784
  this.screenshotManager = null;
20608
20785
  this.config = null;
20609
20786
  this.isInitialized = false;
20787
+ this.initResult = null; // 保存初始化结果
20788
+ this.debug = false; // debug 模式标志
20610
20789
  }
20611
20790
  /**
20612
20791
  * 初始化 SDK
20613
20792
  * @param config SDK配置
20614
20793
  * @param options UI选项(可选)
20794
+ * @returns 返回初始化信息(包含设备ID等)
20615
20795
  */
20616
20796
  async init(config, options) {
20617
20797
  if (this.isInitialized) {
20618
- console.warn('CustomerSDK already initialized');
20619
- return;
20798
+ if (this.debug) {
20799
+ console.warn('CustomerSDK already initialized');
20800
+ }
20801
+ // 如果已经初始化,返回之前保存的初始化信息
20802
+ if (this.initResult) {
20803
+ return this.initResult;
20804
+ }
20805
+ throw new Error('SDK already initialized but cannot retrieve initialization info');
20620
20806
  }
20621
20807
  this.config = config;
20808
+ this.debug = config.debug ?? false;
20622
20809
  try {
20623
20810
  // 获取设备指纹ID
20624
20811
  const deviceId = await this.getDeviceId();
20625
- console.log('Device ID:', deviceId);
20812
+ if (this.debug) {
20813
+ console.log('Device ID:', deviceId);
20814
+ }
20626
20815
  // 构建iframe URL(带参数)
20627
20816
  const iframeUrl = this.buildIframeUrl(config, deviceId);
20817
+ // 准备返回的初始化信息
20818
+ const initResult = {
20819
+ deviceId,
20820
+ iframeUrl,
20821
+ referrer: document.referrer,
20822
+ agent: config.agent,
20823
+ timestamp: Date.now()
20824
+ };
20628
20825
  // 创建悬浮图标管理器(支持自定义位置)
20629
20826
  const iconPosition = options?.iconPosition || undefined;
20630
- this.iconManager = new IconManager(iconPosition);
20827
+ this.iconManager = new IconManager(iconPosition, this.debug);
20631
20828
  await this.iconManager.show();
20632
20829
  // 创建iframe管理器(自动检测设备类型)
20633
20830
  this.iframeManager = new IframeManager({
20634
20831
  src: iframeUrl,
20635
- mode: 'auto', // 自动根据设备类型选择模式
20636
- width: 400,
20637
- height: 600,
20832
+ mode: 'auto', // 自动根据设备类型选择模式(PC弹窗,移动端全屏)
20833
+ width: options?.width || 450, // PC模式宽度(像素,默认450px),移动端不使用
20834
+ height: options?.height || 600, // PC模式高度(像素),移动端不使用(强制全屏)
20638
20835
  allowClose: true,
20836
+ debug: this.debug, // 传递 debug 标志
20639
20837
  onMessage: (messageType, _data) => {
20640
20838
  // 处理来自iframe的消息
20641
20839
  if (messageType === 'new-message') {
@@ -20645,8 +20843,9 @@ class CustomerServiceSDK {
20645
20843
  // checkScreenshot 消息由 ScreenshotManager 处理,不需要在这里处理
20646
20844
  },
20647
20845
  onClose: () => {
20648
- // iframe关闭时,清理图标拖动事件监听器
20846
+ // iframe关闭时,清理图标拖动事件监听器,并重新启用图标点击
20649
20847
  this.iconManager?.forceCleanupDragEvents();
20848
+ this.iconManager?.enableClick();
20650
20849
  },
20651
20850
  ...options
20652
20851
  });
@@ -20657,24 +20856,40 @@ class CustomerServiceSDK {
20657
20856
  // 打开iframe时清除红点通知
20658
20857
  this.clearNotification();
20659
20858
  this.iframeManager?.show();
20859
+ // iframe 打开后,禁用图标点击(防止重复打开)
20860
+ this.iconManager?.disableClick();
20660
20861
  });
20661
20862
  // 初始化截图管理器(如果启用了截图功能)
20662
20863
  if (config.screenshot) {
20663
20864
  // 默认截图目标为 document.body,可以通过配置自定义
20664
20865
  const targetElement = document.body;
20665
20866
  // 传入发送消息到 iframe 的回调函数
20666
- this.screenshotManager = new ScreenshotManager(targetElement, config.screenshot, (data) => {
20867
+ // debug 配置传递给截图管理器(通过 silentMode 的相反值)
20868
+ const screenshotOptions = {
20869
+ ...config.screenshot,
20870
+ silentMode: !this.debug // debug=true 时 silentMode=false(显示日志),debug=false 时 silentMode=true(隐藏日志)
20871
+ };
20872
+ this.screenshotManager = new ScreenshotManager(targetElement, screenshotOptions, (data) => {
20667
20873
  // 通过 IframeManager 发送消息到 iframe
20668
20874
  this.iframeManager?.sendToIframe(data);
20669
20875
  });
20670
20876
  // 自动启用截图功能(用于测试,实际使用时需要通过 iframe 消息启用)
20671
20877
  this.screenshotManager.enable(true);
20672
- console.log('CustomerSDK screenshot manager initialized and enabled');
20878
+ if (this.debug) {
20879
+ console.log('CustomerSDK screenshot manager initialized and enabled');
20880
+ }
20673
20881
  }
20674
20882
  this.isInitialized = true;
20675
- console.log('CustomerSDK initialized successfully (iframe pre-connected to SSE)');
20883
+ // 保存初始化结果,以便后续获取
20884
+ this.initResult = initResult;
20885
+ if (this.debug) {
20886
+ console.log('CustomerSDK initialized successfully (iframe pre-connected to SSE)');
20887
+ }
20888
+ // 返回初始化信息
20889
+ return initResult;
20676
20890
  }
20677
20891
  catch (error) {
20892
+ // 错误始终输出
20678
20893
  console.error('Failed to initialize CustomerSDK:', error);
20679
20894
  throw error;
20680
20895
  }
@@ -20743,7 +20958,9 @@ class CustomerServiceSDK {
20743
20958
  */
20744
20959
  showNotification(badgeCount = 1, options = {}) {
20745
20960
  if (!this.iconManager) {
20746
- console.warn('SDK not initialized');
20961
+ if (this.debug) {
20962
+ console.warn('SDK not initialized');
20963
+ }
20747
20964
  return;
20748
20965
  }
20749
20966
  this.iconManager.showNotification({
@@ -20775,7 +20992,9 @@ class CustomerServiceSDK {
20775
20992
  */
20776
20993
  async captureScreenshot(force = false) {
20777
20994
  if (!this.screenshotManager) {
20778
- console.warn('截图功能未启用');
20995
+ if (this.debug) {
20996
+ console.warn('截图功能未启用');
20997
+ }
20779
20998
  return false;
20780
20999
  }
20781
21000
  return await this.screenshotManager.captureOnce(force);
@@ -20785,11 +21004,15 @@ class CustomerServiceSDK {
20785
21004
  */
20786
21005
  enableScreenshot(enabled) {
20787
21006
  if (!this.screenshotManager) {
20788
- console.warn('截图管理器未初始化');
21007
+ if (this.debug) {
21008
+ console.warn('截图管理器未初始化');
21009
+ }
20789
21010
  return;
20790
21011
  }
20791
21012
  this.screenshotManager.enable(enabled);
20792
- console.log(`📸 截图功能已${enabled ? '启用' : '禁用'}`);
21013
+ if (this.debug) {
21014
+ console.log(`📸 截图功能已${enabled ? '启用' : '禁用'}`);
21015
+ }
20793
21016
  }
20794
21017
  /**
20795
21018
  * 获取截图状态
@@ -20803,11 +21026,15 @@ class CustomerServiceSDK {
20803
21026
  */
20804
21027
  updateScreenshotOptions(options) {
20805
21028
  if (!this.screenshotManager) {
20806
- console.warn('截图管理器未初始化,无法更新配置');
21029
+ if (this.debug) {
21030
+ console.warn('截图管理器未初始化,无法更新配置');
21031
+ }
20807
21032
  return;
20808
21033
  }
20809
21034
  this.screenshotManager.updateOptions(options);
20810
- console.log('📸 截图配置已更新:', options);
21035
+ if (this.debug) {
21036
+ console.log('📸 截图配置已更新:', options);
21037
+ }
20811
21038
  }
20812
21039
  /**
20813
21040
  * 销毁 SDK
@@ -20820,27 +21047,47 @@ class CustomerServiceSDK {
20820
21047
  this.iframeManager = null;
20821
21048
  this.screenshotManager = null;
20822
21049
  this.config = null;
21050
+ this.initResult = null;
20823
21051
  this.isInitialized = false;
20824
- console.log('CustomerSDK destroyed');
21052
+ if (this.debug) {
21053
+ console.log('CustomerSDK destroyed');
21054
+ }
21055
+ }
21056
+ /**
21057
+ * 获取初始化信息(设备ID等)
21058
+ */
21059
+ getInitResult() {
21060
+ return this.initResult;
20825
21061
  }
20826
21062
  /**
20827
21063
  * 获取设备指纹ID
20828
21064
  */
20829
21065
  async getDeviceId() {
20830
- console.log('🔍 Starting to get device fingerprint...');
21066
+ if (this.debug) {
21067
+ console.log('🔍 Starting to get device fingerprint...');
21068
+ }
20831
21069
  try {
20832
- console.log('📦 Loading FingerprintJS...');
21070
+ if (this.debug) {
21071
+ console.log('📦 Loading FingerprintJS...');
21072
+ }
20833
21073
  const fp = await index$1.load();
20834
- console.log('🎯 Getting device fingerprint...');
21074
+ if (this.debug) {
21075
+ console.log('🎯 Getting device fingerprint...');
21076
+ }
20835
21077
  const result = await fp.get();
20836
- console.log('✅ FingerprintJS result:', result);
20837
- console.log('🆔 Device ID obtained:', result.visitorId);
21078
+ if (this.debug) {
21079
+ console.log(' FingerprintJS result:', result);
21080
+ console.log('🆔 Device ID obtained:', result.visitorId);
21081
+ }
20838
21082
  return result.visitorId;
20839
21083
  }
20840
21084
  catch (error) {
21085
+ // 错误始终输出
20841
21086
  console.warn('❌ Failed to get device fingerprint, using fallback:', error);
20842
21087
  const fallbackId = 'device_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
20843
- console.log('🆔 Fallback Device ID:', fallbackId);
21088
+ if (this.debug) {
21089
+ console.log('🆔 Fallback Device ID:', fallbackId);
21090
+ }
20844
21091
  return fallbackId;
20845
21092
  }
20846
21093
  }
@@ -20871,12 +21118,13 @@ let globalSDKInstance = null;
20871
21118
  * 初始化 Customer SDK
20872
21119
  * @param config SDK配置
20873
21120
  * @param options UI选项(可选)
21121
+ * @returns 返回初始化信息(包含设备ID等)
20874
21122
  */
20875
21123
  const init = async (config, options) => {
20876
21124
  if (!globalSDKInstance) {
20877
21125
  globalSDKInstance = new CustomerServiceSDK();
20878
21126
  }
20879
- await globalSDKInstance.init(config, options);
21127
+ return await globalSDKInstance.init(config, options);
20880
21128
  };
20881
21129
  /**
20882
21130
  * 获取全局SDK实例
@@ -20939,6 +21187,13 @@ const getConnectionStatus = () => {
20939
21187
  const sdk = getInstance();
20940
21188
  return sdk.getConnectionStatus();
20941
21189
  };
21190
+ /**
21191
+ * 获取初始化信息(设备ID等)
21192
+ */
21193
+ const getInitResult = () => {
21194
+ const sdk = getInstance();
21195
+ return sdk.getInitResult();
21196
+ };
20942
21197
  /**
20943
21198
  * 消息通知API
20944
21199
  */
@@ -20982,6 +21237,7 @@ const updateScreenshotOptions = (options) => {
20982
21237
  var index = {
20983
21238
  init,
20984
21239
  getInstance,
21240
+ getInitResult,
20985
21241
  showIcon,
20986
21242
  hideIcon,
20987
21243
  setIconPosition,
@@ -21002,4 +21258,4 @@ var index = {
21002
21258
  destroy
21003
21259
  };
21004
21260
 
21005
- export { CustomerServiceSDK, captureScreenshot, clearNotification, closeChat, index as default, destroy, enableScreenshot, getConnectionStatus, getInstance, getScreenshotState, hideIcon, init, isChatOpen, openChat, sendToIframe, setIconCoordinates, setIconPosition, setIconStyle, setScreenshotTarget, showIcon, showNotification, updateScreenshotOptions };
21261
+ export { CustomerServiceSDK, captureScreenshot, clearNotification, closeChat, index as default, destroy, enableScreenshot, getConnectionStatus, getInitResult, getInstance, getScreenshotState, hideIcon, init, isChatOpen, openChat, sendToIframe, setIconCoordinates, setIconPosition, setIconStyle, setScreenshotTarget, showIcon, showNotification, updateScreenshotOptions };