qb-pc-sdk 1.0.8 → 1.1.0
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 +60 -0
- package/package.json +2 -2
- package/src/ad-sdk-wrapper.js +145 -13
- package/src/example-simple.html +7 -3
package/README.md
CHANGED
|
@@ -139,8 +139,33 @@ new AdSDK(config)
|
|
|
139
139
|
- `onAdExpose` (function, 可选): 广告曝光回调
|
|
140
140
|
- `onAdClick` (function, 可选): 广告点击回调
|
|
141
141
|
|
|
142
|
+
### 实例方法
|
|
143
|
+
|
|
144
|
+
#### destroy()
|
|
145
|
+
|
|
146
|
+
销毁广告实例,清理 DOM、解绑事件、释放资源。
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
const adSDK = new AdSDK({
|
|
150
|
+
appId: 'your-app-id',
|
|
151
|
+
placementId: 'your-placement-id',
|
|
152
|
+
container: '#ad-container'
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// 销毁广告
|
|
156
|
+
adSDK.destroy();
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**说明**:
|
|
160
|
+
- 调用后会清空广告容器内容
|
|
161
|
+
- 会调用底层 SDK 的 `destroy()` 方法释放资源
|
|
162
|
+
- 广告会自动显示关闭按钮(右上角 ×),点击即可关闭
|
|
163
|
+
- 也可以手动调用 `destroy()` 方法销毁广告
|
|
164
|
+
|
|
142
165
|
### 示例
|
|
143
166
|
|
|
167
|
+
#### 基本使用
|
|
168
|
+
|
|
144
169
|
```javascript
|
|
145
170
|
const AdSDK = require('qb-pc-sdk');
|
|
146
171
|
|
|
@@ -163,6 +188,41 @@ const adSDK = new AdSDK({
|
|
|
163
188
|
});
|
|
164
189
|
```
|
|
165
190
|
|
|
191
|
+
#### 销毁广告示例
|
|
192
|
+
|
|
193
|
+
```javascript
|
|
194
|
+
const adSDK = new AdSDK({
|
|
195
|
+
appId: 'your-app-id',
|
|
196
|
+
placementId: 'your-placement-id',
|
|
197
|
+
container: '#ad-container'
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// 方式1:点击广告右上角的关闭按钮(自动销毁)
|
|
201
|
+
// 广告渲染时会自动显示关闭按钮
|
|
202
|
+
|
|
203
|
+
// 方式2:手动调用 destroy 方法
|
|
204
|
+
document.getElementById('close-ad-btn').addEventListener('click', () => {
|
|
205
|
+
adSDK.destroy();
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// 方式3:在组件销毁时调用(Vue/React)
|
|
209
|
+
// Vue
|
|
210
|
+
onBeforeUnmount(() => {
|
|
211
|
+
if (adSDK) {
|
|
212
|
+
adSDK.destroy();
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// React
|
|
217
|
+
useEffect(() => {
|
|
218
|
+
return () => {
|
|
219
|
+
if (adSDK) {
|
|
220
|
+
adSDK.destroy();
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
}, []);
|
|
224
|
+
```
|
|
225
|
+
|
|
166
226
|
## 依赖
|
|
167
227
|
|
|
168
228
|
- `qb-pc-ad-sdk-origin`: 底层广告 SDK(已自动在浏览器环境加载)
|
package/package.json
CHANGED
package/src/ad-sdk-wrapper.js
CHANGED
|
@@ -285,8 +285,8 @@
|
|
|
285
285
|
|
|
286
286
|
// 内部常量配置
|
|
287
287
|
const API_CONFIG = {
|
|
288
|
-
INIT: 'http://
|
|
289
|
-
POSITION: 'http://
|
|
288
|
+
INIT: 'http://app.qubiankeji.com:8084/pc/init',
|
|
289
|
+
POSITION: 'http://app.qubiankeji.com:8084/pc/position',
|
|
290
290
|
GDT_SDK_ID: 2 // 标识
|
|
291
291
|
};
|
|
292
292
|
|
|
@@ -300,6 +300,10 @@
|
|
|
300
300
|
.q-ad-desc { font-size: 12px; color: #666; margin: 0 0 10px 0; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
|
|
301
301
|
.q-cta-btn { background: #007bff; color: #fff; border: none; padding: 6px 15px; border-radius: 4px; font-size: 12px; cursor: pointer; align-self: flex-start; }
|
|
302
302
|
.q-ad-tag { position: absolute; right: 5px; bottom: 5px; background: rgba(0,0,0,0.3); color: #fff; font-size: 10px; padding: 2px 4px; border-radius: 2px; }
|
|
303
|
+
.q-ad-close { position: absolute; top: 5px; right: 5px; width: 24px; height: 24px; background: rgba(0,0,0,0.5); color: #fff; border: none; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 16px; line-height: 1; z-index: 10; transition: background 0.2s; }
|
|
304
|
+
.q-ad-close:hover { background: rgba(0,0,0,0.7); }
|
|
305
|
+
.q-ad-close:active { background: rgba(0,0,0,0.9); }
|
|
306
|
+
.q-ad-destroyed { display: none !important; }
|
|
303
307
|
`;
|
|
304
308
|
|
|
305
309
|
class AdSDKWrapper {
|
|
@@ -316,6 +320,9 @@
|
|
|
316
320
|
this.gdtPlacementId = null;
|
|
317
321
|
this.currentAd = null;
|
|
318
322
|
|
|
323
|
+
// 检测移动端环境并提示
|
|
324
|
+
this._checkMobileEnvironment();
|
|
325
|
+
|
|
319
326
|
this._injectStyles();
|
|
320
327
|
|
|
321
328
|
this.container = typeof this.config.container === 'string'
|
|
@@ -329,6 +336,27 @@
|
|
|
329
336
|
}
|
|
330
337
|
}
|
|
331
338
|
|
|
339
|
+
// 检测移动端环境
|
|
340
|
+
_checkMobileEnvironment() {
|
|
341
|
+
if (typeof window !== 'undefined' && window.navigator) {
|
|
342
|
+
const userAgent = window.navigator.userAgent || '';
|
|
343
|
+
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);
|
|
344
|
+
|
|
345
|
+
if (isMobile) {
|
|
346
|
+
console.warn('[AdSDK] ⚠️ 检测到移动端环境,这是 PC 端 SDK,可能在移动端无法正常工作。建议使用移动端专用 SDK。');
|
|
347
|
+
if (this.config.onAdError) {
|
|
348
|
+
// 不立即触发错误,让用户决定是否继续
|
|
349
|
+
setTimeout(() => {
|
|
350
|
+
this.config.onAdError(
|
|
351
|
+
new Error('移动端环境警告'),
|
|
352
|
+
'这是 PC 端 SDK,移动端可能无法正常加载广告'
|
|
353
|
+
);
|
|
354
|
+
}, 100);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
332
360
|
// 注入默认 CSS
|
|
333
361
|
_injectStyles() {
|
|
334
362
|
if (!document.getElementById('q-ad-styles')) {
|
|
@@ -347,14 +375,33 @@
|
|
|
347
375
|
fetch(`${API_CONFIG.POSITION}?positionId=${this.config.placementId}`).then(r => r.json())
|
|
348
376
|
]);
|
|
349
377
|
|
|
378
|
+
// 处理返回数据结构:支持 { data: {...} } 或直接返回数据
|
|
379
|
+
const initData = initRes.data || initRes;
|
|
380
|
+
const posData = posRes.data || posRes;
|
|
381
|
+
|
|
350
382
|
// 2. 提取 AppID
|
|
351
|
-
this.gdtAppId =
|
|
383
|
+
this.gdtAppId = initData.thirdIdMap ? initData.thirdIdMap[String(API_CONFIG.GDT_SDK_ID)] : null;
|
|
352
384
|
|
|
353
385
|
// 3. 执行广告位筛选逻辑
|
|
354
|
-
this.gdtPlacementId = this._selectPlacementId(
|
|
386
|
+
this.gdtPlacementId = this._selectPlacementId(posData);
|
|
387
|
+
|
|
388
|
+
// 添加调试日志(始终输出,便于排查问题)
|
|
389
|
+
console.log('[AdSDK] 初始化数据:', {
|
|
390
|
+
initRes: initRes,
|
|
391
|
+
posRes: posRes,
|
|
392
|
+
initData: initData,
|
|
393
|
+
posData: posData,
|
|
394
|
+
gdtAppId: this.gdtAppId,
|
|
395
|
+
gdtPlacementId: this.gdtPlacementId
|
|
396
|
+
});
|
|
355
397
|
|
|
356
398
|
if (!this.gdtAppId || !this.gdtPlacementId) {
|
|
357
|
-
|
|
399
|
+
const errorMsg = `未找到匹配的(sdkId:2)配置信息 - AppId: ${this.gdtAppId}, PlacementId: ${this.gdtPlacementId}`;
|
|
400
|
+
console.error('[AdSDK]', errorMsg, {
|
|
401
|
+
initData: initData,
|
|
402
|
+
posData: posData
|
|
403
|
+
});
|
|
404
|
+
throw new Error(errorMsg);
|
|
358
405
|
}
|
|
359
406
|
|
|
360
407
|
this._checkAndRun();
|
|
@@ -368,21 +415,62 @@
|
|
|
368
415
|
* 规则:headSetList优先;否则选positionSetList中优先级最高的
|
|
369
416
|
*/
|
|
370
417
|
_selectPlacementId(data) {
|
|
418
|
+
if (!data) {
|
|
419
|
+
console.error('[AdSDK] _selectPlacementId: data 为空');
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// 添加调试信息
|
|
424
|
+
console.log('[AdSDK] _selectPlacementId 输入数据:', data);
|
|
425
|
+
const targetSdkId = API_CONFIG.GDT_SDK_ID;
|
|
426
|
+
console.log('[AdSDK] 查找 sdkId (类型:', typeof targetSdkId, '值:', targetSdkId, ')');
|
|
427
|
+
|
|
371
428
|
// A. 优先检查 headSetList
|
|
372
|
-
if (data.headSetList && data.headSetList.length > 0) {
|
|
373
|
-
|
|
374
|
-
|
|
429
|
+
if (data.headSetList && Array.isArray(data.headSetList) && data.headSetList.length > 0) {
|
|
430
|
+
console.log('[AdSDK] 检查 headSetList:', data.headSetList);
|
|
431
|
+
const headAd = data.headSetList.find(item => {
|
|
432
|
+
// 支持数字和字符串类型的比较
|
|
433
|
+
const itemSdkId = Number(item.sdkId);
|
|
434
|
+
const match = itemSdkId === targetSdkId || String(item.sdkId) === String(targetSdkId);
|
|
435
|
+
if (!match) {
|
|
436
|
+
console.log('[AdSDK] headSetList 项不匹配:', item, 'sdkId:', item.sdkId, '类型:', typeof item.sdkId);
|
|
437
|
+
}
|
|
438
|
+
return match;
|
|
439
|
+
});
|
|
440
|
+
if (headAd) {
|
|
441
|
+
console.log('[AdSDK] ✅ 在 headSetList 中找到匹配项:', headAd);
|
|
442
|
+
return headAd.positionId;
|
|
443
|
+
}
|
|
375
444
|
}
|
|
376
445
|
|
|
377
446
|
// B. 检查 positionSetList
|
|
378
|
-
if (data.positionSetList && data.positionSetList.length > 0) {
|
|
447
|
+
if (data.positionSetList && Array.isArray(data.positionSetList) && data.positionSetList.length > 0) {
|
|
448
|
+
console.log('[AdSDK] 检查 positionSetList:', data.positionSetList);
|
|
379
449
|
const sortedList = data.positionSetList
|
|
380
|
-
.filter(item =>
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
450
|
+
.filter(item => {
|
|
451
|
+
// 支持数字和字符串类型的比较
|
|
452
|
+
const itemSdkId = Number(item.sdkId);
|
|
453
|
+
const match = itemSdkId === targetSdkId || String(item.sdkId) === String(targetSdkId);
|
|
454
|
+
console.log('[AdSDK] positionSetList 项检查:', {
|
|
455
|
+
item: item,
|
|
456
|
+
itemSdkId: item.sdkId,
|
|
457
|
+
itemSdkIdType: typeof item.sdkId,
|
|
458
|
+
targetSdkId: targetSdkId,
|
|
459
|
+
targetSdkIdType: typeof targetSdkId,
|
|
460
|
+
match: match
|
|
461
|
+
});
|
|
462
|
+
return match;
|
|
463
|
+
})
|
|
464
|
+
.sort((a, b) => (b.callbackPriority || 0) - (a.callbackPriority || 0)); // 按优先级从大到小排序
|
|
465
|
+
|
|
466
|
+
console.log('[AdSDK] 筛选后的列表:', sortedList);
|
|
467
|
+
if (sortedList.length > 0) {
|
|
468
|
+
console.log('[AdSDK] ✅ 在 positionSetList 中找到匹配项:', sortedList[0]);
|
|
469
|
+
return sortedList[0].positionId;
|
|
470
|
+
}
|
|
384
471
|
}
|
|
385
472
|
|
|
473
|
+
console.error('[AdSDK] ❌ 未找到匹配的 sdkId:', targetSdkId, '数据:', data);
|
|
386
474
|
return null;
|
|
387
475
|
}
|
|
388
476
|
|
|
@@ -453,6 +541,7 @@
|
|
|
453
541
|
// 构建 HTML 结构
|
|
454
542
|
this.container.innerHTML = `
|
|
455
543
|
<div class="q-ad-wrapper">
|
|
544
|
+
<button class="q-ad-close" title="关闭广告">×</button>
|
|
456
545
|
<div class="q-media-container">
|
|
457
546
|
<div class="q-ad-video" style="display:none"></div>
|
|
458
547
|
<img class="q-ad-img" style="display:none">
|
|
@@ -466,6 +555,15 @@
|
|
|
466
555
|
</div>
|
|
467
556
|
`;
|
|
468
557
|
|
|
558
|
+
// 绑定关闭按钮事件
|
|
559
|
+
const closeBtn = this.container.querySelector('.q-ad-close');
|
|
560
|
+
if (closeBtn) {
|
|
561
|
+
closeBtn.addEventListener('click', (e) => {
|
|
562
|
+
e.stopPropagation(); // 阻止事件冒泡,避免触发广告点击
|
|
563
|
+
this.destroy();
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
|
|
469
567
|
const videoEl = this.container.querySelector('.q-ad-video');
|
|
470
568
|
const imgEl = this.container.querySelector('.q-ad-img');
|
|
471
569
|
const wrapper = this.container.querySelector('.q-ad-wrapper');
|
|
@@ -505,6 +603,40 @@
|
|
|
505
603
|
if (this.config.onAdLoaded) this.config.onAdLoaded(ad);
|
|
506
604
|
}
|
|
507
605
|
|
|
606
|
+
/**
|
|
607
|
+
* 销毁广告实例
|
|
608
|
+
* 清理 DOM、解绑事件、释放资源,并隐藏容器元素
|
|
609
|
+
*/
|
|
610
|
+
destroy() {
|
|
611
|
+
try {
|
|
612
|
+
// 1. 销毁底层广告实例
|
|
613
|
+
if (this.currentAd && typeof this.currentAd.destroy === 'function') {
|
|
614
|
+
this.currentAd.destroy();
|
|
615
|
+
this.currentAd = null;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// 2. 清空容器内容
|
|
619
|
+
if (this.container) {
|
|
620
|
+
this.container.innerHTML = '';
|
|
621
|
+
// 3. 隐藏容器元素
|
|
622
|
+
if (this.container.style) {
|
|
623
|
+
this.container.style.display = 'none';
|
|
624
|
+
} else {
|
|
625
|
+
// 如果 style 不可用,使用 class
|
|
626
|
+
this.container.classList.add('q-ad-destroyed');
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// 4. 清理引用
|
|
631
|
+
this.gdtAppId = null;
|
|
632
|
+
this.gdtPlacementId = null;
|
|
633
|
+
|
|
634
|
+
console.log('[AdSDK] 广告已销毁');
|
|
635
|
+
} catch (err) {
|
|
636
|
+
console.error('[AdSDK] 销毁广告时出错:', err);
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
508
640
|
_handleError(error, message) {
|
|
509
641
|
// 内部错误也进行过滤,防止将过滤掉的错误再次打印
|
|
510
642
|
const errStr = String(error).toLowerCase();
|
package/src/example-simple.html
CHANGED
|
@@ -38,15 +38,19 @@
|
|
|
38
38
|
console.log('✅ 封装 SDK 加载成功');
|
|
39
39
|
|
|
40
40
|
// 初始化广告
|
|
41
|
-
new window.AdSDK({
|
|
42
|
-
appId: '
|
|
43
|
-
placementId: '
|
|
41
|
+
const adSDK = new window.AdSDK({
|
|
42
|
+
appId: '1999364640831725629',
|
|
43
|
+
placementId: '2003009002186760245',
|
|
44
44
|
container: '#ad-slot-1',
|
|
45
45
|
onAdLoaded: () => console.log('✅ 广告加载完成'),
|
|
46
46
|
onAdError: (err, msg) => console.log('❌ 广告报错:', err, msg),
|
|
47
47
|
onAdExpose: () => console.log('👀 广告曝光'),
|
|
48
48
|
onAdClick: () => console.log('🖱️ 用户点击了广告')
|
|
49
49
|
});
|
|
50
|
+
|
|
51
|
+
// 示例:可以通过 destroy 方法销毁广告
|
|
52
|
+
// 广告右上角会自动显示关闭按钮,点击即可关闭
|
|
53
|
+
// 也可以手动调用:adSDK.destroy();
|
|
50
54
|
});
|
|
51
55
|
</script>
|
|
52
56
|
</body>
|