customer-chat-sdk 1.1.16 → 1.1.17
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/IconManager.d.ts +1 -0
- package/dist/core/IconManager.d.ts.map +1 -1
- package/dist/core/IframeManager.d.ts +10 -4
- package/dist/core/IframeManager.d.ts.map +1 -1
- package/dist/customer-sdk.cjs.js +164 -120
- package/dist/customer-sdk.esm.js +164 -120
- package/dist/customer-sdk.min.js +2 -2
- package/package.json +3 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IconManager.d.ts","sourceRoot":"","sources":["../../src/core/IconManager.ts"],"names":[],"mappings":"AAGA,UAAU,mBAAmB;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,UAAU,YAAY;IACpB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACnB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CACpB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,WAAW,CAA2B;IAC9C,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,eAAe,CAA4B;IACnD,OAAO,CAAC,oBAAoB,CAAqC;IACjE,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,MAAM,CAAoC;IAGlD,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,UAAU,CAAiB;IACnC,OAAO,CAAC,iBAAiB,CAAiB;IAC1C,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,cAAc,CAAK;
|
|
1
|
+
{"version":3,"file":"IconManager.d.ts","sourceRoot":"","sources":["../../src/core/IconManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -88,10 +88,16 @@ export declare class IframeManager {
|
|
|
88
88
|
*/
|
|
89
89
|
private handleIframeMessage;
|
|
90
90
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
* 向调用 SDK 的地方(Vue 页面)广播消息
|
|
92
|
+
* 通过 window.dispatchEvent 触发事件,让 Vue 页面中的 window.addEventListener('message') 可以收到
|
|
93
|
+
*
|
|
94
|
+
* 重要说明:
|
|
95
|
+
* 1. window.dispatchEvent 创建的事件会在同一窗口内触发所有监听器,无论 origin 是什么,Vue 页面都能收到
|
|
96
|
+
* 2. MessageEvent 的 origin 属性不能是 '*',必须是有效的 origin 字符串(如 'http://localhost:5173')
|
|
97
|
+
* 3. 我们使用 window.location.origin 来标识消息来源(当前页面的 origin)
|
|
98
|
+
* 4. 如果 Vue 页面需要检查 origin,应该检查 window.location.origin 而不是 '*'
|
|
99
|
+
*/
|
|
100
|
+
private broadcastMessageToPage;
|
|
95
101
|
/**
|
|
96
102
|
* 调整iframe大小(PC模式支持)
|
|
97
103
|
*/
|
|
@@ -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,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM,CAAA;IAC7B,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,gBAAgB,CAA2B;IACnD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,aAAa,CAA2B;IAChD,OAAO,CAAC,cAAc,CAA+C;gBAEzD,MAAM,GAAE,aAAkB;IAetC;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgC3B;;OAEG;IACH,IAAI,IAAI,IAAI;IA2CZ;;OAEG;IACH,IAAI,IAAI,IAAI;IAIZ;;OAEG;IACH,IAAI,IAAI,IAAI;IA6BZ;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,OAAO,IAAI,IAAI;IAsBf;;OAEG;IACH,YAAY,IAAI,OAAO;IAIvB;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI;IAM7B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAa/B;;OAEG;IACH,OAAO,CAAC,YAAY;IAuLpB;;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;
|
|
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,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM,CAAA;IAC7B,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,gBAAgB,CAA2B;IACnD,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,aAAa,CAA2B;IAChD,OAAO,CAAC,cAAc,CAA+C;gBAEzD,MAAM,GAAE,aAAkB;IAetC;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgC3B;;OAEG;IACH,IAAI,IAAI,IAAI;IA2CZ;;OAEG;IACH,IAAI,IAAI,IAAI;IAIZ;;OAEG;IACH,IAAI,IAAI,IAAI;IA6BZ;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,OAAO,IAAI,IAAI;IAsBf;;OAEG;IACH,YAAY,IAAI,OAAO;IAIvB;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI;IAM7B;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAa/B;;OAEG;IACH,OAAO,CAAC,YAAY;IAuLpB;;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;IAgC5B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA8F3B;;;;;;;;;OASG;IACH,OAAO,CAAC,sBAAsB;IA+B9B;;OAEG;IACH,OAAO,CAAC,YAAY;IAOpB;;OAEG;IACH,OAAO,CAAC,gBAAgB;CA0BzB"}
|
package/dist/customer-sdk.cjs.js
CHANGED
|
@@ -22,6 +22,7 @@ class IconManager {
|
|
|
22
22
|
this.lastTouchPosition = { x: 0, y: 0 }; // 最后触摸位置
|
|
23
23
|
this.touchStartTime = 0; // 触摸开始时间
|
|
24
24
|
this.clickThreshold = 15; // 点击阈值(像素)
|
|
25
|
+
this.stoppedByIframe = false; // 是否因为 iframe 而停止拖动(用于避免触发磁性吸附)
|
|
25
26
|
// 性能优化:缓存容器信息,避免频繁查询 DOM
|
|
26
27
|
this.cachedContainer = null;
|
|
27
28
|
this.cachedContainerRect = null;
|
|
@@ -84,6 +85,7 @@ class IconManager {
|
|
|
84
85
|
this.iconElement = document.createElement('div');
|
|
85
86
|
this.iconElement.className = 'customer-sdk-icon';
|
|
86
87
|
// 直接设置样式 - 图标容器
|
|
88
|
+
// 关键优化:使用 pointer-events: none 让事件穿透到 iframe,子元素使用 pointer-events: auto 保持可点击
|
|
87
89
|
const defaultStyle = {
|
|
88
90
|
position: 'absolute',
|
|
89
91
|
width: '30px',
|
|
@@ -100,7 +102,8 @@ class IconManager {
|
|
|
100
102
|
transition: 'transform 0.2s ease',
|
|
101
103
|
border: 'none',
|
|
102
104
|
outline: 'none',
|
|
103
|
-
overflow: 'visible' // 允许红点显示在图标外部
|
|
105
|
+
overflow: 'visible', // 允许红点显示在图标外部
|
|
106
|
+
pointerEvents: 'none' // 让事件穿透到 iframe,只有子元素可以接收事件
|
|
104
107
|
};
|
|
105
108
|
// 如果指定了位置,使用left/top;否则使用默认的bottom/right
|
|
106
109
|
if (this.iconPosition) {
|
|
@@ -198,7 +201,8 @@ class IconManager {
|
|
|
198
201
|
height: '100%',
|
|
199
202
|
borderRadius: '50%',
|
|
200
203
|
overflow: 'hidden', // 限制图片溢出圆形边界
|
|
201
|
-
position: 'relative'
|
|
204
|
+
position: 'relative',
|
|
205
|
+
pointerEvents: 'auto' // 子元素启用指针事件,让 icon 可以点击和拖动
|
|
202
206
|
});
|
|
203
207
|
imgContainer.appendChild(iconImg);
|
|
204
208
|
this.iconElement.appendChild(imgContainer);
|
|
@@ -601,8 +605,38 @@ class IconManager {
|
|
|
601
605
|
target.closest('.customer-sdk-overlay'))) {
|
|
602
606
|
return;
|
|
603
607
|
}
|
|
608
|
+
// 关键优化:检查触摸点是否真的在 icon 的圆形区域内
|
|
609
|
+
// 如果不在,让事件穿透到 iframe,不触发拖动
|
|
610
|
+
if (this.iconElement) {
|
|
611
|
+
const iconRect = this.iconElement.getBoundingClientRect();
|
|
612
|
+
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
|
|
613
|
+
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
|
|
614
|
+
// 计算触摸点相对于 icon 中心的位置
|
|
615
|
+
const iconCenterX = iconRect.left + iconRect.width / 2;
|
|
616
|
+
const iconCenterY = iconRect.top + iconRect.height / 2;
|
|
617
|
+
const iconRadius = Math.min(iconRect.width, iconRect.height) / 2;
|
|
618
|
+
const distanceFromCenter = Math.sqrt(Math.pow(clientX - iconCenterX, 2) + Math.pow(clientY - iconCenterY, 2));
|
|
619
|
+
// 如果触摸点在 icon 圆形区域外(留一点边距,比如 10px),不处理拖动
|
|
620
|
+
// 这样可以让 iframe 内的滑动正常工作
|
|
621
|
+
// 但边距不能太大,否则无法拖动
|
|
622
|
+
if (distanceFromCenter > iconRadius + 10) {
|
|
623
|
+
if (this.debug) {
|
|
624
|
+
console.log('[IconManager] Touch point outside icon circle, allowing event to pass through', {
|
|
625
|
+
distance: distanceFromCenter,
|
|
626
|
+
radius: iconRadius,
|
|
627
|
+
touchPoint: { x: clientX, y: clientY },
|
|
628
|
+
iconCenter: { x: iconCenterX, y: iconCenterY }
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
604
634
|
e.preventDefault();
|
|
605
635
|
e.stopPropagation();
|
|
636
|
+
// 拖动开始时,临时启用 pointer-events 确保拖动事件能正常接收
|
|
637
|
+
if (this.iconElement) {
|
|
638
|
+
this.iconElement.style.pointerEvents = 'auto';
|
|
639
|
+
}
|
|
606
640
|
// 重置状态
|
|
607
641
|
this.hasMoved = false;
|
|
608
642
|
this.dragStarted = false;
|
|
@@ -782,29 +816,8 @@ class IconManager {
|
|
|
782
816
|
});
|
|
783
817
|
}
|
|
784
818
|
}
|
|
785
|
-
//
|
|
786
|
-
|
|
787
|
-
if (clientX >= rect.left &&
|
|
788
|
-
clientX <= rect.right &&
|
|
789
|
-
clientY >= rect.top &&
|
|
790
|
-
clientY <= rect.bottom) {
|
|
791
|
-
// 鼠标在 iframe 上,立即停止拖动
|
|
792
|
-
if (this.debug) {
|
|
793
|
-
console.log('[IconManager] Mouse over iframe detected, stopping drag immediately', {
|
|
794
|
-
mousePosition: { x: clientX, y: clientY },
|
|
795
|
-
iframeRect: {
|
|
796
|
-
left: rect.left,
|
|
797
|
-
top: rect.top,
|
|
798
|
-
right: rect.right,
|
|
799
|
-
bottom: rect.bottom
|
|
800
|
-
},
|
|
801
|
-
iframeSrc: element.src || 'no src'
|
|
802
|
-
});
|
|
803
|
-
}
|
|
804
|
-
this.stopDrag();
|
|
805
|
-
return;
|
|
806
|
-
}
|
|
807
|
-
}
|
|
819
|
+
// 移除 iframe 检测停止拖动的逻辑,让拖动可以在 iframe 上顺畅进行
|
|
820
|
+
// 不再因为检测到 iframe 而停止拖动,保持拖动的流畅性
|
|
808
821
|
}
|
|
809
822
|
// 更新最后记录的鼠标位置和时间戳(用于主动检测)
|
|
810
823
|
const now = Date.now();
|
|
@@ -914,19 +927,18 @@ class IconManager {
|
|
|
914
927
|
// 计算新位置
|
|
915
928
|
let newX = clientX - this.dragOffset.x - containerRect.left;
|
|
916
929
|
let newY = clientY - this.dragOffset.y - containerRect.top;
|
|
917
|
-
//
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
}
|
|
930
|
+
// 允许拖动到容器外(允许负值),但限制在合理范围内(避免拖得太远)
|
|
931
|
+
// 允许拖动到容器外,最多隐藏到只剩一小部分可见
|
|
932
|
+
const minX = -iconWidth + 5; // 允许向左拖动,最多隐藏到只剩 5px
|
|
933
|
+
const maxX = container === document.body
|
|
934
|
+
? window.innerWidth - 5 // 允许向右拖动,最多隐藏到只剩 5px
|
|
935
|
+
: containerRect.width + iconWidth - 5; // 允许超出容器,最多隐藏到只剩 5px
|
|
936
|
+
const minY = -iconHeight + 5; // 允许向上拖动,最多隐藏到只剩 5px
|
|
937
|
+
const maxY = container === document.body
|
|
938
|
+
? window.innerHeight - 5 // 允许向下拖动,最多隐藏到只剩 5px
|
|
939
|
+
: containerRect.height + iconHeight - 5; // 允许超出容器,最多隐藏到只剩 5px
|
|
940
|
+
newX = Math.max(minX, Math.min(newX, maxX));
|
|
941
|
+
newY = Math.max(minY, Math.min(newY, maxY));
|
|
930
942
|
// 性能优化:使用 requestAnimationFrame 节流位置更新
|
|
931
943
|
this.pendingPosition.x = newX;
|
|
932
944
|
this.pendingPosition.y = newY;
|
|
@@ -993,6 +1005,8 @@ class IconManager {
|
|
|
993
1005
|
// 恢复样式
|
|
994
1006
|
this.iconElement.style.transition = 'transform 0.2s ease';
|
|
995
1007
|
this.iconElement.style.cursor = 'pointer';
|
|
1008
|
+
// 拖动结束时,恢复 pointer-events: none 让事件穿透到 iframe
|
|
1009
|
+
this.iconElement.style.pointerEvents = 'none';
|
|
996
1010
|
// 检查是否是点击
|
|
997
1011
|
const touchDuration = Date.now() - this.touchStartTime;
|
|
998
1012
|
const isValidClick = !this.hasMoved && touchDuration < 1000 && !this.dragStarted;
|
|
@@ -1009,26 +1023,37 @@ class IconManager {
|
|
|
1009
1023
|
}
|
|
1010
1024
|
// 先保存点击状态,然后重置拖动状态
|
|
1011
1025
|
const wasClick = isValidClick;
|
|
1012
|
-
//
|
|
1013
|
-
|
|
1014
|
-
|
|
1026
|
+
// 保存 stoppedByIframe 状态,用于后续判断是否启动自动吸附
|
|
1027
|
+
const wasStoppedByIframe = this.stoppedByIframe;
|
|
1028
|
+
// 如果是因为 iframe 而停止拖动,不执行磁性吸附和自动吸附
|
|
1029
|
+
// 这样可以避免拖动到 iframe 上时 icon 缩回去
|
|
1030
|
+
if (this.stoppedByIframe) {
|
|
1031
|
+
if (this.debug) {
|
|
1032
|
+
console.log('[IconManager] Drag stopped by iframe, skipping magnetic snap and auto attach');
|
|
1033
|
+
}
|
|
1015
1034
|
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
//
|
|
1022
|
-
if (
|
|
1023
|
-
const
|
|
1024
|
-
const
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
if (
|
|
1031
|
-
|
|
1035
|
+
else {
|
|
1036
|
+
// 如果真正拖动过,执行磁性吸附
|
|
1037
|
+
if (this.dragStarted && this.magnetic && this.iconElement) {
|
|
1038
|
+
this.magneticSnap();
|
|
1039
|
+
}
|
|
1040
|
+
// 如果真正拖动过,保存当前位置到 iconPosition
|
|
1041
|
+
if (this.dragStarted && this.isDragging && this.iconElement) {
|
|
1042
|
+
const computedStyle = window.getComputedStyle(this.iconElement);
|
|
1043
|
+
const left = computedStyle.left;
|
|
1044
|
+
const top = computedStyle.top;
|
|
1045
|
+
// 如果 left/top 是有效的像素值,保存到 iconPosition
|
|
1046
|
+
if (left !== 'auto' && top !== 'auto') {
|
|
1047
|
+
const leftValue = parseFloat(left);
|
|
1048
|
+
const topValue = parseFloat(top);
|
|
1049
|
+
if (!isNaN(leftValue) && !isNaN(topValue)) {
|
|
1050
|
+
this.iconPosition = {
|
|
1051
|
+
x: leftValue,
|
|
1052
|
+
y: topValue
|
|
1053
|
+
};
|
|
1054
|
+
if (this.debug) {
|
|
1055
|
+
console.log('Icon position saved:', this.iconPosition);
|
|
1056
|
+
}
|
|
1032
1057
|
}
|
|
1033
1058
|
}
|
|
1034
1059
|
}
|
|
@@ -1038,8 +1063,10 @@ class IconManager {
|
|
|
1038
1063
|
this.isDragging = false;
|
|
1039
1064
|
this.dragStarted = false;
|
|
1040
1065
|
this.isStoppingDrag = false; // 重置停止标志
|
|
1066
|
+
this.stoppedByIframe = false; // 重置 iframe 停止标志
|
|
1041
1067
|
// 如果拖动后没有吸附到侧边,启动自动吸附定时器
|
|
1042
|
-
|
|
1068
|
+
// 但如果是因为 iframe 而停止拖动,不启动自动吸附
|
|
1069
|
+
if (!wasStoppedByIframe && !this.isAttachedToSide && this.sideAttach && this.autoAttachDelay > 0) {
|
|
1043
1070
|
this.startAutoAttachTimer();
|
|
1044
1071
|
}
|
|
1045
1072
|
if (wasClick) {
|
|
@@ -1107,30 +1134,8 @@ class IconManager {
|
|
|
1107
1134
|
console.log(`[IconManager] Active detection: Updated iframe cache (${this.cachedIframes.length} iframes)`);
|
|
1108
1135
|
}
|
|
1109
1136
|
}
|
|
1110
|
-
//
|
|
1111
|
-
|
|
1112
|
-
if (clientX >= rect.left &&
|
|
1113
|
-
clientX <= rect.right &&
|
|
1114
|
-
clientY >= rect.top &&
|
|
1115
|
-
clientY <= rect.bottom) {
|
|
1116
|
-
// 鼠标在 iframe 上,立即停止拖动
|
|
1117
|
-
if (this.debug) {
|
|
1118
|
-
console.log('[IconManager] Active detection: Mouse over iframe detected, stopping drag immediately', {
|
|
1119
|
-
mousePosition: { x: clientX, y: clientY },
|
|
1120
|
-
iframeRect: {
|
|
1121
|
-
left: rect.left,
|
|
1122
|
-
top: rect.top,
|
|
1123
|
-
right: rect.right,
|
|
1124
|
-
bottom: rect.bottom
|
|
1125
|
-
},
|
|
1126
|
-
iframeSrc: element.src || 'no src',
|
|
1127
|
-
timeSinceLastMove
|
|
1128
|
-
});
|
|
1129
|
-
}
|
|
1130
|
-
this.stopDrag();
|
|
1131
|
-
return;
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1137
|
+
// 移除 iframe 检测停止拖动的逻辑,让拖动可以在 iframe 上顺畅进行
|
|
1138
|
+
// 不再因为检测到 iframe 而停止拖动,保持拖动的流畅性
|
|
1134
1139
|
}
|
|
1135
1140
|
// 继续检测
|
|
1136
1141
|
this.activeDetectionRafId = requestAnimationFrame(checkMousePosition);
|
|
@@ -1483,6 +1488,7 @@ class IconManager {
|
|
|
1483
1488
|
justifyContent: 'center',
|
|
1484
1489
|
zIndex: '1000001',
|
|
1485
1490
|
boxShadow: '0 -1px 4px rgba(0, 0, 0, 0.3), 0 2px 8px rgba(0, 0, 0, 0.15)',
|
|
1491
|
+
pointerEvents: 'auto', // 子元素启用指针事件,让 badge 可以接收事件
|
|
1486
1492
|
...(count === 0 && text === '' && {
|
|
1487
1493
|
width: '12px',
|
|
1488
1494
|
height: '12px',
|
|
@@ -1974,10 +1980,23 @@ class IframeManager {
|
|
|
1974
1980
|
}
|
|
1975
1981
|
// 创建新的消息处理器并保存引用
|
|
1976
1982
|
this.messageHandler = (event) => {
|
|
1977
|
-
//
|
|
1978
|
-
|
|
1979
|
-
|
|
1983
|
+
// 关键:过滤掉自己通过 dispatchEvent 发送的消息,避免无限循环
|
|
1984
|
+
// 自己发送的消息 source 是 window,而 iframe 发送的消息 source 是 iframe.contentWindow
|
|
1985
|
+
if (event.source === window) {
|
|
1986
|
+
if (this.debug) {
|
|
1987
|
+
console.log('[IframeManager] Ignoring self-broadcasted message to prevent infinite loop:', event.data);
|
|
1988
|
+
}
|
|
1989
|
+
return;
|
|
1990
|
+
}
|
|
1991
|
+
// 不验证来源,直接处理所有消息(确保消息能够被接收)
|
|
1992
|
+
if (this.debug) {
|
|
1993
|
+
console.log('[IframeManager] Message received:', {
|
|
1994
|
+
data: event.data,
|
|
1995
|
+
origin: event.origin,
|
|
1996
|
+
source: event.source
|
|
1997
|
+
});
|
|
1980
1998
|
}
|
|
1999
|
+
this.handleIframeMessage(event.data);
|
|
1981
2000
|
};
|
|
1982
2001
|
window.addEventListener('message', this.messageHandler, false);
|
|
1983
2002
|
}
|
|
@@ -1986,7 +2005,7 @@ class IframeManager {
|
|
|
1986
2005
|
*/
|
|
1987
2006
|
handleIframeMessage(data) {
|
|
1988
2007
|
if (this.debug) {
|
|
1989
|
-
console.log('Message from iframe:', data);
|
|
2008
|
+
console.log('[IframeManager] Message from iframe received:', data);
|
|
1990
2009
|
}
|
|
1991
2010
|
// 判断data是字符串还是对象,兼容两种格式
|
|
1992
2011
|
let messageType;
|
|
@@ -1998,10 +2017,13 @@ class IframeManager {
|
|
|
1998
2017
|
}
|
|
1999
2018
|
else {
|
|
2000
2019
|
if (this.debug) {
|
|
2001
|
-
console.log('Unknown message format:', data);
|
|
2020
|
+
console.log('[IframeManager] Unknown message format:', data);
|
|
2002
2021
|
}
|
|
2003
2022
|
return;
|
|
2004
2023
|
}
|
|
2024
|
+
if (this.debug) {
|
|
2025
|
+
console.log('[IframeManager] Parsed message type:', messageType);
|
|
2026
|
+
}
|
|
2005
2027
|
// 根据消息类型处理不同的操作
|
|
2006
2028
|
switch (messageType) {
|
|
2007
2029
|
case 'iframe_ready':
|
|
@@ -2034,11 +2056,22 @@ class IframeManager {
|
|
|
2034
2056
|
}
|
|
2035
2057
|
break;
|
|
2036
2058
|
case 'goto-login':
|
|
2037
|
-
// 登录跳转消息 -
|
|
2059
|
+
// 登录跳转消息 - 广播给调用 SDK 的 Vue 页面
|
|
2060
|
+
if (this.debug) {
|
|
2061
|
+
console.log('Received goto-login message, broadcasting to Vue page');
|
|
2062
|
+
}
|
|
2063
|
+
this.broadcastMessageToPage(data);
|
|
2064
|
+
if (this.config.onMessage) {
|
|
2065
|
+
this.config.onMessage(messageType, data);
|
|
2066
|
+
}
|
|
2067
|
+
break;
|
|
2068
|
+
case 'DCR_DEPOSIT':
|
|
2069
|
+
// 存款跳转消息 - 广播给调用 SDK 的 Vue 页面
|
|
2070
|
+
// payload.path 是动态的,例如: "?depositType=0&walletId=69382"
|
|
2038
2071
|
if (this.debug) {
|
|
2039
|
-
console.log('Received
|
|
2072
|
+
console.log('Received DCR_DEPOSIT message, broadcasting to Vue page:', data);
|
|
2040
2073
|
}
|
|
2041
|
-
this.
|
|
2074
|
+
this.broadcastMessageToPage(data);
|
|
2042
2075
|
if (this.config.onMessage) {
|
|
2043
2076
|
this.config.onMessage(messageType, data);
|
|
2044
2077
|
}
|
|
@@ -2046,11 +2079,11 @@ class IframeManager {
|
|
|
2046
2079
|
default:
|
|
2047
2080
|
// 处理动态消息:gotoActivityDetailById:xxx
|
|
2048
2081
|
if (typeof messageType === 'string' && messageType.startsWith('gotoActivityDetailById:')) {
|
|
2049
|
-
// 活动详情跳转消息 -
|
|
2082
|
+
// 活动详情跳转消息 - 广播给调用 SDK 的 Vue 页面
|
|
2050
2083
|
if (this.debug) {
|
|
2051
|
-
console.log('Received gotoActivityDetailById message,
|
|
2084
|
+
console.log('Received gotoActivityDetailById message, broadcasting to Vue page:', messageType);
|
|
2052
2085
|
}
|
|
2053
|
-
this.
|
|
2086
|
+
this.broadcastMessageToPage(data);
|
|
2054
2087
|
if (this.config.onMessage) {
|
|
2055
2088
|
this.config.onMessage(messageType, data);
|
|
2056
2089
|
}
|
|
@@ -2065,31 +2098,42 @@ class IframeManager {
|
|
|
2065
2098
|
}
|
|
2066
2099
|
}
|
|
2067
2100
|
/**
|
|
2068
|
-
*
|
|
2069
|
-
*
|
|
2101
|
+
* 向调用 SDK 的地方(Vue 页面)广播消息
|
|
2102
|
+
* 通过 window.dispatchEvent 触发事件,让 Vue 页面中的 window.addEventListener('message') 可以收到
|
|
2103
|
+
*
|
|
2104
|
+
* 重要说明:
|
|
2105
|
+
* 1. window.dispatchEvent 创建的事件会在同一窗口内触发所有监听器,无论 origin 是什么,Vue 页面都能收到
|
|
2106
|
+
* 2. MessageEvent 的 origin 属性不能是 '*',必须是有效的 origin 字符串(如 'http://localhost:5173')
|
|
2107
|
+
* 3. 我们使用 window.location.origin 来标识消息来源(当前页面的 origin)
|
|
2108
|
+
* 4. 如果 Vue 页面需要检查 origin,应该检查 window.location.origin 而不是 '*'
|
|
2070
2109
|
*/
|
|
2071
|
-
|
|
2110
|
+
broadcastMessageToPage(data) {
|
|
2072
2111
|
try {
|
|
2073
|
-
//
|
|
2074
|
-
//
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
// 方式2:使用 dispatchEvent 创建 MessageEvent(在同一窗口内触发所有监听器)
|
|
2079
|
-
// 这样 Vue 页面中的 window.addEventListener('message') 也能收到
|
|
2112
|
+
// 使用 dispatchEvent 创建 MessageEvent,在同一窗口内触发所有监听器
|
|
2113
|
+
// 这样调用 SDK 的 Vue 页面中的 window.addEventListener('message') 也能收到
|
|
2114
|
+
// 注意:MessageEvent 的 origin 属性不能是 '*',必须是有效的 origin 字符串
|
|
2115
|
+
// 我们使用 window.location.origin 来标识消息来源(当前页面的 origin)
|
|
2116
|
+
// Vue 页面可以收到消息,因为 dispatchEvent 会在同一窗口内触发所有监听器
|
|
2080
2117
|
const messageEvent = new MessageEvent('message', {
|
|
2081
2118
|
data: data,
|
|
2082
|
-
origin: window.location.origin,
|
|
2083
|
-
source: window
|
|
2119
|
+
origin: window.location.origin, // 使用当前页面的 origin,不是 '*'(MessageEvent 不支持 '*')
|
|
2120
|
+
source: window,
|
|
2121
|
+
bubbles: true,
|
|
2122
|
+
cancelable: true
|
|
2084
2123
|
});
|
|
2085
2124
|
window.dispatchEvent(messageEvent);
|
|
2086
2125
|
if (this.debug) {
|
|
2087
|
-
console.log('Message
|
|
2126
|
+
console.log('[IframeManager] Message broadcasted to Vue page via window.dispatchEvent:', {
|
|
2127
|
+
data: data,
|
|
2128
|
+
origin: window.location.origin,
|
|
2129
|
+
type: 'message',
|
|
2130
|
+
note: 'origin is window.location.origin, not "*" (MessageEvent does not support "*")'
|
|
2131
|
+
});
|
|
2088
2132
|
}
|
|
2089
2133
|
}
|
|
2090
2134
|
catch (error) {
|
|
2091
2135
|
if (this.debug) {
|
|
2092
|
-
console.error('Failed to
|
|
2136
|
+
console.error('[IframeManager] Failed to broadcast message:', error);
|
|
2093
2137
|
}
|
|
2094
2138
|
}
|
|
2095
2139
|
}
|
|
@@ -2926,20 +2970,20 @@ function copyInputValue(node, cloned) {
|
|
|
2926
2970
|
}
|
|
2927
2971
|
|
|
2928
2972
|
const pseudoClasses = [
|
|
2929
|
-
"
|
|
2930
|
-
"
|
|
2931
|
-
// '
|
|
2973
|
+
"::before",
|
|
2974
|
+
"::after"
|
|
2975
|
+
// '::placeholder', TODO
|
|
2932
2976
|
];
|
|
2933
2977
|
const scrollbarPseudoClasses = [
|
|
2934
|
-
"
|
|
2935
|
-
"
|
|
2936
|
-
// '
|
|
2937
|
-
"
|
|
2938
|
-
"
|
|
2939
|
-
"
|
|
2940
|
-
// '
|
|
2941
|
-
"
|
|
2942
|
-
"
|
|
2978
|
+
"::-webkit-scrollbar",
|
|
2979
|
+
"::-webkit-scrollbar-button",
|
|
2980
|
+
// '::-webkit-scrollbar:horizontal', TODO
|
|
2981
|
+
"::-webkit-scrollbar-thumb",
|
|
2982
|
+
"::-webkit-scrollbar-track",
|
|
2983
|
+
"::-webkit-scrollbar-track-piece",
|
|
2984
|
+
// '::-webkit-scrollbar:vertical', TODO
|
|
2985
|
+
"::-webkit-scrollbar-corner",
|
|
2986
|
+
"::-webkit-resizer"
|
|
2943
2987
|
];
|
|
2944
2988
|
function copyPseudoClass(node, cloned, copyScrollbar, context, addWordToFontFamilies) {
|
|
2945
2989
|
const { ownerWindow, svgStyleElement, svgStyles, currentNodeStyle } = context;
|
|
@@ -2983,7 +3027,7 @@ function copyPseudoClass(node, cloned, copyScrollbar, context, addWordToFontFami
|
|
|
2983
3027
|
allClasses = [];
|
|
2984
3028
|
svgStyles.set(cssText, allClasses);
|
|
2985
3029
|
}
|
|
2986
|
-
allClasses.push(`.${klasses[0]}
|
|
3030
|
+
allClasses.push(`.${klasses[0]}${pseudoClass}`);
|
|
2987
3031
|
}
|
|
2988
3032
|
pseudoClasses.forEach(copyBy);
|
|
2989
3033
|
if (copyScrollbar)
|