customer-chat-sdk 1.1.2 → 1.1.4

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.
@@ -52,8 +52,13 @@ export declare class IframeManager {
52
52
  * 向iframe发送消息
53
53
  */
54
54
  sendToIframe(data: any): void;
55
+ /**
56
+ * 清理页面上孤立的遮罩层和容器元素(防止重复创建)
57
+ */
58
+ private cleanupOrphanedElements;
55
59
  /**
56
60
  * 创建遮罩层(PC模式使用)
61
+ * @param showImmediately 是否立即显示,默认 false(用于 init() 时创建但不显示)
57
62
  */
58
63
  private createOverlay;
59
64
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"IframeManager.d.ts","sourceRoot":"","sources":["../../src/core/IframeManager.ts"],"names":[],"mappings":"AACA,UAAU,aAAa;IACrB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,OAAO,CAAA;IACtC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,SAAS,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,IAAI,CAAA;IACpD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,aAAa,CAAiC;IACtD,OAAO,CAAC,cAAc,CAA2B;IACjD,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,KAAK,CAAiB;gBAElB,MAAM,GAAE,aAAkB;IActC;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAiB3B;;OAEG;IACH,IAAI,IAAI,IAAI;IAsEZ;;OAEG;IACH,IAAI,IAAI,IAAI;IAIZ;;OAEG;IACH,IAAI,IAAI,IAAI;IA0CZ;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,OAAO,IAAI,IAAI;IAef;;OAEG;IACH,YAAY,IAAI,OAAO;IAIvB;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI;IAM7B;;OAEG;IACH,OAAO,CAAC,aAAa;IAoDrB;;OAEG;IACH,OAAO,CAAC,YAAY;IAuIpB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA8D1B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAoCzB;;OAEG;IACH,OAAO,CAAC,cAAc;IAKtB;;;OAGG;IACH,OAAO,CAAC,aAAa;IAOrB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAS5B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAwD3B;;OAEG;IACH,OAAO,CAAC,YAAY;CAMrB"}
1
+ {"version":3,"file":"IframeManager.d.ts","sourceRoot":"","sources":["../../src/core/IframeManager.ts"],"names":[],"mappings":"AACA,UAAU,aAAa;IACrB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,OAAO,CAAA;IACtC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,SAAS,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,IAAI,CAAA;IACpD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,aAAa,CAAiC;IACtD,OAAO,CAAC,cAAc,CAA2B;IACjD,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,KAAK,CAAiB;gBAElB,MAAM,GAAE,aAAkB;IActC;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA8B3B;;OAEG;IACH,IAAI,IAAI,IAAI;IA2FZ;;OAEG;IACH,IAAI,IAAI,IAAI;IAIZ;;OAEG;IACH,IAAI,IAAI,IAAI;IA0CZ;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,OAAO,IAAI,IAAI;IAqBf;;OAEG;IACH,YAAY,IAAI,OAAO;IAIvB;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI;IAM7B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAwB/B;;;OAGG;IACH,OAAO,CAAC,aAAa;IAmDrB;;OAEG;IACH,OAAO,CAAC,YAAY;IA8IpB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA8D1B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAoCzB;;OAEG;IACH,OAAO,CAAC,cAAc;IAKtB;;;OAGG;IACH,OAAO,CAAC,aAAa;IAOrB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAS5B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAwD3B;;OAEG;IACH,OAAO,CAAC,YAAY;CAMrB"}
@@ -627,7 +627,18 @@ class IframeManager {
627
627
  */
628
628
  async init() {
629
629
  try {
630
+ // 关键修复:在初始化前,先清理页面上所有旧的遮罩层和容器元素
631
+ // 防止切换模式或多次初始化时产生重复的元素
632
+ this.cleanupOrphanedElements();
633
+ // PC模式:预创建遮罩层(隐藏状态),避免后续移动DOM导致iframe重新加载
634
+ const actualMode = this.getActualMode();
635
+ const isPC = actualMode === 'popup';
636
+ if (isPC) {
637
+ // 创建遮罩层但不立即显示(showImmediately = false)
638
+ this.createOverlay(false);
639
+ }
630
640
  // 创建隐藏的iframe(预连接到SSE)
641
+ // 注意:createIframe 会将容器添加到遮罩层(PC模式)或 body(移动端)
631
642
  this.createIframe();
632
643
  this.isCreated = true;
633
644
  if (this.debug) {
@@ -655,10 +666,30 @@ class IframeManager {
655
666
  const isPC = actualMode === 'popup';
656
667
  // PC模式:创建或显示遮罩层
657
668
  if (isPC) {
658
- this.createOverlay(); // createOverlay 内部会检查是否已存在
669
+ // 如果遮罩层不存在,创建并显示;如果已存在,直接显示
670
+ this.createOverlay(true); // showImmediately = true,立即显示
659
671
  }
660
672
  // 显示已创建的容器
673
+ // 关键优化:容器在 init() 时已经添加到正确位置,这里只改变样式,不移动 DOM
674
+ // 这样可以避免 iframe 重新加载
661
675
  if (this.containerElement) {
676
+ // 确保容器在 DOM 中(理论上已经在 init() 时添加,但为了安全)
677
+ if (!this.containerElement.parentNode) {
678
+ // 如果容器不在 DOM 中(异常情况),根据模式添加到正确位置
679
+ if (isPC && this.overlayElement) {
680
+ // PC模式:确保遮罩层在 DOM 中,然后将容器添加到遮罩层
681
+ if (!this.overlayElement.parentNode) {
682
+ document.body.appendChild(this.overlayElement);
683
+ }
684
+ this.overlayElement.appendChild(this.containerElement);
685
+ }
686
+ else {
687
+ // 移动端模式:直接添加到 body
688
+ document.body.appendChild(this.containerElement);
689
+ }
690
+ }
691
+ // 注意:不再检查容器是否在遮罩层内,因为 init() 时已经正确放置
692
+ // 如果移动容器会导致 iframe 重新加载,所以不移动
662
693
  if (isPC) {
663
694
  // PC模式:配置为居中弹窗样式
664
695
  Object.assign(this.containerElement.style, {
@@ -670,17 +701,18 @@ class IframeManager {
670
701
  opacity: '1',
671
702
  display: 'block'
672
703
  });
673
- // 关键优化:避免重复移动容器导致 iframe 重新加载
674
- // 只有当容器不在遮罩层内时才移动,且确保遮罩层在 DOM 中
704
+ // 显示遮罩层(确保在 DOM 中)
675
705
  if (this.overlayElement) {
676
- // 如果遮罩层不在 DOM 中,先添加到 DOM
706
+ // 确保遮罩层在 DOM
677
707
  if (!this.overlayElement.parentNode) {
678
708
  document.body.appendChild(this.overlayElement);
679
709
  }
680
- // 只有当容器不在遮罩层内时才移动(避免重复移动导致 iframe 重新加载)
681
- if (this.containerElement.parentNode !== this.overlayElement) {
682
- this.overlayElement.appendChild(this.containerElement);
683
- }
710
+ // 显示遮罩层(只改变样式,不移动)
711
+ Object.assign(this.overlayElement.style, {
712
+ visibility: 'visible',
713
+ opacity: '1',
714
+ display: 'flex'
715
+ });
684
716
  }
685
717
  }
686
718
  else {
@@ -761,11 +793,16 @@ class IframeManager {
761
793
  */
762
794
  destroy() {
763
795
  this.hide();
796
+ // 移除容器和遮罩层
764
797
  if (this.containerElement) {
765
798
  this.containerElement.remove();
766
799
  this.containerElement = null;
767
800
  this.iframeElement = null;
768
801
  }
802
+ if (this.overlayElement) {
803
+ this.overlayElement.remove();
804
+ this.overlayElement = null;
805
+ }
769
806
  this.isCreated = false;
770
807
  if (this.debug) {
771
808
  console.log('CustomerSDK container destroyed');
@@ -785,27 +822,50 @@ class IframeManager {
785
822
  this.iframeElement.contentWindow.postMessage(data, '*');
786
823
  }
787
824
  }
825
+ /**
826
+ * 清理页面上孤立的遮罩层和容器元素(防止重复创建)
827
+ */
828
+ cleanupOrphanedElements() {
829
+ // 清理所有旧的遮罩层元素(不属于当前实例的)
830
+ const existingOverlays = document.querySelectorAll('.customer-sdk-overlay');
831
+ existingOverlays.forEach((overlay) => {
832
+ if (overlay !== this.overlayElement) {
833
+ overlay.remove();
834
+ if (this.debug) {
835
+ console.log('清理旧的遮罩层元素');
836
+ }
837
+ }
838
+ });
839
+ // 清理所有旧的容器元素(不属于当前实例的)
840
+ const existingContainers = document.querySelectorAll('.customer-sdk-container');
841
+ existingContainers.forEach((container) => {
842
+ if (container !== this.containerElement) {
843
+ container.remove();
844
+ if (this.debug) {
845
+ console.log('清理旧的容器元素');
846
+ }
847
+ }
848
+ });
849
+ }
788
850
  /**
789
851
  * 创建遮罩层(PC模式使用)
852
+ * @param showImmediately 是否立即显示,默认 false(用于 init() 时创建但不显示)
790
853
  */
791
- createOverlay() {
792
- // 如果遮罩层已存在,直接显示即可
793
- if (this.overlayElement && this.overlayElement.parentNode) {
794
- Object.assign(this.overlayElement.style, {
795
- visibility: 'visible',
796
- opacity: '1',
797
- display: 'flex'
798
- });
799
- return;
800
- }
801
- // 如果遮罩层存在但不在 DOM 中,重新添加到 DOM
802
- if (this.overlayElement && !this.overlayElement.parentNode) {
803
- document.body.appendChild(this.overlayElement);
804
- Object.assign(this.overlayElement.style, {
805
- visibility: 'visible',
806
- opacity: '1',
807
- display: 'flex'
808
- });
854
+ createOverlay(showImmediately = false) {
855
+ // 如果遮罩层已存在
856
+ if (this.overlayElement) {
857
+ // 如果不在 DOM 中,添加到 DOM
858
+ if (!this.overlayElement.parentNode) {
859
+ document.body.appendChild(this.overlayElement);
860
+ }
861
+ // 根据参数决定是否显示
862
+ if (showImmediately) {
863
+ Object.assign(this.overlayElement.style, {
864
+ visibility: 'visible',
865
+ opacity: '1',
866
+ display: 'flex'
867
+ });
868
+ }
809
869
  return;
810
870
  }
811
871
  // 创建新的遮罩层
@@ -819,10 +879,12 @@ class IframeManager {
819
879
  height: '100%',
820
880
  backgroundColor: 'rgba(0, 0, 0, 0.5)',
821
881
  zIndex: '999998',
822
- display: 'flex',
882
+ display: showImmediately ? 'flex' : 'none', // 根据参数决定初始显示状态
823
883
  alignItems: 'center',
824
884
  justifyContent: 'center',
825
- cursor: this.config.allowClose ? 'pointer' : 'default'
885
+ cursor: this.config.allowClose ? 'pointer' : 'default',
886
+ visibility: showImmediately ? 'visible' : 'hidden',
887
+ opacity: showImmediately ? '1' : '0'
826
888
  });
827
889
  // 点击遮罩层关闭(只添加一次事件监听器)
828
890
  if (this.config.allowClose) {
@@ -952,8 +1014,16 @@ class IframeManager {
952
1014
  this.injectMobileStyles();
953
1015
  }
954
1016
  });
955
- // 添加到body(预连接SSE,但不显示)
956
- document.body.appendChild(this.containerElement);
1017
+ // 关键优化:PC模式将容器添加到遮罩层(如果遮罩层已创建),避免后续移动DOM导致iframe重新加载
1018
+ // 移动端直接添加到body
1019
+ if (isPC && this.overlayElement) {
1020
+ // PC模式:添加到遮罩层(遮罩层已在 init() 中创建)
1021
+ this.overlayElement.appendChild(this.containerElement);
1022
+ }
1023
+ else {
1024
+ // 移动端模式:直接添加到body
1025
+ document.body.appendChild(this.containerElement);
1026
+ }
957
1027
  if (this.debug) {
958
1028
  console.log('CustomerSDK container created (hidden, ready for SSE)');
959
1029
  }
@@ -623,7 +623,18 @@ class IframeManager {
623
623
  */
624
624
  async init() {
625
625
  try {
626
+ // 关键修复:在初始化前,先清理页面上所有旧的遮罩层和容器元素
627
+ // 防止切换模式或多次初始化时产生重复的元素
628
+ this.cleanupOrphanedElements();
629
+ // PC模式:预创建遮罩层(隐藏状态),避免后续移动DOM导致iframe重新加载
630
+ const actualMode = this.getActualMode();
631
+ const isPC = actualMode === 'popup';
632
+ if (isPC) {
633
+ // 创建遮罩层但不立即显示(showImmediately = false)
634
+ this.createOverlay(false);
635
+ }
626
636
  // 创建隐藏的iframe(预连接到SSE)
637
+ // 注意:createIframe 会将容器添加到遮罩层(PC模式)或 body(移动端)
627
638
  this.createIframe();
628
639
  this.isCreated = true;
629
640
  if (this.debug) {
@@ -651,10 +662,30 @@ class IframeManager {
651
662
  const isPC = actualMode === 'popup';
652
663
  // PC模式:创建或显示遮罩层
653
664
  if (isPC) {
654
- this.createOverlay(); // createOverlay 内部会检查是否已存在
665
+ // 如果遮罩层不存在,创建并显示;如果已存在,直接显示
666
+ this.createOverlay(true); // showImmediately = true,立即显示
655
667
  }
656
668
  // 显示已创建的容器
669
+ // 关键优化:容器在 init() 时已经添加到正确位置,这里只改变样式,不移动 DOM
670
+ // 这样可以避免 iframe 重新加载
657
671
  if (this.containerElement) {
672
+ // 确保容器在 DOM 中(理论上已经在 init() 时添加,但为了安全)
673
+ if (!this.containerElement.parentNode) {
674
+ // 如果容器不在 DOM 中(异常情况),根据模式添加到正确位置
675
+ if (isPC && this.overlayElement) {
676
+ // PC模式:确保遮罩层在 DOM 中,然后将容器添加到遮罩层
677
+ if (!this.overlayElement.parentNode) {
678
+ document.body.appendChild(this.overlayElement);
679
+ }
680
+ this.overlayElement.appendChild(this.containerElement);
681
+ }
682
+ else {
683
+ // 移动端模式:直接添加到 body
684
+ document.body.appendChild(this.containerElement);
685
+ }
686
+ }
687
+ // 注意:不再检查容器是否在遮罩层内,因为 init() 时已经正确放置
688
+ // 如果移动容器会导致 iframe 重新加载,所以不移动
658
689
  if (isPC) {
659
690
  // PC模式:配置为居中弹窗样式
660
691
  Object.assign(this.containerElement.style, {
@@ -666,17 +697,18 @@ class IframeManager {
666
697
  opacity: '1',
667
698
  display: 'block'
668
699
  });
669
- // 关键优化:避免重复移动容器导致 iframe 重新加载
670
- // 只有当容器不在遮罩层内时才移动,且确保遮罩层在 DOM 中
700
+ // 显示遮罩层(确保在 DOM 中)
671
701
  if (this.overlayElement) {
672
- // 如果遮罩层不在 DOM 中,先添加到 DOM
702
+ // 确保遮罩层在 DOM
673
703
  if (!this.overlayElement.parentNode) {
674
704
  document.body.appendChild(this.overlayElement);
675
705
  }
676
- // 只有当容器不在遮罩层内时才移动(避免重复移动导致 iframe 重新加载)
677
- if (this.containerElement.parentNode !== this.overlayElement) {
678
- this.overlayElement.appendChild(this.containerElement);
679
- }
706
+ // 显示遮罩层(只改变样式,不移动)
707
+ Object.assign(this.overlayElement.style, {
708
+ visibility: 'visible',
709
+ opacity: '1',
710
+ display: 'flex'
711
+ });
680
712
  }
681
713
  }
682
714
  else {
@@ -757,11 +789,16 @@ class IframeManager {
757
789
  */
758
790
  destroy() {
759
791
  this.hide();
792
+ // 移除容器和遮罩层
760
793
  if (this.containerElement) {
761
794
  this.containerElement.remove();
762
795
  this.containerElement = null;
763
796
  this.iframeElement = null;
764
797
  }
798
+ if (this.overlayElement) {
799
+ this.overlayElement.remove();
800
+ this.overlayElement = null;
801
+ }
765
802
  this.isCreated = false;
766
803
  if (this.debug) {
767
804
  console.log('CustomerSDK container destroyed');
@@ -781,27 +818,50 @@ class IframeManager {
781
818
  this.iframeElement.contentWindow.postMessage(data, '*');
782
819
  }
783
820
  }
821
+ /**
822
+ * 清理页面上孤立的遮罩层和容器元素(防止重复创建)
823
+ */
824
+ cleanupOrphanedElements() {
825
+ // 清理所有旧的遮罩层元素(不属于当前实例的)
826
+ const existingOverlays = document.querySelectorAll('.customer-sdk-overlay');
827
+ existingOverlays.forEach((overlay) => {
828
+ if (overlay !== this.overlayElement) {
829
+ overlay.remove();
830
+ if (this.debug) {
831
+ console.log('清理旧的遮罩层元素');
832
+ }
833
+ }
834
+ });
835
+ // 清理所有旧的容器元素(不属于当前实例的)
836
+ const existingContainers = document.querySelectorAll('.customer-sdk-container');
837
+ existingContainers.forEach((container) => {
838
+ if (container !== this.containerElement) {
839
+ container.remove();
840
+ if (this.debug) {
841
+ console.log('清理旧的容器元素');
842
+ }
843
+ }
844
+ });
845
+ }
784
846
  /**
785
847
  * 创建遮罩层(PC模式使用)
848
+ * @param showImmediately 是否立即显示,默认 false(用于 init() 时创建但不显示)
786
849
  */
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
- });
850
+ createOverlay(showImmediately = false) {
851
+ // 如果遮罩层已存在
852
+ if (this.overlayElement) {
853
+ // 如果不在 DOM 中,添加到 DOM
854
+ if (!this.overlayElement.parentNode) {
855
+ document.body.appendChild(this.overlayElement);
856
+ }
857
+ // 根据参数决定是否显示
858
+ if (showImmediately) {
859
+ Object.assign(this.overlayElement.style, {
860
+ visibility: 'visible',
861
+ opacity: '1',
862
+ display: 'flex'
863
+ });
864
+ }
805
865
  return;
806
866
  }
807
867
  // 创建新的遮罩层
@@ -815,10 +875,12 @@ class IframeManager {
815
875
  height: '100%',
816
876
  backgroundColor: 'rgba(0, 0, 0, 0.5)',
817
877
  zIndex: '999998',
818
- display: 'flex',
878
+ display: showImmediately ? 'flex' : 'none', // 根据参数决定初始显示状态
819
879
  alignItems: 'center',
820
880
  justifyContent: 'center',
821
- cursor: this.config.allowClose ? 'pointer' : 'default'
881
+ cursor: this.config.allowClose ? 'pointer' : 'default',
882
+ visibility: showImmediately ? 'visible' : 'hidden',
883
+ opacity: showImmediately ? '1' : '0'
822
884
  });
823
885
  // 点击遮罩层关闭(只添加一次事件监听器)
824
886
  if (this.config.allowClose) {
@@ -948,8 +1010,16 @@ class IframeManager {
948
1010
  this.injectMobileStyles();
949
1011
  }
950
1012
  });
951
- // 添加到body(预连接SSE,但不显示)
952
- document.body.appendChild(this.containerElement);
1013
+ // 关键优化:PC模式将容器添加到遮罩层(如果遮罩层已创建),避免后续移动DOM导致iframe重新加载
1014
+ // 移动端直接添加到body
1015
+ if (isPC && this.overlayElement) {
1016
+ // PC模式:添加到遮罩层(遮罩层已在 init() 中创建)
1017
+ this.overlayElement.appendChild(this.containerElement);
1018
+ }
1019
+ else {
1020
+ // 移动端模式:直接添加到body
1021
+ document.body.appendChild(this.containerElement);
1022
+ }
953
1023
  if (this.debug) {
954
1024
  console.log('CustomerSDK container created (hidden, ready for SSE)');
955
1025
  }