抖音小说阅读器插件依赖于行业SDK权限
,因此一定要先在代码配置行业SDK权限。如果尚未配置过,可参考uni-app(Vue2)编译为抖音小程序时,自动生成package.json文件配置行业SDK权限。
小程序服务类目为「文娱-文娱-在线阅读」无需申请,会自动开通小说资源内容库接入依赖的接口权限。
能力申请
自定义页面结构
申请入口:抖音开放平台-控制台-能力-互动能力-容器界面-自定义页面结构
阅读器有自定义导航栏,需要开启【自定义页面结构】后在页面style
中设置"navigationStyle": "custom"
支持沉浸式阅读,如果暂时不满足能力开通条件,可以使用"transparentTitle": "always"
设置导航栏透明来临时避免双重导航栏UI情况。
添加到桌面
申请入口:抖音开放平台-控制台-能力-互动能力-系统能力-添加到桌面
如果要在阅读器中展示【加桌按钮】,需要提前申请开通此能力。
阅读器页面
阅读器接入的硬性规定:阅读器页面路径必须是/pages/novel_plugin/index
。创建页面后在pages.json
绑定行业SDK,配置其他可选参数,示例如下:
// pages.json
// #ifdef MP-TOUTIAO
{
"path": "pages/novel_plugin/index",
"style": {
"navigationBarTitleText": "...",
"navigationStyle": "custom",
// "transparentTitle": "always",
"mp-toutiao": {
"extends": "ext://industry/novel",
"isPageExtension": true,
"pluginConfig": {
"addBookshelf": true,
"addDesktopIcon": true,
"preventScreenRecorder": "never",
"pageTurnMode": "horizontalSlide",
"refreshLockChapter": true
},
"usingComponents": {
"payModal": "/components/novel-modal/pay"
}
}
}
},
// #endif
页面样式
阅读器自带了自定义导航栏,因此需要开通【自定义页面结构】能力,配置"navigationStyle": "custom"
自定义导航栏来支持沉浸式阅读,避免双重导航栏UI情况。
如果小程序暂不满足能力申请条件,临时解决方案:配置"navigationStyle": "default", "transparentTitle": "always",
设置导航栏透明也可以【注意:IDE显示可能会有错乱】。
绑定行业能力
绑定小说行业能力是固定写法"extends": "ext://industry/novel", "isPageExtension": true
,没有什么转圜余地。
小说插件配置项
pluginConfig
对应插件配置项。
配置
"addBookshelf": true
展示加入书架按钮,boolean
类型,默认值true
配置
"addDesktopIcon": true
展示加桌按钮,boolean
类型,默认值false
配置
"preventScreenRecorder": "never"
是否防录屏,iOS 3160
版本以上生效,枚举值always|never
,默认值never
配置
"pageTurnMode": "horizontalSlide"
新用户首次进入阅读器时默认翻页模式为左右滑动翻页,枚举值verticalScroll|horizontalSlide
对应上下滑动翻页|左右滑动翻页
,默认值verticalScroll
配置
"refreshLockChapter": true
解锁状态变更时强制刷新章节内容,boolean
类型,默认值false
注意事项
加桌按钮展示
addDesktopIcon
依赖于互动能力能力,开通成功后才可以正常使用。申请入口:抖音开放平台-控制台-互动能力-系统能力-添加到桌面。章节内容刷新
refreshLockChapter
,官方说法如下:
阅读器出于性能考量,传入章节文本内容后会做缓存,解锁状态变更时,不会重新拉取文本内容;因此增加一个强制刷新开关,开启章节强制刷新后,当章节解锁状态发生变更,即从未解锁变为解锁状态,会重新调用queryChapterInfo查询章节内容,即使用后一次查询的章节文本内容,覆盖之前传入的章节文本,并重新渲染;
页面插槽组件的引用
usingComponents
对应页面插槽组件的定义。
截止目前(2025-03-20)官方只支持两种组件:自定义支付弹窗组件payModal
和自定义挂件点击拉起的自定义面板组件floatModal
。
由于uniapp编译后组件名都是采用的连接线格式(这里组件名是固定的驼峰格式),所以我们需要在这里把页面中引入过的或者easycom自动引入的组件再重新引入一下,否则目前调用组件会显示空白的,也就是阅读器页面找不到组件。
阅读器实现
页面完整代码可以参考官方文档接入示例代码,示例会有更新,一定要记得去官方文档查看最新示例代码。
和常规uniapp的Vue2页面代码规范稍有不同,就是把data
、methods
和页面生命周期函数的定义都放在一个对象变量NovelExtensionOption
内部,然后传递给tt.NovelExtension(NovelExtensionOption)
,大致形式如下:
import payModal from "@/components/novel-modal/pay.vue"
const {
NovelExtension,
getNovelManager
} = tt;
let novelManage = null;
const NovelExtensionOption = {
data() {
return {}
},
onLoad(options) {
novelManage = getNovelManager()
novelManage.onNovelError((e) => {
//...
})
novelManage.onClickUnlockPrimaryButton((res) => {
console.log('解锁按钮1触发', res)
})
uni.$on('payModalEvent', this.handlePayModalEvent)
},
onReady(){},
onShow(){},
onHide(){},
onUnload(){
uni.$off('payModalEvent', this.handlePayModalEvent)
},
methods: {
queryContentsInfo(params) {
const { bookId, page, pageSize} = params;
return {
chapterList: [
{"chapterId": "1", "status": 0}
]
}
},
queryLockModalText(chapterId) {
return {
title: "弹窗标题",
primary: "弹窗第一个按钮内容"
}
},
// 其他使用的方法函数
handlePayModalEvent(params) {},
//...
}
}
NovelExtension(NovelExtensionOption)
export default {
...NovelExtensionOption,
components: {
payModal
}
}
支付弹窗组件
组件的实现没什么区别,主要有两点需要注意。
参数接收
在props
中定义三个参数bookId
、chapterId
和params
,其中bookId
和chapterId
是阅读器页面固定传入的抖音小说资源库对应的ID,params
是在监听支付弹窗点击时回调函数showPayModal
传递的对象参数。
props: {
bookId: "",
chapterId: "",
params: {
type: Object,
default(){
return {}
}
},
}
事件传递
正常组件调用时我们可以定义事件后在组件内通过this.$emit('change', params)
传递给页面,但是抖音阅读器页面是不支持这种形式的。我们只能通过全局事件的传递uni.$emit('change', params)
触发在页面或者其他地方监听的uni.on('change', (params) => {})
接收。
需要注意的是,我们的页面的onLoad
周期中进行监听,最好就在onUnload
周期中取消监听。
uni.$emit('payModalEvent', {
type: 'account'
})
自定义支付弹窗
自定义支付弹窗的一个实战逻辑:
1、在methods
中实现queryLockModalText
函数返回弹窗文案
2、阅读器页面在锁定章节弹出解锁组件
3、用户点击解锁组件按钮,触发onClickUnlockPrimaryButton|onClickUnlockSecondaryButton
事件监听,根据我们实现queryLockModalText
函数的逻辑判断展示广告还是支付弹窗
4、如果是支付弹窗,我们就可以获取onClickUnlockPrimaryButton(res)|onClickUnlockSecondaryButton(res)
事件回调的res
参数中的showPayModal
和closePayModal
函数
5、使用$novel.setClosePayModal(closePayModal)
把closePayModal
函数存到全局,等待我们之后在其他地方调用它关闭支付弹窗。
6、使用showPayModal(params)
函数传递参数,唤起支付弹窗组件后可以在组件内接收。
7、在支付弹窗组件内,用户触发关闭按钮事件时,使用$novel.callClosePayModal()
关闭组件。用户触发购买或付费完成事件时,使用uni.emit('payModalEvent', params)
通信父页面(阅读器),在uni.on('payModalEvent', this.handlePayModalEvent)
监听事件中接收参数进行业务处理,最后再使用$novel.callClosePayModal()
关闭弹窗组件。
示例代码:
在公用js文件中定义closePayModal
的存取函数
// novel.js
let closePayModal;
function setClosePayModal(fn) {
closePayModal = fn
}
function callClosePayModal() {
if(typeof closePayModal === 'function') {
closePayModal()
}
}
在阅读器页面中使用
// pages/novel-plugin/index
novelManage.onClickUnlockPrimaryButton((res) => {
console.log('解锁按钮1触发', res)
novelManage.onClickUnlockPrimaryButton((res) => {
console.log('解锁按钮1触发', res)
const {
showPayModal,
closePayModal
} = res
showPayModal({
test: '解锁按钮1触发',
bookInfo
})
$novel.setClosePayModal(closePayModal)
})
})
// 充值等事件完成触发关闭支付弹窗
$novel.callClosePayModal()
在支付弹窗组件中触发关闭事件时使用
$novel.callClosePayModal()
常见问题
1、未解锁章节免费字数
免费字数是由服务端调用小说资源接入接口时更新控制的,小程序内部无法通过传入参数来设置。
2、小说等级的作用
小说资源接入时grade
等级枚举值只有A|B
,A级
需要把小说所有章节内容都接入(至少1章)才能上架,强制只有A级
接入的小说才可以通过巨量引擎进行投放。
3、阅读器内目录信息的展示
阅读器内目录信息的展示(包含且不限于章节数、章节标题、字数)都是由服务端接入小说资源库创建更新的。小程序内调用queryContentsInfo
查询更新章节解锁信息时,只有章节IDchapterId
和解锁状态status
字段是有意义的。
4、完成内容库接入
完成内容库的接口对接,且小程序内上传/被授权的小说内容的dau占比该小程序总dau的80%以上,视为该小程序“完成内容库接入”。
接入完成的标准是根据改造后的阅读器path为pages/novel_plugin/index
,统计小程序的dau占比该小程序总dau的80%以上,视为该小程序”完成内容库接入”。也就是统计投放入口路径,pages/novel_plugin/index
路径/投放入口路径总数大于80%代表接入完成。
写在最后
相关文章: uni-app(Vue2)编译为抖音小程序时,自动生成package.json文件配置行业SDK权限
参考文档: 抖音小程序小说阅读器开发者文档
参考文档: 抖音小程序小说资源接入文档V2