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.
- package/dist/core/IconManager.d.ts +14 -1
- package/dist/core/IconManager.d.ts.map +1 -1
- package/dist/core/IframeManager.d.ts +5 -2
- package/dist/core/IframeManager.d.ts.map +1 -1
- package/dist/customer-sdk.cjs.js +378 -121
- package/dist/customer-sdk.esm.js +378 -122
- package/dist/customer-sdk.min.js +2 -2
- package/dist/index.d.ts +18 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/customer-sdk.cjs.js
CHANGED
|
@@ -5,7 +5,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
5
5
|
// 直接使用base64字符串,避免打包后路径问题
|
|
6
6
|
const iconImage = 'data:image/png;base64,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';
|
|
7
7
|
class IconManager {
|
|
8
|
-
constructor(position) {
|
|
8
|
+
constructor(position, debug = false) {
|
|
9
9
|
this.iconElement = null;
|
|
10
10
|
this.badgeElement = null;
|
|
11
11
|
this.onClickCallback = null;
|
|
@@ -17,8 +17,14 @@ 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; // 图标位置配置
|
|
24
|
+
this.debug = false; // debug 模式标志
|
|
25
|
+
this.isClickEnabled = true; // 是否允许点击(iframe 打开时禁用)
|
|
21
26
|
this.iconPosition = position || null;
|
|
27
|
+
this.debug = debug;
|
|
22
28
|
}
|
|
23
29
|
/**
|
|
24
30
|
* 显示悬浮图标
|
|
@@ -32,7 +38,7 @@ class IconManager {
|
|
|
32
38
|
this.iconElement.className = 'customer-sdk-icon';
|
|
33
39
|
// 直接设置样式 - 图标容器
|
|
34
40
|
const defaultStyle = {
|
|
35
|
-
position: '
|
|
41
|
+
position: 'absolute',
|
|
36
42
|
width: '30px',
|
|
37
43
|
height: '30px',
|
|
38
44
|
backgroundColor: 'transparent', // 移除背景色,让图片直接显示
|
|
@@ -41,7 +47,7 @@ class IconManager {
|
|
|
41
47
|
alignItems: 'center',
|
|
42
48
|
justifyContent: 'center',
|
|
43
49
|
cursor: 'pointer',
|
|
44
|
-
zIndex: '
|
|
50
|
+
zIndex: '1000002', // 确保图标始终在最上层(遮罩层 999998,iframe 容器 999999)
|
|
45
51
|
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
|
|
46
52
|
userSelect: 'none',
|
|
47
53
|
transition: 'transform 0.2s ease',
|
|
@@ -86,7 +92,9 @@ class IconManager {
|
|
|
86
92
|
iconImg.alt = 'Customer Service';
|
|
87
93
|
// 添加图片加载成功处理
|
|
88
94
|
iconImg.onload = () => {
|
|
89
|
-
|
|
95
|
+
if (this.debug) {
|
|
96
|
+
console.log('Icon image loaded successfully');
|
|
97
|
+
}
|
|
90
98
|
if (this.iconElement) {
|
|
91
99
|
// 确保图片可见
|
|
92
100
|
iconImg.style.opacity = '1';
|
|
@@ -95,6 +103,7 @@ class IconManager {
|
|
|
95
103
|
};
|
|
96
104
|
// 添加图片加载错误处理
|
|
97
105
|
iconImg.onerror = (e) => {
|
|
106
|
+
// 错误始终输出
|
|
98
107
|
console.error('Failed to load icon image', e);
|
|
99
108
|
// 只记录错误,不使用默认样式,必须使用外部提供的图片
|
|
100
109
|
};
|
|
@@ -116,7 +125,9 @@ class IconManager {
|
|
|
116
125
|
this.setupDragEvents();
|
|
117
126
|
// 添加到页面
|
|
118
127
|
document.body.appendChild(this.iconElement);
|
|
119
|
-
|
|
128
|
+
if (this.debug) {
|
|
129
|
+
console.log('CustomerSDK icon displayed');
|
|
130
|
+
}
|
|
120
131
|
}
|
|
121
132
|
/**
|
|
122
133
|
* 强制清理所有拖动事件监听器
|
|
@@ -129,7 +140,9 @@ class IconManager {
|
|
|
129
140
|
document.removeEventListener('touchmove', this.dragMoveHandler);
|
|
130
141
|
}
|
|
131
142
|
catch (e) {
|
|
132
|
-
|
|
143
|
+
if (this.debug) {
|
|
144
|
+
console.warn('Error removing drag move listeners:', e);
|
|
145
|
+
}
|
|
133
146
|
}
|
|
134
147
|
}
|
|
135
148
|
if (this.dragEndHandler) {
|
|
@@ -138,7 +151,9 @@ class IconManager {
|
|
|
138
151
|
document.removeEventListener('touchend', this.dragEndHandler);
|
|
139
152
|
}
|
|
140
153
|
catch (e) {
|
|
141
|
-
|
|
154
|
+
if (this.debug) {
|
|
155
|
+
console.warn('Error removing drag end listeners:', e);
|
|
156
|
+
}
|
|
142
157
|
}
|
|
143
158
|
}
|
|
144
159
|
// 重置拖动状态
|
|
@@ -161,7 +176,9 @@ class IconManager {
|
|
|
161
176
|
this.onClickCallback = null;
|
|
162
177
|
this.dragMoveHandler = null;
|
|
163
178
|
this.dragEndHandler = null;
|
|
164
|
-
|
|
179
|
+
if (this.debug) {
|
|
180
|
+
console.log('CustomerSDK icon hidden');
|
|
181
|
+
}
|
|
165
182
|
}
|
|
166
183
|
}
|
|
167
184
|
/**
|
|
@@ -218,7 +235,9 @@ class IconManager {
|
|
|
218
235
|
*/
|
|
219
236
|
showNotification(options) {
|
|
220
237
|
if (!this.iconElement) {
|
|
221
|
-
|
|
238
|
+
if (this.debug) {
|
|
239
|
+
console.warn('Icon not displayed, cannot show notification');
|
|
240
|
+
}
|
|
222
241
|
return;
|
|
223
242
|
}
|
|
224
243
|
const { showBadge = true, badgeCount = 0, badgeText = '', pulse = false } = options;
|
|
@@ -230,7 +249,9 @@ class IconManager {
|
|
|
230
249
|
if (this.notificationCallback) {
|
|
231
250
|
this.notificationCallback({ badgeCount, badgeText, pulse });
|
|
232
251
|
}
|
|
233
|
-
|
|
252
|
+
if (this.debug) {
|
|
253
|
+
console.log('Notification shown:', { badgeCount, badgeText });
|
|
254
|
+
}
|
|
234
255
|
}
|
|
235
256
|
/**
|
|
236
257
|
* 清除消息订阅
|
|
@@ -240,7 +261,9 @@ class IconManager {
|
|
|
240
261
|
this.badgeElement.remove();
|
|
241
262
|
this.badgeElement = null;
|
|
242
263
|
}
|
|
243
|
-
|
|
264
|
+
if (this.debug) {
|
|
265
|
+
console.log('Notification cleared');
|
|
266
|
+
}
|
|
244
267
|
}
|
|
245
268
|
/**
|
|
246
269
|
* 设置拖动和点击事件
|
|
@@ -251,9 +274,11 @@ class IconManager {
|
|
|
251
274
|
// 绑定事件处理器(用于后续清理)
|
|
252
275
|
this.dragMoveHandler = this.handleDragMove.bind(this);
|
|
253
276
|
this.dragEndHandler = this.handleDragEnd.bind(this);
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
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 });
|
|
257
282
|
}
|
|
258
283
|
/**
|
|
259
284
|
* 开始拖动
|
|
@@ -276,12 +301,33 @@ class IconManager {
|
|
|
276
301
|
const rect = this.iconElement.getBoundingClientRect();
|
|
277
302
|
this.iconStartX = rect.left;
|
|
278
303
|
this.iconStartY = rect.top;
|
|
279
|
-
//
|
|
280
|
-
|
|
281
|
-
this.iconElement.style.cursor = 'grabbing';
|
|
304
|
+
// 注意:不要在这里立即移除 transition 和设置 cursor
|
|
305
|
+
// 只有在真正开始拖动时才修改样式,避免点击时图标位置跳动
|
|
282
306
|
// 只在真正开始拖动时添加document事件监听
|
|
283
307
|
// 先添加一个临时的move监听器来检测是否真的在拖动
|
|
284
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
|
+
}
|
|
285
331
|
const moveX = 'touches' in moveEvent ? moveEvent.touches[0].clientX : moveEvent.clientX;
|
|
286
332
|
const moveY = 'touches' in moveEvent ? moveEvent.touches[0].clientY : moveEvent.clientY;
|
|
287
333
|
const deltaX = moveX - this.dragStartX;
|
|
@@ -289,12 +335,20 @@ class IconManager {
|
|
|
289
335
|
// 如果移动距离超过5px,开始拖动
|
|
290
336
|
if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
|
|
291
337
|
this.isDragging = true;
|
|
292
|
-
//
|
|
293
|
-
if (
|
|
294
|
-
|
|
338
|
+
// 只有在真正开始拖动时才移除 transition 和设置 cursor
|
|
339
|
+
if (this.iconElement) {
|
|
340
|
+
this.iconElement.style.transition = 'none';
|
|
341
|
+
this.iconElement.style.cursor = 'grabbing';
|
|
295
342
|
}
|
|
296
|
-
|
|
297
|
-
|
|
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;
|
|
298
352
|
}
|
|
299
353
|
// 添加正式的事件监听器
|
|
300
354
|
if (this.dragMoveHandler && this.dragEndHandler) {
|
|
@@ -309,13 +363,15 @@ class IconManager {
|
|
|
309
363
|
}
|
|
310
364
|
}
|
|
311
365
|
};
|
|
366
|
+
// 保存 checkDrag 引用,以便后续清理
|
|
367
|
+
this.checkDragHandler = checkDrag;
|
|
312
368
|
// 添加临时检测监听器
|
|
313
369
|
if ('touches' in e) {
|
|
314
|
-
document.addEventListener('touchmove',
|
|
370
|
+
document.addEventListener('touchmove', this.checkDragHandler, { passive: false });
|
|
315
371
|
document.addEventListener('touchend', this.dragEndHandler);
|
|
316
372
|
}
|
|
317
373
|
else {
|
|
318
|
-
document.addEventListener('mousemove',
|
|
374
|
+
document.addEventListener('mousemove', this.checkDragHandler);
|
|
319
375
|
document.addEventListener('mouseup', this.dragEndHandler);
|
|
320
376
|
}
|
|
321
377
|
}
|
|
@@ -327,20 +383,9 @@ class IconManager {
|
|
|
327
383
|
return;
|
|
328
384
|
const clientX = 'touches' in e ? e.touches[0].clientX : e.clientX;
|
|
329
385
|
const clientY = 'touches' in e ? e.touches[0].clientY : e.clientY;
|
|
330
|
-
//
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
// 检查是否是iframe相关元素
|
|
334
|
-
const isIframeElement = target.tagName === 'IFRAME' ||
|
|
335
|
-
target.closest('iframe') ||
|
|
336
|
-
target.closest('.customer-sdk-container') ||
|
|
337
|
-
target.closest('.customer-sdk-overlay');
|
|
338
|
-
if (isIframeElement) {
|
|
339
|
-
// 如果事件发生在iframe相关元素上,停止拖动
|
|
340
|
-
this.handleDragEnd();
|
|
341
|
-
return;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
386
|
+
// 注意:拖动过程中不检测 iframe 元素,因为用户可能只是想将图标拖动到 iframe 附近或上方
|
|
387
|
+
// 只有在拖动开始时(checkDrag 阶段)才检测 iframe,防止误判为拖动
|
|
388
|
+
// 一旦开始拖动,就应该允许拖动到任何位置,包括 iframe 上方
|
|
344
389
|
// 计算从拖动开始位置到当前位置的距离
|
|
345
390
|
const deltaX = clientX - this.dragStartX;
|
|
346
391
|
const deltaY = clientY - this.dragStartY;
|
|
@@ -375,10 +420,15 @@ class IconManager {
|
|
|
375
420
|
/**
|
|
376
421
|
* 结束拖动
|
|
377
422
|
*/
|
|
378
|
-
handleDragEnd(
|
|
423
|
+
handleDragEnd(_e) {
|
|
379
424
|
if (!this.iconElement)
|
|
380
425
|
return;
|
|
381
|
-
// 清理document
|
|
426
|
+
// 清理document上的所有事件监听器(包括临时检测监听器)
|
|
427
|
+
if (this.checkDragHandler) {
|
|
428
|
+
document.removeEventListener('mousemove', this.checkDragHandler);
|
|
429
|
+
document.removeEventListener('touchmove', this.checkDragHandler);
|
|
430
|
+
this.checkDragHandler = null;
|
|
431
|
+
}
|
|
382
432
|
if (this.dragMoveHandler) {
|
|
383
433
|
document.removeEventListener('mousemove', this.dragMoveHandler);
|
|
384
434
|
document.removeEventListener('touchmove', this.dragMoveHandler);
|
|
@@ -387,6 +437,8 @@ class IconManager {
|
|
|
387
437
|
document.removeEventListener('mouseup', this.dragEndHandler);
|
|
388
438
|
document.removeEventListener('touchend', this.dragEndHandler);
|
|
389
439
|
}
|
|
440
|
+
// 恢复样式(如果之前被修改过)
|
|
441
|
+
// 注意:如果只是点击(没有拖动),这些样式可能没有被修改,但恢复操作是安全的
|
|
390
442
|
this.iconElement.style.transition = 'transform 0.2s ease';
|
|
391
443
|
this.iconElement.style.cursor = 'pointer';
|
|
392
444
|
// 如果只是点击(没有拖动),触发点击事件
|
|
@@ -399,10 +451,52 @@ class IconManager {
|
|
|
399
451
|
* 处理点击事件
|
|
400
452
|
*/
|
|
401
453
|
handleClick() {
|
|
454
|
+
// 如果点击被禁用(iframe 打开时),不执行点击回调
|
|
455
|
+
if (!this.isClickEnabled) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
402
458
|
if (this.onClickCallback) {
|
|
403
459
|
this.onClickCallback();
|
|
404
460
|
}
|
|
405
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
|
+
}
|
|
406
500
|
/**
|
|
407
501
|
* 创建消息徽章(简化版)
|
|
408
502
|
*/
|
|
@@ -464,14 +558,16 @@ class IframeManager {
|
|
|
464
558
|
this.containerElement = null; // 包装容器,包含iframe和关闭按钮
|
|
465
559
|
this.isOpen = false;
|
|
466
560
|
this.isCreated = false;
|
|
561
|
+
this.debug = false; // debug 模式标志
|
|
467
562
|
this.config = {
|
|
468
563
|
src: '',
|
|
469
564
|
mode: 'auto', // 默认自动检测设备类型
|
|
470
|
-
width:
|
|
565
|
+
width: 450, // PC 模式默认宽度
|
|
471
566
|
height: 600,
|
|
472
567
|
allowClose: true,
|
|
473
568
|
...config
|
|
474
569
|
};
|
|
570
|
+
this.debug = config.debug ?? false;
|
|
475
571
|
this.setupMessageListener();
|
|
476
572
|
}
|
|
477
573
|
/**
|
|
@@ -483,9 +579,12 @@ class IframeManager {
|
|
|
483
579
|
// 创建隐藏的iframe(预连接到SSE)
|
|
484
580
|
this.createIframe();
|
|
485
581
|
this.isCreated = true;
|
|
486
|
-
|
|
582
|
+
if (this.debug) {
|
|
583
|
+
console.log('CustomerSDK iframe initialized (hidden, SSE connected)');
|
|
584
|
+
}
|
|
487
585
|
}
|
|
488
586
|
catch (error) {
|
|
587
|
+
// 错误始终输出
|
|
489
588
|
console.error('Failed to initialize iframe:', error);
|
|
490
589
|
throw error;
|
|
491
590
|
}
|
|
@@ -503,9 +602,9 @@ class IframeManager {
|
|
|
503
602
|
try {
|
|
504
603
|
const actualMode = this.getActualMode();
|
|
505
604
|
const isPC = actualMode === 'popup';
|
|
506
|
-
//
|
|
605
|
+
// PC模式:创建或显示遮罩层
|
|
507
606
|
if (isPC) {
|
|
508
|
-
this.createOverlay();
|
|
607
|
+
this.createOverlay(); // createOverlay 内部会检查是否已存在
|
|
509
608
|
}
|
|
510
609
|
// 显示已创建的容器
|
|
511
610
|
if (this.containerElement) {
|
|
@@ -520,8 +619,18 @@ class IframeManager {
|
|
|
520
619
|
opacity: '1',
|
|
521
620
|
display: 'block'
|
|
522
621
|
});
|
|
523
|
-
//
|
|
524
|
-
|
|
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
|
+
}
|
|
525
634
|
}
|
|
526
635
|
else {
|
|
527
636
|
// 移动端模式:直接全屏显示,不需要遮罩层
|
|
@@ -536,9 +645,12 @@ class IframeManager {
|
|
|
536
645
|
}
|
|
537
646
|
}
|
|
538
647
|
this.isOpen = true;
|
|
539
|
-
|
|
648
|
+
if (this.debug) {
|
|
649
|
+
console.log('CustomerSDK iframe shown');
|
|
650
|
+
}
|
|
540
651
|
}
|
|
541
652
|
catch (error) {
|
|
653
|
+
// 错误始终输出
|
|
542
654
|
console.error('Failed to show iframe:', error);
|
|
543
655
|
}
|
|
544
656
|
}
|
|
@@ -555,26 +667,33 @@ class IframeManager {
|
|
|
555
667
|
if (!this.isOpen) {
|
|
556
668
|
return;
|
|
557
669
|
}
|
|
558
|
-
// 隐藏容器但保留DOM
|
|
670
|
+
// 隐藏容器但保留DOM元素(不移动容器,避免 iframe 重新加载)
|
|
559
671
|
if (this.containerElement) {
|
|
560
672
|
Object.assign(this.containerElement.style, {
|
|
561
673
|
visibility: 'hidden',
|
|
562
674
|
opacity: '0',
|
|
563
675
|
display: 'none'
|
|
564
676
|
});
|
|
677
|
+
// 注意:不移动容器,保持容器在当前位置(遮罩层或 body),避免 iframe 重新加载
|
|
565
678
|
}
|
|
566
|
-
//
|
|
679
|
+
// 隐藏遮罩层但不移除(仅PC模式,避免重新创建导致 iframe 重新加载)
|
|
567
680
|
if (this.overlayElement) {
|
|
568
|
-
this.overlayElement.
|
|
569
|
-
|
|
681
|
+
Object.assign(this.overlayElement.style, {
|
|
682
|
+
visibility: 'hidden',
|
|
683
|
+
opacity: '0',
|
|
684
|
+
display: 'none'
|
|
685
|
+
});
|
|
686
|
+
// 不移除遮罩层,下次显示时直接显示即可
|
|
570
687
|
}
|
|
571
688
|
// 恢复body滚动(移动端模式)
|
|
572
|
-
const
|
|
573
|
-
if (
|
|
689
|
+
const actualModeForScroll = this.getActualMode();
|
|
690
|
+
if (actualModeForScroll === 'fullscreen') {
|
|
574
691
|
this.preventBodyScroll(false);
|
|
575
692
|
}
|
|
576
693
|
this.isOpen = false;
|
|
577
|
-
|
|
694
|
+
if (this.debug) {
|
|
695
|
+
console.log('CustomerSDK iframe hidden (SSE still connected)');
|
|
696
|
+
}
|
|
578
697
|
// 触发关闭回调
|
|
579
698
|
if (this.config.onClose) {
|
|
580
699
|
this.config.onClose();
|
|
@@ -597,7 +716,9 @@ class IframeManager {
|
|
|
597
716
|
this.iframeElement = null;
|
|
598
717
|
}
|
|
599
718
|
this.isCreated = false;
|
|
600
|
-
|
|
719
|
+
if (this.debug) {
|
|
720
|
+
console.log('CustomerSDK container destroyed');
|
|
721
|
+
}
|
|
601
722
|
}
|
|
602
723
|
/**
|
|
603
724
|
* 检查是否已打开
|
|
@@ -614,9 +735,29 @@ class IframeManager {
|
|
|
614
735
|
}
|
|
615
736
|
}
|
|
616
737
|
/**
|
|
617
|
-
*
|
|
738
|
+
* 创建遮罩层(PC模式使用)
|
|
618
739
|
*/
|
|
619
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
|
+
// 创建新的遮罩层
|
|
620
761
|
this.overlayElement = document.createElement('div');
|
|
621
762
|
this.overlayElement.className = 'customer-sdk-overlay';
|
|
622
763
|
Object.assign(this.overlayElement.style, {
|
|
@@ -632,7 +773,7 @@ class IframeManager {
|
|
|
632
773
|
justifyContent: 'center',
|
|
633
774
|
cursor: this.config.allowClose ? 'pointer' : 'default'
|
|
634
775
|
});
|
|
635
|
-
//
|
|
776
|
+
// 点击遮罩层关闭(只添加一次事件监听器)
|
|
636
777
|
if (this.config.allowClose) {
|
|
637
778
|
this.overlayElement.addEventListener('click', (e) => {
|
|
638
779
|
if (e.target === this.overlayElement) {
|
|
@@ -684,45 +825,59 @@ class IframeManager {
|
|
|
684
825
|
'allow-pointer-lock', // 允许指针锁定
|
|
685
826
|
'allow-storage-access-by-user-activation' // 允许用户激活的存储访问
|
|
686
827
|
].join(' '));
|
|
687
|
-
//
|
|
828
|
+
// 根据设备类型设置模式
|
|
688
829
|
const actualMode = this.getActualMode();
|
|
689
830
|
const isPC = actualMode === 'popup';
|
|
690
831
|
this.iframeElement.scrolling = isPC ? 'auto' : 'no'; // PC显示滚动条,移动端禁用
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
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',
|
|
696
840
|
backgroundColor: '#ffffff',
|
|
697
|
-
borderRadius:
|
|
698
|
-
boxShadow:
|
|
699
|
-
? '0 20px 40px rgba(0, 0, 0, 0.15)'
|
|
700
|
-
: '0 -4px 16px rgba(0, 0, 0, 0.25)',
|
|
841
|
+
borderRadius: '12px',
|
|
842
|
+
boxShadow: '0 20px 40px rgba(0, 0, 0, 0.15)',
|
|
701
843
|
border: 'none',
|
|
702
844
|
position: 'fixed',
|
|
703
845
|
zIndex: '999999',
|
|
704
|
-
// PC
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
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',
|
|
719
874
|
// 初始隐藏的关键样式
|
|
720
875
|
visibility: 'hidden',
|
|
721
876
|
opacity: '0',
|
|
722
877
|
display: 'none'
|
|
723
878
|
};
|
|
724
879
|
Object.assign(this.containerElement.style, containerStyles);
|
|
725
|
-
// iframe
|
|
880
|
+
// iframe填充整个容器
|
|
726
881
|
const iframeStyles = {
|
|
727
882
|
width: '100%',
|
|
728
883
|
height: '100%',
|
|
@@ -748,7 +903,9 @@ class IframeManager {
|
|
|
748
903
|
});
|
|
749
904
|
// 添加到body(预连接SSE,但不显示)
|
|
750
905
|
document.body.appendChild(this.containerElement);
|
|
751
|
-
|
|
906
|
+
if (this.debug) {
|
|
907
|
+
console.log('CustomerSDK container created (hidden, ready for SSE)');
|
|
908
|
+
}
|
|
752
909
|
}
|
|
753
910
|
/**
|
|
754
911
|
* 向iframe注入移动端优化样式(隐藏滚动条)
|
|
@@ -800,12 +957,16 @@ class IframeManager {
|
|
|
800
957
|
`;
|
|
801
958
|
// 注入样式
|
|
802
959
|
iframeDoc.head?.appendChild(style);
|
|
803
|
-
|
|
960
|
+
if (this.debug) {
|
|
961
|
+
console.log('CustomerSDK mobile styles injected successfully');
|
|
962
|
+
}
|
|
804
963
|
}
|
|
805
964
|
}
|
|
806
965
|
catch (error) {
|
|
807
966
|
// 跨域限制时静默忽略
|
|
808
|
-
|
|
967
|
+
if (this.debug) {
|
|
968
|
+
console.log('Cannot inject styles due to cross-origin restrictions:', error);
|
|
969
|
+
}
|
|
809
970
|
}
|
|
810
971
|
}
|
|
811
972
|
/**
|
|
@@ -850,6 +1011,7 @@ class IframeManager {
|
|
|
850
1011
|
}
|
|
851
1012
|
/**
|
|
852
1013
|
* 获取当前显示模式
|
|
1014
|
+
* PC 模式使用弹窗,移动端使用全屏
|
|
853
1015
|
*/
|
|
854
1016
|
getActualMode() {
|
|
855
1017
|
if (this.config.mode === 'auto') {
|
|
@@ -872,7 +1034,9 @@ class IframeManager {
|
|
|
872
1034
|
* 处理来自iframe的消息
|
|
873
1035
|
*/
|
|
874
1036
|
handleIframeMessage(data) {
|
|
875
|
-
|
|
1037
|
+
if (this.debug) {
|
|
1038
|
+
console.log('Message from iframe:', data);
|
|
1039
|
+
}
|
|
876
1040
|
// 判断data是字符串还是对象,兼容两种格式
|
|
877
1041
|
let messageType;
|
|
878
1042
|
if (typeof data === 'string') {
|
|
@@ -882,13 +1046,17 @@ class IframeManager {
|
|
|
882
1046
|
messageType = data.type;
|
|
883
1047
|
}
|
|
884
1048
|
else {
|
|
885
|
-
|
|
1049
|
+
if (this.debug) {
|
|
1050
|
+
console.log('Unknown message format:', data);
|
|
1051
|
+
}
|
|
886
1052
|
return;
|
|
887
1053
|
}
|
|
888
1054
|
// 根据消息类型处理不同的操作
|
|
889
1055
|
switch (messageType) {
|
|
890
1056
|
case 'iframe_ready':
|
|
891
|
-
|
|
1057
|
+
if (this.debug) {
|
|
1058
|
+
console.log('Iframe is ready');
|
|
1059
|
+
}
|
|
892
1060
|
break;
|
|
893
1061
|
case 'close_iframe':
|
|
894
1062
|
case 'close':
|
|
@@ -896,29 +1064,38 @@ class IframeManager {
|
|
|
896
1064
|
break;
|
|
897
1065
|
case 'resize_iframe':
|
|
898
1066
|
case 'resize':
|
|
899
|
-
|
|
1067
|
+
// PC模式支持 resize,移动端忽略
|
|
1068
|
+
const actualMode = this.getActualMode();
|
|
1069
|
+
if (actualMode === 'popup' && data.width && data.height) {
|
|
900
1070
|
this.resizeIframe(data.width, data.height);
|
|
901
1071
|
}
|
|
1072
|
+
else if (this.debug) {
|
|
1073
|
+
console.log('Resize request ignored (fullscreen mode)');
|
|
1074
|
+
}
|
|
902
1075
|
break;
|
|
903
1076
|
case 'new-message':
|
|
904
1077
|
// 新消息通知 - 触发回调让外层处理
|
|
905
|
-
|
|
1078
|
+
if (this.debug) {
|
|
1079
|
+
console.log('Received new message notification');
|
|
1080
|
+
}
|
|
906
1081
|
if (this.config.onMessage) {
|
|
907
1082
|
this.config.onMessage(messageType, data);
|
|
908
1083
|
}
|
|
909
1084
|
break;
|
|
910
1085
|
default:
|
|
911
1086
|
// 可以在这里添加自定义消息处理
|
|
912
|
-
|
|
1087
|
+
if (this.debug) {
|
|
1088
|
+
console.log('Custom message:', data);
|
|
1089
|
+
}
|
|
913
1090
|
}
|
|
914
1091
|
}
|
|
915
1092
|
/**
|
|
916
|
-
* 调整iframe
|
|
1093
|
+
* 调整iframe大小(PC模式支持)
|
|
917
1094
|
*/
|
|
918
1095
|
resizeIframe(width, height) {
|
|
919
|
-
if (this.
|
|
920
|
-
this.
|
|
921
|
-
this.
|
|
1096
|
+
if (this.containerElement) {
|
|
1097
|
+
this.containerElement.style.width = `${width}px`;
|
|
1098
|
+
this.containerElement.style.height = `${height}px`;
|
|
922
1099
|
}
|
|
923
1100
|
}
|
|
924
1101
|
}
|
|
@@ -20611,35 +20788,56 @@ class CustomerServiceSDK {
|
|
|
20611
20788
|
this.screenshotManager = null;
|
|
20612
20789
|
this.config = null;
|
|
20613
20790
|
this.isInitialized = false;
|
|
20791
|
+
this.initResult = null; // 保存初始化结果
|
|
20792
|
+
this.debug = false; // debug 模式标志
|
|
20614
20793
|
}
|
|
20615
20794
|
/**
|
|
20616
20795
|
* 初始化 SDK
|
|
20617
20796
|
* @param config SDK配置
|
|
20618
20797
|
* @param options UI选项(可选)
|
|
20798
|
+
* @returns 返回初始化信息(包含设备ID等)
|
|
20619
20799
|
*/
|
|
20620
20800
|
async init(config, options) {
|
|
20621
20801
|
if (this.isInitialized) {
|
|
20622
|
-
|
|
20623
|
-
|
|
20802
|
+
if (this.debug) {
|
|
20803
|
+
console.warn('CustomerSDK already initialized');
|
|
20804
|
+
}
|
|
20805
|
+
// 如果已经初始化,返回之前保存的初始化信息
|
|
20806
|
+
if (this.initResult) {
|
|
20807
|
+
return this.initResult;
|
|
20808
|
+
}
|
|
20809
|
+
throw new Error('SDK already initialized but cannot retrieve initialization info');
|
|
20624
20810
|
}
|
|
20625
20811
|
this.config = config;
|
|
20812
|
+
this.debug = config.debug ?? false;
|
|
20626
20813
|
try {
|
|
20627
20814
|
// 获取设备指纹ID
|
|
20628
20815
|
const deviceId = await this.getDeviceId();
|
|
20629
|
-
|
|
20816
|
+
if (this.debug) {
|
|
20817
|
+
console.log('Device ID:', deviceId);
|
|
20818
|
+
}
|
|
20630
20819
|
// 构建iframe URL(带参数)
|
|
20631
20820
|
const iframeUrl = this.buildIframeUrl(config, deviceId);
|
|
20821
|
+
// 准备返回的初始化信息
|
|
20822
|
+
const initResult = {
|
|
20823
|
+
deviceId,
|
|
20824
|
+
iframeUrl,
|
|
20825
|
+
referrer: document.referrer,
|
|
20826
|
+
agent: config.agent,
|
|
20827
|
+
timestamp: Date.now()
|
|
20828
|
+
};
|
|
20632
20829
|
// 创建悬浮图标管理器(支持自定义位置)
|
|
20633
20830
|
const iconPosition = options?.iconPosition || undefined;
|
|
20634
|
-
this.iconManager = new IconManager(iconPosition);
|
|
20831
|
+
this.iconManager = new IconManager(iconPosition, this.debug);
|
|
20635
20832
|
await this.iconManager.show();
|
|
20636
20833
|
// 创建iframe管理器(自动检测设备类型)
|
|
20637
20834
|
this.iframeManager = new IframeManager({
|
|
20638
20835
|
src: iframeUrl,
|
|
20639
|
-
mode: 'auto', //
|
|
20640
|
-
width:
|
|
20641
|
-
height: 600,
|
|
20836
|
+
mode: 'auto', // 自动根据设备类型选择模式(PC弹窗,移动端全屏)
|
|
20837
|
+
width: options?.width || 450, // PC模式宽度(像素,默认450px),移动端不使用
|
|
20838
|
+
height: options?.height || 600, // PC模式高度(像素),移动端不使用(强制全屏)
|
|
20642
20839
|
allowClose: true,
|
|
20840
|
+
debug: this.debug, // 传递 debug 标志
|
|
20643
20841
|
onMessage: (messageType, _data) => {
|
|
20644
20842
|
// 处理来自iframe的消息
|
|
20645
20843
|
if (messageType === 'new-message') {
|
|
@@ -20649,8 +20847,9 @@ class CustomerServiceSDK {
|
|
|
20649
20847
|
// checkScreenshot 消息由 ScreenshotManager 处理,不需要在这里处理
|
|
20650
20848
|
},
|
|
20651
20849
|
onClose: () => {
|
|
20652
|
-
// iframe
|
|
20850
|
+
// iframe关闭时,清理图标拖动事件监听器,并重新启用图标点击
|
|
20653
20851
|
this.iconManager?.forceCleanupDragEvents();
|
|
20852
|
+
this.iconManager?.enableClick();
|
|
20654
20853
|
},
|
|
20655
20854
|
...options
|
|
20656
20855
|
});
|
|
@@ -20661,24 +20860,40 @@ class CustomerServiceSDK {
|
|
|
20661
20860
|
// 打开iframe时清除红点通知
|
|
20662
20861
|
this.clearNotification();
|
|
20663
20862
|
this.iframeManager?.show();
|
|
20863
|
+
// iframe 打开后,禁用图标点击(防止重复打开)
|
|
20864
|
+
this.iconManager?.disableClick();
|
|
20664
20865
|
});
|
|
20665
20866
|
// 初始化截图管理器(如果启用了截图功能)
|
|
20666
20867
|
if (config.screenshot) {
|
|
20667
20868
|
// 默认截图目标为 document.body,可以通过配置自定义
|
|
20668
20869
|
const targetElement = document.body;
|
|
20669
20870
|
// 传入发送消息到 iframe 的回调函数
|
|
20670
|
-
|
|
20871
|
+
// 将 debug 配置传递给截图管理器(通过 silentMode 的相反值)
|
|
20872
|
+
const screenshotOptions = {
|
|
20873
|
+
...config.screenshot,
|
|
20874
|
+
silentMode: !this.debug // debug=true 时 silentMode=false(显示日志),debug=false 时 silentMode=true(隐藏日志)
|
|
20875
|
+
};
|
|
20876
|
+
this.screenshotManager = new ScreenshotManager(targetElement, screenshotOptions, (data) => {
|
|
20671
20877
|
// 通过 IframeManager 发送消息到 iframe
|
|
20672
20878
|
this.iframeManager?.sendToIframe(data);
|
|
20673
20879
|
});
|
|
20674
20880
|
// 自动启用截图功能(用于测试,实际使用时需要通过 iframe 消息启用)
|
|
20675
20881
|
this.screenshotManager.enable(true);
|
|
20676
|
-
|
|
20882
|
+
if (this.debug) {
|
|
20883
|
+
console.log('CustomerSDK screenshot manager initialized and enabled');
|
|
20884
|
+
}
|
|
20677
20885
|
}
|
|
20678
20886
|
this.isInitialized = true;
|
|
20679
|
-
|
|
20887
|
+
// 保存初始化结果,以便后续获取
|
|
20888
|
+
this.initResult = initResult;
|
|
20889
|
+
if (this.debug) {
|
|
20890
|
+
console.log('CustomerSDK initialized successfully (iframe pre-connected to SSE)');
|
|
20891
|
+
}
|
|
20892
|
+
// 返回初始化信息
|
|
20893
|
+
return initResult;
|
|
20680
20894
|
}
|
|
20681
20895
|
catch (error) {
|
|
20896
|
+
// 错误始终输出
|
|
20682
20897
|
console.error('Failed to initialize CustomerSDK:', error);
|
|
20683
20898
|
throw error;
|
|
20684
20899
|
}
|
|
@@ -20747,7 +20962,9 @@ class CustomerServiceSDK {
|
|
|
20747
20962
|
*/
|
|
20748
20963
|
showNotification(badgeCount = 1, options = {}) {
|
|
20749
20964
|
if (!this.iconManager) {
|
|
20750
|
-
|
|
20965
|
+
if (this.debug) {
|
|
20966
|
+
console.warn('SDK not initialized');
|
|
20967
|
+
}
|
|
20751
20968
|
return;
|
|
20752
20969
|
}
|
|
20753
20970
|
this.iconManager.showNotification({
|
|
@@ -20779,7 +20996,9 @@ class CustomerServiceSDK {
|
|
|
20779
20996
|
*/
|
|
20780
20997
|
async captureScreenshot(force = false) {
|
|
20781
20998
|
if (!this.screenshotManager) {
|
|
20782
|
-
|
|
20999
|
+
if (this.debug) {
|
|
21000
|
+
console.warn('截图功能未启用');
|
|
21001
|
+
}
|
|
20783
21002
|
return false;
|
|
20784
21003
|
}
|
|
20785
21004
|
return await this.screenshotManager.captureOnce(force);
|
|
@@ -20789,11 +21008,15 @@ class CustomerServiceSDK {
|
|
|
20789
21008
|
*/
|
|
20790
21009
|
enableScreenshot(enabled) {
|
|
20791
21010
|
if (!this.screenshotManager) {
|
|
20792
|
-
|
|
21011
|
+
if (this.debug) {
|
|
21012
|
+
console.warn('截图管理器未初始化');
|
|
21013
|
+
}
|
|
20793
21014
|
return;
|
|
20794
21015
|
}
|
|
20795
21016
|
this.screenshotManager.enable(enabled);
|
|
20796
|
-
|
|
21017
|
+
if (this.debug) {
|
|
21018
|
+
console.log(`📸 截图功能已${enabled ? '启用' : '禁用'}`);
|
|
21019
|
+
}
|
|
20797
21020
|
}
|
|
20798
21021
|
/**
|
|
20799
21022
|
* 获取截图状态
|
|
@@ -20807,11 +21030,15 @@ class CustomerServiceSDK {
|
|
|
20807
21030
|
*/
|
|
20808
21031
|
updateScreenshotOptions(options) {
|
|
20809
21032
|
if (!this.screenshotManager) {
|
|
20810
|
-
|
|
21033
|
+
if (this.debug) {
|
|
21034
|
+
console.warn('截图管理器未初始化,无法更新配置');
|
|
21035
|
+
}
|
|
20811
21036
|
return;
|
|
20812
21037
|
}
|
|
20813
21038
|
this.screenshotManager.updateOptions(options);
|
|
20814
|
-
|
|
21039
|
+
if (this.debug) {
|
|
21040
|
+
console.log('📸 截图配置已更新:', options);
|
|
21041
|
+
}
|
|
20815
21042
|
}
|
|
20816
21043
|
/**
|
|
20817
21044
|
* 销毁 SDK
|
|
@@ -20824,27 +21051,47 @@ class CustomerServiceSDK {
|
|
|
20824
21051
|
this.iframeManager = null;
|
|
20825
21052
|
this.screenshotManager = null;
|
|
20826
21053
|
this.config = null;
|
|
21054
|
+
this.initResult = null;
|
|
20827
21055
|
this.isInitialized = false;
|
|
20828
|
-
|
|
21056
|
+
if (this.debug) {
|
|
21057
|
+
console.log('CustomerSDK destroyed');
|
|
21058
|
+
}
|
|
21059
|
+
}
|
|
21060
|
+
/**
|
|
21061
|
+
* 获取初始化信息(设备ID等)
|
|
21062
|
+
*/
|
|
21063
|
+
getInitResult() {
|
|
21064
|
+
return this.initResult;
|
|
20829
21065
|
}
|
|
20830
21066
|
/**
|
|
20831
21067
|
* 获取设备指纹ID
|
|
20832
21068
|
*/
|
|
20833
21069
|
async getDeviceId() {
|
|
20834
|
-
|
|
21070
|
+
if (this.debug) {
|
|
21071
|
+
console.log('🔍 Starting to get device fingerprint...');
|
|
21072
|
+
}
|
|
20835
21073
|
try {
|
|
20836
|
-
|
|
21074
|
+
if (this.debug) {
|
|
21075
|
+
console.log('📦 Loading FingerprintJS...');
|
|
21076
|
+
}
|
|
20837
21077
|
const fp = await index$1.load();
|
|
20838
|
-
|
|
21078
|
+
if (this.debug) {
|
|
21079
|
+
console.log('🎯 Getting device fingerprint...');
|
|
21080
|
+
}
|
|
20839
21081
|
const result = await fp.get();
|
|
20840
|
-
|
|
20841
|
-
|
|
21082
|
+
if (this.debug) {
|
|
21083
|
+
console.log('✅ FingerprintJS result:', result);
|
|
21084
|
+
console.log('🆔 Device ID obtained:', result.visitorId);
|
|
21085
|
+
}
|
|
20842
21086
|
return result.visitorId;
|
|
20843
21087
|
}
|
|
20844
21088
|
catch (error) {
|
|
21089
|
+
// 错误始终输出
|
|
20845
21090
|
console.warn('❌ Failed to get device fingerprint, using fallback:', error);
|
|
20846
21091
|
const fallbackId = 'device_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
|
20847
|
-
|
|
21092
|
+
if (this.debug) {
|
|
21093
|
+
console.log('🆔 Fallback Device ID:', fallbackId);
|
|
21094
|
+
}
|
|
20848
21095
|
return fallbackId;
|
|
20849
21096
|
}
|
|
20850
21097
|
}
|
|
@@ -20875,12 +21122,13 @@ let globalSDKInstance = null;
|
|
|
20875
21122
|
* 初始化 Customer SDK
|
|
20876
21123
|
* @param config SDK配置
|
|
20877
21124
|
* @param options UI选项(可选)
|
|
21125
|
+
* @returns 返回初始化信息(包含设备ID等)
|
|
20878
21126
|
*/
|
|
20879
21127
|
const init = async (config, options) => {
|
|
20880
21128
|
if (!globalSDKInstance) {
|
|
20881
21129
|
globalSDKInstance = new CustomerServiceSDK();
|
|
20882
21130
|
}
|
|
20883
|
-
await globalSDKInstance.init(config, options);
|
|
21131
|
+
return await globalSDKInstance.init(config, options);
|
|
20884
21132
|
};
|
|
20885
21133
|
/**
|
|
20886
21134
|
* 获取全局SDK实例
|
|
@@ -20943,6 +21191,13 @@ const getConnectionStatus = () => {
|
|
|
20943
21191
|
const sdk = getInstance();
|
|
20944
21192
|
return sdk.getConnectionStatus();
|
|
20945
21193
|
};
|
|
21194
|
+
/**
|
|
21195
|
+
* 获取初始化信息(设备ID等)
|
|
21196
|
+
*/
|
|
21197
|
+
const getInitResult = () => {
|
|
21198
|
+
const sdk = getInstance();
|
|
21199
|
+
return sdk.getInitResult();
|
|
21200
|
+
};
|
|
20946
21201
|
/**
|
|
20947
21202
|
* 消息通知API
|
|
20948
21203
|
*/
|
|
@@ -20986,6 +21241,7 @@ const updateScreenshotOptions = (options) => {
|
|
|
20986
21241
|
var index = {
|
|
20987
21242
|
init,
|
|
20988
21243
|
getInstance,
|
|
21244
|
+
getInitResult,
|
|
20989
21245
|
showIcon,
|
|
20990
21246
|
hideIcon,
|
|
20991
21247
|
setIconPosition,
|
|
@@ -21014,6 +21270,7 @@ exports.default = index;
|
|
|
21014
21270
|
exports.destroy = destroy;
|
|
21015
21271
|
exports.enableScreenshot = enableScreenshot;
|
|
21016
21272
|
exports.getConnectionStatus = getConnectionStatus;
|
|
21273
|
+
exports.getInitResult = getInitResult;
|
|
21017
21274
|
exports.getInstance = getInstance;
|
|
21018
21275
|
exports.getScreenshotState = getScreenshotState;
|
|
21019
21276
|
exports.hideIcon = hideIcon;
|