|
@@ -0,0 +1,723 @@
|
|
|
|
|
+# 微信官方阅读器插件 - 自定义段落组件
|
|
|
|
|
+
|
|
|
|
|
+## ⚠️ 重要说明
|
|
|
|
|
+
|
|
|
|
|
+**本组件使用微信原生格式(.wxml, .wxss, .js, .json),而非 Taro/React 格式**
|
|
|
|
|
+
|
|
|
|
|
+原因:
|
|
|
|
|
+- 微信官方阅读器插件的抽象节点组件必须是微信原生组件
|
|
|
|
|
+- Taro 编译后的 React组件无法被插件正确识别
|
|
|
|
|
+- 因此需要使用 `.wxml`, `.wxss`, `.js`, `.json` 原生文件格式
|
|
|
|
|
+
|
|
|
|
|
+## 概述
|
|
|
|
|
+
|
|
|
|
|
+该组件用于在微信官方阅读器插件中,在每章内容的底部展示自定义内容(二维码图片)。
|
|
|
|
|
+
|
|
|
|
|
+## 功能特性
|
|
|
|
|
+
|
|
|
|
|
+- ✅ 在每章内容底部自动显示自定义二维码图片
|
|
|
|
|
+- ✅ 支持动态配置二维码 URL(通过 `ext` 参数)
|
|
|
|
|
+- ✅ **支持长按识别二维码**(微信原生功能)
|
|
|
|
|
+- ✅ 符合微信官方阅读器插件规范
|
|
|
|
|
+- ✅ 使用微信原生组件格式,确保兼容性
|
|
|
|
|
+
|
|
|
|
|
+## 配置说明
|
|
|
|
|
+
|
|
|
|
|
+### 1. app.config.ts 配置
|
|
|
|
|
+
|
|
|
|
|
+已在 `src/app.config.ts` 中添加了插件配置:
|
|
|
|
|
+
|
|
|
|
|
+``json
|
|
|
|
|
+{
|
|
|
|
|
+ "plugins": {
|
|
|
|
|
+ "novel-plugin": {
|
|
|
|
|
+ "version": "latest",
|
|
|
|
|
+ "provider": "wx293c4b6097a8a4d0",
|
|
|
|
|
+ "genericsImplementation": {
|
|
|
|
|
+ "novel": {
|
|
|
|
|
+ "custom-paragraph": "components/novelPlugin/customParagraph/index"
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**注意**:路径是相对于小程序根目录(dist)的路径,不需要文件扩展名。
|
|
|
|
|
+
|
|
|
|
|
+### 2. 组件文件结构
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+src/components/novelPlugin/customParagraph/
|
|
|
|
|
+├── index.wxml # 组件模板文件(微信原生)
|
|
|
|
|
+├── index.wxss # 组件样式文件(微信原生)
|
|
|
|
|
+├── index.js # 组件逻辑文件(微信原生)
|
|
|
|
|
+└── index.json # 组件配置文件(微信原生)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 3. 全局类型声明
|
|
|
|
|
+
|
|
|
|
|
+已在 `types/global.d.ts` 中添加了组件的类型声明,确保 TypeScript 能正确识别。
|
|
|
|
|
+
|
|
|
|
|
+## 组件属性
|
|
|
|
|
+
|
|
|
|
|
+阅读器插件会自动传入以下属性:
|
|
|
|
|
+
|
|
|
|
|
+| 参数 | 类型 | 描述 |
|
|
|
|
|
+| --- | --- | --- |
|
|
|
|
|
+| novelManagerId | Number | 阅读器实例 id |
|
|
|
|
|
+| bookId | String | 书籍 id |
|
|
|
|
|
+| chapterIndex | Number | 章节下标 |
|
|
|
|
|
+| chapterId | String | 章节 id |
|
|
|
|
|
+| paragraphIndex | Number | 段落下标 |
|
|
|
|
|
+| originalId | String | 章节主键 original_id |
|
|
|
|
|
+| **ext** | **String** | **透传参数(JSON 字符串格式),用于传递二维码 URL 等自定义数据** |
|
|
|
|
|
+
|
|
|
|
|
+## 📱 长按识别二维码功能
|
|
|
|
|
+
|
|
|
|
|
+### 功能说明
|
|
|
|
|
+
|
|
|
|
|
+组件已启用微信小程序的**长按识别二维码**功能。用户在阅读时:
|
|
|
|
|
+
|
|
|
|
|
+1. **长按**二维码图片(按住约 1 秒)
|
|
|
|
|
+2. 系统自动弹出菜单
|
|
|
|
|
+3. 选择"**识别图中二维码**"
|
|
|
|
|
+4. 即可跳转到对应的页面或小程序
|
|
|
|
|
+
|
|
|
|
|
+### 技术实现
|
|
|
|
|
+
|
|
|
|
|
+在 WXML 中添加了 `show-menu-by-longpress` 属性:
|
|
|
|
|
+
|
|
|
|
|
+```xml
|
|
|
|
|
+<image
|
|
|
|
|
+ src="{{qrcodeUrl}}"
|
|
|
|
|
+ class="qrcode-image"
|
|
|
|
|
+ mode="aspectFit"
|
|
|
|
|
+ show-menu-by-longpress="{{true}}"
|
|
|
|
|
+></image>
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 属性说明
|
|
|
|
|
+
|
|
|
|
|
+| 属性 | 值 | 说明 |
|
|
|
|
|
+|------|-----|------|
|
|
|
|
|
+| `show-menu-by-longpress` | `{{true}}` | 启用长按识别功能 |
|
|
|
|
|
+
|
|
|
|
|
+### 支持的二维码类型
|
|
|
|
|
+
|
|
|
|
|
+- ✅ **小程序码**:识别后打开对应小程序
|
|
|
|
|
+- ✅ **普通二维码**:识别后显示链接信息
|
|
|
|
|
+- ✅ **微信公众号二维码**:识别后关注公众号
|
|
|
|
|
+- ✅ **企业微信二维码**:识别后添加企业微信
|
|
|
|
|
+
|
|
|
|
|
+### 注意事项
|
|
|
|
|
+
|
|
|
|
|
+#### 1. 二维码图片要求
|
|
|
|
|
+
|
|
|
|
|
+- **格式**:建议使用 PNG 或 JPG 格式
|
|
|
|
|
+- **尺寸**:建议不小于 300x300 像素
|
|
|
|
|
+- **清晰度**:确保二维码清晰可识别
|
|
|
|
|
+- **对比度**:二维码与背景要有足够对比度
|
|
|
|
|
+
|
|
|
|
|
+#### 2. 用户体验建议
|
|
|
|
|
+
|
|
|
|
|
+```xml
|
|
|
|
|
+<!-- 提示文字 -->
|
|
|
|
|
+<text class="qrcode-tip">长按识别二维码</text>
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**最佳实践:**
|
|
|
|
|
+- ✅ 在二维码下方显示"长按识别二维码"提示
|
|
|
|
|
+- ✅ 二维码大小适中(建议宽度 280-320rpx)
|
|
|
|
|
+- ✅ 留有足够的边距,避免误触
|
|
|
|
|
+- ✅ 放置在章节末尾,不影响阅读体验
|
|
|
|
|
+
|
|
|
|
|
+#### 3. 兼容性
|
|
|
|
|
+
|
|
|
|
|
+- ✅ 微信 iOS 版本 7.0.0+
|
|
|
|
|
+- ✅ 微信 Android 版本 7.0.0+
|
|
|
|
|
+- ✅ 所有支持的小程序基础库版本
|
|
|
|
|
+
|
|
|
|
|
+### 效果示例
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+┌─────────────────────────────┐
|
|
|
|
|
+│ │
|
|
|
|
|
+│ [二维码图片] │
|
|
|
|
|
+│ │
|
|
|
|
|
+│ 长按识别二维码 │
|
|
|
|
|
+│ │
|
|
|
|
|
+└─────────────────────────────┘
|
|
|
|
|
+
|
|
|
|
|
+用户操作:
|
|
|
|
|
+1. 手指长按二维码 ────────────→
|
|
|
|
|
+2. 弹出系统菜单 ────────────→
|
|
|
|
|
+3. 选择"识别图中二维码" ───────→
|
|
|
|
|
+4. 跳转到目标页面/小程序 │
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 测试方法
|
|
|
|
|
+
|
|
|
|
|
+1. 在微信开发者工具中打开项目
|
|
|
|
|
+2. 使用真机预览(扫码预览到手机)
|
|
|
|
|
+3. 在阅读器插件中打开书籍
|
|
|
|
|
+4. 滚动到设置了二维码的段落
|
|
|
|
|
+5. 长按图中二维码测试识别功能
|
|
|
|
|
+
|
|
|
|
|
+### 常见问题
|
|
|
|
|
+
|
|
|
|
|
+#### Q: 为什么长按没有反应?
|
|
|
|
|
+
|
|
|
|
|
+**A:** 检查以下几点:
|
|
|
|
|
+1. 确认 `show-menu-by-longpress="{{true}}"` 已添加
|
|
|
|
|
+2. 必须是真机测试,模拟器可能不支持
|
|
|
|
|
+3. 确保按压力度足够,时间够长(约 1 秒)
|
|
|
|
|
+4. 检查微信版本是否支持此功能
|
|
|
|
|
+
|
|
|
|
|
+#### Q: 可以自定义菜单吗?
|
|
|
|
|
+
|
|
|
|
|
+**A:** 不可以。这是微信系统级别的菜单,无法自定义。用户长按后会看到微信默认的操作菜单。
|
|
|
|
|
+
|
|
|
|
|
+#### Q: 如何统计扫码数据?
|
|
|
|
|
+
|
|
|
|
|
+**A:** 可以在二维码 URL 中添加追踪参数:
|
|
|
|
|
+
|
|
|
|
|
+```javascript
|
|
|
|
|
+novelManager.setParagraphBlock({
|
|
|
|
|
+ chapterIndex: 0,
|
|
|
|
|
+ paragraphIndex: 5,
|
|
|
|
|
+ ext: JSON.stringify({
|
|
|
|
|
+ qrcodeUrl: 'https://example.com/qrcode?source=novel&chapter=0&bookId=xxx'
|
|
|
|
|
+ })
|
|
|
|
|
+})
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+然后在后台统计这些参数的访问数据。
|
|
|
|
|
+
|
|
|
|
|
+### 完整代码示例
|
|
|
|
|
+
|
|
|
|
|
+```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.setParagraphBlock({
|
|
|
|
|
+ chapterIndex: 0,
|
|
|
|
|
+ paragraphIndex: 10,
|
|
|
|
|
+ ext: JSON.stringify({
|
|
|
|
|
+ qrcodeUrl: 'https://your-domain.com/qrcode.jpg'
|
|
|
|
|
+ // 这个二维码会被自动设置为可长按识别
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+```xml
|
|
|
|
|
+<!-- components/novelPlugin/customParagraph/index.wxml -->
|
|
|
|
|
+<view class="custom-paragraph-container">
|
|
|
|
|
+ <view class="qrcode-box" wx:if="{{qrcodeUrl}}">
|
|
|
|
|
+ <image
|
|
|
|
|
+ src="{{qrcodeUrl}}"
|
|
|
|
|
+ class="qrcode-image"
|
|
|
|
|
+ mode="aspectFit"
|
|
|
|
|
+ show-menu-by-longpress="{{true}}" <!-- 关键属性 -->
|
|
|
|
|
+ ></image>
|
|
|
|
|
+ <text class="qrcode-tip">长按识别二维码</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+</view>
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+现在你的二维码已经支持**长按识别**功能了!用户只需长按二维码即可直接识别,无需截图或其他操作。🎉
|
|
|
|
|
+
|
|
|
|
|
+## 使用方法
|
|
|
|
|
+
|
|
|
|
|
+### 1. 在 app.tsx 中设置段落扩展信息
|
|
|
|
|
+
|
|
|
|
|
+使用 `NovelManager.setParagraphBlock()` 方法时,通过 `ext` 参数传递二维码 URL:
|
|
|
|
|
+
|
|
|
|
|
+```javascript
|
|
|
|
|
+// app.js 或 app.tsx
|
|
|
|
|
+const novelPlugin = requirePlugin('novel-plugin')
|
|
|
|
|
+
|
|
|
|
|
+App({
|
|
|
|
|
+ onLaunch() {
|
|
|
|
|
+ novelPlugin.onPageLoad(this.onNovelPluginLoad)
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ onNovelPluginLoad(data) {
|
|
|
|
|
+ const novelManager = novelPlugin.getNovelManager(data.id)
|
|
|
|
|
+
|
|
|
|
|
+ // 设置自定义段落块,通过 ext 传递二维码 URL
|
|
|
|
|
+ novelManager.setParagraphBlock({
|
|
|
|
|
+ chapterIndex: 0, // 章节下标
|
|
|
|
|
+ paragraphIndex: 5, // 段落下标(在第 6 段后显示)
|
|
|
|
|
+ ext: JSON.stringify({
|
|
|
|
|
+ qrcodeUrl: 'https://your-domain.com/your-qrcode.jpg'
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 或者直接传递 URL 字符串
|
|
|
|
|
+ novelManager.setParagraphBlock({
|
|
|
|
|
+ chapterIndex: 0,
|
|
|
|
|
+ paragraphIndex: 5,
|
|
|
|
|
+ ext: 'https://your-domain.com/your-qrcode.jpg'
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 2. ext 参数支持的格式
|
|
|
|
|
+
|
|
|
|
|
+组件支持以下三种 ext 格式:
|
|
|
|
|
+
|
|
|
|
|
+#### 格式 1:JSON 对象(推荐)
|
|
|
|
|
+```javascript
|
|
|
|
|
+ext: JSON.stringify({
|
|
|
|
|
+ qrcodeUrl: 'https://your-domain.com/qrcode.jpg'
|
|
|
|
|
+})
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 格式 2:JSON 对象(其他字段名)
|
|
|
|
|
+```javascript
|
|
|
|
|
+ext: JSON.stringify({
|
|
|
|
|
+ imageUrl: 'https://your-domain.com/image.jpg'
|
|
|
|
|
+})
|
|
|
|
|
+// 或
|
|
|
|
|
+ext: JSON.stringify({
|
|
|
|
|
+ url: 'https://your-domain.com/link.jpg'
|
|
|
|
|
+})
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 格式 3:直接传递 URL 字符串
|
|
|
|
|
+```javascript
|
|
|
|
|
+ext: 'https://your-domain.com/qrcode.jpg'
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 3. 组件解析逻辑
|
|
|
|
|
+
|
|
|
|
|
+组件会按以下顺序查找二维码 URL:
|
|
|
|
|
+
|
|
|
|
|
+1. 尝试解析 `ext` 为 JSON 对象
|
|
|
|
|
+2. 依次检查字段:`qrcodeUrl` → `imageUrl` → `url`
|
|
|
|
|
+3. 如果解析失败,检查是否以 `http` 开头,是则直接作为 URL 使用
|
|
|
|
|
+4. 如果都没有找到,不显示二维码
|
|
|
|
|
+
|
|
|
|
|
+### 4. 示例代码
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+// 示例 1:在指定章节后显示二维码
|
|
|
|
|
+novelManager.setParagraphBlock({
|
|
|
|
|
+ chapterIndex: 0, // 第一章
|
|
|
|
|
+ paragraphIndex: 10, // 第 11 段后
|
|
|
|
|
+ ext: JSON.stringify({
|
|
|
|
|
+ qrcodeUrl: 'https://example.com/qrcode1.jpg',
|
|
|
|
|
+ customData: '可以传递其他自定义数据'
|
|
|
|
|
+ })
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// 示例 2:在多个位置显示不同的二维码
|
|
|
|
|
+novelManager.setParagraphBlock({
|
|
|
|
|
+ chapterIndex: 0,
|
|
|
|
|
+ paragraphIndex: 5,
|
|
|
|
|
+ ext: JSON.stringify({
|
|
|
|
|
+ qrcodeUrl: 'https://example.com/qrcode-a.jpg'
|
|
|
|
|
+ })
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+novelManager.setParagraphBlock({
|
|
|
|
|
+ chapterIndex: 0,
|
|
|
|
|
+ paragraphIndex: 15,
|
|
|
|
|
+ ext: JSON.stringify({
|
|
|
|
|
+ qrcodeUrl: 'https://example.com/qrcode-b.jpg'
|
|
|
|
|
+ })
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// 示例 3:动态生成二维码 URL
|
|
|
|
|
+const bookId = 'book-123'
|
|
|
|
|
+const qrcodeUrl = `https://example.com/qrcode?bookId=${bookId}&chapter=0`
|
|
|
|
|
+
|
|
|
|
|
+novelManager.setParagraphBlock({
|
|
|
|
|
+ chapterIndex: 0,
|
|
|
|
|
+ paragraphIndex: 8,
|
|
|
|
|
+ ext: JSON.stringify({
|
|
|
|
|
+ qrcodeUrl: qrcodeUrl
|
|
|
|
|
+ })
|
|
|
|
|
+})
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## 自定义修改
|
|
|
|
|
+
|
|
|
|
|
+### 修改二维码图片
|
|
|
|
|
+
|
|
|
|
|
+编辑 `src/components/novelPlugin/customParagraph/index.wxml` 文件中的图片地址:
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+<image
|
|
|
|
|
+ src="你的二维码图片地址"
|
|
|
|
|
+ class="qrcode-image"
|
|
|
|
|
+ mode="aspectFit"
|
|
|
|
|
+></image>
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+当前使用的图片地址:
|
|
|
|
|
+```
|
|
|
|
|
+https://corp-msg.oss-cn-hangzhou.aliyuncs.com/17738993366268346745849EF42B39D9459749C6B2784.jpg
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 修改提示文字
|
|
|
|
|
+
|
|
|
|
|
+编辑 `src/components/novelPlugin/customParagraph/index.wxml` 文件中的文字:
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+<text class="qrcode-tip">扫码了解更多</text>
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 修改样式
|
|
|
|
|
+
|
|
|
|
|
+编辑 `src/components/novelPlugin/customParagraph/index.wxss` 文件来自定义样式:
|
|
|
|
|
+
|
|
|
|
|
+- `.custom-paragraph-container` - 容器样式
|
|
|
|
|
+- `.qrcode-box` - 二维码容器样式
|
|
|
|
|
+- `.qrcode-image` - 二维码图片样式(注意使用 rpx 单位)
|
|
|
|
|
+- `.qrcode-tip` - 提示文字样式
|
|
|
|
|
+
|
|
|
|
|
+### 修改组件逻辑
|
|
|
|
|
+
|
|
|
|
|
+编辑 `src/components/novelPlugin/customParagraph/index.js` 文件来处理组件的生命周期和事件。
|
|
|
|
|
+
|
|
|
|
|
+## 使用说明
|
|
|
|
|
+
|
|
|
|
|
+1. 确保小程序已开通【文娱 - 小说】类目
|
|
|
|
|
+2. 确保已在微信公众平台配置小说阅读器插件权限
|
|
|
|
|
+3. **必须构建项目**:运行 `npm run build:weapp` 将 Taro 代码编译到 dist 目录
|
|
|
|
|
+4. 使用官方阅读器插件打开书籍时,自定义段落会自动显示在每章内容底部
|
|
|
|
|
+5. 无需额外代码调用,插件会自动加载该组件
|
|
|
|
|
+
|
|
|
|
|
+## ⚠️ 注意事项
|
|
|
|
|
+
|
|
|
|
|
+1. **组件格式**:必须使用微信原生格式(.wxml, .wxss, .js, .json),不能使用 Taro/React 格式
|
|
|
|
|
+2. **路径配置**:组件路径必须与 `app.config.ts` 中配置的路径一致
|
|
|
|
|
+3. **构建顺序**:先运行 `npm run build:weapp` 构建,然后在微信开发者工具中预览
|
|
|
|
|
+4. **文件位置**:Taro 会将 src 目录编译到 dist 目录,确保源文件在 src 目录下
|
|
|
|
|
+5. **单位使用**:样式文件中建议使用 rpx 单位以适配不同屏幕
|
|
|
|
|
+6. **组件优化**:确保组件不会被编译工具优化掉(抽象节点可能被误判为未使用)
|
|
|
|
|
+
|
|
|
|
|
+## 🚀 构建和测试步骤
|
|
|
|
|
+
|
|
|
|
|
+### 1. 构建项目
|
|
|
|
|
+
|
|
|
|
|
+由于使用了微信原生格式的组件,需要先构建项目:
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+# 进入项目目录
|
|
|
|
|
+cd /Users/shenwu/Desktop/gs-items/new-taro-mini-book
|
|
|
|
|
+
|
|
|
|
|
+# 构建微信小程序
|
|
|
|
|
+npm run build:weapp
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 2. 验证编译产物
|
|
|
|
|
+
|
|
|
|
|
+构建完成后,检查 `dist` 目录中是否生成了正确的文件:
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+cd dist/components/novelPlugin/customParagraph/
|
|
|
|
|
+# 应该看到以下文件:
|
|
|
|
|
+# - index.wxml
|
|
|
|
|
+# - index.wxss
|
|
|
|
|
+# - index.js
|
|
|
|
|
+# - index.json
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 3. 在微信开发者工具中预览
|
|
|
|
|
+
|
|
|
|
|
+1. 打开微信开发者工具
|
|
|
|
|
+2. 导入项目(如果已导入可跳过)
|
|
|
|
|
+3. 确保项目根目录设置为 `./dist`
|
|
|
|
|
+4. 编译项目(Cmd+B / Ctrl+B)
|
|
|
|
|
+5. 使用阅读器插件打开书籍
|
|
|
|
|
+
|
|
|
|
|
+### 4. 调试
|
|
|
|
|
+
|
|
|
|
|
+打开微信开发者工具的调试器:
|
|
|
|
|
+- 查看控制台输出,应该能看到 "自定义段落组件加载" 的日志
|
|
|
|
|
+- 查看 Wxml 面板,确认组件结构正确
|
|
|
|
|
+- 如果组件未显示,检查路径配置和文件是否存在
|
|
|
|
|
+
|
|
|
|
|
+## ❗ 常见问题
|
|
|
|
|
+
|
|
|
|
|
+### Q: 为什么报错 "Component is not found in path"?
|
|
|
|
|
+
|
|
|
|
|
+**A:** 可能的原因:
|
|
|
|
|
+1. 没有先运行 `npm run build:weapp` 构建项目
|
|
|
|
|
+2. 路径配置错误,确保是 `components/novelPlugin/customParagraph/index`
|
|
|
|
|
+3. 缺少必要的文件(.wxml, .wxss, .js, .json)
|
|
|
|
|
+4. Taro 配置问题,检查 app.config.ts 中的路径
|
|
|
|
|
+
|
|
|
|
|
+### Q: 为什么组件不显示?
|
|
|
|
|
+
|
|
|
|
|
+**A:** 检查以下几点:
|
|
|
|
|
+1. 是否在阅读器插件中使用(普通页面不会显示)
|
|
|
|
|
+2. 章节是否正确配置了状态(免费/付费)
|
|
|
|
|
+3. 图片地址是否可访问
|
|
|
|
|
+4. 样式是否有问题(如高度为 0)
|
|
|
|
|
+
|
|
|
|
|
+### Q: 可以使用 React/Taro语法吗?
|
|
|
|
|
+
|
|
|
|
|
+**A:** **不可以!** 抽象节点组件必须使用微信原生格式:
|
|
|
|
|
+- ❌ 不能使用 JSX/TSX
|
|
|
|
|
+- ❌ 不能使用 React hooks
|
|
|
|
|
+- ❌ 不能使用 Taro 组件
|
|
|
|
|
+- ✅ 只能使用 Component 构造器
|
|
|
|
|
+- ✅ 只能使用 .wxml, .wxss, .js, .json 文件
|
|
|
|
|
+
|
|
|
|
|
+## 为什么使用原生格式?
|
|
|
|
|
+
|
|
|
|
|
+微信官方阅读器插件的抽象节点组件(如 custom-paragraph)有特殊要求:
|
|
|
|
|
+
|
|
|
|
|
+1. **插件机制限制**:阅读器插件在加载抽象节点时,使用的是微信小程序的原生组件系统
|
|
|
|
|
+2. **编译时机**:Taro 的 React组件需要先编译成原生代码,这个过程可能导致路径映射问题
|
|
|
|
|
+3. **类型识别**:插件需要识别标准的 Component 构造器,而不是 React组件
|
|
|
|
|
+4. **官方文档示例**:微信官方文档中的所有示例都使用原生格式
|
|
|
|
|
+
|
|
|
|
|
+## 调试技巧
|
|
|
|
|
+
|
|
|
|
|
+1. 在微信开发者工具中查看编译后的 `dist/components/novelPlugin/customParagraph/` 目录
|
|
|
|
|
+2. 确认以下文件存在:
|
|
|
|
|
+ - `index.wxml`
|
|
|
|
|
+ - `index.wxss`
|
|
|
|
|
+ - `index.js`
|
|
|
|
|
+ - `index.json`
|
|
|
|
|
+3. 在 `index.js` 的 `attached` 生命周期中添加 console.log 来验证组件是否被加载
|
|
|
|
|
+4. 检查微信开发者工具的控制台输出
|
|
|
|
|
+
|
|
|
|
|
+## 📝 完整使用示例
|
|
|
|
|
+
|
|
|
|
|
+### 在 app.tsx 中的完整代码
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+// 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)
|
|
|
|
|
+
|
|
|
|
|
+ // data.id - 阅读器实例 id,每个插件页对应一个阅读器实例
|
|
|
|
|
+ const novelManager = novelPlugin.getNovelManager(data.id)
|
|
|
|
|
+
|
|
|
|
|
+ // 获取自定义参数
|
|
|
|
|
+ let customParams = novelManager.getCustomServerParams();
|
|
|
|
|
+ if (customParams) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ customParams = JSON.parse(decodeURIComponent(customParams));
|
|
|
|
|
+ console.log("customParams", customParams)
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('解析失败', e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 设置目录状态(示例)
|
|
|
|
|
+ novelManager.setContents({
|
|
|
|
|
+ contents: [
|
|
|
|
|
+ {
|
|
|
|
|
+ index: 0, // 第一章
|
|
|
|
|
+ status: 0, // 免费
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ index: 1, // 第二章
|
|
|
|
|
+ status: 2, // 未解锁
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ index: 2, // 第三章
|
|
|
|
|
+ status: 1, // 已解锁
|
|
|
|
|
+ },
|
|
|
|
|
+ ],
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 设置自定义段落二维码 ==========
|
|
|
|
|
+
|
|
|
|
|
+ // 示例 1:在第一章第 5 段后显示二维码
|
|
|
|
|
+ novelManager.setParagraphBlock({
|
|
|
|
|
+ chapterIndex: 0, // 第一章
|
|
|
|
|
+ paragraphIndex: 5, // 第 6 段后
|
|
|
|
|
+ ext: JSON.stringify({
|
|
|
|
|
+ qrcodeUrl: 'https://corp-msg.oss-cn-hangzhou.aliyuncs.com/17738993366268346745849EF42B39D9459749C6B2784.jpg'
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 示例 2:在第一章第 10 段后显示另一个二维码
|
|
|
|
|
+ novelManager.setParagraphBlock({
|
|
|
|
|
+ chapterIndex: 0,
|
|
|
|
|
+ paragraphIndex: 10,
|
|
|
|
|
+ ext: JSON.stringify({
|
|
|
|
|
+ qrcodeUrl: 'https://example.com/another-qrcode.jpg',
|
|
|
|
|
+ // 可以传递其他自定义数据
|
|
|
|
|
+ customData: {
|
|
|
|
|
+ campaignId: 'activity-001',
|
|
|
|
|
+ source: 'chapter-1'
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 示例 3:在多个章节都显示二维码
|
|
|
|
|
+ for (let chapterIndex = 0; chapterIndex < 10; chapterIndex++) {
|
|
|
|
|
+ novelManager.setParagraphBlock({
|
|
|
|
|
+ chapterIndex: chapterIndex,
|
|
|
|
|
+ paragraphIndex: 8, // 每章的第 9 段后
|
|
|
|
|
+ ext: JSON.stringify({
|
|
|
|
|
+ qrcodeUrl: `https://example.com/qrcode?chapter=${chapterIndex}`
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 示例 4:动态生成带参数的二维码
|
|
|
|
|
+ const bookId = customParams?.bookId || ''
|
|
|
|
|
+ const qrcodeUrl = `https://your-domain.com/qrcode?bookId=${bookId}&source=novel`
|
|
|
|
|
+
|
|
|
|
|
+ novelManager.setParagraphBlock({
|
|
|
|
|
+ chapterIndex: 0,
|
|
|
|
|
+ paragraphIndex: 3,
|
|
|
|
|
+ ext: JSON.stringify({
|
|
|
|
|
+ qrcodeUrl: qrcodeUrl
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 监听用户行为事件
|
|
|
|
|
+ novelManager.onUserTriggerEvent(res => {
|
|
|
|
|
+ console.log('onUserTriggerEvent', res.event_id, res)
|
|
|
|
|
+
|
|
|
|
|
+ // 可以在这里处理各种事件
|
|
|
|
|
+ switch (res.event_id) {
|
|
|
|
|
+ case "start_read":
|
|
|
|
|
+ console.log("开始阅读")
|
|
|
|
|
+ break
|
|
|
|
|
+ case "change_chapter":
|
|
|
|
|
+ console.log("切换章节", res)
|
|
|
|
|
+ // 可以在这里根据新章节动态设置段落块
|
|
|
|
|
+ this.updateDynamicParagraphBlock(novelManager, res.chapter_index)
|
|
|
|
|
+ break
|
|
|
|
|
+ // ... 其他事件
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 动态更新段落块的方法
|
|
|
|
|
+ updateDynamicParagraphBlock(novelManager, chapterIndex) {
|
|
|
|
|
+ // 清除旧的段落块(如果需要)
|
|
|
|
|
+ // novelManager.clearParagraphBlocks()
|
|
|
|
|
+
|
|
|
|
|
+ // 设置新的段落块
|
|
|
|
|
+ novelManager.setParagraphBlock({
|
|
|
|
|
+ chapterIndex: chapterIndex,
|
|
|
|
|
+ paragraphIndex: 5,
|
|
|
|
|
+ ext: JSON.stringify({
|
|
|
|
|
+ qrcodeUrl: `https://example.com/dynamic-qrcode?chapter=${chapterIndex}`
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 常见使用场景
|
|
|
|
|
+
|
|
|
|
|
+#### 场景 1:每章固定位置显示统一二维码
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+// 在每章的第 10 段后显示相同的二维码
|
|
|
|
|
+for (let i = 0; i < totalChapters; i++) {
|
|
|
|
|
+ novelManager.setParagraphBlock({
|
|
|
|
|
+ chapterIndex: i,
|
|
|
|
|
+ paragraphIndex: 10,
|
|
|
|
|
+ ext: JSON.stringify({
|
|
|
|
|
+ qrcodeUrl: 'https://your-domain.com/fixed-qrcode.jpg'
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 场景 2:根据章节动态显示不同二维码
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+// 根据章节内容显示不同的推广二维码
|
|
|
|
|
+function getQrcodeForChapter(chapterIndex) {
|
|
|
|
|
+ if (chapterIndex < 5) {
|
|
|
|
|
+ return 'https://example.com/qrcode-early.jpg'
|
|
|
|
|
+ } else if (chapterIndex < 20) {
|
|
|
|
|
+ return 'https://example.com/qrcode-middle.jpg'
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return 'https://example.com/qrcode-late.jpg'
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+for (let i = 0; i < totalChapters; i++) {
|
|
|
|
|
+ novelManager.setParagraphBlock({
|
|
|
|
|
+ chapterIndex: i,
|
|
|
|
|
+ paragraphIndex: 8,
|
|
|
|
|
+ ext: JSON.stringify({
|
|
|
|
|
+ qrcodeUrl: getQrcodeForChapter(i)
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 场景 3:只在特定章节显示
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+// 只在关键章节显示二维码
|
|
|
|
|
+const specialChapters = [0, 5, 10, 20] // 第 1、6、11、21 章
|
|
|
|
|
+
|
|
|
|
|
+specialChapters.forEach(chapterIndex => {
|
|
|
|
|
+ novelManager.setParagraphBlock({
|
|
|
|
|
+ chapterIndex: chapterIndex,
|
|
|
|
|
+ paragraphIndex: 5,
|
|
|
|
|
+ ext: JSON.stringify({
|
|
|
|
|
+ qrcodeUrl: `https://example.com/special-qrcode-${chapterIndex}.jpg`
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+})
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 场景 4:A/B 测试不同的二维码
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+// 随机显示 A 或 B 版本的二维码
|
|
|
|
|
+const useVersionA = Math.random() < 0.5
|
|
|
|
|
+
|
|
|
|
|
+novelManager.setParagraphBlock({
|
|
|
|
|
+ chapterIndex: 0,
|
|
|
|
|
+ paragraphIndex: 10,
|
|
|
|
|
+ ext: JSON.stringify({
|
|
|
|
|
+ qrcodeUrl: useVersionA
|
|
|
|
|
+ ? 'https://example.com/qrcode-a.jpg'
|
|
|
|
|
+ : 'https://example.com/qrcode-b.jpg',
|
|
|
|
|
+ abTest: useVersionA ? 'A' : 'B'
|
|
|
|
|
+ })
|
|
|
|
|
+})
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## 相关文档
|
|
|
|
|
+
|
|
|
|
|
+- [微信官方阅读器插件文档](https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/novel.html)
|
|
|
|
|
+- [自定义段落](https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/industry/novel.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E6%AE%B5%E8%90%BD)
|
|
|
|
|
+- [微信小程序自定义组件](https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/)
|