hjr 2 ヶ月 前
コミット
d9420d354c

+ 243 - 0
src/components/ad-example.ux

@@ -0,0 +1,243 @@
+<template>
+  <div class="ad-example">
+    <text class="title">广告示例</text>
+    
+    <!-- Banner广告容器 -->
+    <div class="banner-container" if="{{showBanner}}">
+      <ad-banner 
+        pos-id="{{bannerPosId}}"
+        style="width: 750px; height: 100px;"
+        onload="onBannerLoad"
+        onerror="onBannerError"
+        onclose="onBannerClose">
+      </ad-banner>
+    </div>
+    
+    <!-- 原生广告容器 -->
+    <div class="native-container" if="{{showNative}}">
+      <ad-native 
+        pos-id="{{nativePosId}}"
+        onload="onNativeLoad"
+        onerror="onNativeError"
+        onclick="onNativeClick">
+      </ad-native>
+    </div>
+    
+    <!-- 广告控制按钮 -->
+    <div class="ad-controls">
+      <button class="ad-btn" onclick="showBannerAd">显示Banner广告</button>
+      <button class="ad-btn" onclick="hideBannerAd">隐藏Banner广告</button>
+      <button class="ad-btn" onclick="showInterstitialAd">显示插屏广告</button>
+      <button class="ad-btn" onclick="showNativeAd">显示原生广告</button>
+      <button class="ad-btn" onclick="showRewardedVideoAd">显示激励视频</button>
+    </div>
+    
+    <!-- 状态显示 -->
+    <div class="status">
+      <text class="status-text">广告状态: {{adStatus}}</text>
+    </div>
+  </div>
+</template>
+
+<script>
+import adManager from '../helper/ad-manager.js'
+
+export default {
+  data() {
+    return {
+      showBanner: false,
+      showNative: false,
+      adStatus: '未初始化',
+      bannerPosId: 'your_banner_pos_id',
+      nativePosId: 'your_native_pos_id'
+    }
+  },
+  
+  onInit() {
+    // 页面初始化时预加载广告
+    this.initAds()
+  },
+  
+  onReady() {
+    // 页面准备完成后的处理
+  },
+  
+  onDestroy() {
+    // 页面销毁时清理广告资源
+    adManager.destroyAllAds()
+  },
+  
+  methods: {
+    /**
+     * 初始化广告
+     */
+    initAds() {
+      this.adStatus = '正在初始化广告...'
+      
+      // 预加载所有广告
+      adManager.preloadAllAds()
+      
+      // 设置激励视频完成回调
+      adManager.onRewardedVideoComplete = () => {
+        this.adStatus = '激励视频播放完成,发放奖励'
+        // 在这里处理奖励逻辑
+        this.giveReward()
+      }
+      
+      this.adStatus = '广告初始化完成'
+    },
+    
+    /**
+     * 显示Banner广告
+     */
+    showBannerAd() {
+      this.showBanner = true
+      adManager.showBannerAd()
+      this.adStatus = 'Banner广告已显示'
+    },
+    
+    /**
+     * 隐藏Banner广告
+     */
+    hideBannerAd() {
+      this.showBanner = false
+      adManager.hideBannerAd()
+      this.adStatus = 'Banner广告已隐藏'
+    },
+    
+    /**
+     * 显示插屏广告
+     */
+    showInterstitialAd() {
+      adManager.showInterstitialAd()
+      this.adStatus = '插屏广告已显示'
+    },
+    
+    /**
+     * 显示原生广告
+     */
+    showNativeAd() {
+      this.showNative = true
+      adManager.showNativeAd()
+      this.adStatus = '原生广告已显示'
+    },
+    
+    /**
+     * 显示激励视频广告
+     */
+    showRewardedVideoAd() {
+      adManager.showRewardedVideoAd()
+      this.adStatus = '激励视频广告已显示'
+    },
+    
+    /**
+     * Banner广告事件处理
+     */
+    onBannerLoad() {
+      console.log('Banner广告加载完成')
+      this.adStatus = 'Banner广告加载完成'
+    },
+    
+    onBannerError(err) {
+      console.error('Banner广告加载失败:', err)
+      this.adStatus = 'Banner广告加载失败'
+    },
+    
+    onBannerClose() {
+      console.log('Banner广告被关闭')
+      this.adStatus = 'Banner广告被关闭'
+    },
+    
+    /**
+     * 原生广告事件处理
+     */
+    onNativeLoad(res) {
+      console.log('原生广告加载完成:', res)
+      this.adStatus = '原生广告加载完成'
+    },
+    
+    onNativeError(err) {
+      console.error('原生广告加载失败:', err)
+      this.adStatus = '原生广告加载失败'
+    },
+    
+    onNativeClick() {
+      console.log('原生广告被点击')
+      this.adStatus = '原生广告被点击'
+    },
+    
+    /**
+     * 发放奖励
+     */
+    giveReward() {
+      // 在这里实现奖励逻辑
+      console.log('发放奖励')
+      // 例如:增加金币、解锁内容等
+    }
+  }
+}
+</script>
+
+<style>
+.ad-example {
+  flex-direction: column;
+  padding: 20px;
+}
+
+.title {
+  font-size: 24px;
+  font-weight: bold;
+  text-align: center;
+  margin-bottom: 30px;
+  color: #333;
+}
+
+.banner-container {
+  width: 750px;
+  height: 100px;
+  margin-bottom: 20px;
+  background-color: #f5f5f5;
+  justify-content: center;
+  align-items: center;
+}
+
+.native-container {
+  width: 750px;
+  min-height: 200px;
+  margin-bottom: 20px;
+  background-color: #f5f5f5;
+  justify-content: center;
+  align-items: center;
+}
+
+.ad-controls {
+  flex-direction: column;
+  margin-bottom: 20px;
+}
+
+.ad-btn {
+  width: 300px;
+  height: 60px;
+  margin: 10px auto;
+  background-color: #007aff;
+  color: white;
+  border-radius: 8px;
+  font-size: 16px;
+}
+
+.ad-btn:active {
+  background-color: #0056cc;
+}
+
+.status {
+  padding: 15px;
+  background-color: #f8f9fa;
+  border-radius: 8px;
+}
+
+.status-text {
+  font-size: 14px;
+  color: #666;
+  text-align: center;
+}
+</style> 

