customer-chat-sdk 1.1.0 → 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.
- package/dist/core/IconManager.d.ts +12 -0
- 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/customer-sdk.cjs.js +212 -79
- package/dist/customer-sdk.esm.js +212 -79
- package/dist/customer-sdk.min.js +2 -2
- package/dist/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -20,8 +20,12 @@ export declare class IconManager {
|
|
|
20
20
|
private iconStartY;
|
|
21
21
|
private dragMoveHandler;
|
|
22
22
|
private dragEndHandler;
|
|
23
|
+
private checkDragHandler;
|
|
24
|
+
private dragStartHandler;
|
|
25
|
+
private touchStartHandler;
|
|
23
26
|
private iconPosition;
|
|
24
27
|
private debug;
|
|
28
|
+
private isClickEnabled;
|
|
25
29
|
constructor(position?: IconPosition, debug?: boolean);
|
|
26
30
|
/**
|
|
27
31
|
* 显示悬浮图标
|
|
@@ -83,6 +87,14 @@ export declare class IconManager {
|
|
|
83
87
|
* 处理点击事件
|
|
84
88
|
*/
|
|
85
89
|
private handleClick;
|
|
90
|
+
/**
|
|
91
|
+
* 禁用点击和拖拽(iframe 打开时调用)
|
|
92
|
+
*/
|
|
93
|
+
disableClick(): void;
|
|
94
|
+
/**
|
|
95
|
+
* 启用点击和拖拽(iframe 关闭时调用)
|
|
96
|
+
*/
|
|
97
|
+
enableClick(): void;
|
|
86
98
|
/**
|
|
87
99
|
* 创建消息徽章(简化版)
|
|
88
100
|
*/
|
|
@@ -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,UAAU,CAAQ;IAC1B,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,eAAe,CAAsD;IAC7E,OAAO,CAAC,cAAc,CAAsD;IAC5E,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,KAAK,CAAiB;
|
|
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,UAAU,CAAQ;IAC1B,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,eAAe,CAAsD;IAC7E,OAAO,CAAC,cAAc,CAAsD;IAC5E,OAAO,CAAC,gBAAgB,CAAsD;IAC9E,OAAO,CAAC,gBAAgB,CAAsD;IAC9E,OAAO,CAAC,iBAAiB,CAAyC;IAClE,OAAO,CAAC,YAAY,CAA4B;IAChD,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,cAAc,CAAgB;gBAE1B,QAAQ,CAAC,EAAE,YAAY,EAAE,KAAK,GAAE,OAAe;IAK3D;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA+G3B;;OAEG;IACH,sBAAsB,IAAI,IAAI;IAiC9B;;OAEG;IACH,IAAI,IAAI,IAAI;IAgBZ;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,GAAG,GAAG,IAAI;IAIhC;;OAEG;IACH,eAAe,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI;IAwB7C;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,GAAG,GAAG,IAAI;IAI1B;;OAEG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAInC;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAInD;;OAEG;IACH,gBAAgB,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI;IA8BpD;;OAEG;IACH,iBAAiB,IAAI,IAAI;IAUzB;;OAEG;IACH,OAAO,CAAC,eAAe;IAcvB;;OAEG;IACH,OAAO,CAAC,eAAe;IAuGvB;;OAEG;IACH,OAAO,CAAC,cAAc;IAmDtB;;OAEG;IACH,OAAO,CAAC,aAAa;IA+BrB;;OAEG;IACH,OAAO,CAAC,WAAW;IAUnB;;OAEG;IACH,YAAY,IAAI,IAAI;IAoBpB;;OAEG;IACH,WAAW,IAAI,IAAI;IAiBnB;;OAEG;IACH,OAAO,CAAC,WAAW;CAmDpB"}
|
|
@@ -53,7 +53,7 @@ export declare class IframeManager {
|
|
|
53
53
|
*/
|
|
54
54
|
sendToIframe(data: any): void;
|
|
55
55
|
/**
|
|
56
|
-
*
|
|
56
|
+
* 创建遮罩层(PC模式使用)
|
|
57
57
|
*/
|
|
58
58
|
private createOverlay;
|
|
59
59
|
/**
|
|
@@ -74,6 +74,7 @@ export declare class IframeManager {
|
|
|
74
74
|
private isMobileDevice;
|
|
75
75
|
/**
|
|
76
76
|
* 获取当前显示模式
|
|
77
|
+
* PC 模式使用弹窗,移动端使用全屏
|
|
77
78
|
*/
|
|
78
79
|
private getActualMode;
|
|
79
80
|
/**
|
|
@@ -85,7 +86,7 @@ export declare class IframeManager {
|
|
|
85
86
|
*/
|
|
86
87
|
private handleIframeMessage;
|
|
87
88
|
/**
|
|
88
|
-
* 调整iframe
|
|
89
|
+
* 调整iframe大小(PC模式支持)
|
|
89
90
|
*/
|
|
90
91
|
private resizeIframe;
|
|
91
92
|
}
|
|
@@ -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;
|
|
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"}
|
package/dist/customer-sdk.cjs.js
CHANGED
|
@@ -17,8 +17,12 @@ class IconManager {
|
|
|
17
17
|
this.iconStartY = 0;
|
|
18
18
|
this.dragMoveHandler = null;
|
|
19
19
|
this.dragEndHandler = null;
|
|
20
|
+
this.checkDragHandler = null; // 临时拖动检测监听器
|
|
21
|
+
this.dragStartHandler = null; // 拖动开始事件监听器
|
|
22
|
+
this.touchStartHandler = null; // 触摸开始事件监听器
|
|
20
23
|
this.iconPosition = null; // 图标位置配置
|
|
21
24
|
this.debug = false; // debug 模式标志
|
|
25
|
+
this.isClickEnabled = true; // 是否允许点击(iframe 打开时禁用)
|
|
22
26
|
this.iconPosition = position || null;
|
|
23
27
|
this.debug = debug;
|
|
24
28
|
}
|
|
@@ -34,7 +38,7 @@ class IconManager {
|
|
|
34
38
|
this.iconElement.className = 'customer-sdk-icon';
|
|
35
39
|
// 直接设置样式 - 图标容器
|
|
36
40
|
const defaultStyle = {
|
|
37
|
-
position: '
|
|
41
|
+
position: 'absolute',
|
|
38
42
|
width: '30px',
|
|
39
43
|
height: '30px',
|
|
40
44
|
backgroundColor: 'transparent', // 移除背景色,让图片直接显示
|
|
@@ -43,7 +47,7 @@ class IconManager {
|
|
|
43
47
|
alignItems: 'center',
|
|
44
48
|
justifyContent: 'center',
|
|
45
49
|
cursor: 'pointer',
|
|
46
|
-
zIndex: '
|
|
50
|
+
zIndex: '1000002', // 确保图标始终在最上层(遮罩层 999998,iframe 容器 999999)
|
|
47
51
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
|
|
48
52
|
userSelect: 'none',
|
|
49
53
|
transition: 'transform 0.2s ease',
|
|
@@ -270,9 +274,11 @@ class IconManager {
|
|
|
270
274
|
// 绑定事件处理器(用于后续清理)
|
|
271
275
|
this.dragMoveHandler = this.handleDragMove.bind(this);
|
|
272
276
|
this.dragEndHandler = this.handleDragEnd.bind(this);
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
this.iconElement.addEventListener('
|
|
277
|
+
this.dragStartHandler = this.handleDragStart.bind(this);
|
|
278
|
+
// 只在图标上监听开始事件(保存引用以便后续移除)
|
|
279
|
+
this.iconElement.addEventListener('mousedown', this.dragStartHandler);
|
|
280
|
+
this.touchStartHandler = this.handleDragStart.bind(this);
|
|
281
|
+
this.iconElement.addEventListener('touchstart', this.touchStartHandler, { passive: false });
|
|
276
282
|
}
|
|
277
283
|
/**
|
|
278
284
|
* 开始拖动
|
|
@@ -295,12 +301,33 @@ class IconManager {
|
|
|
295
301
|
const rect = this.iconElement.getBoundingClientRect();
|
|
296
302
|
this.iconStartX = rect.left;
|
|
297
303
|
this.iconStartY = rect.top;
|
|
298
|
-
//
|
|
299
|
-
|
|
300
|
-
this.iconElement.style.cursor = 'grabbing';
|
|
304
|
+
// 注意:不要在这里立即移除 transition 和设置 cursor
|
|
305
|
+
// 只有在真正开始拖动时才修改样式,避免点击时图标位置跳动
|
|
301
306
|
// 只在真正开始拖动时添加document事件监听
|
|
302
307
|
// 先添加一个临时的move监听器来检测是否真的在拖动
|
|
303
308
|
const checkDrag = (moveEvent) => {
|
|
309
|
+
// 检测事件目标:如果事件发生在iframe或其他元素上,停止检测拖动
|
|
310
|
+
const target = moveEvent.target;
|
|
311
|
+
if (target && target !== this.iconElement && !this.iconElement?.contains(target)) {
|
|
312
|
+
// 检查是否是iframe相关元素
|
|
313
|
+
const isIframeElement = target.tagName === 'IFRAME' ||
|
|
314
|
+
target.closest('iframe') ||
|
|
315
|
+
target.closest('.customer-sdk-container') ||
|
|
316
|
+
target.closest('.customer-sdk-overlay');
|
|
317
|
+
if (isIframeElement) {
|
|
318
|
+
// 如果事件发生在iframe相关元素上,停止检测并清理监听器
|
|
319
|
+
if (this.checkDragHandler) {
|
|
320
|
+
if ('touches' in moveEvent) {
|
|
321
|
+
document.removeEventListener('touchmove', this.checkDragHandler);
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
document.removeEventListener('mousemove', this.checkDragHandler);
|
|
325
|
+
}
|
|
326
|
+
this.checkDragHandler = null;
|
|
327
|
+
}
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
304
331
|
const moveX = 'touches' in moveEvent ? moveEvent.touches[0].clientX : moveEvent.clientX;
|
|
305
332
|
const moveY = 'touches' in moveEvent ? moveEvent.touches[0].clientY : moveEvent.clientY;
|
|
306
333
|
const deltaX = moveX - this.dragStartX;
|
|
@@ -308,12 +335,20 @@ class IconManager {
|
|
|
308
335
|
// 如果移动距离超过5px,开始拖动
|
|
309
336
|
if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
|
|
310
337
|
this.isDragging = true;
|
|
311
|
-
//
|
|
312
|
-
if (
|
|
313
|
-
|
|
338
|
+
// 只有在真正开始拖动时才移除 transition 和设置 cursor
|
|
339
|
+
if (this.iconElement) {
|
|
340
|
+
this.iconElement.style.transition = 'none';
|
|
341
|
+
this.iconElement.style.cursor = 'grabbing';
|
|
314
342
|
}
|
|
315
|
-
|
|
316
|
-
|
|
343
|
+
// 移除临时监听器
|
|
344
|
+
if (this.checkDragHandler) {
|
|
345
|
+
if ('touches' in moveEvent) {
|
|
346
|
+
document.removeEventListener('touchmove', this.checkDragHandler);
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
document.removeEventListener('mousemove', this.checkDragHandler);
|
|
350
|
+
}
|
|
351
|
+
this.checkDragHandler = null;
|
|
317
352
|
}
|
|
318
353
|
// 添加正式的事件监听器
|
|
319
354
|
if (this.dragMoveHandler && this.dragEndHandler) {
|
|
@@ -328,13 +363,15 @@ class IconManager {
|
|
|
328
363
|
}
|
|
329
364
|
}
|
|
330
365
|
};
|
|
366
|
+
// 保存 checkDrag 引用,以便后续清理
|
|
367
|
+
this.checkDragHandler = checkDrag;
|
|
331
368
|
// 添加临时检测监听器
|
|
332
369
|
if ('touches' in e) {
|
|
333
|
-
document.addEventListener('touchmove',
|
|
370
|
+
document.addEventListener('touchmove', this.checkDragHandler, { passive: false });
|
|
334
371
|
document.addEventListener('touchend', this.dragEndHandler);
|
|
335
372
|
}
|
|
336
373
|
else {
|
|
337
|
-
document.addEventListener('mousemove',
|
|
374
|
+
document.addEventListener('mousemove', this.checkDragHandler);
|
|
338
375
|
document.addEventListener('mouseup', this.dragEndHandler);
|
|
339
376
|
}
|
|
340
377
|
}
|
|
@@ -346,20 +383,9 @@ class IconManager {
|
|
|
346
383
|
return;
|
|
347
384
|
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
|
|
348
385
|
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
|
|
349
|
-
//
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
// 检查是否是iframe相关元素
|
|
353
|
-
const isIframeElement = target.tagName === 'IFRAME' ||
|
|
354
|
-
target.closest('iframe') ||
|
|
355
|
-
target.closest('.customer-sdk-container') ||
|
|
356
|
-
target.closest('.customer-sdk-overlay');
|
|
357
|
-
if (isIframeElement) {
|
|
358
|
-
// 如果事件发生在iframe相关元素上,停止拖动
|
|
359
|
-
this.handleDragEnd();
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
386
|
+
// 注意:拖动过程中不检测 iframe 元素,因为用户可能只是想将图标拖动到 iframe 附近或上方
|
|
387
|
+
// 只有在拖动开始时(checkDrag 阶段)才检测 iframe,防止误判为拖动
|
|
388
|
+
// 一旦开始拖动,就应该允许拖动到任何位置,包括 iframe 上方
|
|
363
389
|
// 计算从拖动开始位置到当前位置的距离
|
|
364
390
|
const deltaX = clientX - this.dragStartX;
|
|
365
391
|
const deltaY = clientY - this.dragStartY;
|
|
@@ -394,10 +420,15 @@ class IconManager {
|
|
|
394
420
|
/**
|
|
395
421
|
* 结束拖动
|
|
396
422
|
*/
|
|
397
|
-
handleDragEnd(
|
|
423
|
+
handleDragEnd(_e) {
|
|
398
424
|
if (!this.iconElement)
|
|
399
425
|
return;
|
|
400
|
-
// 清理document
|
|
426
|
+
// 清理document上的所有事件监听器(包括临时检测监听器)
|
|
427
|
+
if (this.checkDragHandler) {
|
|
428
|
+
document.removeEventListener('mousemove', this.checkDragHandler);
|
|
429
|
+
document.removeEventListener('touchmove', this.checkDragHandler);
|
|
430
|
+
this.checkDragHandler = null;
|
|
431
|
+
}
|
|
401
432
|
if (this.dragMoveHandler) {
|
|
402
433
|
document.removeEventListener('mousemove', this.dragMoveHandler);
|
|
403
434
|
document.removeEventListener('touchmove', this.dragMoveHandler);
|
|
@@ -406,6 +437,8 @@ class IconManager {
|
|
|
406
437
|
document.removeEventListener('mouseup', this.dragEndHandler);
|
|
407
438
|
document.removeEventListener('touchend', this.dragEndHandler);
|
|
408
439
|
}
|
|
440
|
+
// 恢复样式(如果之前被修改过)
|
|
441
|
+
// 注意:如果只是点击(没有拖动),这些样式可能没有被修改,但恢复操作是安全的
|
|
409
442
|
this.iconElement.style.transition = 'transform 0.2s ease';
|
|
410
443
|
this.iconElement.style.cursor = 'pointer';
|
|
411
444
|
// 如果只是点击(没有拖动),触发点击事件
|
|
@@ -418,10 +451,52 @@ class IconManager {
|
|
|
418
451
|
* 处理点击事件
|
|
419
452
|
*/
|
|
420
453
|
handleClick() {
|
|
454
|
+
// 如果点击被禁用(iframe 打开时),不执行点击回调
|
|
455
|
+
if (!this.isClickEnabled) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
421
458
|
if (this.onClickCallback) {
|
|
422
459
|
this.onClickCallback();
|
|
423
460
|
}
|
|
424
461
|
}
|
|
462
|
+
/**
|
|
463
|
+
* 禁用点击和拖拽(iframe 打开时调用)
|
|
464
|
+
*/
|
|
465
|
+
disableClick() {
|
|
466
|
+
this.isClickEnabled = false;
|
|
467
|
+
// 移除拖动事件监听器
|
|
468
|
+
if (this.iconElement) {
|
|
469
|
+
if (this.dragStartHandler) {
|
|
470
|
+
this.iconElement.removeEventListener('mousedown', this.dragStartHandler);
|
|
471
|
+
}
|
|
472
|
+
if (this.touchStartHandler) {
|
|
473
|
+
this.iconElement.removeEventListener('touchstart', this.touchStartHandler);
|
|
474
|
+
}
|
|
475
|
+
// 禁用所有鼠标事件(包括点击和拖拽)
|
|
476
|
+
this.iconElement.style.pointerEvents = 'none';
|
|
477
|
+
this.iconElement.style.cursor = 'default';
|
|
478
|
+
}
|
|
479
|
+
// 清理可能正在进行的拖动
|
|
480
|
+
this.forceCleanupDragEvents();
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* 启用点击和拖拽(iframe 关闭时调用)
|
|
484
|
+
*/
|
|
485
|
+
enableClick() {
|
|
486
|
+
this.isClickEnabled = true;
|
|
487
|
+
// 重新添加拖动事件监听器
|
|
488
|
+
if (this.iconElement) {
|
|
489
|
+
if (this.dragStartHandler) {
|
|
490
|
+
this.iconElement.addEventListener('mousedown', this.dragStartHandler);
|
|
491
|
+
}
|
|
492
|
+
if (this.touchStartHandler) {
|
|
493
|
+
this.iconElement.addEventListener('touchstart', this.touchStartHandler, { passive: false });
|
|
494
|
+
}
|
|
495
|
+
// 恢复鼠标事件
|
|
496
|
+
this.iconElement.style.pointerEvents = 'auto';
|
|
497
|
+
this.iconElement.style.cursor = 'pointer';
|
|
498
|
+
}
|
|
499
|
+
}
|
|
425
500
|
/**
|
|
426
501
|
* 创建消息徽章(简化版)
|
|
427
502
|
*/
|
|
@@ -487,7 +562,7 @@ class IframeManager {
|
|
|
487
562
|
this.config = {
|
|
488
563
|
src: '',
|
|
489
564
|
mode: 'auto', // 默认自动检测设备类型
|
|
490
|
-
width:
|
|
565
|
+
width: 450, // PC 模式默认宽度
|
|
491
566
|
height: 600,
|
|
492
567
|
allowClose: true,
|
|
493
568
|
...config
|
|
@@ -527,9 +602,9 @@ class IframeManager {
|
|
|
527
602
|
try {
|
|
528
603
|
const actualMode = this.getActualMode();
|
|
529
604
|
const isPC = actualMode === 'popup';
|
|
530
|
-
//
|
|
605
|
+
// PC模式:创建或显示遮罩层
|
|
531
606
|
if (isPC) {
|
|
532
|
-
this.createOverlay();
|
|
607
|
+
this.createOverlay(); // createOverlay 内部会检查是否已存在
|
|
533
608
|
}
|
|
534
609
|
// 显示已创建的容器
|
|
535
610
|
if (this.containerElement) {
|
|
@@ -544,8 +619,18 @@ class IframeManager {
|
|
|
544
619
|
opacity: '1',
|
|
545
620
|
display: 'block'
|
|
546
621
|
});
|
|
547
|
-
//
|
|
548
|
-
|
|
622
|
+
// 关键优化:避免重复移动容器导致 iframe 重新加载
|
|
623
|
+
// 只有当容器不在遮罩层内时才移动,且确保遮罩层在 DOM 中
|
|
624
|
+
if (this.overlayElement) {
|
|
625
|
+
// 如果遮罩层不在 DOM 中,先添加到 DOM
|
|
626
|
+
if (!this.overlayElement.parentNode) {
|
|
627
|
+
document.body.appendChild(this.overlayElement);
|
|
628
|
+
}
|
|
629
|
+
// 只有当容器不在遮罩层内时才移动(避免重复移动导致 iframe 重新加载)
|
|
630
|
+
if (this.containerElement.parentNode !== this.overlayElement) {
|
|
631
|
+
this.overlayElement.appendChild(this.containerElement);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
549
634
|
}
|
|
550
635
|
else {
|
|
551
636
|
// 移动端模式:直接全屏显示,不需要遮罩层
|
|
@@ -582,22 +667,27 @@ class IframeManager {
|
|
|
582
667
|
if (!this.isOpen) {
|
|
583
668
|
return;
|
|
584
669
|
}
|
|
585
|
-
// 隐藏容器但保留DOM
|
|
670
|
+
// 隐藏容器但保留DOM元素(不移动容器,避免 iframe 重新加载)
|
|
586
671
|
if (this.containerElement) {
|
|
587
672
|
Object.assign(this.containerElement.style, {
|
|
588
673
|
visibility: 'hidden',
|
|
589
674
|
opacity: '0',
|
|
590
675
|
display: 'none'
|
|
591
676
|
});
|
|
677
|
+
// 注意:不移动容器,保持容器在当前位置(遮罩层或 body),避免 iframe 重新加载
|
|
592
678
|
}
|
|
593
|
-
//
|
|
679
|
+
// 隐藏遮罩层但不移除(仅PC模式,避免重新创建导致 iframe 重新加载)
|
|
594
680
|
if (this.overlayElement) {
|
|
595
|
-
this.overlayElement.
|
|
596
|
-
|
|
681
|
+
Object.assign(this.overlayElement.style, {
|
|
682
|
+
visibility: 'hidden',
|
|
683
|
+
opacity: '0',
|
|
684
|
+
display: 'none'
|
|
685
|
+
});
|
|
686
|
+
// 不移除遮罩层,下次显示时直接显示即可
|
|
597
687
|
}
|
|
598
688
|
// 恢复body滚动(移动端模式)
|
|
599
|
-
const
|
|
600
|
-
if (
|
|
689
|
+
const actualModeForScroll = this.getActualMode();
|
|
690
|
+
if (actualModeForScroll === 'fullscreen') {
|
|
601
691
|
this.preventBodyScroll(false);
|
|
602
692
|
}
|
|
603
693
|
this.isOpen = false;
|
|
@@ -645,9 +735,29 @@ class IframeManager {
|
|
|
645
735
|
}
|
|
646
736
|
}
|
|
647
737
|
/**
|
|
648
|
-
*
|
|
738
|
+
* 创建遮罩层(PC模式使用)
|
|
649
739
|
*/
|
|
650
740
|
createOverlay() {
|
|
741
|
+
// 如果遮罩层已存在,直接显示即可
|
|
742
|
+
if (this.overlayElement && this.overlayElement.parentNode) {
|
|
743
|
+
Object.assign(this.overlayElement.style, {
|
|
744
|
+
visibility: 'visible',
|
|
745
|
+
opacity: '1',
|
|
746
|
+
display: 'flex'
|
|
747
|
+
});
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
// 如果遮罩层存在但不在 DOM 中,重新添加到 DOM
|
|
751
|
+
if (this.overlayElement && !this.overlayElement.parentNode) {
|
|
752
|
+
document.body.appendChild(this.overlayElement);
|
|
753
|
+
Object.assign(this.overlayElement.style, {
|
|
754
|
+
visibility: 'visible',
|
|
755
|
+
opacity: '1',
|
|
756
|
+
display: 'flex'
|
|
757
|
+
});
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
// 创建新的遮罩层
|
|
651
761
|
this.overlayElement = document.createElement('div');
|
|
652
762
|
this.overlayElement.className = 'customer-sdk-overlay';
|
|
653
763
|
Object.assign(this.overlayElement.style, {
|
|
@@ -663,7 +773,7 @@ class IframeManager {
|
|
|
663
773
|
justifyContent: 'center',
|
|
664
774
|
cursor: this.config.allowClose ? 'pointer' : 'default'
|
|
665
775
|
});
|
|
666
|
-
//
|
|
776
|
+
// 点击遮罩层关闭(只添加一次事件监听器)
|
|
667
777
|
if (this.config.allowClose) {
|
|
668
778
|
this.overlayElement.addEventListener('click', (e) => {
|
|
669
779
|
if (e.target === this.overlayElement) {
|
|
@@ -715,45 +825,59 @@ class IframeManager {
|
|
|
715
825
|
'allow-pointer-lock', // 允许指针锁定
|
|
716
826
|
'allow-storage-access-by-user-activation' // 允许用户激活的存储访问
|
|
717
827
|
].join(' '));
|
|
718
|
-
//
|
|
828
|
+
// 根据设备类型设置模式
|
|
719
829
|
const actualMode = this.getActualMode();
|
|
720
830
|
const isPC = actualMode === 'popup';
|
|
721
831
|
this.iframeElement.scrolling = isPC ? 'auto' : 'no'; // PC显示滚动条,移动端禁用
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
832
|
+
// PC 模式:使用配置的宽度和高度
|
|
833
|
+
// 移动端:使用全屏
|
|
834
|
+
const containerStyles = isPC ? {
|
|
835
|
+
// PC 弹窗模式
|
|
836
|
+
width: `${this.config.width || 450}px`,
|
|
837
|
+
height: `${this.config.height || 600}px`,
|
|
838
|
+
maxWidth: '90vw',
|
|
839
|
+
maxHeight: '90vh',
|
|
727
840
|
backgroundColor: '#ffffff',
|
|
728
|
-
borderRadius:
|
|
729
|
-
boxShadow:
|
|
730
|
-
? '0 20px 40px rgba(0, 0, 0, 0.15)'
|
|
731
|
-
: '0 -4px 16px rgba(0, 0, 0, 0.25)',
|
|
841
|
+
borderRadius: '12px',
|
|
842
|
+
boxShadow: '0 20px 40px rgba(0, 0, 0, 0.15)',
|
|
732
843
|
border: 'none',
|
|
733
844
|
position: 'fixed',
|
|
734
845
|
zIndex: '999999',
|
|
735
|
-
// PC
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
846
|
+
// PC模式:居中显示
|
|
847
|
+
top: '50%',
|
|
848
|
+
left: '50%',
|
|
849
|
+
transform: 'translate(-50%, -50%)',
|
|
850
|
+
overflow: 'hidden',
|
|
851
|
+
// 初始隐藏的关键样式
|
|
852
|
+
visibility: 'hidden',
|
|
853
|
+
opacity: '0',
|
|
854
|
+
display: 'none'
|
|
855
|
+
} : {
|
|
856
|
+
// 移动端全屏模式(强制 100% 宽度和高度)
|
|
857
|
+
width: '100%',
|
|
858
|
+
height: '100%',
|
|
859
|
+
maxWidth: '100%',
|
|
860
|
+
maxHeight: '100%',
|
|
861
|
+
backgroundColor: '#ffffff',
|
|
862
|
+
borderRadius: '12px 12px 0 0',
|
|
863
|
+
boxShadow: '0 -4px 16px rgba(0, 0, 0, 0.25)',
|
|
864
|
+
border: 'none',
|
|
865
|
+
position: 'fixed',
|
|
866
|
+
zIndex: '999999',
|
|
867
|
+
// 全屏模式 - 占满整个屏幕
|
|
868
|
+
top: '0',
|
|
869
|
+
left: '0',
|
|
870
|
+
bottom: '0',
|
|
871
|
+
right: '0',
|
|
872
|
+
transform: 'none',
|
|
873
|
+
overflow: 'hidden',
|
|
750
874
|
// 初始隐藏的关键样式
|
|
751
875
|
visibility: 'hidden',
|
|
752
876
|
opacity: '0',
|
|
753
877
|
display: 'none'
|
|
754
878
|
};
|
|
755
879
|
Object.assign(this.containerElement.style, containerStyles);
|
|
756
|
-
// iframe
|
|
880
|
+
// iframe填充整个容器
|
|
757
881
|
const iframeStyles = {
|
|
758
882
|
width: '100%',
|
|
759
883
|
height: '100%',
|
|
@@ -887,6 +1011,7 @@ class IframeManager {
|
|
|
887
1011
|
}
|
|
888
1012
|
/**
|
|
889
1013
|
* 获取当前显示模式
|
|
1014
|
+
* PC 模式使用弹窗,移动端使用全屏
|
|
890
1015
|
*/
|
|
891
1016
|
getActualMode() {
|
|
892
1017
|
if (this.config.mode === 'auto') {
|
|
@@ -939,9 +1064,14 @@ class IframeManager {
|
|
|
939
1064
|
break;
|
|
940
1065
|
case 'resize_iframe':
|
|
941
1066
|
case 'resize':
|
|
942
|
-
|
|
1067
|
+
// PC模式支持 resize,移动端忽略
|
|
1068
|
+
const actualMode = this.getActualMode();
|
|
1069
|
+
if (actualMode === 'popup' && data.width && data.height) {
|
|
943
1070
|
this.resizeIframe(data.width, data.height);
|
|
944
1071
|
}
|
|
1072
|
+
else if (this.debug) {
|
|
1073
|
+
console.log('Resize request ignored (fullscreen mode)');
|
|
1074
|
+
}
|
|
945
1075
|
break;
|
|
946
1076
|
case 'new-message':
|
|
947
1077
|
// 新消息通知 - 触发回调让外层处理
|
|
@@ -960,12 +1090,12 @@ class IframeManager {
|
|
|
960
1090
|
}
|
|
961
1091
|
}
|
|
962
1092
|
/**
|
|
963
|
-
* 调整iframe
|
|
1093
|
+
* 调整iframe大小(PC模式支持)
|
|
964
1094
|
*/
|
|
965
1095
|
resizeIframe(width, height) {
|
|
966
|
-
if (this.
|
|
967
|
-
this.
|
|
968
|
-
this.
|
|
1096
|
+
if (this.containerElement) {
|
|
1097
|
+
this.containerElement.style.width = `${width}px`;
|
|
1098
|
+
this.containerElement.style.height = `${height}px`;
|
|
969
1099
|
}
|
|
970
1100
|
}
|
|
971
1101
|
}
|
|
@@ -20703,9 +20833,9 @@ class CustomerServiceSDK {
|
|
|
20703
20833
|
// 创建iframe管理器(自动检测设备类型)
|
|
20704
20834
|
this.iframeManager = new IframeManager({
|
|
20705
20835
|
src: iframeUrl,
|
|
20706
|
-
mode: 'auto', //
|
|
20707
|
-
width:
|
|
20708
|
-
height: 600,
|
|
20836
|
+
mode: 'auto', // 自动根据设备类型选择模式(PC弹窗,移动端全屏)
|
|
20837
|
+
width: options?.width || 450, // PC模式宽度(像素,默认450px),移动端不使用
|
|
20838
|
+
height: options?.height || 600, // PC模式高度(像素),移动端不使用(强制全屏)
|
|
20709
20839
|
allowClose: true,
|
|
20710
20840
|
debug: this.debug, // 传递 debug 标志
|
|
20711
20841
|
onMessage: (messageType, _data) => {
|
|
@@ -20717,8 +20847,9 @@ class CustomerServiceSDK {
|
|
|
20717
20847
|
// checkScreenshot 消息由 ScreenshotManager 处理,不需要在这里处理
|
|
20718
20848
|
},
|
|
20719
20849
|
onClose: () => {
|
|
20720
|
-
// iframe
|
|
20850
|
+
// iframe关闭时,清理图标拖动事件监听器,并重新启用图标点击
|
|
20721
20851
|
this.iconManager?.forceCleanupDragEvents();
|
|
20852
|
+
this.iconManager?.enableClick();
|
|
20722
20853
|
},
|
|
20723
20854
|
...options
|
|
20724
20855
|
});
|
|
@@ -20729,6 +20860,8 @@ class CustomerServiceSDK {
|
|
|
20729
20860
|
// 打开iframe时清除红点通知
|
|
20730
20861
|
this.clearNotification();
|
|
20731
20862
|
this.iframeManager?.show();
|
|
20863
|
+
// iframe 打开后,禁用图标点击(防止重复打开)
|
|
20864
|
+
this.iconManager?.disableClick();
|
|
20732
20865
|
});
|
|
20733
20866
|
// 初始化截图管理器(如果启用了截图功能)
|
|
20734
20867
|
if (config.screenshot) {
|