customer-chat-sdk 1.0.20 → 1.0.21
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/README.md +72 -2
- package/dist/core/ScreenshotManager.d.ts +167 -0
- package/dist/core/ScreenshotManager.d.ts.map +1 -0
- package/dist/customer-sdk.cjs.js +798 -0
- package/dist/customer-sdk.esm.js +796 -1
- package/dist/customer-sdk.min.js +1 -1
- package/dist/index.d.ts +63 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/types/index.d.ts +25 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +3 -2
package/dist/customer-sdk.cjs.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
+
var html2canvas = require('html2canvas');
|
|
6
|
+
|
|
5
7
|
// 直接使用base64字符串,避免打包后路径问题
|
|
6
8
|
const iconImage = 'data:image/png;base64,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';
|
|
7
9
|
class IconManager {
|
|
@@ -923,6 +925,748 @@ class IframeManager {
|
|
|
923
925
|
}
|
|
924
926
|
}
|
|
925
927
|
|
|
928
|
+
// @ts-ignore - html2canvas may not have type definitions
|
|
929
|
+
/**
|
|
930
|
+
* 截图管理器
|
|
931
|
+
* 负责页面截图、压缩和上传功能
|
|
932
|
+
*/
|
|
933
|
+
class ScreenshotManager {
|
|
934
|
+
constructor(targetElement, options = {}) {
|
|
935
|
+
this.targetElement = null;
|
|
936
|
+
this.isRunning = false;
|
|
937
|
+
this.screenshotCount = 0;
|
|
938
|
+
this.screenshotHistory = [];
|
|
939
|
+
this.lastScreenshotTime = 0;
|
|
940
|
+
this.error = null;
|
|
941
|
+
this.isEnabled = false;
|
|
942
|
+
// 上传相关状态
|
|
943
|
+
this.isUploading = false;
|
|
944
|
+
this.uploadError = null;
|
|
945
|
+
this.uploadProgress = { success: 0, failed: 0 };
|
|
946
|
+
this.currentUploadConfig = null;
|
|
947
|
+
// WebWorker 相关
|
|
948
|
+
this.worker = null;
|
|
949
|
+
this.screenshotTimer = null;
|
|
950
|
+
// PostMessage 监听器
|
|
951
|
+
this.messageHandler = null;
|
|
952
|
+
// 动态轮询间隔(由 iframe 消息控制)
|
|
953
|
+
this.dynamicInterval = null;
|
|
954
|
+
// 过期定时器
|
|
955
|
+
this.expirationTimer = null;
|
|
956
|
+
// 图片代理缓存
|
|
957
|
+
this.imageProxyCache = new Map();
|
|
958
|
+
// 全局错误处理器
|
|
959
|
+
this.globalErrorHandler = null;
|
|
960
|
+
this.globalRejectionHandler = null;
|
|
961
|
+
this.targetElement = targetElement;
|
|
962
|
+
this.options = {
|
|
963
|
+
interval: options.interval ?? 5000,
|
|
964
|
+
quality: options.quality ?? 0.4,
|
|
965
|
+
scale: options.scale ?? 1,
|
|
966
|
+
maxHistory: options.maxHistory ?? 10,
|
|
967
|
+
compress: options.compress ?? false,
|
|
968
|
+
maxWidth: options.maxWidth ?? 1600,
|
|
969
|
+
maxHeight: options.maxHeight ?? 900,
|
|
970
|
+
outputFormat: options.outputFormat ?? 'webp',
|
|
971
|
+
enableCORS: options.enableCORS ?? true,
|
|
972
|
+
proxyUrl: options.proxyUrl ?? '',
|
|
973
|
+
engine: options.engine ?? 'html2canvas',
|
|
974
|
+
corsMode: options.corsMode ?? 'canvas-proxy',
|
|
975
|
+
silentMode: options.silentMode ?? false,
|
|
976
|
+
maxRetries: options.maxRetries ?? 2
|
|
977
|
+
};
|
|
978
|
+
this.setupMessageListener();
|
|
979
|
+
this.setupVisibilityChangeListener();
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* 设置目标元素
|
|
983
|
+
*/
|
|
984
|
+
setTargetElement(element) {
|
|
985
|
+
this.targetElement = element;
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* 设置消息监听
|
|
989
|
+
*/
|
|
990
|
+
setupMessageListener() {
|
|
991
|
+
if (this.messageHandler) {
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
this.messageHandler = (event) => {
|
|
995
|
+
this.handleIframeMessage(event);
|
|
996
|
+
};
|
|
997
|
+
window.addEventListener('message', this.messageHandler);
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* 设置页面可见性监听
|
|
1001
|
+
*/
|
|
1002
|
+
setupVisibilityChangeListener() {
|
|
1003
|
+
document.addEventListener('visibilitychange', () => {
|
|
1004
|
+
if (document.hidden) {
|
|
1005
|
+
if (!this.options.silentMode) {
|
|
1006
|
+
console.log('📸 页面隐藏,截图轮询已暂停');
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
else {
|
|
1010
|
+
if (!this.options.silentMode) {
|
|
1011
|
+
console.log('📸 页面显示,截图轮询已恢复');
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* 处理 iframe postMessage 消息
|
|
1018
|
+
*/
|
|
1019
|
+
handleIframeMessage(event) {
|
|
1020
|
+
try {
|
|
1021
|
+
// 验证消息类型
|
|
1022
|
+
if (!event.data || event.data.type !== 'checkScreenshot') {
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
if (!this.options.silentMode) {
|
|
1026
|
+
console.log('📸 [iframe] 收到消息:', event.data);
|
|
1027
|
+
}
|
|
1028
|
+
// 解析上传配置
|
|
1029
|
+
const config = this.parseUploadConfig(event.data.data);
|
|
1030
|
+
if (!config) {
|
|
1031
|
+
console.error('📸 [iframe] 解析配置失败');
|
|
1032
|
+
this.uploadError = '解析上传配置失败';
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
// 保存当前配置
|
|
1036
|
+
this.currentUploadConfig = config;
|
|
1037
|
+
// 根据 expirationMinutes 判断是否开启截图功能
|
|
1038
|
+
if (config.expirationMinutes > 0) {
|
|
1039
|
+
// 启用截图功能
|
|
1040
|
+
if (!this.isEnabled) {
|
|
1041
|
+
if (!this.options.silentMode) {
|
|
1042
|
+
console.log('📸 [iframe] 启用截图功能');
|
|
1043
|
+
}
|
|
1044
|
+
this.isEnabled = true;
|
|
1045
|
+
}
|
|
1046
|
+
// 设置动态轮询间隔(使用 duration,单位:毫秒)
|
|
1047
|
+
this.dynamicInterval = config.duration || this.options.interval;
|
|
1048
|
+
// 启动或更新截图轮询
|
|
1049
|
+
if (!this.options.silentMode) {
|
|
1050
|
+
console.log(`📸 [iframe] 设置轮询间隔: ${this.dynamicInterval}ms,过期时间: ${config.expirationMinutes}分钟`);
|
|
1051
|
+
}
|
|
1052
|
+
this.startScreenshot(this.dynamicInterval);
|
|
1053
|
+
// 设置过期定时器
|
|
1054
|
+
if (this.expirationTimer) {
|
|
1055
|
+
clearTimeout(this.expirationTimer);
|
|
1056
|
+
this.expirationTimer = null;
|
|
1057
|
+
}
|
|
1058
|
+
this.expirationTimer = setTimeout(() => {
|
|
1059
|
+
if (!this.options.silentMode) {
|
|
1060
|
+
console.log('📸 [iframe] 上传配置已过期,停止截图');
|
|
1061
|
+
}
|
|
1062
|
+
this.stopScreenshot();
|
|
1063
|
+
this.isEnabled = false;
|
|
1064
|
+
this.currentUploadConfig = null;
|
|
1065
|
+
this.expirationTimer = null;
|
|
1066
|
+
}, config.expirationMinutes * 60 * 1000);
|
|
1067
|
+
}
|
|
1068
|
+
else {
|
|
1069
|
+
// 禁用截图功能
|
|
1070
|
+
if (!this.options.silentMode) {
|
|
1071
|
+
console.log('📸 [iframe] expirationMinutes <= 0,禁用截图功能');
|
|
1072
|
+
}
|
|
1073
|
+
this.stopScreenshot();
|
|
1074
|
+
this.isEnabled = false;
|
|
1075
|
+
this.currentUploadConfig = null;
|
|
1076
|
+
if (this.expirationTimer) {
|
|
1077
|
+
clearTimeout(this.expirationTimer);
|
|
1078
|
+
this.expirationTimer = null;
|
|
1079
|
+
}
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
// 获取最新截图并上传
|
|
1083
|
+
const latestScreenshot = this.getLatestScreenshot();
|
|
1084
|
+
if (!latestScreenshot) {
|
|
1085
|
+
if (!this.options.silentMode) {
|
|
1086
|
+
console.warn('📸 [iframe] 没有可用的截图,等待下次截图后上传');
|
|
1087
|
+
}
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
// 执行上传
|
|
1091
|
+
this.isUploading = true;
|
|
1092
|
+
this.uploadError = null;
|
|
1093
|
+
this.uploadScreenshot(latestScreenshot, config)
|
|
1094
|
+
.then((success) => {
|
|
1095
|
+
if (success) {
|
|
1096
|
+
if (!this.options.silentMode) {
|
|
1097
|
+
console.log('📸 [iframe] ✅ 截图上传成功');
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
else {
|
|
1101
|
+
console.error('📸 [iframe] ❌ 截图上传失败');
|
|
1102
|
+
}
|
|
1103
|
+
})
|
|
1104
|
+
.catch((error) => {
|
|
1105
|
+
console.error('📸 [iframe] ❌ 上传异常:', error);
|
|
1106
|
+
this.uploadError = error instanceof Error ? error.message : String(error);
|
|
1107
|
+
})
|
|
1108
|
+
.finally(() => {
|
|
1109
|
+
this.isUploading = false;
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
catch (error) {
|
|
1113
|
+
console.error('📸 [iframe] 处理消息失败:', error);
|
|
1114
|
+
this.uploadError = error instanceof Error ? error.message : String(error);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
/**
|
|
1118
|
+
* 解析上传配置
|
|
1119
|
+
*/
|
|
1120
|
+
parseUploadConfig(data) {
|
|
1121
|
+
try {
|
|
1122
|
+
const configStr = typeof data === 'string' ? data : JSON.stringify(data);
|
|
1123
|
+
const config = JSON.parse(configStr);
|
|
1124
|
+
if (!config.uploadUrl || !config.contentType) {
|
|
1125
|
+
console.error('📸 [上传] 配置缺少必需字段:', config);
|
|
1126
|
+
return null;
|
|
1127
|
+
}
|
|
1128
|
+
if (typeof config.duration !== 'number' || config.duration <= 0) {
|
|
1129
|
+
config.duration = this.options.interval;
|
|
1130
|
+
}
|
|
1131
|
+
return config;
|
|
1132
|
+
}
|
|
1133
|
+
catch (error) {
|
|
1134
|
+
console.error('📸 [上传] 解析配置失败:', error, '原始数据:', data);
|
|
1135
|
+
return null;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
/**
|
|
1139
|
+
* 开始轮询截图
|
|
1140
|
+
*/
|
|
1141
|
+
startScreenshot(customInterval) {
|
|
1142
|
+
if (!this.isEnabled) {
|
|
1143
|
+
console.warn('📸 截图功能已禁用,无法启动');
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
const currentInterval = customInterval || this.dynamicInterval || this.options.interval;
|
|
1147
|
+
if (this.isRunning) {
|
|
1148
|
+
if (!this.options.silentMode) {
|
|
1149
|
+
console.log(`📸 更新轮询间隔: ${currentInterval}ms`);
|
|
1150
|
+
}
|
|
1151
|
+
this.stopScreenshot();
|
|
1152
|
+
}
|
|
1153
|
+
if (!this.options.silentMode) {
|
|
1154
|
+
console.log(`📸 开始轮询截图,间隔: ${currentInterval}ms`);
|
|
1155
|
+
}
|
|
1156
|
+
this.isRunning = true;
|
|
1157
|
+
// 创建 WebWorker
|
|
1158
|
+
if (!this.worker && this.options.compress) {
|
|
1159
|
+
this.worker = this.createWorker();
|
|
1160
|
+
}
|
|
1161
|
+
// 设置定时器
|
|
1162
|
+
this.screenshotTimer = setInterval(async () => {
|
|
1163
|
+
if (this.isRunning && this.isEnabled && !document.hidden) {
|
|
1164
|
+
await this.takeScreenshot();
|
|
1165
|
+
}
|
|
1166
|
+
}, currentInterval);
|
|
1167
|
+
// 立即执行一次
|
|
1168
|
+
setTimeout(() => {
|
|
1169
|
+
if (this.isRunning && this.isEnabled) {
|
|
1170
|
+
this.takeScreenshot();
|
|
1171
|
+
}
|
|
1172
|
+
}, 0);
|
|
1173
|
+
}
|
|
1174
|
+
/**
|
|
1175
|
+
* 停止轮询截图
|
|
1176
|
+
*/
|
|
1177
|
+
stopScreenshot() {
|
|
1178
|
+
if (!this.isRunning) {
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1181
|
+
if (!this.options.silentMode) {
|
|
1182
|
+
console.log('📸 停止轮询截图');
|
|
1183
|
+
}
|
|
1184
|
+
this.isRunning = false;
|
|
1185
|
+
if (this.screenshotTimer) {
|
|
1186
|
+
clearInterval(this.screenshotTimer);
|
|
1187
|
+
this.screenshotTimer = null;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* 手动截图一次
|
|
1192
|
+
*/
|
|
1193
|
+
async captureOnce() {
|
|
1194
|
+
if (!this.isEnabled) {
|
|
1195
|
+
console.warn('📸 截图功能已禁用,无法执行截图');
|
|
1196
|
+
return false;
|
|
1197
|
+
}
|
|
1198
|
+
return await this.takeScreenshot();
|
|
1199
|
+
}
|
|
1200
|
+
/**
|
|
1201
|
+
* 执行截图
|
|
1202
|
+
*/
|
|
1203
|
+
async takeScreenshot() {
|
|
1204
|
+
if (!this.targetElement) {
|
|
1205
|
+
console.warn('📸 目标元素不存在');
|
|
1206
|
+
return false;
|
|
1207
|
+
}
|
|
1208
|
+
this.setupGlobalErrorHandlers();
|
|
1209
|
+
try {
|
|
1210
|
+
if (!this.options.silentMode) {
|
|
1211
|
+
console.log(`📸 开始截图 #${this.screenshotCount + 1}...`);
|
|
1212
|
+
}
|
|
1213
|
+
// 等待 CSS 和字体加载完成
|
|
1214
|
+
await Promise.all([
|
|
1215
|
+
this.waitForStylesAndFonts(),
|
|
1216
|
+
this.waitForFonts()
|
|
1217
|
+
]);
|
|
1218
|
+
// 选择截图引擎(仅支持 html2canvas)
|
|
1219
|
+
const selectedEngine = 'html2canvas';
|
|
1220
|
+
if (!this.options.silentMode) {
|
|
1221
|
+
console.log(`📸 使用截图引擎: ${selectedEngine}`);
|
|
1222
|
+
}
|
|
1223
|
+
// 预处理网络图片
|
|
1224
|
+
if (this.options.enableCORS) {
|
|
1225
|
+
await this.preprocessNetworkImages(this.targetElement);
|
|
1226
|
+
await this.waitForImagesToLoad(this.targetElement);
|
|
1227
|
+
}
|
|
1228
|
+
let dataUrl;
|
|
1229
|
+
// 等待一小段时间,确保 DOM 更新完成
|
|
1230
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
1231
|
+
// 使用 html2canvas 截图
|
|
1232
|
+
dataUrl = await this.takeScreenshotWithHtml2Canvas(this.targetElement);
|
|
1233
|
+
const timestamp = Date.now();
|
|
1234
|
+
// 更新状态
|
|
1235
|
+
this.screenshotCount++;
|
|
1236
|
+
this.lastScreenshotTime = timestamp;
|
|
1237
|
+
// 管理历史记录
|
|
1238
|
+
if (this.screenshotHistory.length >= this.options.maxHistory) {
|
|
1239
|
+
this.screenshotHistory.shift();
|
|
1240
|
+
}
|
|
1241
|
+
this.screenshotHistory.push(dataUrl);
|
|
1242
|
+
// 打印基本信息
|
|
1243
|
+
const base64Data = dataUrl.split(',')[1];
|
|
1244
|
+
if (!this.options.silentMode) {
|
|
1245
|
+
console.log('📸 截图完成:');
|
|
1246
|
+
console.log(`📸 编号: #${this.screenshotCount}`);
|
|
1247
|
+
console.log(`📸 时间: ${new Date(timestamp).toLocaleTimeString()}`);
|
|
1248
|
+
console.log(`📸 原始大小: ${Math.round(base64Data.length * 0.75 / 1024)} KB`);
|
|
1249
|
+
console.log(`📸 Base64 长度: ${base64Data.length} 字符`);
|
|
1250
|
+
}
|
|
1251
|
+
// 检测移动设备和低端设备
|
|
1252
|
+
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
1253
|
+
const isLowEndDevice = navigator.hardwareConcurrency && navigator.hardwareConcurrency <= 4;
|
|
1254
|
+
// 如果启用压缩(仅在非移动设备上启用)
|
|
1255
|
+
const shouldCompress = this.options.compress && !isMobile && !isLowEndDevice;
|
|
1256
|
+
if (shouldCompress && this.worker) {
|
|
1257
|
+
if (!this.options.silentMode) {
|
|
1258
|
+
console.log('📸 发送到 WebWorker 进行压缩...');
|
|
1259
|
+
}
|
|
1260
|
+
this.worker.postMessage({
|
|
1261
|
+
type: 'COMPRESS_IMAGE',
|
|
1262
|
+
data: {
|
|
1263
|
+
dataUrl,
|
|
1264
|
+
maxWidth: this.options.maxWidth,
|
|
1265
|
+
maxHeight: this.options.maxHeight,
|
|
1266
|
+
quality: this.options.quality,
|
|
1267
|
+
outputFormat: this.options.outputFormat,
|
|
1268
|
+
timestamp,
|
|
1269
|
+
count: this.screenshotCount
|
|
1270
|
+
}
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
this.error = null;
|
|
1274
|
+
return true;
|
|
1275
|
+
}
|
|
1276
|
+
catch (err) {
|
|
1277
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
1278
|
+
const isCorsError = errorMessage.includes('CORS') ||
|
|
1279
|
+
errorMessage.includes('Access-Control-Allow-Origin') ||
|
|
1280
|
+
errorMessage.includes('cross-origin') ||
|
|
1281
|
+
errorMessage.includes('XMLHttpRequest') ||
|
|
1282
|
+
errorMessage.includes('Status:0');
|
|
1283
|
+
if (!isCorsError) {
|
|
1284
|
+
console.error('📸 截图失败:', err);
|
|
1285
|
+
this.error = errorMessage;
|
|
1286
|
+
}
|
|
1287
|
+
else if (!this.options.silentMode) {
|
|
1288
|
+
console.warn('📸 截图遇到跨域问题(已忽略)');
|
|
1289
|
+
}
|
|
1290
|
+
return false;
|
|
1291
|
+
}
|
|
1292
|
+
finally {
|
|
1293
|
+
this.removeGlobalErrorHandlers();
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
/**
|
|
1297
|
+
* 使用 html2canvas 截图
|
|
1298
|
+
*/
|
|
1299
|
+
async takeScreenshotWithHtml2Canvas(element) {
|
|
1300
|
+
if (!this.options.silentMode) {
|
|
1301
|
+
console.log('📸 使用 html2canvas 引擎截图...');
|
|
1302
|
+
}
|
|
1303
|
+
const { width, height } = this.calculateCompressedSize(element.scrollWidth, element.scrollHeight, this.options.maxWidth, this.options.maxHeight);
|
|
1304
|
+
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
1305
|
+
const isLowEndDevice = navigator.hardwareConcurrency && navigator.hardwareConcurrency <= 4;
|
|
1306
|
+
const mobileQuality = isMobile || isLowEndDevice ? Math.max(this.options.quality * 0.7, 0.3) : this.options.quality;
|
|
1307
|
+
const mobileWidth = isMobile ? Math.min(width, 1280) : width;
|
|
1308
|
+
const mobileHeight = isMobile ? Math.min(height, 720) : height;
|
|
1309
|
+
const canvas = await html2canvas(element, {
|
|
1310
|
+
useCORS: this.options.enableCORS,
|
|
1311
|
+
allowTaint: true,
|
|
1312
|
+
scale: isMobile ? 0.8 : this.options.scale,
|
|
1313
|
+
backgroundColor: '#ffffff',
|
|
1314
|
+
logging: false,
|
|
1315
|
+
width: mobileWidth,
|
|
1316
|
+
height: mobileHeight,
|
|
1317
|
+
scrollX: 0,
|
|
1318
|
+
scrollY: 0,
|
|
1319
|
+
removeContainer: false,
|
|
1320
|
+
foreignObjectRendering: false,
|
|
1321
|
+
onclone: (clonedDoc) => {
|
|
1322
|
+
const clonedImages = clonedDoc.querySelectorAll('img');
|
|
1323
|
+
clonedImages.forEach((img) => {
|
|
1324
|
+
if (img.src && !img.src.startsWith('data:') && !img.src.startsWith('blob:')) {
|
|
1325
|
+
try {
|
|
1326
|
+
const imgUrl = new URL(img.src, window.location.href);
|
|
1327
|
+
const currentOrigin = window.location.origin;
|
|
1328
|
+
if (this.options.proxyUrl && img.src.includes(this.options.proxyUrl.split('/api/image-proxy')[0])) {
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
if (imgUrl.origin !== currentOrigin) {
|
|
1332
|
+
if (this.options.corsMode === 'canvas-proxy') {
|
|
1333
|
+
const cachedDataUrl = this.imageProxyCache.get(img.src);
|
|
1334
|
+
if (cachedDataUrl) {
|
|
1335
|
+
img.src = cachedDataUrl;
|
|
1336
|
+
}
|
|
1337
|
+
else if (img.crossOrigin) {
|
|
1338
|
+
img.removeAttribute('crossOrigin');
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
else if (!img.crossOrigin) {
|
|
1342
|
+
img.crossOrigin = 'anonymous';
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
catch (e) {
|
|
1347
|
+
// URL 解析失败,跳过
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
});
|
|
1351
|
+
},
|
|
1352
|
+
ignoreElements: (element) => {
|
|
1353
|
+
const htmlElement = element;
|
|
1354
|
+
return element.classList.contains('van-popup') ||
|
|
1355
|
+
element.classList.contains('van-overlay') ||
|
|
1356
|
+
element.classList.contains('van-toast') ||
|
|
1357
|
+
element.classList.contains('van-dialog') ||
|
|
1358
|
+
element.classList.contains('van-loading') ||
|
|
1359
|
+
htmlElement.style.display === 'none' ||
|
|
1360
|
+
htmlElement.style.visibility === 'hidden';
|
|
1361
|
+
},
|
|
1362
|
+
imageTimeout: 15000
|
|
1363
|
+
});
|
|
1364
|
+
let mimeType = 'image/jpeg';
|
|
1365
|
+
let finalQuality = mobileQuality;
|
|
1366
|
+
if (this.options.outputFormat === 'webp' && !isMobile) {
|
|
1367
|
+
try {
|
|
1368
|
+
const testCanvas = document.createElement('canvas');
|
|
1369
|
+
testCanvas.width = 1;
|
|
1370
|
+
testCanvas.height = 1;
|
|
1371
|
+
const testDataUrl = testCanvas.toDataURL('image/webp');
|
|
1372
|
+
if (testDataUrl.indexOf('webp') !== -1) {
|
|
1373
|
+
mimeType = 'image/webp';
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
catch {
|
|
1377
|
+
mimeType = 'image/jpeg';
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
else if (this.options.outputFormat === 'png') {
|
|
1381
|
+
mimeType = 'image/png';
|
|
1382
|
+
finalQuality = undefined;
|
|
1383
|
+
}
|
|
1384
|
+
const dataUrl = mimeType === 'image/png'
|
|
1385
|
+
? canvas.toDataURL(mimeType)
|
|
1386
|
+
: canvas.toDataURL(mimeType, finalQuality);
|
|
1387
|
+
return dataUrl;
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* 预处理网络图片
|
|
1391
|
+
*/
|
|
1392
|
+
async preprocessNetworkImages(element) {
|
|
1393
|
+
const images = element.querySelectorAll('img');
|
|
1394
|
+
const networkImages = Array.from(images).filter(img => {
|
|
1395
|
+
const isBlob = img.src.startsWith('blob:');
|
|
1396
|
+
const isData = img.src.startsWith('data:');
|
|
1397
|
+
const isSameOrigin = img.src.startsWith(window.location.origin);
|
|
1398
|
+
return !isBlob && !isData && !isSameOrigin;
|
|
1399
|
+
});
|
|
1400
|
+
if (networkImages.length === 0) {
|
|
1401
|
+
return;
|
|
1402
|
+
}
|
|
1403
|
+
if (!this.options.silentMode) {
|
|
1404
|
+
console.log(`📸 发现 ${networkImages.length} 个跨域图片,开始预加载并缓存...`);
|
|
1405
|
+
}
|
|
1406
|
+
// 简化处理:只缓存已代理的图片
|
|
1407
|
+
// 实际代理逻辑需要根据项目需求实现
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* 等待图片加载完成
|
|
1411
|
+
*/
|
|
1412
|
+
async waitForImagesToLoad(element) {
|
|
1413
|
+
const images = element.querySelectorAll('img');
|
|
1414
|
+
const imagePromises = [];
|
|
1415
|
+
Array.from(images).forEach((img) => {
|
|
1416
|
+
if (img.complete) {
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
imagePromises.push(new Promise((resolve) => {
|
|
1420
|
+
const timeout = setTimeout(() => {
|
|
1421
|
+
resolve();
|
|
1422
|
+
}, 5000);
|
|
1423
|
+
img.onload = () => {
|
|
1424
|
+
clearTimeout(timeout);
|
|
1425
|
+
resolve();
|
|
1426
|
+
};
|
|
1427
|
+
img.onerror = () => {
|
|
1428
|
+
clearTimeout(timeout);
|
|
1429
|
+
resolve();
|
|
1430
|
+
};
|
|
1431
|
+
}));
|
|
1432
|
+
});
|
|
1433
|
+
if (imagePromises.length > 0) {
|
|
1434
|
+
await Promise.all(imagePromises);
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1438
|
+
* 等待 CSS 和字体加载完成
|
|
1439
|
+
*/
|
|
1440
|
+
async waitForStylesAndFonts() {
|
|
1441
|
+
return new Promise((resolve) => {
|
|
1442
|
+
setTimeout(() => {
|
|
1443
|
+
resolve();
|
|
1444
|
+
}, 100);
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
/**
|
|
1448
|
+
* 等待字体加载完成
|
|
1449
|
+
*/
|
|
1450
|
+
async waitForFonts() {
|
|
1451
|
+
if (!document.fonts) {
|
|
1452
|
+
return Promise.resolve();
|
|
1453
|
+
}
|
|
1454
|
+
try {
|
|
1455
|
+
await document.fonts.ready;
|
|
1456
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
1457
|
+
}
|
|
1458
|
+
catch {
|
|
1459
|
+
// 忽略错误
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
/**
|
|
1463
|
+
* 计算压缩后的尺寸
|
|
1464
|
+
*/
|
|
1465
|
+
calculateCompressedSize(originalWidth, originalHeight, maxW, maxH) {
|
|
1466
|
+
let width = originalWidth;
|
|
1467
|
+
let height = originalHeight;
|
|
1468
|
+
if (width > maxW || height > maxH) {
|
|
1469
|
+
const widthRatio = maxW / width;
|
|
1470
|
+
const heightRatio = maxH / height;
|
|
1471
|
+
const ratio = Math.min(widthRatio, heightRatio);
|
|
1472
|
+
width = Math.round(width * ratio);
|
|
1473
|
+
height = Math.round(height * ratio);
|
|
1474
|
+
}
|
|
1475
|
+
return { width, height };
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1478
|
+
* 创建 WebWorker
|
|
1479
|
+
*/
|
|
1480
|
+
createWorker() {
|
|
1481
|
+
if (typeof Worker === 'undefined' || typeof OffscreenCanvas === 'undefined') {
|
|
1482
|
+
return null;
|
|
1483
|
+
}
|
|
1484
|
+
try {
|
|
1485
|
+
// 简化的 Worker 代码(实际使用时需要完整实现)
|
|
1486
|
+
const workerCode = `
|
|
1487
|
+
self.onmessage = function(e) {
|
|
1488
|
+
const { type, data } = e.data;
|
|
1489
|
+
if (type === 'COMPRESS_IMAGE') {
|
|
1490
|
+
// 压缩逻辑(简化版)
|
|
1491
|
+
self.postMessage({
|
|
1492
|
+
type: 'SCREENSHOT_RESULT',
|
|
1493
|
+
data: { compressed: { dataUrl: data.dataUrl } }
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
};
|
|
1497
|
+
`;
|
|
1498
|
+
const blob = new Blob([workerCode], { type: 'application/javascript' });
|
|
1499
|
+
const workerUrl = URL.createObjectURL(blob);
|
|
1500
|
+
const newWorker = new Worker(workerUrl);
|
|
1501
|
+
newWorker.onmessage = (e) => {
|
|
1502
|
+
const { type, data } = e.data;
|
|
1503
|
+
if (type === 'SCREENSHOT_RESULT' && data?.compressed) {
|
|
1504
|
+
if (this.screenshotHistory.length > 0) {
|
|
1505
|
+
this.screenshotHistory[this.screenshotHistory.length - 1] = data.compressed.dataUrl;
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
};
|
|
1509
|
+
newWorker.onerror = (e) => {
|
|
1510
|
+
console.error('📸 WebWorker 错误:', e);
|
|
1511
|
+
};
|
|
1512
|
+
URL.revokeObjectURL(workerUrl);
|
|
1513
|
+
return newWorker;
|
|
1514
|
+
}
|
|
1515
|
+
catch (err) {
|
|
1516
|
+
console.error('📸 创建 WebWorker 失败:', err);
|
|
1517
|
+
return null;
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
/**
|
|
1521
|
+
* 设置全局错误处理器
|
|
1522
|
+
*/
|
|
1523
|
+
setupGlobalErrorHandlers() {
|
|
1524
|
+
this.globalErrorHandler = (event) => {
|
|
1525
|
+
const errorMessage = event.message || event.error?.message || '';
|
|
1526
|
+
const isCorsError = errorMessage.includes('CORS') ||
|
|
1527
|
+
errorMessage.includes('Access-Control-Allow-Origin') ||
|
|
1528
|
+
errorMessage.includes('cross-origin');
|
|
1529
|
+
if (isCorsError) {
|
|
1530
|
+
event.preventDefault();
|
|
1531
|
+
event.stopPropagation();
|
|
1532
|
+
return false;
|
|
1533
|
+
}
|
|
1534
|
+
return true;
|
|
1535
|
+
};
|
|
1536
|
+
this.globalRejectionHandler = (event) => {
|
|
1537
|
+
const errorMessage = event.reason?.message || String(event.reason) || '';
|
|
1538
|
+
const isCorsError = errorMessage.includes('CORS') ||
|
|
1539
|
+
errorMessage.includes('Access-Control-Allow-Origin') ||
|
|
1540
|
+
errorMessage.includes('cross-origin');
|
|
1541
|
+
if (isCorsError) {
|
|
1542
|
+
event.preventDefault();
|
|
1543
|
+
return false;
|
|
1544
|
+
}
|
|
1545
|
+
return true;
|
|
1546
|
+
};
|
|
1547
|
+
window.addEventListener('error', this.globalErrorHandler, true);
|
|
1548
|
+
window.addEventListener('unhandledrejection', this.globalRejectionHandler, true);
|
|
1549
|
+
}
|
|
1550
|
+
/**
|
|
1551
|
+
* 移除全局错误处理器
|
|
1552
|
+
*/
|
|
1553
|
+
removeGlobalErrorHandlers() {
|
|
1554
|
+
if (this.globalErrorHandler) {
|
|
1555
|
+
window.removeEventListener('error', this.globalErrorHandler, true);
|
|
1556
|
+
this.globalErrorHandler = null;
|
|
1557
|
+
}
|
|
1558
|
+
if (this.globalRejectionHandler) {
|
|
1559
|
+
window.removeEventListener('unhandledrejection', this.globalRejectionHandler, true);
|
|
1560
|
+
this.globalRejectionHandler = null;
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
/**
|
|
1564
|
+
* 上传截图到 S3
|
|
1565
|
+
*/
|
|
1566
|
+
async uploadScreenshot(dataUrl, config) {
|
|
1567
|
+
try {
|
|
1568
|
+
if (!this.options.silentMode) {
|
|
1569
|
+
console.log('📸 [上传] 开始上传截图...');
|
|
1570
|
+
}
|
|
1571
|
+
const blob = this.dataUrlToBlob(dataUrl, config.contentType);
|
|
1572
|
+
const response = await fetch(config.uploadUrl, {
|
|
1573
|
+
method: 'PUT',
|
|
1574
|
+
body: blob,
|
|
1575
|
+
headers: {
|
|
1576
|
+
'Content-Type': config.contentType
|
|
1577
|
+
}
|
|
1578
|
+
});
|
|
1579
|
+
if (response.status === 200) {
|
|
1580
|
+
if (!this.options.silentMode) {
|
|
1581
|
+
console.log('📸 [上传] ✅ 上传成功');
|
|
1582
|
+
}
|
|
1583
|
+
this.uploadProgress.success++;
|
|
1584
|
+
return true;
|
|
1585
|
+
}
|
|
1586
|
+
else {
|
|
1587
|
+
const errorText = await response.text().catch(() => '');
|
|
1588
|
+
const errorMsg = `上传失败: HTTP ${response.status} ${response.statusText}${errorText ? ` - ${errorText.substring(0, 200)}` : ''}`;
|
|
1589
|
+
console.error('📸 [上传] ❌', errorMsg);
|
|
1590
|
+
this.uploadError = errorMsg;
|
|
1591
|
+
this.uploadProgress.failed++;
|
|
1592
|
+
return false;
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
catch (error) {
|
|
1596
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1597
|
+
console.error('📸 [上传] ❌ 上传异常:', errorMsg);
|
|
1598
|
+
this.uploadError = `上传异常: ${errorMsg}`;
|
|
1599
|
+
this.uploadProgress.failed++;
|
|
1600
|
+
return false;
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
/**
|
|
1604
|
+
* 将 base64 data URL 转换为 Blob
|
|
1605
|
+
*/
|
|
1606
|
+
dataUrlToBlob(dataUrl, contentType) {
|
|
1607
|
+
const arr = dataUrl.split(',');
|
|
1608
|
+
const mimeMatch = arr[0].match(/:(.*?);/);
|
|
1609
|
+
const mime = mimeMatch ? mimeMatch[1] : contentType;
|
|
1610
|
+
const bstr = atob(arr[1]);
|
|
1611
|
+
let n = bstr.length;
|
|
1612
|
+
const u8arr = new Uint8Array(n);
|
|
1613
|
+
while (n--) {
|
|
1614
|
+
u8arr[n] = bstr.charCodeAt(n);
|
|
1615
|
+
}
|
|
1616
|
+
return new Blob([u8arr], { type: mime });
|
|
1617
|
+
}
|
|
1618
|
+
/**
|
|
1619
|
+
* 获取最新截图
|
|
1620
|
+
*/
|
|
1621
|
+
getLatestScreenshot() {
|
|
1622
|
+
return this.screenshotHistory[this.screenshotHistory.length - 1] || null;
|
|
1623
|
+
}
|
|
1624
|
+
/**
|
|
1625
|
+
* 启用/禁用截图功能
|
|
1626
|
+
*/
|
|
1627
|
+
enable(enabled) {
|
|
1628
|
+
this.isEnabled = enabled;
|
|
1629
|
+
if (!enabled && this.isRunning) {
|
|
1630
|
+
this.stopScreenshot();
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
/**
|
|
1634
|
+
* 清理资源
|
|
1635
|
+
*/
|
|
1636
|
+
destroy() {
|
|
1637
|
+
this.stopScreenshot();
|
|
1638
|
+
if (this.worker) {
|
|
1639
|
+
this.worker.terminate();
|
|
1640
|
+
this.worker = null;
|
|
1641
|
+
}
|
|
1642
|
+
if (this.expirationTimer) {
|
|
1643
|
+
clearTimeout(this.expirationTimer);
|
|
1644
|
+
this.expirationTimer = null;
|
|
1645
|
+
}
|
|
1646
|
+
if (this.messageHandler) {
|
|
1647
|
+
window.removeEventListener('message', this.messageHandler);
|
|
1648
|
+
this.messageHandler = null;
|
|
1649
|
+
}
|
|
1650
|
+
this.removeGlobalErrorHandlers();
|
|
1651
|
+
}
|
|
1652
|
+
/**
|
|
1653
|
+
* 获取状态
|
|
1654
|
+
*/
|
|
1655
|
+
getState() {
|
|
1656
|
+
return {
|
|
1657
|
+
isRunning: this.isRunning,
|
|
1658
|
+
screenshotCount: this.screenshotCount,
|
|
1659
|
+
lastScreenshotTime: this.lastScreenshotTime,
|
|
1660
|
+
error: this.error,
|
|
1661
|
+
isEnabled: this.isEnabled,
|
|
1662
|
+
isUploading: this.isUploading,
|
|
1663
|
+
uploadError: this.uploadError,
|
|
1664
|
+
uploadProgress: { ...this.uploadProgress },
|
|
1665
|
+
currentUploadConfig: this.currentUploadConfig
|
|
1666
|
+
};
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1669
|
+
|
|
926
1670
|
/******************************************************************************
|
|
927
1671
|
Copyright (c) Microsoft Corporation.
|
|
928
1672
|
|
|
@@ -4197,6 +4941,7 @@ class CustomerServiceSDK {
|
|
|
4197
4941
|
constructor() {
|
|
4198
4942
|
this.iconManager = null;
|
|
4199
4943
|
this.iframeManager = null;
|
|
4944
|
+
this.screenshotManager = null;
|
|
4200
4945
|
this.config = null;
|
|
4201
4946
|
this.isInitialized = false;
|
|
4202
4947
|
}
|
|
@@ -4234,6 +4979,7 @@ class CustomerServiceSDK {
|
|
|
4234
4979
|
// 显示红点通知(只显示红点,不显示数字)
|
|
4235
4980
|
this.showNotification(0, { pulse: true });
|
|
4236
4981
|
}
|
|
4982
|
+
// checkScreenshot 消息由 ScreenshotManager 处理,不需要在这里处理
|
|
4237
4983
|
},
|
|
4238
4984
|
onClose: () => {
|
|
4239
4985
|
// iframe关闭时,清理图标拖动事件监听器
|
|
@@ -4249,6 +4995,13 @@ class CustomerServiceSDK {
|
|
|
4249
4995
|
this.clearNotification();
|
|
4250
4996
|
this.iframeManager?.show();
|
|
4251
4997
|
});
|
|
4998
|
+
// 初始化截图管理器(如果启用了截图功能)
|
|
4999
|
+
if (config.screenshot) {
|
|
5000
|
+
// 默认截图目标为 document.body,可以通过配置自定义
|
|
5001
|
+
const targetElement = document.body;
|
|
5002
|
+
this.screenshotManager = new ScreenshotManager(targetElement, config.screenshot);
|
|
5003
|
+
console.log('CustomerSDK screenshot manager initialized');
|
|
5004
|
+
}
|
|
4252
5005
|
this.isInitialized = true;
|
|
4253
5006
|
console.log('CustomerSDK initialized successfully (iframe pre-connected to SSE)');
|
|
4254
5007
|
}
|
|
@@ -4342,14 +5095,38 @@ class CustomerServiceSDK {
|
|
|
4342
5095
|
clearNotification() {
|
|
4343
5096
|
this.iconManager?.clearNotification();
|
|
4344
5097
|
}
|
|
5098
|
+
/**
|
|
5099
|
+
* 设置截图目标元素
|
|
5100
|
+
*/
|
|
5101
|
+
setScreenshotTarget(element) {
|
|
5102
|
+
this.screenshotManager?.setTargetElement(element);
|
|
5103
|
+
}
|
|
5104
|
+
/**
|
|
5105
|
+
* 手动触发截图
|
|
5106
|
+
*/
|
|
5107
|
+
async captureScreenshot() {
|
|
5108
|
+
if (!this.screenshotManager) {
|
|
5109
|
+
console.warn('截图功能未启用');
|
|
5110
|
+
return false;
|
|
5111
|
+
}
|
|
5112
|
+
return await this.screenshotManager.captureOnce();
|
|
5113
|
+
}
|
|
5114
|
+
/**
|
|
5115
|
+
* 获取截图状态
|
|
5116
|
+
*/
|
|
5117
|
+
getScreenshotState() {
|
|
5118
|
+
return this.screenshotManager?.getState() || null;
|
|
5119
|
+
}
|
|
4345
5120
|
/**
|
|
4346
5121
|
* 销毁 SDK
|
|
4347
5122
|
*/
|
|
4348
5123
|
destroy() {
|
|
4349
5124
|
this.iconManager?.hide();
|
|
4350
5125
|
this.iframeManager?.close();
|
|
5126
|
+
this.screenshotManager?.destroy();
|
|
4351
5127
|
this.iconManager = null;
|
|
4352
5128
|
this.iframeManager = null;
|
|
5129
|
+
this.screenshotManager = null;
|
|
4353
5130
|
this.config = null;
|
|
4354
5131
|
this.isInitialized = false;
|
|
4355
5132
|
console.log('CustomerSDK destroyed');
|
|
@@ -4482,6 +5259,21 @@ const destroy = () => {
|
|
|
4482
5259
|
sdk.destroy();
|
|
4483
5260
|
globalSDKInstance = null;
|
|
4484
5261
|
};
|
|
5262
|
+
/**
|
|
5263
|
+
* 截图相关API
|
|
5264
|
+
*/
|
|
5265
|
+
const setScreenshotTarget = (element) => {
|
|
5266
|
+
const sdk = getInstance();
|
|
5267
|
+
sdk.setScreenshotTarget(element);
|
|
5268
|
+
};
|
|
5269
|
+
const captureScreenshot = async () => {
|
|
5270
|
+
const sdk = getInstance();
|
|
5271
|
+
return await sdk.captureScreenshot();
|
|
5272
|
+
};
|
|
5273
|
+
const getScreenshotState = () => {
|
|
5274
|
+
const sdk = getInstance();
|
|
5275
|
+
return sdk.getScreenshotState();
|
|
5276
|
+
};
|
|
4485
5277
|
// 默认导出
|
|
4486
5278
|
var index = {
|
|
4487
5279
|
init,
|
|
@@ -4498,16 +5290,21 @@ var index = {
|
|
|
4498
5290
|
getConnectionStatus,
|
|
4499
5291
|
showNotification,
|
|
4500
5292
|
clearNotification,
|
|
5293
|
+
setScreenshotTarget,
|
|
5294
|
+
captureScreenshot,
|
|
5295
|
+
getScreenshotState,
|
|
4501
5296
|
destroy
|
|
4502
5297
|
};
|
|
4503
5298
|
|
|
4504
5299
|
exports.CustomerServiceSDK = CustomerServiceSDK;
|
|
5300
|
+
exports.captureScreenshot = captureScreenshot;
|
|
4505
5301
|
exports.clearNotification = clearNotification;
|
|
4506
5302
|
exports.closeChat = closeChat;
|
|
4507
5303
|
exports.default = index;
|
|
4508
5304
|
exports.destroy = destroy;
|
|
4509
5305
|
exports.getConnectionStatus = getConnectionStatus;
|
|
4510
5306
|
exports.getInstance = getInstance;
|
|
5307
|
+
exports.getScreenshotState = getScreenshotState;
|
|
4511
5308
|
exports.hideIcon = hideIcon;
|
|
4512
5309
|
exports.init = init;
|
|
4513
5310
|
exports.isChatOpen = isChatOpen;
|
|
@@ -4516,5 +5313,6 @@ exports.sendToIframe = sendToIframe;
|
|
|
4516
5313
|
exports.setIconCoordinates = setIconCoordinates;
|
|
4517
5314
|
exports.setIconPosition = setIconPosition;
|
|
4518
5315
|
exports.setIconStyle = setIconStyle;
|
|
5316
|
+
exports.setScreenshotTarget = setScreenshotTarget;
|
|
4519
5317
|
exports.showIcon = showIcon;
|
|
4520
5318
|
exports.showNotification = showNotification;
|