فهرست منبع

添加了自定义段落组件

shenwu 4 روز پیش
والد
کامیت
830707ba39

+ 4 - 0
config/index.ts

@@ -19,6 +19,10 @@ const config = {
       { 
         from: 'src/components/novelPlugin/customParagraph', 
         to: 'dist/components/novelPlugin/customParagraph' 
+      },
+      { 
+        from: 'src/components/novelPlugin/fullScreen', 
+        to: 'dist/components/novelPlugin/fullScreen' 
       }
     ],
     options: {

+ 2 - 1
src/app.config.ts

@@ -57,7 +57,8 @@ export default {
       "provider": "wx293c4b6097a8a4d0",
       "genericsImplementation": {
         "novel": {
-          "paragraph-block": "components/novelPlugin/customParagraph/index"
+          "paragraph-block": "components/novelPlugin/customParagraph/index",
+          "full-screen": "components/novelPlugin/fullScreen/index"
         }
       }
     },

+ 23 - 13
src/app.tsx

@@ -41,11 +41,29 @@ const store: Store = {
   modalStore
 }
 class App extends Component {
+  // 定义全局数据
+  globalData:any = {
+    isShow: true,           // 控制是否显示全屏组件
+    customParams: null,     // 自定义参数
+    userInfo: null,         // 用户信息
+    vipLevel: 0,           // VIP 等级
+    readingMode: 'normal',  // 阅读模式:normal/immersive
+    adConfig: {
+      enabled: true,        // 是否启用广告
+      frequency: 5,         // 广告频率(每几章显示一次)
+      type: 'fullscreen'    // 广告类型:fullscreen/banner
+    }
+  }
 
   //每次打开小程序触发,后台切入不算
   onLaunch(options) {
     console.log("apponLaunch", options)
-    // 监听进入插件页事件
+    
+    // 初始化全局数据
+    this.globalData.isShow = true
+    this.globalData.customParams = {}
+    
+    // 监听进入插件页事件    
     novelPlugin.onPageLoad(this.onNovelPluginLoad)
   }
   onNovelPluginLoad(data) {
@@ -72,18 +90,6 @@ class App extends Component {
     // 默认设置目录状态
     novelManager.setContents({
       contents: [
-        {
-          index: 0, // 第一章
-          status: 0, // 免费
-        },
-        {
-          index: 1, // 第二章
-          status: 0, // 未解锁
-        },
-        {
-          index: 2, // 第三章
-          status: 0, // 已解锁
-        },
       ],
     })
     //自定义段落
@@ -96,6 +102,10 @@ class App extends Component {
         },
       ]
     })
+    // 自义定全屏组件
+    // novelManager.setFullScreenComponentStatus({
+    //   show: true
+    // })
     // 监听用户行为事件
     novelManager.onUserTriggerEvent(res => {
       /**

+ 613 - 0
src/components/novelPlugin/GLOBAL_DATA_USAGE.md

@@ -0,0 +1,613 @@
+# 在插件组件中获取 App 全局数据
+
+## 📖 概述
+
+在微信官方阅读器插件的自定义组件中,可以通过 `getApp()` 方法获取小程序的 App 实例,从而访问在 App 中定义的全局数据(globalData)。
+
+## 🔧 实现步骤
+
+### 步骤 1:在 app.tsx 中定义 globalData
+
+```typescript
+// src/app.tsx
+class App extends Component {
+  // 定义全局数据
+  globalData = {
+    isShow: false,           // 控制是否显示全屏组件
+    customParams: null,     // 自定义参数
+    userInfo: null,         // 用户信息
+    vipLevel: 0,           // VIP 等级
+    readingMode: 'normal',  // 阅读模式:normal/immersive
+    adConfig: {
+      enabled: true,        // 是否启用广告
+      frequency: 5,         // 广告频率(每几章显示一次)
+      type: 'fullscreen'    // 广告类型:fullscreen/banner
+    }
+  }
+
+  onLaunch(options) {
+    console.log("apponLaunch", options)
+    
+    // 初始化全局数据
+    this.globalData.isShow = true
+    this.globalData.customParams = {}
+    
+    // 监听进入插件页事件    
+    novelPlugin.onPageLoad(this.onNovelPluginLoad)
+  }
+  
+  // ... 其他代码
+}
+```
+
+### 步骤 2:在插件组件中获取 globalData
+
+```javascript
+// components/novelPlugin/fullScreen/index.js
+Component({
+  lifetimes: {
+    attached() {
+      // ========== 获取 App 全局数据 ==========
+      const app = getApp()
+      console.log('获取 App 实例:', app)
+      console.log('App globalData:', app.globalData)
+      
+      // 读取你在 app.tsx 中定义的参数
+      if (app.globalData) {
+        const isShow = app.globalData.isShow
+        const customParams = app.globalData.customParams
+        const vipLevel = app.globalData.vipLevel
+        const adConfig = app.globalData.adConfig
+        
+        console.log('isShow:', isShow)
+        console.log('customParams:', customParams)
+        console.log('vipLevel:', vipLevel)
+        console.log('adConfig:', adConfig)
+        
+        // 根据 isShow 决定是否显示全屏组件
+        if (isShow === false) {
+          console.log('根据配置不显示全屏组件')
+          // 可以在这里调用关闭逻辑
+          // this.handleClose()
+        }
+        
+        // 根据 VIP 等级显示不同的推荐内容
+        if (vipLevel >= 3) {
+          // 显示 VIP 专属推荐
+          this.loadVipRecommendations()
+        } else {
+          // 显示普通推荐
+          this.loadNormalRecommendations()
+        }
+        
+        // 检查广告是否启用
+        if (!adConfig.enabled) {
+          console.log('广告已禁用,不显示全屏组件')
+          return
+        }
+      }
+      // ====================================
+      
+      // ... 其他初始化逻辑
+    },
+  },
+  
+  methods: {
+    loadVipRecommendations() {
+      // 加载 VIP 专属推荐
+    },
+    
+    loadNormalRecommendations() {
+      // 加载普通推荐
+    }
+  }
+})
+```
+
+## 💡 常见使用场景
+
+### 场景 1:根据全局配置控制显示
+
+**app.tsx:**
+```typescript
+class App extends Component {
+  globalData = {
+    showFullScreenAd: true,  // 全局控制是否显示全屏广告
+    adFrequency: 5          // 广告频率
+  }
+}
+```
+
+**组件中:**
+```javascript
+attached() {
+  const app = getApp()
+  
+  if (!app.globalData.showFullScreenAd) {
+    console.log('全屏广告已禁用')
+    return
+  }
+  
+  // 继续显示逻辑...
+}
+```
+
+### 场景 2:根据 VIP 等级显示不同内容
+
+**app.tsx:**
+```typescript
+class App extends Component {
+  globalData = {
+    userInfo: {
+      isVip: true,
+      vipLevel: 3,
+      userId: 'user-123'
+    }
+  }
+}
+```
+
+**组件中:**
+```javascript
+attached() {
+  const app = getApp()
+  const { isVip, vipLevel, userId } = app.globalData.userInfo
+  
+  if (isVip) {
+    // VIP 用户显示专属推荐
+    this.setData({
+      recommendList: this.getVipBooks(vipLevel)
+    })
+  } else {
+    // 非 VIP 用户显示普通推荐
+    this.setData({
+      recommendList: this.getNormalBooks()
+    })
+  }
+  
+  // 记录用户行为
+  this.reportAnalytics('fullscreen_show', {
+    userId,
+    isVip,
+    vipLevel
+  })
+}
+```
+
+### 场景 3:动态配置广告策略
+
+**app.tsx:**
+```typescript
+class App extends Component {
+  globalData = {
+    adStrategy: {
+      enableChapterEnd: true,    // 章节末尾显示
+      enableBookEnd: true,       // 书籍末尾显示
+      minChapterInterval: 3,     // 最小章节间隔
+      maxDailyShows: 5,          // 每日最大展示次数
+      blacklistedUsers: []       // 黑名单用户
+    }
+  }
+}
+```
+
+**组件中:**
+```javascript
+attached() {
+  const app = getApp()
+  const strategy = app.globalData.adStrategy
+  
+  // 检查是否在黑名单中
+  if (strategy.blacklistedUsers.includes(currentUserId)) {
+    console.log('用户在黑名单中,不显示广告')
+    return
+  }
+  
+  // 检查今日展示次数
+  const todayShows = Taro.getStorageSync('todayAdShows') || 0
+  if (todayShows >= strategy.maxDailyShows) {
+    console.log('今日展示次数已达上限')
+    return
+  }
+  
+  // 继续显示逻辑...
+}
+```
+
+### 场景 4:多语言支持
+
+**app.tsx:**
+```typescript
+class App extends Component {
+  globalData = {
+    language: 'zh-CN',  // zh-CN / en-US
+    translations: {
+      'zh-CN': {
+        close: '关闭',
+        recommend: '推荐阅读'
+      },
+      'en-US': {
+        close: 'Close',
+        recommend: 'Recommended'
+      }
+    }
+  }
+}
+```
+
+**组件中:**
+```javascript
+attached() {
+  const app = getApp()
+  const lang = app.globalData.language
+  const t = app.globalData.translations[lang]
+  
+  this.setData({
+    i18n: {
+      closeText: t.close,
+      recommendTitle: t.recommend
+    }
+  })
+}
+```
+
+## 🎯 完整示例
+
+### app.tsx 完整代码
+
+```typescript
+// src/app.tsx
+import Taro from '@tarojs/taro'
+
+const novelPlugin = Taro.requirePlugin('novel-plugin')
+
+class App extends Component {
+  // 定义全局数据
+  globalData = {
+    // 基础配置
+    isShow: true,
+    customParams: null,
+    
+    // 用户信息
+    userInfo: null,
+    vipLevel: 0,
+    
+    // 阅读设置
+    readingMode: 'normal',
+    fontSize: 16,
+    theme: 'light',
+    
+    // 广告配置
+    adConfig: {
+      enabled: true,
+      frequency: 5,
+      type: 'fullscreen',
+      strategies: {
+        showInChapterEnd: true,
+        showInBookEnd: true,
+        minInterval: 3
+      }
+    },
+    
+    // 多语言
+    language: 'zh-CN',
+    translations: {
+      'zh-CN': { close: '关闭', recommend: '推荐阅读' },
+      'en-US': { close: 'Close', recommend: 'Recommended' }
+    }
+  }
+
+  onLaunch(options) {
+    console.log("apponLaunch", options)
+    
+    // 初始化全局数据
+    this.initGlobalData()
+    
+    // 监听进入插件页事件
+    novelPlugin.onPageLoad(this.onNovelPluginLoad)
+  }
+  
+  initGlobalData() {
+    // 从本地缓存读取配置
+    const config = Taro.getStorageSync('appConfig')
+    if (config) {
+      this.globalData = {
+        ...this.globalData,
+        ...config
+      }
+    }
+    
+    // 从服务器获取最新配置
+    this.fetchRemoteConfig()
+  }
+  
+  async fetchRemoteConfig() {
+    try {
+      const response = await Taro.request({
+        url: 'https://api.example.com/config'
+      })
+      
+      // 更新全局配置
+      this.globalData = {
+        ...this.globalData,
+        ...response.data
+      }
+      
+      // 保存到本地缓存
+      Taro.setStorageSync('appConfig', this.globalData)
+    } catch (error) {
+      console.error('获取远程配置失败:', error)
+    }
+  }
+  
+  // 更新全局数据的方法
+  updateGlobalData(key, value) {
+    this.globalData[key] = value
+    
+    // 同步到本地缓存
+    Taro.setStorageSync('appConfig', this.globalData)
+  }
+  
+  // ... 其他代码
+}
+```
+
+### 组件中完整使用
+
+```javascript
+// components/novelPlugin/fullScreen/index.js
+const novelPlugin = requirePlugin('novel-plugin')
+
+Component({
+  properties: {
+    novelManagerId: Number,
+    bookId: String,
+    chapterIndex: Number,
+    ext: String,
+  },
+
+  data: {
+    recommendList: [],
+    i18n: {},
+    isVip: false,
+  },
+
+  lifetimes: {
+    attached() {
+      // 获取 App 全局数据
+      const app = getApp()
+      const { 
+        userInfo, 
+        vipLevel, 
+        adConfig, 
+        language, 
+        translations 
+      } = app.globalData
+      
+      console.log('全局配置:', {
+        userInfo,
+        vipLevel,
+        adConfig,
+        language
+      })
+      
+      // 检查广告是否启用
+      if (!adConfig.enabled) {
+        console.log('广告已禁用')
+        this.handleClose()
+        return
+      }
+      
+      // 检查用户 VIP 等级
+      const isVip = !!userInfo && vipLevel > 0
+      this.setData({ isVip, i18n: translations[language] })
+      
+      // 根据 VIP 等级加载不同内容
+      if (isVip) {
+        this.loadVipContent(vipLevel)
+      } else {
+        this.loadNormalContent()
+      }
+      
+      // 解析 ext 参数
+      this.parseExtParams()
+      
+      // 上报展示数据
+      this.reportImpression({
+        userId: userInfo?.id,
+        isVip,
+        vipLevel,
+        chapterIndex: this.properties.chapterIndex
+      })
+    },
+  },
+
+  methods: {
+    loadVipContent(vipLevel) {
+      // VIP 专属内容
+      const vipBooks = [
+        {
+          id: 'vip-1',
+          title: 'VIP 专享书籍 1',
+          description: '仅限 VIP 阅读',
+          coverUrl: 'https://example.com/vip1.jpg'
+        }
+      ]
+      
+      this.setData({ recommendList: vipBooks })
+    },
+    
+    loadNormalContent() {
+      // 普通内容
+      const normalBooks = [
+        {
+          id: 'normal-1',
+          title: '热门推荐 1',
+          description: '大家都在看',
+          coverUrl: 'https://example.com/normal1.jpg'
+        }
+      ]
+      
+      this.setData({ recommendList: normalBooks })
+    },
+    
+    parseExtParams() {
+      if (this.properties.ext) {
+        try {
+          const extData = JSON.parse(this.properties.ext)
+          if (extData.recommendList) {
+            this.setData({ recommendList: extData.recommendList })
+          }
+        } catch (e) {
+          console.error('解析 ext 失败:', e)
+        }
+      }
+    },
+    
+    reportImpression(data) {
+      // 上报展示数据到后台
+      console.log('上报展示:', data)
+    },
+    
+    handleClose() {
+      const novelManager = novelPlugin.getNovelManager(this.properties.novelManagerId)
+      if (novelManager) {
+        novelManager.setFullScreenComponentStatus({ show: false })
+      }
+    }
+  }
+})
+```
+
+## ⚠️ 注意事项
+
+### 1. getApp() 的时机
+
+```javascript
+// ✅ 正确:在 lifetimes 中使用
+lifetimes: {
+  attached() {
+    const app = getApp()
+    console.log(app.globalData)
+  }
+}
+
+// ❌ 错误:在 properties 定义时使用
+Component({
+  properties: {
+    test: getApp().globalData.test  // 此时可能无法获取
+  }
+})
+```
+
+### 2. 数据同步问题
+
+```javascript
+// app.tsx
+class App extends Component {
+  globalData = {
+    count: 0
+  }
+  
+  // 提供更新方法
+  updateCount(newCount) {
+    this.globalData.count = newCount
+  }
+}
+
+// 组件中
+attached() {
+  const app = getApp()
+  console.log('初始值:', app.globalData.count)
+  
+  // 注意:组件中的数据不会自动更新
+  // 需要在合适的时机重新获取
+}
+```
+
+### 3. 类型安全(TypeScript)
+
+```typescript
+// types/global.d.ts
+declare interface CustomApp {
+  globalData: {
+    isShow: boolean
+    customParams: any
+    userInfo: UserInfo | null
+    vipLevel: number
+    // ... 其他字段
+  }
+}
+
+// 组件中使用
+const app = getApp<CustomApp>()
+console.log(app.globalData.isShow) // 有类型提示
+```
+
+## 📊 最佳实践
+
+### 1. 集中管理配置
+
+```typescript
+// config/global.ts
+export default {
+  adConfig: {
+    enabled: true,
+    frequency: 5
+  },
+  vipConfig: {
+    levels: [1, 2, 3, 4, 5],
+    benefits: ['专属内容', '去广告', '折扣']
+  }
+}
+
+// app.tsx
+import globalConfig from './config/global'
+
+class App extends Component {
+  globalData = globalConfig
+}
+```
+
+### 2. 提供 getter/setter
+
+```typescript
+class App extends Component {
+  globalData = {
+    isShow: true
+  }
+  
+  // 提供访问方法
+  getIsShow() {
+    return this.globalData.isShow
+  }
+  
+  setIsShow(value: boolean) {
+    this.globalData.isShow = value
+    // 触发更新逻辑
+    this.notifyComponents()
+  }
+}
+```
+
+### 3. 监听变化
+
+```javascript
+// 组件中监听全局数据变化
+attached() {
+  const app = getApp()
+  
+  // 定期检查或使用事件监听
+  setInterval(() => {
+    if (app.globalData.isShow !== this.lastIsShow) {
+      this.lastIsShow = app.globalData.isShow
+      this.handleIsShowChange(app.globalData.isShow)
+    }
+  }, 1000)
+}
+```
+
+## 🔗 相关文档
+
+- [微信小程序 App API 文档](https://developers.weixin.qq.com/miniprogram/dev/reference/api/App.html)
+- [getApp() 使用说明](https://developers.weixin.qq.com/miniprogram/dev/reference/api/getApp.html)

+ 255 - 0
src/components/novelPlugin/USAGE.md

@@ -0,0 +1,255 @@
+# 微信官方阅读器插件 - 组件使用指南
+
+## 📦 项目包含的组件
+
+本项目已集成两个微信官方阅读器插件的自定义组件:
+
+### 1. 自定义段落组件(paragraph-block)
+- **路径**:`components/novelPlugin/customParagraph/`
+- **功能**:在章节内容底部显示自定义内容(二维码等)
+- **特性**:支持长按识别、动态配置 URL
+
+### 2. 全屏组件(full-screen)
+- **路径**:`components/novelPlugin/fullScreen/`
+- **功能**:全屏展示推荐内容、广告等
+- **特性**:可配置推荐列表、智能展示
+
+---
+
+## 🚀 快速开始
+
+### 步骤 1:确认配置
+
+在 `src/app.config.ts` 中已配置好两个组件:
+
+```json
+{
+  "plugins": {
+    "novel-plugin": {
+      "version": "latest",
+      "provider": "wx293c4b6097a8a4d0",
+      "genericsImplementation": {
+        "novel": {
+          "paragraph-block": "components/novelPlugin/customParagraph/index",
+          "full-screen": "components/novelPlugin/fullScreen/index"
+        }
+      }
+    }
+  }
+}
+```
+
+### 步骤 2:初始化插件
+
+在 `src/app.tsx` 中添加插件初始化逻辑:
+
+```typescript
+// src/app.tsx
+import Taro from '@tarojs/taro'
+
+// 引入官方小说阅读插件
+const novelPlugin = Taro.requirePlugin('novel-plugin')
+
+class App extends Component {
+  onLaunch(options) {
+    console.log("apponLaunch", options)
+    // 监听进入插件页事件
+    novelPlugin.onPageLoad(this.onNovelPluginLoad)
+  }
+
+  onNovelPluginLoad(data) {
+    console.log('onNovelPluginLoad', data)
+    
+    const novelManager = novelPlugin.getNovelManager(data.id)
+    
+    // ========== 设置自定义段落二维码 ==========
+    novelManager.setParagraphBlock({
+      chapterIndex: 0,           // 第一章
+      paragraphIndex: 5,         // 第 6 段后
+      ext: JSON.stringify({
+        qrcodeUrl: 'https://your-domain.com/qrcode.jpg'
+      })
+    })
+    
+    // ========== 设置全屏推荐(示例)==========
+    // 监听用户行为,在合适时机显示全屏推荐
+    novelManager.onUserTriggerEvent(res => {
+      if (res.event_id === 'leave_chapter' && res.chapter_index === 5) {
+        // 在第 6 章结束后显示全屏推荐
+        novelManager.showFullScreen({
+          ext: JSON.stringify({
+            recommendList: [
+              {
+                id: 1,
+                bookId: 'book-001',
+                title: '推荐书籍 1',
+                description: '这是一本很好看的书',
+                coverUrl: 'https://example.com/cover1.jpg'
+              }
+            ]
+          })
+        })
+      }
+    })
+  }
+}
+```
+
+### 步骤 3:构建项目
+
+```bash
+cd /Users/shenwu/Desktop/gs-items/new-taro-mini-book
+npm run build:weapp
+```
+
+### 步骤 4:测试验证
+
+1. 打开微信开发者工具
+2. 导入项目(dist 目录)
+3. 编译并预览
+4. 使用阅读器插件打开书籍
+5. 验证组件是否正常工作
+
+---
+
+## 📱 组件对比
+
+| 特性 | 自定义段落组件 | 全屏组件 |
+|------|--------------|---------|
+| **显示位置** | 章节内容底部 | 全屏覆盖 |
+| **触发方式** | 自动显示 | 需调用 API 显示 |
+| **用途** | 二维码、简短提示 | 推荐列表、广告 |
+| **用户体验** | 轻量,不打断阅读 | 重量级,会打断阅读 |
+| **建议频率** | 每章都可显示 | 适度使用(如每 3-5 章) |
+
+---
+
+## 🎯 最佳实践
+
+### 自定义段落组件使用场景
+
+✅ **适合:**
+- 每章末尾的推广二维码
+- 作者公众号引流
+- 读者群二维码
+- 简短的提示信息
+
+❌ **不适合:**
+- 复杂的交互内容
+- 长文本介绍
+- 视频等多媒体内容
+
+### 全屏组件使用场景
+
+✅ **适合:**
+- 重要活动推广
+- VIP 专属推荐
+- 新书上架宣传
+- 阶段性阅读奖励
+
+❌ **不适合:**
+- 每章都显示(影响体验)
+- 无关的垃圾广告
+- 过于频繁的弹窗
+
+---
+
+## 💡 使用建议
+
+### 1. 平衡商业化和用户体验
+
+```javascript
+// ❌ 不好的做法:每章都显示全屏广告
+for (let i = 0; i < totalChapters; i++) {
+  novelManager.showFullScreen({...})
+}
+
+// ✅ 好的做法:适度显示
+for (let i = 0; i < totalChapters; i += 5) { // 每 5 章显示一次
+  novelManager.showFullScreen({...})
+}
+```
+
+### 2. 智能推荐
+
+```javascript
+// 根据阅读进度智能推荐
+if (chapterIndex < 10) {
+  // 新手期:推荐入门书籍
+  showRecommend(noviceBooks)
+} else if (chapterIndex < 50) {
+  // 成长期:推荐进阶书籍
+  showRecommend(intermediateBooks)
+} else {
+  // 成熟期:推荐高级书籍
+  showRecommend(advancedBooks)
+}
+```
+
+### 3. 数据追踪
+
+```javascript
+// 记录组件展示和点击数据
+novelManager.onUserTriggerEvent(res => {
+  if (res.event_id === 'full_screen_close') {
+    // 上报关闭事件
+    reportAnalytics('fullscreen_close', {
+      chapterIndex: res.chapter_index,
+      timestamp: Date.now()
+    })
+  }
+})
+```
+
+---
+
+## 🔧 常见问题
+
+### Q1: 组件不显示?
+
+**A:** 检查以下几点:
+1. 确认已运行 `npm run build:weapp`
+2. 检查 `dist` 目录是否有组件文件
+3. 确认 `app.json` 中配置正确
+4. 查看控制台是否有错误日志
+
+### Q2: ext 参数解析失败?
+
+**A:** 确保:
+1. 使用 `JSON.stringify()` 转换对象
+2. URL 使用 HTTPS 协议
+3. 不包含特殊字符或已正确转义
+
+### Q3: 全屏组件无法关闭?
+
+**A:** 确保调用了正确的 API:
+```javascript
+const novelManager = novelPlugin.getNovelManager(novelManagerId)
+novelManager.closeFullScreen() // 使用此方法关闭
+```
+
+### Q4: 如何统计扫码数据?
+
+**A:** 在二维码 URL 中添加追踪参数:
+```javascript
+ext: JSON.stringify({
+  qrcodeUrl: 'https://example.com/qrcode?source=novel&chapter=0&bookId=xxx'
+})
+```
+
+---
+
+## 📚 相关文档
+
+- [自定义段落组件文档](./customParagraph/README.md)
+- [全屏组件文档](./fullScreen/README.md)
+- [微信官方文档](https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/novel.html)
+
+---
+
+## 📞 技术支持
+
+如有问题,请查阅:
+1. 微信官方文档
+2. 微信开放社区
+3. 项目 README 文档

+ 356 - 0
src/components/novelPlugin/fullScreen/README.md

@@ -0,0 +1,356 @@
+# 微信官方阅读器插件 - 全屏组件
+
+## 概述
+
+全屏组件是微信官方阅读器插件提供的另一种自定义组件类型,用于在全屏模式下展示广告、推荐内容或其他自定义信息。
+
+## 功能特性
+
+- ✅ 支持全屏展示自定义内容
+- ✅ 可用于推荐书籍、广告展示等场景
+- ✅ 支持动态配置内容(通过 `ext` 参数)
+- ✅ 符合微信官方阅读器插件规范
+- ✅ 使用微信原生组件格式,确保兼容性
+
+## 组件文件结构
+
+```
+src/components/novelPlugin/fullScreen/
+├── index.wxml    # 组件模板文件
+├── index.wxss    # 组件样式文件
+├── index.js      # 组件逻辑文件
+└── index.json    # 组件配置文件
+```
+
+## 配置说明
+
+### app.config.ts 配置
+
+已在 `src/app.config.ts` 中添加了全屏组件配置:
+
+```json
+{
+  "plugins": {
+    "novel-plugin": {
+      "version": "latest",
+      "provider": "wx293c4b6097a8a4d0",
+      "genericsImplementation": {
+        "novel": {
+          "full-screen": "components/novelPlugin/fullScreen/index"
+        }
+      }
+    }
+  }
+}
+```
+
+**注意**:路径是相对于小程序根目录(dist)的路径,不需要文件扩展名。
+
+## 组件属性
+
+阅读器插件会自动传入以下属性:
+
+| 参数 | 类型 | 描述 |
+| --- | --- | --- |
+| novelManagerId | Number | 阅读器实例 id |
+| bookId | String | 书籍 id |
+| chapterIndex | Number | 章节下标 |
+| chapterId | String | 章节 id |
+| originalId | String | 章节主键 original_id |
+| **ext** | **String** | **透传参数(JSON 字符串),用于传递推荐数据等** |
+
+## 使用方法
+
+### 1. 在 app.tsx 中设置全屏组件
+
+使用 `NovelManager.showFullScreen()` 方法显示全屏组件:
+
+```javascript
+// app.js 或 app.tsx
+const novelPlugin = requirePlugin('novel-plugin')
+
+App({
+  onLaunch() {
+    novelPlugin.onPageLoad(this.onNovelPluginLoad)
+  },
+  
+  onNovelPluginLoad(data) {
+    const novelManager = novelPlugin.getNovelManager(data.id)
+    
+    // 在特定条件下显示全屏组件
+    // 例如:用户阅读完某一章后
+    novelManager.onUserTriggerEvent(res => {
+      if (res.event_id === 'leave_chapter' && res.chapter_index === 5) {
+        // 在第 6 章结束后显示全屏推荐
+        novelManager.showFullScreen({
+          ext: JSON.stringify({
+            recommendList: [
+              {
+                id: 1,
+                bookId: 'book-001',
+                title: '推荐书籍 1',
+                description: '这是一本很好看的书',
+                coverUrl: 'https://example.com/cover1.jpg'
+              },
+              {
+                id: 2,
+                bookId: 'book-002',
+                title: '推荐书籍 2',
+                description: '热门推荐,不容错过',
+                coverUrl: 'https://example.com/cover2.jpg'
+              }
+            ]
+          })
+        })
+      }
+    })
+  }
+})
+```
+
+### 2. ext 参数格式
+
+```javascript
+ext: JSON.stringify({
+  recommendList: [
+    {
+      id: 1,                    // 推荐项 ID
+      bookId: 'book-xxx',       // 书籍ID
+      title: '书名',            // 标题
+      description: '简介',      // 描述
+      coverUrl: 'https://...'   // 封面图片 URL
+    },
+    // ... 更多推荐项
+  ]
+})
+```
+
+也支持简化格式:
+
+```javascript
+ext: JSON.stringify({
+  recommendBooks: [...]  // 使用 recommendBooks 字段
+})
+```
+
+## 完整示例
+
+### 示例 1:阅读完章节后显示推荐
+
+```javascript
+const novelPlugin = requirePlugin('novel-plugin')
+
+App({
+  onLaunch() {
+    novelPlugin.onPageLoad(onNovelPluginLoad)
+  }
+})
+
+function onNovelPluginLoad(data) {
+  const novelManager = novelPlugin.getNovelManager(data.id)
+  
+  // 监听用户离开章节事件
+  novelManager.onUserTriggerEvent(res => {
+    if (res.event_id === 'leave_chapter') {
+      // 每阅读完 3 章显示一次推荐
+      if ((res.chapter_index + 1) % 3 === 0) {
+        showRecommendFullScreen(novelManager, res.chapter_index)
+      }
+    }
+  })
+}
+
+function showRecommendFullScreen(novelManager, chapterIndex) {
+  // 根据章节智能推荐
+  const recommendData = getRecommendByChapter(chapterIndex)
+  
+  novelManager.showFullScreen({
+    ext: JSON.stringify({
+      recommendList: recommendData
+    })
+  })
+}
+
+function getRecommendByChapter(chapterIndex) {
+  // 根据章节返回推荐数据
+  if (chapterIndex < 10) {
+    return [
+      {
+        id: 1,
+        bookId: 'book-early-1',
+        title: '初期推荐书籍',
+        description: '适合新手阅读',
+        coverUrl: 'https://example.com/cover1.jpg'
+      }
+    ]
+  } else {
+    return [
+      {
+        id: 2,
+        bookId: 'book-mid-1',
+        title: '中期推荐书籍',
+        description: '进阶阅读',
+        coverUrl: 'https://example.com/cover2.jpg'
+      }
+    ]
+  }
+}
+```
+
+### 示例 2:VIP 专属推荐
+
+```javascript
+// 针对 VIP 用户显示专属推荐
+if (userInfoStore.isVip) {
+  novelManager.showFullScreen({
+    ext: JSON.stringify({
+      recommendList: [
+        {
+          id: 1,
+          bookId: 'vip-book-1',
+          title: 'VIP 专享书籍',
+          description: '仅限 VIP 阅读',
+          coverUrl: 'https://example.com/vip-cover.jpg'
+        }
+      ]
+    })
+  })
+}
+```
+
+### 示例 3:活动推广
+
+```javascript
+// 在特定时间段显示活动推广
+const now = new Date()
+if (now.getMonth() === 11 && now.getDate() <= 25) { // 圣诞节期间
+  novelManager.showFullScreen({
+    ext: JSON.stringify({
+      recommendList: [
+        {
+          id: 1,
+          bookId: 'xmas-book-1',
+          title: '圣诞特辑',
+          description: '圣诞节限时免费',
+          coverUrl: 'https://example.com/xmas-cover.jpg'
+        }
+      ],
+      activityInfo: {
+        title: '圣诞快乐',
+        subtitle: '精选好书限时优惠'
+      }
+    })
+  })
+}
+```
+
+## 自定义内容
+
+你可以根据业务需求修改 WXML 模板来自定义全屏内容:
+
+### 修改推荐列表布局
+
+编辑 `index.wxml`:
+
+```xml
+<!-- 网格布局 -->
+<view class="recommend-grid">
+  <view 
+    class="grid-item" 
+    wx:for="{{recommendList}}" 
+    wx:key="id"
+    bindtap="handleRecommendClick" 
+    data-item="{{item}}"
+  >
+    <image class="grid-cover" src="{{item.coverUrl}}" mode="aspectFill"></image>
+    <text class="grid-title">{{item.title}}</text>
+  </view>
+</view>
+```
+
+### 添加关闭确认对话框
+
+编辑 `index.js`:
+
+```javascript
+handleClose() {
+  wx.showModal({
+    title: '提示',
+    content: '确定要关闭推荐吗?',
+    success: (res) => {
+      if (res.confirm) {
+        const novelManager = novelPlugin.getNovelManager(this.properties.novelManagerId)
+        if (novelManager) {
+          novelManager.closeFullScreen()
+        }
+      }
+    }
+  })
+}
+```
+
+## 注意事项
+
+1. **使用时机**:全屏组件会打断用户阅读,应在合适的时机显示(如章节结束)
+2. **用户体验**:不要频繁显示,避免影响阅读体验
+3. **内容审核**:确保推荐内容符合平台规范
+4. **性能优化**:推荐列表不宜过长,建议不超过 10 项
+5. **图片加载**:使用 CDN 加速图片加载
+6. **真机测试**:确保在真机上测试全屏效果
+
+## 常见使用场景
+
+### 1. 章节推荐
+在阅读完一定章节后显示相关书籍推荐
+
+### 2. 活动推广
+在特殊节日或活动期间显示促销信息
+
+### 3. VIP 专属
+针对 VIP 用户显示专属内容
+
+### 4. 新书上架
+推荐最新上架的书籍
+
+### 5. 热门榜单
+展示当前最热门的书籍
+
+## 调试技巧
+
+1. 打开微信开发者工具调试器
+2. 查看控制台输出确认组件加载
+3. 检查 ext 参数是否正确传递和解析
+4. 验证推荐数据是否正确显示
+5. 测试关闭功能是否正常工作
+
+## 相关 API
+
+### NovelManager.showFullScreen(options)
+
+显示全屏组件
+
+**参数:**
+- `options.ext` String - 透传参数(JSON 字符串)
+
+**示例:**
+```javascript
+novelManager.showFullScreen({
+  ext: JSON.stringify({
+    recommendList: [...]
+  })
+})
+```
+
+### NovelManager.closeFullScreen()
+
+关闭全屏组件
+
+**示例:**
+```javascript
+novelManager.closeFullScreen()
+```
+
+## 相关文档
+
+- [微信官方阅读器插件文档](https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/novel.html)
+- [全屏组件](https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/novel.html#%E5%85%A8%E5%B1%8F%E7%BB%84%E4%BB%B6)

+ 107 - 0
src/components/novelPlugin/fullScreen/index.js

@@ -0,0 +1,107 @@
+// components/novelPlugin/fullScreen/index.js
+const novelPlugin = requirePlugin('novel-plugin')
+
+Component({
+  properties: {
+    novelManagerId: {
+      type: Number,
+      value: -1,
+    },
+    bookId: {
+      type: String,
+      value: '',
+    },
+    chapterIndex: {
+      type: Number,
+      value: -1,
+    },
+    chapterId: {
+      type: String,
+      value: '',
+    },
+    originalId: {
+      type: String,
+      value: '',
+    },
+    ext: {
+      type: String,
+      value: '',
+    },
+  },
+
+  data: {
+    recommendList: [], // 推荐列表数据
+  },
+
+  lifetimes: {
+    attached() {
+      console.log('全屏组件加载:', {
+        novelManagerId: this.properties.novelManagerId,
+        bookId: this.properties.bookId,
+        chapterIndex: this.properties.chapterIndex,
+        ext: this.properties.ext,
+      })
+      
+      // ========== 获取 App 全局数据 ==========
+      const app = getApp()
+      console.log('获取 App 实例:', app)
+      console.log('App globalData:', app.$app.globalData)
+      
+      // 读取你在 app.tsx 中定义的参数
+      if (app.globalData) {
+        const isShow = app.globalData.isShow
+        const customParams = app.globalData.customParams
+        console.log('isShow:', isShow)
+        console.log('customParams:', customParams)
+        
+        // 根据 isShow 决定是否显示全屏组件
+        if (isShow === false) {
+          console.log('根据配置不显示全屏组件')
+          // 可以在这里调用关闭逻辑
+          // this.handleClose()
+        }
+      }
+      // ====================================
+      
+      // 解析 ext 参数获取推荐数据
+      if (this.properties.ext) {
+        try {
+          const extData = JSON.parse(this.properties.ext)
+          console.log('解析 ext 数据:', extData)
+          
+          // 从 ext 中获取推荐列表
+          if (extData.recommendList && Array.isArray(extData.recommendList)) {
+            this.setData({
+              recommendList: extData.recommendList
+            })
+          } else if (extData.recommendBooks && Array.isArray(extData.recommendBooks)) {
+            this.setData({
+              recommendList: extData.recommendBooks
+            })
+          }
+        } catch (e) {
+          console.error('解析 ext 失败:', e)
+        }
+      }
+    },
+  },
+
+  methods: {
+    // 关闭全屏组件
+    handleClose() {
+      console.log('用户关闭全屏组件')
+      
+      // 获取阅读器实例
+      const novelManager = novelPlugin.getNovelManager(this.properties.novelManagerId)
+      
+      if (novelManager) {
+        // 通知阅读器关闭全屏组件
+        novelManager.setFullScreenComponentStatus({show:false})
+      }
+    },
+
+    // 点击推荐项
+    handleRecommendClick(e) {
+    },
+  },
+})

+ 4 - 0
src/components/novelPlugin/fullScreen/index.json

@@ -0,0 +1,4 @@
+{
+  "component": true,
+  "usingComponents": {}
+}

+ 30 - 0
src/components/novelPlugin/fullScreen/index.wxml

@@ -0,0 +1,30 @@
+<!--components/novelPlugin/fullScreen/index.wxml-->
+<view class="full-screen-container">
+  <view class="full-screen-content">
+    <!-- 这里可以放置你的全屏广告、推荐内容等 -->
+    <view class="full-screen-header">
+      <text class="full-screen-title">推荐阅读</text>
+    </view>
+    
+    <view class="full-screen-body">
+      <!-- 示例:推荐书籍列表 -->
+      <view class="recommend-item" wx:for="{{recommendList}}" wx:key="id" bindtap="handleRecommendClick" data-item="{{item}}">
+        <image class="recommend-cover" src="{{item.coverUrl}}" mode="aspectFill"></image>
+        <view class="recommend-info">
+          <text class="recommend-title">{{item.title}}</text>
+          <text class="recommend-desc">{{item.description}}</text>
+        </view>
+      </view>
+      
+      <!-- 如果没有推荐数据,显示默认内容 -->
+      <view wx:if="{{recommendList.length === 0}}" class="empty-state">
+        <image class="empty-image" src="/images/empty.png" mode="aspectFit"></image>
+        <text class="empty-text">暂无推荐内容</text>
+      </view>
+    </view>
+    
+    <view class="full-screen-footer">
+      <button class="close-btn" bindtap="handleClose">关闭</button>
+    </view>
+  </view>
+</view>

+ 130 - 0
src/components/novelPlugin/fullScreen/index.wxss

@@ -0,0 +1,130 @@
+/* components/novelPlugin/fullScreen/index.wxss */
+.full-screen-container {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0, 0, 0, 0.7);
+  z-index: 9999;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.full-screen-content {
+  width: 100%;
+  height: 100%;
+  background-color: #ffffff;
+  display: flex;
+  flex-direction: column;
+}
+
+.full-screen-header {
+  height: 88rpx;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-bottom: 2rpx solid #f0f0f0;
+}
+
+.full-screen-title {
+  font-size: 36rpx;
+  font-weight: 600;
+  color: #333333;
+}
+
+.full-screen-body {
+  flex: 1;
+  overflow-y: auto;
+  padding: 20rpx;
+}
+
+.recommend-item {
+  display: flex;
+  margin-bottom: 30rpx;
+  padding: 20rpx;
+  background-color: #f8f8f8;
+  border-radius: 10rpx;
+}
+
+.recommend-cover {
+  width: 120rpx;
+  height: 160rpx;
+  border-radius: 8rpx;
+  margin-right: 20rpx;
+  flex-shrink: 0;
+}
+
+.recommend-info {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+}
+
+.recommend-title {
+  font-size: 32rpx;
+  font-weight: 500;
+  color: #333333;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+}
+
+.recommend-desc {
+  font-size: 26rpx;
+  color: #999999;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+}
+
+.empty-state {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  height: 100%;
+}
+
+.empty-image {
+  width: 200rpx;
+  height: 200rpx;
+  margin-bottom: 20rpx;
+}
+
+.empty-text {
+  font-size: 28rpx;
+  color: #999999;
+}
+
+.full-screen-footer {
+  height: 100rpx;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-top: 2rpx solid #f0f0f0;
+  padding: 0 40rpx;
+}
+
+.close-btn {
+  width: 300rpx;
+  height: 72rpx;
+  line-height: 72rpx;
+  background-color: #f0f0f0;
+  border-radius: 36rpx;
+  font-size: 28rpx;
+  color: #666666;
+  border: none;
+}
+
+.close-btn:active {
+  background-color: #e0e0e0;
+}

+ 9 - 0
types/global.d.ts

@@ -28,6 +28,15 @@ declare namespace JSX {
       ext: string; // 透传参数,JSON 字符串格式
       style: React.CSSProperties;
     }>
+    'full-screen': Partial<{
+      novelManagerId: number;
+      bookId: string;
+      chapterIndex: number;
+      chapterId: string;
+      originalId: string;
+      ext: string; // 透传参数,JSON 字符串格式
+      style: React.CSSProperties;
+    }>
   }
 }