+ 16 - 1
src/components/tools-mall/index.ux

@@ -57,12 +57,27 @@ export default {
 			],
 		}
 	],
-    page: 1
+    page: 1,
+     adUnitId: "9bcd5671a506459c9e6ef9c642468dc9", // 这个id是vivo的com.quickapp.center创建的,需要修改manifest的package才能预览出效果
+      ad: null
   },
   onReady() {
     // this.refreshHostList()
     console.log('onReady')
+    this.initAd()
   },
+  initAd() {      
+      try {        
+        this.ad = require('@service.ad').createBannerAd({  // 使用require方式避免在不支持的广告接口的厂商运行是报错
+          adUnitId: this.adUnitId,
+        })        
+        this.ad.onLoad(() => {          
+          this.ad.show();
+        })
+      } catch (e) {        
+        console.log(e)
+      }
+    },
   fnTest(type){
     console.log('type',type)
     vibrator.vibrate({

+ 141 - 0
src/config/ad-config.js

@@ -0,0 +1,141 @@
+/**
+ * 广告配置文件
+ * 用于管理不同环境下的广告位ID
+ */
+
+// 开发环境配置
+const devConfig = {
+  banner: {
+    posId: 'dev_banner_pos_id',
+    style: {
+      width: 750,
+      height: 100
+    }
+  },
+  interstitial: {
+    posId: 'dev_interstitial_pos_id'
+  },
+  native: {
+    posId: 'dev_native_pos_id'
+  },
+  rewarded: {
+    posId: 'dev_rewarded_pos_id'
+  }
+}
+
+// 测试环境配置
+const testConfig = {
+  banner: {
+    posId: 'test_banner_pos_id',
+    style: {
+      width: 750,
+      height: 100
+    }
+  },
+  interstitial: {
+    posId: 'test_interstitial_pos_id'
+  },
+  native: {
+    posId: 'test_native_pos_id'
+  },
+  rewarded: {
+    posId: 'test_rewarded_pos_id'
+  }
+}
+
+// 生产环境配置
+const prodConfig = {
+  banner: {
+    posId: 'prod_banner_pos_id',
+    style: {
+      width: 750,
+      height: 100
+    }
+  },
+  interstitial: {
+    posId: 'prod_interstitial_pos_id'
+  },
+  native: {
+    posId: 'prod_native_pos_id'
+  },
+  rewarded: {
+    posId: 'prod_rewarded_pos_id'
+  }
+}
+
+// 根据环境返回对应配置
+function getAdConfig() {
+  // 这里可以根据实际的环境判断逻辑来返回对应配置
+  // 例如:根据编译时的环境变量、应用版本等
+  
+  // 示例:根据应用版本判断环境
+  const appVersion = process.env.NODE_ENV || 'development'
+  
+  switch (appVersion) {
+    case 'production':
+      return prodConfig
+    case 'test':
+      return testConfig
+    case 'development':
+    default:
+      return devConfig
+  }
+}
+
+// 广告展示策略配置
+const adStrategyConfig = {
+  // Banner广告展示策略
+  banner: {
+    minInterval: 30000, // 最小展示间隔(毫秒)
+    maxShowCount: 10,   // 最大展示次数
+    autoHide: true,     // 是否自动隐藏
+    autoHideDelay: 5000 // 自动隐藏延迟(毫秒)
+  },
+  
+  // 插屏广告展示策略
+  interstitial: {
+    minInterval: 60000, // 最小展示间隔(毫秒)
+    maxShowCount: 5,    // 最大展示次数
+    showOnPageChange: true, // 页面切换时是否展示
+    showOnUserAction: true  // 用户操作后是否展示
+  },
+  
+  // 原生广告展示策略
+  native: {
+    minInterval: 45000, // 最小展示间隔(毫秒)
+    maxShowCount: 8,    // 最大展示次数
+    showInList: true,   // 是否在列表中展示
+    showInDetail: true  // 是否在详情页展示
+  },
+  
+  // 激励视频广告展示策略
+  rewarded: {
+    minInterval: 120000, // 最小展示间隔(毫秒)
+    maxShowCount: 3,     // 最大展示次数
+    showOnReward: true,  // 需要奖励时是否展示
+    showOnUnlock: true   // 解锁内容时是否展示
+  }
+}
+
+// 错误处理配置
+const errorConfig = {
+  // 重试配置
+  retry: {
+    maxRetries: 3,      // 最大重试次数
+    retryDelay: 1000,   // 重试延迟(毫秒)
+    backoffMultiplier: 2 // 退避倍数
+  },
+  
+  // 降级配置
+  fallback: {
+    enableFallback: true, // 是否启用降级
+    fallbackAdType: 'banner', // 降级广告类型
+    fallbackPosId: 'fallback_pos_id' // 降级广告位ID
+  }
+}
+
+export default {
+  getAdConfig,
+  adStrategyConfig,
+  errorConfig
+} 

+ 367 - 0
src/docs/ad-integration-guide.md

@@ -0,0 +1,367 @@
+# 快应用广告接入指南
+
+## 概述
+
+快应用支持多种类型的广告,包括 Banner广告、插屏广告、原生广告和激励视频广告。本文档将详细介绍如何在快应用中接入这些广告。
+
+## 前置条件
+
+### 1. 配置权限
+
+在 `manifest.json` 中添加广告服务权限:
+
+```json
+{
+  "features": [
+    {
+      "name": "service.ad"
+    }
+  ]
+}
+```
+
+### 2. 获取广告位ID
+
+在广告平台(如华为、小米、OPPO等)申请广告位ID,包括:
+- Banner广告位ID
+- 插屏广告位ID  
+- 原生广告位ID
+- 激励视频广告位ID
+
+## 广告类型详解
+
+### 1. Banner广告
+
+Banner广告是固定在页面底部的横幅广告。
+
+#### 基本用法
+
+```javascript
+import ad from '@service.ad'
+
+// 创建Banner广告
+const bannerAd = ad.createBannerAd({
+  posId: 'your_banner_pos_id',
+  style: {
+    width: 750,
+    height: 100
+  }
+})
+
+// 监听事件
+bannerAd.onLoad(() => {
+  console.log('Banner广告加载完成')
+})
+
+bannerAd.onError((err) => {
+  console.error('Banner广告加载失败:', err)
+})
+
+bannerAd.onClose(() => {
+  console.log('Banner广告被关闭')
+})
+
+// 显示广告
+bannerAd.show()
+
+// 隐藏广告
+bannerAd.hide()
+
+// 销毁广告
+bannerAd.destroy()
+```
+
+#### 在模板中使用
+
+```html
+<template>
+  <div class="banner-container">
+    <ad-banner 
+      pos-id="your_banner_pos_id"
+      style="width: 750px; height: 100px;"
+      onload="onBannerLoad"
+      onerror="onBannerError"
+      onclose="onBannerClose">
+    </ad-banner>
+  </div>
+</template>
+```
+
+### 2. 插屏广告
+
+插屏广告是覆盖整个屏幕的广告,通常在页面切换时显示。
+
+#### 基本用法
+
+```javascript
+import ad from '@service.ad'
+
+// 创建插屏广告
+const interstitialAd = ad.createInterstitialAd({
+  posId: 'your_interstitial_pos_id'
+})
+
+// 监听事件
+interstitialAd.onLoad(() => {
+  console.log('插屏广告加载完成')
+})
+
+interstitialAd.onError((err) => {
+  console.error('插屏广告加载失败:', err)
+})
+
+interstitialAd.onClose(() => {
+  console.log('插屏广告被关闭')
+})
+
+// 显示广告
+interstitialAd.show()
+```
+
+### 3. 原生广告
+
+原生广告可以自定义样式,更好地融入应用界面。
+
+#### 基本用法
+
+```javascript
+import ad from '@service.ad'
+
+// 创建原生广告
+const nativeAd = ad.createNativeAd({
+  posId: 'your_native_pos_id'
+})
+
+// 监听事件
+nativeAd.onLoad((res) => {
+  console.log('原生广告加载完成:', res)
+  // res 包含广告数据,如标题、描述、图片等
+})
+
+nativeAd.onError((err) => {
+  console.error('原生广告加载失败:', err)
+})
+
+nativeAd.onClick(() => {
+  console.log('原生广告被点击')
+})
+
+// 显示广告
+nativeAd.show()
+```
+
+#### 在模板中使用
+
+```html
+<template>
+  <div class="native-container">
+    <ad-native 
+      pos-id="your_native_pos_id"
+      onload="onNativeLoad"
+      onerror="onNativeError"
+      onclick="onNativeClick">
+    </ad-native>
+  </div>
+</template>
+```
+
+### 4. 激励视频广告
+
+激励视频广告要求用户观看完整视频来获得奖励。
+
+#### 基本用法
+
+```javascript
+import ad from '@service.ad'
+
+// 创建激励视频广告
+const rewardedVideoAd = ad.createRewardedVideoAd({
+  posId: 'your_rewarded_pos_id'
+})
+
+// 监听事件
+rewardedVideoAd.onLoad(() => {
+  console.log('激励视频广告加载完成')
+})
+
+rewardedVideoAd.onError((err) => {
+  console.error('激励视频广告加载失败:', err)
+})
+
+rewardedVideoAd.onClose((res) => {
+  if (res && res.isEnded) {
+    // 正常播放结束,可以下发游戏奖励
+    console.log('激励视频播放完成,可以发放奖励')
+    this.giveReward()
+  } else {
+    // 播放中途退出,不下发游戏奖励
+    console.log('激励视频播放中途退出')
+  }
+})
+
+// 显示广告
+rewardedVideoAd.show()
+```
+
+## 最佳实践
+
+### 1. 广告管理器
+
+使用统一的广告管理器来管理所有广告:
+
+```javascript
+// src/helper/ad-manager.js
+import ad from '@service.ad'
+
+class AdManager {
+  constructor() {
+    this.bannerAd = null
+    this.interstitialAd = null
+    this.nativeAd = null
+    this.rewardedVideoAd = null
+  }
+  
+  // 初始化各种广告
+  initBannerAd() { /* ... */ }
+  initInterstitialAd() { /* ... */ }
+  initNativeAd() { /* ... */ }
+  initRewardedVideoAd() { /* ... */ }
+  
+  // 显示各种广告
+  showBannerAd() { /* ... */ }
+  showInterstitialAd() { /* ... */ }
+  showNativeAd() { /* ... */ }
+  showRewardedVideoAd() { /* ... */ }
+  
+  // 清理资源
+  destroyAllAds() { /* ... */ }
+}
+
+export default new AdManager()
+```
+
+### 2. 错误处理
+
+```javascript
+// 统一的错误处理
+function handleAdError(error, adType) {
+  console.error(`${adType}广告错误:`, error)
+  
+  // 根据错误类型进行不同处理
+  switch (error.code) {
+    case 'NO_AD':
+      console.log('暂无广告可展示')
+      break
+    case 'NETWORK_ERROR':
+      console.log('网络错误,稍后重试')
+      break
+    default:
+      console.log('未知错误')
+  }
+}
+```
+
+### 3. 广告展示策略
+
+```javascript
+// 智能广告展示策略
+class AdStrategy {
+  // 控制广告展示频率
+  static canShowAd(adType) {
+    const lastShowTime = this.getLastShowTime(adType)
+    const minInterval = this.getMinInterval(adType)
+    
+    return Date.now() - lastShowTime > minInterval
+  }
+  
+  // 根据用户行为决定是否展示广告
+  static shouldShowAd(userBehavior) {
+    // 根据用户停留时间、操作频率等决定
+    return userBehavior.stayTime > 30000 // 停留超过30秒
+  }
+}
+```
+
+### 4. 性能优化
+
+```javascript
+// 预加载广告
+function preloadAds() {
+  // 在应用启动时预加载广告
+  adManager.initBannerAd()
+  adManager.initInterstitialAd()
+  adManager.initNativeAd()
+  adManager.initRewardedVideoAd()
+}
+
+// 延迟加载
+function lazyLoadAds() {
+  // 在用户进入页面后再加载广告
+  setTimeout(() => {
+    adManager.initInterstitialAd()
+  }, 2000)
+}
+```
+
+## 常见问题
+
+### 1. 广告不显示
+
+- 检查广告位ID是否正确
+- 确认网络连接正常
+- 查看控制台错误信息
+- 确认广告平台配置正确
+
+### 2. 广告加载失败
+
+```javascript
+// 重试机制
+function retryLoadAd(adInstance, maxRetries = 3) {
+  let retryCount = 0
+  
+  function loadAd() {
+    adInstance.load().catch(err => {
+      if (retryCount < maxRetries) {
+        retryCount++
+        setTimeout(loadAd, 1000 * retryCount) // 递增延迟
+      }
+    })
+  }
+  
+  loadAd()
+}
+```
+
+### 3. 激励视频奖励发放
+
+```javascript
+// 确保奖励只发放一次
+let rewardGiven = false
+
+rewardedVideoAd.onClose((res) => {
+  if (res && res.isEnded && !rewardGiven) {
+    rewardGiven = true
+    this.giveReward()
+  }
+})
+```
+
+## 测试建议
+
+1. **真机测试**:广告功能需要在真机上测试,模拟器可能无法正常显示
+2. **网络测试**:测试不同网络环境下的广告加载情况
+3. **用户场景测试**:模拟真实用户使用场景,测试广告展示时机
+4. **性能测试**:监控广告对应用性能的影响
+
+## 注意事项
+
+1. **用户体验**:合理控制广告展示频率,避免影响用户体验
+2. **合规性**:遵守各平台的广告政策和使用规范
+3. **隐私保护**:注意用户隐私保护,合理使用用户数据
+4. **资源管理**:及时销毁广告实例,避免内存泄漏
+
+## 相关资源
+
+- [快应用官方文档](https://doc.quickapp.cn/)
+- [华为快应用广告文档](https://developer.huawei.com/consumer/cn/doc/development/quickApp-References/ad-service)
+- [小米快应用广告文档](https://doc.quickapp.cn/features/service/ad.html) 

+ 258 - 0
src/helper/ad-manager.js

@@ -0,0 +1,258 @@
+/**
+ * 快应用广告管理器
+ * 支持 Banner广告、插屏广告、原生广告、激励视频广告
+ */
+
+import ad from '@service.ad'
+
+class AdManager {
+  constructor() {
+    this.bannerAd = null
+    this.interstitialAd = null
+    this.nativeAd = null
+    this.rewardedVideoAd = null
+    
+    // 广告位ID配置 - 请替换为您的实际广告位ID
+    this.adConfig = {
+      banner: {
+        posId: 'your_banner_pos_id', // Banner广告位ID
+        style: {
+          width: 750,
+          height: 100
+        }
+      },
+      interstitial: {
+        posId: 'your_interstitial_pos_id' // 插屏广告位ID
+      },
+      native: {
+        posId: 'your_native_pos_id' // 原生广告位ID
+      },
+      rewarded: {
+        posId: 'your_rewarded_pos_id' // 激励视频广告位ID
+      }
+    }
+  }
+
+  /**
+   * 初始化Banner广告
+   */
+  initBannerAd() {
+    try {
+      this.bannerAd = ad.createBannerAd({
+        posId: this.adConfig.banner.posId,
+        style: this.adConfig.banner.style
+      })
+
+      // 监听广告加载完成
+      this.bannerAd.onLoad(() => {
+        console.log('Banner广告加载完成')
+      })
+
+      // 监听广告加载失败
+      this.bannerAd.onError((err) => {
+        console.error('Banner广告加载失败:', err)
+      })
+
+      // 监听广告点击
+      this.bannerAd.onClose(() => {
+        console.log('Banner广告被关闭')
+      })
+
+      return this.bannerAd
+    } catch (error) {
+      console.error('初始化Banner广告失败:', error)
+      return null
+    }
+  }
+
+  /**
+   * 显示Banner广告
+   */
+  showBannerAd() {
+    if (this.bannerAd) {
+      this.bannerAd.show()
+    } else {
+      console.warn('Banner广告未初始化')
+    }
+  }
+
+  /**
+   * 隐藏Banner广告
+   */
+  hideBannerAd() {
+    if (this.bannerAd) {
+      this.bannerAd.hide()
+    }
+  }
+
+  /**
+   * 初始化插屏广告
+   */
+  initInterstitialAd() {
+    try {
+      this.interstitialAd = ad.createInterstitialAd({
+        posId: this.adConfig.interstitial.posId
+      })
+
+      // 监听广告加载完成
+      this.interstitialAd.onLoad(() => {
+        console.log('插屏广告加载完成')
+      })
+
+      // 监听广告加载失败
+      this.interstitialAd.onError((err) => {
+        console.error('插屏广告加载失败:', err)
+      })
+
+      // 监听广告关闭
+      this.interstitialAd.onClose(() => {
+        console.log('插屏广告被关闭')
+      })
+
+      return this.interstitialAd
+    } catch (error) {
+      console.error('初始化插屏广告失败:', error)
+      return null
+    }
+  }
+
+  /**
+   * 显示插屏广告
+   */
+  showInterstitialAd() {
+    if (this.interstitialAd) {
+      this.interstitialAd.show()
+    } else {
+      console.warn('插屏广告未初始化')
+    }
+  }
+
+  /**
+   * 初始化原生广告
+   */
+  initNativeAd() {
+    try {
+      this.nativeAd = ad.createNativeAd({
+        posId: this.adConfig.native.posId
+      })
+
+      // 监听广告加载完成
+      this.nativeAd.onLoad((res) => {
+        console.log('原生广告加载完成:', res)
+      })
+
+      // 监听广告加载失败
+      this.nativeAd.onError((err) => {
+        console.error('原生广告加载失败:', err)
+      })
+
+      // 监听广告点击
+      this.nativeAd.onClick(() => {
+        console.log('原生广告被点击')
+      })
+
+      return this.nativeAd
+    } catch (error) {
+      console.error('初始化原生广告失败:', error)
+      return null
+    }
+  }
+
+  /**
+   * 显示原生广告
+   */
+  showNativeAd() {
+    if (this.nativeAd) {
+      this.nativeAd.show()
+    } else {
+      console.warn('原生广告未初始化')
+    }
+  }
+
+  /**
+   * 初始化激励视频广告
+   */
+  initRewardedVideoAd() {
+    try {
+      this.rewardedVideoAd = ad.createRewardedVideoAd({
+        posId: this.adConfig.rewarded.posId
+      })
+
+      // 监听广告加载完成
+      this.rewardedVideoAd.onLoad(() => {
+        console.log('激励视频广告加载完成')
+      })
+
+      // 监听广告加载失败
+      this.rewardedVideoAd.onError((err) => {
+        console.error('激励视频广告加载失败:', err)
+      })
+
+      // 监听广告关闭
+      this.rewardedVideoAd.onClose((res) => {
+        console.log('激励视频广告被关闭:', res)
+        if (res && res.isEnded) {
+          // 正常播放结束,可以下发游戏奖励
+          console.log('激励视频播放完成,可以发放奖励')
+          this.onRewardedVideoComplete && this.onRewardedVideoComplete()
+        } else {
+          // 播放中途退出,不下发游戏奖励
+          console.log('激励视频播放中途退出')
+        }
+      })
+
+      return this.rewardedVideoAd
+    } catch (error) {
+      console.error('初始化激励视频广告失败:', error)
+      return null
+    }
+  }
+
+  /**
+   * 显示激励视频广告
+   */
+  showRewardedVideoAd() {
+    if (this.rewardedVideoAd) {
+      this.rewardedVideoAd.show()
+    } else {
+      console.warn('激励视频广告未初始化')
+    }
+  }
+
+  /**
+   * 预加载所有广告
+   */
+  preloadAllAds() {
+    this.initBannerAd()
+    this.initInterstitialAd()
+    this.initNativeAd()
+    this.initRewardedVideoAd()
+  }
+
+  /**
+   * 销毁所有广告
+   */
+  destroyAllAds() {
+    if (this.bannerAd) {
+      this.bannerAd.destroy()
+      this.bannerAd = null
+    }
+    if (this.interstitialAd) {
+      this.interstitialAd.destroy()
+      this.interstitialAd = null
+    }
+    if (this.nativeAd) {
+      this.nativeAd.destroy()
+      this.nativeAd = null
+    }
+    if (this.rewardedVideoAd) {
+      this.rewardedVideoAd.destroy()
+      this.rewardedVideoAd = null
+    }
+  }
+}
+
+// 创建单例实例
+const adManager = new AdManager()
+
+export default adManager 

+ 2 - 2
src/manifest.json

@@ -1,8 +1,8 @@
 {
   "package": "com.ytcalendar.module",
   "name": "柚趣屋",
-  "versionName": "1.0.1",
-  "versionCode": 5,
+  "versionName": "1.0.2",
+  "versionCode": 7,
   "minPlatformVersion": 1080,
   "icon": "/assets/logo.png",
   "features": [

+ 29 - 0
src/pages/book-content/index.ux

@@ -136,6 +136,7 @@ import { getBookContent } from '../../assets/data/book-content.js'
 import { contentsData } from '../../assets/data/contents.js'
 import fetch from '@system.fetch'
 import device from '@system.device'
+import ad from '@service.ad'
 
 export default {
   public: {
@@ -165,6 +166,8 @@ export default {
       fontFamily: 'default'
     },
     colors: ['#EAEAEF', '#FAF9DE', '#E3EDCD', '#DCE2F1', '#FFF2E2'],
+    bannerAd: null,
+
     fonts: [
       {
         name: '系统字体',
@@ -206,6 +209,8 @@ export default {
   },
   async onInit() {
     var that = this
+    this.initAd()
+
     device.getInfo({
      success: function(ret) {
        that.screenHeight = (ret.screenHeight * 1 - 500) || 1100
@@ -237,6 +242,30 @@ export default {
     this.chapterTitle = infos.chapterName
     this.getTxtContent(infos.content)
     console.log('this.content',this.content)
+  },
+   initAd() {
+    try {
+      // ad.
+      this.bannerAd =  require('@service.ad').createBannerAd({  // 使用require方式避免在不支持广告接口的厂商运行时报错
+        adUnitId: '9bcd5671a506459c9e6ef9c642468dc9',
+        style:{
+            width:750,
+            height:100,
+        }
+      })
+      this.bannerAd.onLoad(() => {     // 监听广告加载
+        console.log('onLoad event emit')
+        this.bannerAd.show()
+      })
+      this.bannerAd.onError((err) => { // 监听广告出错
+        console.log('onError event emit', err)
+      })
+      this.bannerAd.onClose((res) => { // 监听广告关闭
+        console.log('onClose event emit', res)
+      })
+    } catch (e) {
+      console.log('initAd',e)
+    }
   },
   // 解析TXT文件的主方法
   getTxtContent(url) {

+ 34 - 0
src/pages/book-detail/index.ux

@@ -78,6 +78,8 @@ export default {
     shelfList: [],
     shelfInfo:{},//书架信息
     linkInfo:{},
+    adList:[],
+
   },
   async onInit() {
     // 此处直接将图书内容传进来,实际开发中也可以传入id查询图书信息
@@ -104,6 +106,38 @@ export default {
       const res = await $apis.common.novelDetail({id:this.bookInfo.novelId})
       this.bookInfo = res.data.ytNovel
     }
+    this.indexNativeAd()  // 原生退出广告弹窗
+  },
+  indexNativeAd(){
+    // 原生广告 1.0
+    let nativeAd = ad.createNativeAd({  // 使用require方式避免在不支持的广告接口的厂商运行是报错
+        adUnitId:  'e2528e5d2baf4cef9a359f3074ca193c',
+    })
+    nativeAd.onLoad((data) => {          
+      console.log('Native ad resources!', data); // 广告加载成功,返回原生广告资源,根据但是资源自行渲染展现
+    })        
+    nativeAd.load();
+    // 原生广告2.0
+    let adParams = {
+      adUnitId: 'e2528e5d2baf4cef9a359f3074ca193c', // 原生广告2.0广告位id
+      type: 'native',          // 原生广告2.0广告类型 
+      adCount: 2,              // 原生广告2.0广告预期返回广告条数   
+    }
+    ad.preloadAd({
+      ...adParams,
+      success: (data) => {
+        this.adList = data.adList
+        console.log('data',data)
+        // prompt.showToast({
+        //   message: `success! data=${JSON.stringify(data)}`
+        // })
+      }, fail: (data, code) => {
+        console.log(data, code)
+        // prompt.showToast({
+        //   message: `fail! data=${JSON.stringify(data)}, code=${code}`
+        // })
+      }
+    })
   },
   /* -------------------SelfCustomEvent------------------ */
   goToComment() {

+ 100 - 14
src/pages/main/index.ux

@@ -14,12 +14,12 @@
   <div class="page-wrapper">
     <tabs index="{{selectedTab}}" onchange="changeTab">
       <tab-content scrollable="{{false}}">
-        <!-- <bookshelf
+        <bookshelf
           shelf-list="{{shelfList}}"
           onupdate-shelf="updateShelf"
         ></bookshelf>
         <library></library>
-        <search-book></search-book> -->
+        <!-- <search-book></search-book> -->
         <weather></weather>
         <calendar></calendar>
         <!--  if="{{ brand == 'vivo' }}" -->
@@ -67,6 +67,8 @@
 <script>
 import { bookListData } from '../../assets/data/book-list.js'
 import device from '@system.device'
+import ad from '@service.ad'
+import prompt from '@system.prompt'
 
 export default {
   public: {
@@ -74,16 +76,16 @@ export default {
   },
   private: {
     tabList: [
-      // {
-      //   title: '书架',
-      //   icon: '\ueb90',
-      //   activatedIcon: '\ue634'
-      // },
-      // {
-      //   title: '书城',
-      //   icon: '\ueb91',
-      //   activatedIcon: '\ue60a'
-      // },
+      {
+        title: '书架',
+        icon: '\ueb90',
+        activatedIcon: '\ue634'
+      },
+      {
+        title: '书城',
+        icon: '\ueb91',
+        activatedIcon: '\ue60a'
+      },
       // {
       //   title: '查找',
       //   icon: '\ue60d',
@@ -110,14 +112,25 @@ export default {
         activatedIcon: '\ue602'
       }
     ],
-    selectedTab: 3,
+    selectedTab: 0,
     defaultShelfList: [],//bookListData.slice(0, 2),
     shelfList: [],
     brand:'vivo', // 根据机型修改样式
-    showModal:true,
+    showModal:false,
+    bannerAd: null,
+    adUnitId: "9bcd5671a506459c9e6ef9c642468dc9",// banner 广告
+    adList:[],
   },
   async onInit() {
     // this.selectedTab = this.tab // || 0 // 带参数则跳转对应页面
+    console.log(ad.getProvider())
+    this.initAd()
+    this.indexNativeAd(); // 
+    this.splashswitch(true)
+    const data = await $utils.getStorage('agreePrivacy')
+    if(!data){
+      this.showModal = true
+    }
     this.getBookShelf()
     var that = this
     device.getInfo({
@@ -132,8 +145,81 @@ export default {
       }
     })
   },
+  indexNativeAd(){
+    // 原生广告 1.0
+    let nativeAd = ad.createNativeAd({  // 使用require方式避免在不支持的广告接口的厂商运行是报错
+        adUnitId:  '805a483bc3b14205a1d160cb97d0eca0',
+    })
+    nativeAd.onLoad((data) => {          
+      console.log('Native ad resources!', data); // 广告加载成功,返回原生广告资源,根据但是资源自行渲染展现
+    })        
+    nativeAd.load();
+    // 原生广告2.0
+    let adParams = {
+      adUnitId: '805a483bc3b14205a1d160cb97d0eca0', // 原生广告2.0广告位id
+      type: 'native',          // 原生广告2.0广告类型 
+      adCount: 2,              // 原生广告2.0广告预期返回广告条数   
+    }
+    ad.preloadAd({
+      ...adParams,
+      success: (data) => {
+        this.adList = data.adList
+        console.log('data',data)
+        // prompt.showToast({
+        //   message: `success! data=${JSON.stringify(data)}`
+        // })
+      }, fail: (data, code) => {
+        console.log(data, code)
+        // prompt.showToast({
+        //   message: `fail! data=${JSON.stringify(data)}, code=${code}`
+        // })
+      }
+    })
+  },
+  splashswitch(isOpen) {
+    	let result = ad.setSplashedSwitch({switch: isOpen})
+      setTimeout(()=>{
+        prompt.showToast({ message: result });
+      },5000)
+      // 获取开屏广告信息
+      this.getsplashAdInfo()
+  	},
+  	getsplashswitch() {
+    	// let switch = ad.splashAdSwitch()
+  	},
+  getsplashAdInfo() {
+    ad.getSplashAdInfo({
+      	success: function (data) {
+        	prompt.showToast({message: 'splashAd :' + JSON.stringify(data)})
+      	},
+      	fail: function (data, code) {
+        	prompt.showToast({message: 'splashAd :' + JSON.stringify(data)})
+      	}
+    })
+  },
+  initAd() {
+    try {
+      this.bannerAd = ad.createBannerAd({  // 使用require方式避免在不支持广告接口的厂商运行时报错
+        adUnitId: this.adUnitId,
+      })
+      this.bannerAd.onLoad(() => {     // 监听广告加载
+        console.log('onLoad event emit')
+        this.bannerAd.show()
+      })
+      this.bannerAd.onError((err) => { // 监听广告出错
+        console.log('onError event emit', err)
+      })
+      this.bannerAd.onClose((res) => { // 监听广告关闭
+        console.log('onClose event emit', res)
+      })
+    } catch (e) {
+      console.log('initAd',e)
+    }
+  },
   closeModal(){
     this.showModal = false
+    // 存储设置为true 已经同意 下次进入不必再显示
+    $utils.setStorage('agreePrivacy',true)
   },
   // 退出快应用
   exitApp(){

+ 16 - 2
src/pages/tools/yiJianShengDian/index.ux

@@ -48,14 +48,28 @@ export default {
       statusBarHeight: 0,
       powerNum: 90,
       chartCategories: ["待机", "上网", "导航", "影片", "音乐", "打电话", "游戏"],
-      chartData: [66, 36, 31, 33, 13, 34, 10]
+      chartData: [66, 36, 31, 33, 13, 34, 10],
+       adUnitId: "6f2e50489ff74bae800ce65c992bfec0", // 这个id是vivo的com.quickapp.center创建的,需要修改manifest的package才能预览出效果
+        ad: null
     }
   },
 
   onInit() {
      
   },
-
+   initAd() {      
+      try {        
+        this.ad = require('@service.ad').createNativeAd({  // 使用require方式避免在不支持的广告接口的厂商运行是报错
+          adUnitId: this.adUnitId,
+        })        
+        this.ad.onLoad((data) => {          
+          console.log('Native ad resources!', data); // 广告加载成功,返回原生广告资源,根据但是资源自行渲染展现
+        })        
+        this.ad.load();
+      } catch (e) {        
+        console.log(e);
+      }
+    },
   onShow() {
     var that = this
        battery.getStatus({