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