
使用uni-app实现小程序动态底部TabBar
由于微信小程序原生并不支持动态的底部TabBar,但是我们有些场景需要这个功能。那么可以采用变通的方式实现。
1、首先需要知道uniapp或者说微信小程序有什么样的限制
性能问题,在uni-app的vue版本上,自定义tabbar让您不得不在一个webview内模拟出多个页面,这存在严重的性能问题
要使用原生tabbar,在pages.json文件里面必须有tabBar节点,节点里面的pagePath必须在pages节点里面存在,并且路径上不能有参数。
2、针对这些限制做什么样的变通
这里我使用了uView里面的u-tabbar组件。
在pages
节点里面增加了5个页面
"pages": [{
"path": "pages/feitui/tab1/tab1",
"style": {
"enablePullDownRefresh": true
}
},{
"path": "pages/feitui/tab2/tab2",
"style": {
"enablePullDownRefresh": true
}
},
{
"path": "pages/feitui/tab3/tab3",
"style": {
"enablePullDownRefresh": true
}
},
{
"path": "pages/feitui/tab4/tab4",
"style": {
"enablePullDownRefresh": true
}
},
{
"path": "pages/feitui/tab5/tab5",
"style": {
"enablePullDownRefresh": true
}
}]
tabBar
节点里面把这5个路径都加进去,如下:
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#3cc51f",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "pages/feitui/tab1/tab1",
"text": "tab1"
}, {
"pagePath": "pages/feitui/tab2/tab2",
"text": "tab2"
}, {
"pagePath": "pages/feitui/tab3/tab3",
"text": "tab3"
}, {
"pagePath": "pages/feitui/tab4/tab4",
"text": "tab4"
}, {
"pagePath": "pages/feitui/tab5/tab5",
"text": "tab5"
}]
}
tab1--tab5这5个页面里都引用content.vue组件,在Content组件里面引用<u-tabbar>
组件,并动态的从后端获取tabBar的list数据,通过参数控制是否显示tabbar,显示哪些tabItem 通过tab1--tab5插入不同的tab标识,获取每个tab需要呈现的页面内容。具体代码可参考 飞腿编辑器前端项目模板
3、最终实现效果
通过后台的在线编辑TabBar,前端小程序动态渲染。
前端小程序效果
由于微信小程序原生并不支持动态的底部TabBar,但是我们有些场景需要这个功能。那么可以采用变通的方式实现。
1、首先需要知道uniapp或者说微信小程序有什么样的限制
性能问题,在uni-app的vue版本上,自定义tabbar让您不得不在一个webview内模拟出多个页面,这存在严重的性能问题
要使用原生tabbar,在pages.json文件里面必须有tabBar节点,节点里面的pagePath必须在pages节点里面存在,并且路径上不能有参数。
2、针对这些限制做什么样的变通
这里我使用了uView里面的u-tabbar组件。
在pages
节点里面增加了5个页面
"pages": [{
"path": "pages/feitui/tab1/tab1",
"style": {
"enablePullDownRefresh": true
}
},{
"path": "pages/feitui/tab2/tab2",
"style": {
"enablePullDownRefresh": true
}
},
{
"path": "pages/feitui/tab3/tab3",
"style": {
"enablePullDownRefresh": true
}
},
{
"path": "pages/feitui/tab4/tab4",
"style": {
"enablePullDownRefresh": true
}
},
{
"path": "pages/feitui/tab5/tab5",
"style": {
"enablePullDownRefresh": true
}
}]
tabBar
节点里面把这5个路径都加进去,如下:
"tabBar": {
"color": "#7A7E83",
"selectedColor": "#3cc51f",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "pages/feitui/tab1/tab1",
"text": "tab1"
}, {
"pagePath": "pages/feitui/tab2/tab2",
"text": "tab2"
}, {
"pagePath": "pages/feitui/tab3/tab3",
"text": "tab3"
}, {
"pagePath": "pages/feitui/tab4/tab4",
"text": "tab4"
}, {
"pagePath": "pages/feitui/tab5/tab5",
"text": "tab5"
}]
}
tab1--tab5这5个页面里都引用content.vue组件,在Content组件里面引用<u-tabbar>
组件,并动态的从后端获取tabBar的list数据,通过参数控制是否显示tabbar,显示哪些tabItem 通过tab1--tab5插入不同的tab标识,获取每个tab需要呈现的页面内容。具体代码可参考 飞腿编辑器前端项目模板
3、最终实现效果
通过后台的在线编辑TabBar,前端小程序动态渲染。
前端小程序效果

iOS14.5之后,请务必配置NSUserTrackingUsageDescription隐私描述, 否则会出现app闪退,导致审核被拒。
今天本人提交了一款 ios 应用,审核几分钟就被拒绝了,在开发者后台看了下原因,说是app在ios 14以上的版本崩溃了。于是打了一个自定义基座,分别用ios 12的系统和ios 14的系统测试,果然ios14的闪退,ios12 正常。通过查看苹果附上的崩溃日志,发现一行关键字:Termination Reason: TCC, This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSUserTrackingUsageDescription key with a string value explaining to the user how the app uses this data. 其中有一个关键词"NSUserTrackingUsageDescription ",正是最近官方提到的这个问题,原链接:https://ask.dcloud.net.cn/article/36107,于是按照官方的提示,配置了NSUserTrackingUsageDescription隐私描述,再打自定义基座测试,果然正常了。 于是写下了此文分享给大家。
今天本人提交了一款 ios 应用,审核几分钟就被拒绝了,在开发者后台看了下原因,说是app在ios 14以上的版本崩溃了。于是打了一个自定义基座,分别用ios 12的系统和ios 14的系统测试,果然ios14的闪退,ios12 正常。通过查看苹果附上的崩溃日志,发现一行关键字:Termination Reason: TCC, This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSUserTrackingUsageDescription key with a string value explaining to the user how the app uses this data. 其中有一个关键词"NSUserTrackingUsageDescription ",正是最近官方提到的这个问题,原链接:https://ask.dcloud.net.cn/article/36107,于是按照官方的提示,配置了NSUserTrackingUsageDescription隐私描述,再打自定义基座测试,果然正常了。 于是写下了此文分享给大家。
收起阅读 »
uniapp全局改变字体大小
动态改变字体大小的页面需要添加page-meta标签 <page-meta :root-font-size="size"></page-meta>
setting.json 设置rem与px的转换比例 设置1rem=10px 那么设计稿24px 就是2.4rem,
动态设置root-font-size根元素字体大小 size 默认10px(与750设计稿一比一还原)
获取配置 存入缓存或者全局变量,页面加载时需要获取全局变量或者缓存给组件赋值。
动态改变字体大小的页面需要添加page-meta标签 <page-meta :root-font-size="size"></page-meta>
setting.json 设置rem与px的转换比例 设置1rem=10px 那么设计稿24px 就是2.4rem,
动态设置root-font-size根元素字体大小 size 默认10px(与750设计稿一比一还原)
获取配置 存入缓存或者全局变量,页面加载时需要获取全局变量或者缓存给组件赋值。
收起阅读 »
类似BOSS直聘APP的项目外包
急需开发一套类似BOSS直聘APP的项目,采用uniapp,需要集成第三方即时通讯IM,必须要完全兼容android和IOS,有能力技术过硬的个人或团队可合作,签合同,广州地区,最好能有一段时间进驻公司对接,面对面开发。有兴趣加微信:dongtao-z,邮箱:mp5a6mg@163.com
急需开发一套类似BOSS直聘APP的项目,采用uniapp,需要集成第三方即时通讯IM,必须要完全兼容android和IOS,有能力技术过硬的个人或团队可合作,签合同,广州地区,最好能有一段时间进驻公司对接,面对面开发。有兴趣加微信:dongtao-z,邮箱:mp5a6mg@163.com
收起阅读 »
音视频 uni 插件集成步骤
快速跑通 Demo
开始之前请先下载 HBuilderX 并登录,并完成 uni 开发者相关认证等。
步骤
-
浏览器打开插件主页
-
点击右侧 使用 HBuilderX 导入示例项目(你也可以选择下载 ZIP 文件)

- 根据提示操作,来到 HBuilderX 界面,点击创建
- 创建完成过回到 HBuilderX ,打开 mainfest.json ,点击基础配置,将可以看到 系统自动获取的 AppId。
- 接着,回到 插件主页,点击购买插件。
按步骤操作,输入应用包名。
- 回到 HBuilderX ,点击 App 原生插件配置,选择云端插件,选择刚刚购买的插件。
- 制作自定义基座,如果你会原生开发,可以生成本地 App 开发资源,到原生环境去打包。不会的话,请选择 uni 的云打包。
步骤:
- 运行
- 运行到手机或模拟器
- 制作自定义基台
注意:云打包的时候,如果你的 uni 开发者帐号未完成社区身份验证,则会出现下图内容
只需根据它的提示完成验证即可。 验证完成按上面步骤 7 重新打包即可。
打包成功后,控制台会输出
此时自定义基座,已经成功生成,在输出的目录找到 apk ,安装在手机上即可。注意了,这里要安装基座才行。
-
有了自定义基座,我们就可有开始运行这个项目了,但在这之前,我们得先选择自定义基座,再配置 anyRTC 的开发者信息。
-
选择自定义基座
- 配置开发者信息
如果你还未注册 anyRTC 开发者帐号,请前往 anyRTC 官网注册,并创建一个 App。并复制 AppId 填入下方文件中。
AppId:anyRTC 生成的应用ID
channel:频道,多台设备进入同一个频道即可通信
uid:在频道内的个人id
uid 和 channel 可不填写,代码里已生成
- 运行。
常见问题
-
joinChannl of undefined
一般都是插件未引入成功,请仔细检查是否有配置原生插件以及选择自定义基座。 -
一直转圈圈正在加入?
这是因为没加入频道成功。如果插件配置好了,并且已经注册获取 anyRTC AppId,请检查 AppId 是否填写正确
快速跑通 Demo
开始之前请先下载 HBuilderX 并登录,并完成 uni 开发者相关认证等。
步骤
-
浏览器打开插件主页
-
点击右侧 使用 HBuilderX 导入示例项目(你也可以选择下载 ZIP 文件)
- 根据提示操作,来到 HBuilderX 界面,点击创建
- 创建完成过回到 HBuilderX ,打开 mainfest.json ,点击基础配置,将可以看到 系统自动获取的 AppId。
- 接着,回到 插件主页,点击购买插件。
按步骤操作,输入应用包名。
- 回到 HBuilderX ,点击 App 原生插件配置,选择云端插件,选择刚刚购买的插件。
- 制作自定义基座,如果你会原生开发,可以生成本地 App 开发资源,到原生环境去打包。不会的话,请选择 uni 的云打包。
步骤:
- 运行
- 运行到手机或模拟器
- 制作自定义基台
注意:云打包的时候,如果你的 uni 开发者帐号未完成社区身份验证,则会出现下图内容
只需根据它的提示完成验证即可。 验证完成按上面步骤 7 重新打包即可。
打包成功后,控制台会输出
此时自定义基座,已经成功生成,在输出的目录找到 apk ,安装在手机上即可。注意了,这里要安装基座才行。
-
有了自定义基座,我们就可有开始运行这个项目了,但在这之前,我们得先选择自定义基座,再配置 anyRTC 的开发者信息。
-
选择自定义基座
- 配置开发者信息
如果你还未注册 anyRTC 开发者帐号,请前往 anyRTC 官网注册,并创建一个 App。并复制 AppId 填入下方文件中。
AppId:anyRTC 生成的应用ID
channel:频道,多台设备进入同一个频道即可通信
uid:在频道内的个人id
uid 和 channel 可不填写,代码里已生成
- 运行。
常见问题
-
joinChannl of undefined
一般都是插件未引入成功,请仔细检查是否有配置原生插件以及选择自定义基座。 -
一直转圈圈正在加入?
这是因为没加入频道成功。如果插件配置好了,并且已经注册获取 anyRTC AppId,请检查 AppId 是否填写正确

uni-app rtc插件集成指南及常见问题--iOS
一、如何跑通示例demo?
1、前往插件市场下载示例项目anyRTC音视频SDK插件
2、若手动下载示例zip,解压后在mainfest.json中获取DCloud_APPID。通过HBuilderX导入无需获取;
3、前往插件市场anyRTC音视频SDK插件,点击购买(0元)for云打包,选择mainfest.json中获取到的DCloud_APPID,如果没有找到,请刷新界面,点击下一步;
4、填写iOS包名,可在HBuilderX菜单栏中,选择"运行"->“运行到手机或模拟器”->"制作自定义基座"找到包名,请确保填入的包名与打包时的包名一致,点击下一步;
5、绑定云打包成功
6、在mainfest.json中"APP原生插件配置"中,点击"选择云端插件",勾选anyRTC音视频SDK插件,点击"确定"
7、前往anyRTC官网注册账号,创建应用,获取应用的APPID,在index.nvue中,填入获取到的APPID,请注意保存更改内容。
8、在HBuilderX菜单栏中,选择"运行"->“运行到手机或模拟器”->"制作自定义基座"
9、勾选iOS(ipa包),选择证书profile文件、私钥证书,请确保当前证书可用,证书问题,可点击页面上如何申请证书,点击打包
10、当出现如图所示日志时,则表示打自定义调试基座包成功
11、在HBuilderX菜单栏中,选择"运行"->“运行到手机或模拟器”->“运行基座选择”->"自定义调试基座(iOS)"
12、点击运行真机即可跑通项目
二、常见问题
【1】HBuilderX 官方常见问题:
【2】出现如图所示错误
解决方法:请使用真机进行调试、确保已勾选自定义打包基座、请检查证书是否有效、确保mainfest.json中App原生插件配置已勾选(可删除后重新选择)、删除手机App清除缓存
【3】加入房间一直转圈无法加入
解决方法:请确保index.nvue中appid已填写,填写正确且已保存。
【4】加入房间没有画面或声音
解决方法:请检查当前是否打开相机权限or麦克风权限
一、如何跑通示例demo?
1、前往插件市场下载示例项目anyRTC音视频SDK插件
2、若手动下载示例zip,解压后在mainfest.json中获取DCloud_APPID。通过HBuilderX导入无需获取;
3、前往插件市场anyRTC音视频SDK插件,点击购买(0元)for云打包,选择mainfest.json中获取到的DCloud_APPID,如果没有找到,请刷新界面,点击下一步;
4、填写iOS包名,可在HBuilderX菜单栏中,选择"运行"->“运行到手机或模拟器”->"制作自定义基座"找到包名,请确保填入的包名与打包时的包名一致,点击下一步;
5、绑定云打包成功
6、在mainfest.json中"APP原生插件配置"中,点击"选择云端插件",勾选anyRTC音视频SDK插件,点击"确定"
7、前往anyRTC官网注册账号,创建应用,获取应用的APPID,在index.nvue中,填入获取到的APPID,请注意保存更改内容。
8、在HBuilderX菜单栏中,选择"运行"->“运行到手机或模拟器”->"制作自定义基座"
9、勾选iOS(ipa包),选择证书profile文件、私钥证书,请确保当前证书可用,证书问题,可点击页面上如何申请证书,点击打包
10、当出现如图所示日志时,则表示打自定义调试基座包成功
11、在HBuilderX菜单栏中,选择"运行"->“运行到手机或模拟器”->“运行基座选择”->"自定义调试基座(iOS)"
12、点击运行真机即可跑通项目
二、常见问题
【1】HBuilderX 官方常见问题:
【2】出现如图所示错误
解决方法:请使用真机进行调试、确保已勾选自定义打包基座、请检查证书是否有效、确保mainfest.json中App原生插件配置已勾选(可删除后重新选择)、删除手机App清除缓存
【3】加入房间一直转圈无法加入
解决方法:请确保index.nvue中appid已填写,填写正确且已保存。
【4】加入房间没有画面或声音
解决方法:请检查当前是否打开相机权限or麦克风权限
收起阅读 »
uni-app rtm插件集成指南及常见问题--iOS
一、如何跑通示例demo?
1、前往插件市场下载示例项目anyRTC实时消息SDK插件
2、若手动下载示例zip,解压后在mainfest.json中获取DCloud_APPID。通过HBuilderX导入无需获取;
3、前往插件市场anyRTC实时消息SDK插件,点击购买(0元)for云打包,选择mainfest.json中获取到的DCloud_APPID,如果没有找到,请刷新界面,点击下一步;
4、填写iOS包名,可在HBuilderX菜单栏中,选择"运行"->“运行到手机或模拟器”->"制作自定义基座"找到包名,请确保填入的包名与打包时的包名一致,点击下一步;
5、绑定云打包成功
6、在mainfest.json中"APP原生插件配置"中,点击"选择云端插件",勾选anyRTC实时消息SDK插件,点击"确定"
7、前往anyRTC官网注册账号,创建应用,获取应用的APPID,在component.nvue中,填入获取到的APPID,请注意保存更改内容。
8、在HBuilderX菜单栏中,选择"运行"->“运行到手机或模拟器”->"制作自定义基座"
9、勾选iOS(ipa包),选择证书profile文件、私钥证书,请确保当前证书可用,证书问题,可点击页面上如何申请证书,点击打包
10、当出现如图所示日志时,则表示打自定义调试基座包成功
11、在HBuilderX菜单栏中,选择"运行"->“运行到手机或模拟器”->“运行基座选择”->"自定义调试基座(iOS)"
12、点击运行真机即可跑通项目
二、常见问题
【1】HBuilderX 官方常见问题:
HBuilder/HBuilderX真机运行、手机运行、真机联调常见问题
【2】出现如图所示错误
解决方法:请使用真机进行调试、确保已勾选自定义打包基座、请检查证书是否有效、确保mainfest.json中App原生插件配置已勾选(可删除后重新选择)、删除手机上的App清除缓存
【3】登录失败
解决方法:请确保component.nvue中appid已填写,填写正确且已保存。
一、如何跑通示例demo?
1、前往插件市场下载示例项目anyRTC实时消息SDK插件
2、若手动下载示例zip,解压后在mainfest.json中获取DCloud_APPID。通过HBuilderX导入无需获取;
3、前往插件市场anyRTC实时消息SDK插件,点击购买(0元)for云打包,选择mainfest.json中获取到的DCloud_APPID,如果没有找到,请刷新界面,点击下一步;
4、填写iOS包名,可在HBuilderX菜单栏中,选择"运行"->“运行到手机或模拟器”->"制作自定义基座"找到包名,请确保填入的包名与打包时的包名一致,点击下一步;
5、绑定云打包成功
6、在mainfest.json中"APP原生插件配置"中,点击"选择云端插件",勾选anyRTC实时消息SDK插件,点击"确定"
7、前往anyRTC官网注册账号,创建应用,获取应用的APPID,在component.nvue中,填入获取到的APPID,请注意保存更改内容。
8、在HBuilderX菜单栏中,选择"运行"->“运行到手机或模拟器”->"制作自定义基座"
9、勾选iOS(ipa包),选择证书profile文件、私钥证书,请确保当前证书可用,证书问题,可点击页面上如何申请证书,点击打包
10、当出现如图所示日志时,则表示打自定义调试基座包成功
11、在HBuilderX菜单栏中,选择"运行"->“运行到手机或模拟器”->“运行基座选择”->"自定义调试基座(iOS)"
12、点击运行真机即可跑通项目
二、常见问题
【1】HBuilderX 官方常见问题:
HBuilder/HBuilderX真机运行、手机运行、真机联调常见问题
【2】出现如图所示错误
解决方法:请使用真机进行调试、确保已勾选自定义打包基座、请检查证书是否有效、确保mainfest.json中App原生插件配置已勾选(可删除后重新选择)、删除手机上的App清除缓存
【3】登录失败
解决方法:请确保component.nvue中appid已填写,填写正确且已保存。
收起阅读 »
easypush的简单使用
获取appkey
【点击这里】先注册一个账号,然后在应用管理中创建一个新的应用,这样就可以获得一个appkey
客户端集成jssdk
在客户端页面通过以下代码引入jssdk:
<script type="text/javascript" src="http://ep.eastech-inc.com/js/EasyPush-min.js"></script>
初始化EasyPush
【控制台】
通过以下代码初始化
EasyPush: easyPush = EasyPush.init({ host:'pushapi.eastech-inc.com', appkey:'idF9J2pwWPl2pWwV', clientId:'user001' });
参数解释:
参数名称 : host 是否必填:是 参数值示例:pushapi.eastech-inc.com 参数解释: 云推送服务器域名,目前固定是pushapi.eastech-inc.com
参数名称 : appkey 是否必填:是 参数值示例: idF9J2pwWPl2pWwV 参数解释: step0中获取的appkey,每个应用都有一个appkey,通过appkey区分不用的应用,可以在控制台中查看
参数名称 : clientId 是否必填:否 参数值示例: user001 参数解释: 表示当前是哪个客户端连接到服务器,通过这个标识可以区分出客户端的身份,建议可以填写当前系统中能够区分身份的唯一标识,例如用户id等等
连接到云推送服务器
通过以下代码连接到推送服务器:
easyPush.connect({ onSuccess:function() { // dosomething }, onFailed:function(message) { console.log(message); } });
订阅通道接收消息
通过以下代码订阅通道接收消息:
easyPush.subscribe({ channel:'channel0', onMessage:function(channel,data){ // dosomething }, onSuccess:function() { // dosomething } });
其中channel表示需要订阅的通道名称,通道不需要预先定义,在使用过程中会自动创建。回调函数onMessage会在收到消息时自动触发,channel代表当前接收到消息的通道名称,data代表消息
发送消息
通过以下代码可以发送消息:
easyPush.publish({ message:'Hello,EasyPush', channel:'channel0', onSuccess:function() { // dosomething } });
其中message表示发送的内容,channel表示需要发送到哪个通道,一旦发送成功,step4中订阅该通道的所有客户端会收到message
通过以上6个步骤就可以使用EasyPush消息推送功能了。
最后,这里有一个完整版的demo,能够让你更好的了解如何使用jssdk,点击【此处】下载。
获取appkey
【点击这里】先注册一个账号,然后在应用管理中创建一个新的应用,这样就可以获得一个appkey
客户端集成jssdk
在客户端页面通过以下代码引入jssdk:
<script type="text/javascript" src="http://ep.eastech-inc.com/js/EasyPush-min.js"></script>
初始化EasyPush
【控制台】
通过以下代码初始化
EasyPush: easyPush = EasyPush.init({ host:'pushapi.eastech-inc.com', appkey:'idF9J2pwWPl2pWwV', clientId:'user001' });
参数解释:
参数名称 : host 是否必填:是 参数值示例:pushapi.eastech-inc.com 参数解释: 云推送服务器域名,目前固定是pushapi.eastech-inc.com
参数名称 : appkey 是否必填:是 参数值示例: idF9J2pwWPl2pWwV 参数解释: step0中获取的appkey,每个应用都有一个appkey,通过appkey区分不用的应用,可以在控制台中查看
参数名称 : clientId 是否必填:否 参数值示例: user001 参数解释: 表示当前是哪个客户端连接到服务器,通过这个标识可以区分出客户端的身份,建议可以填写当前系统中能够区分身份的唯一标识,例如用户id等等
连接到云推送服务器
通过以下代码连接到推送服务器:
easyPush.connect({ onSuccess:function() { // dosomething }, onFailed:function(message) { console.log(message); } });
订阅通道接收消息
通过以下代码订阅通道接收消息:
easyPush.subscribe({ channel:'channel0', onMessage:function(channel,data){ // dosomething }, onSuccess:function() { // dosomething } });
其中channel表示需要订阅的通道名称,通道不需要预先定义,在使用过程中会自动创建。回调函数onMessage会在收到消息时自动触发,channel代表当前接收到消息的通道名称,data代表消息
发送消息
通过以下代码可以发送消息:
easyPush.publish({ message:'Hello,EasyPush', channel:'channel0', onSuccess:function() { // dosomething } });
其中message表示发送的内容,channel表示需要发送到哪个通道,一旦发送成功,step4中订阅该通道的所有客户端会收到message
通过以上6个步骤就可以使用EasyPush消息推送功能了。
最后,这里有一个完整版的demo,能够让你更好的了解如何使用jssdk,点击【此处】下载。
收起阅读 »
找能把银联云闪付原生SDK,封装成插件的
有没有会把银联云闪付的微信小程序、支付宝小程序支付封装成uniapp插件的,有偿,做过的来
QQ 764523371
有没有会把银联云闪付的微信小程序、支付宝小程序支付封装成uniapp插件的,有偿,做过的来
QQ 764523371

uniapp webview H5 postMessage实时提交数据
注意要点:H5里面的 webview uni对象 是存在层级问题的:uni.webView.postMessage 这个才对。uni.postMessage是不能的使用的。
注意要点:H5里面的 webview uni对象 是存在层级问题的:uni.webView.postMessage 这个才对。uni.postMessage是不能的使用的。

关于Android漏洞风险说明及修复方案
此文档已迁移,Android安全漏洞风险问题请参考uni-app官方文档
<!--
陆续收到开发者同学反馈在腾讯云、百度云、爱加密等平台检测到 uni-app 或 5+ App 的漏洞风险问题。我们也在不断跟进和修复。
目前收集到的相关漏铜风险分为高风险、中风险和低风险三种危险程度。
高风险:可被恶意程序利用 且几乎不需要认证
中风险:可能被中级入侵经验者利用、且不一定需要认证
低风险:仅可能被本地利用且需要认证
风险问题多数为低风险。理论上这些低风险也不会影响应用的安全质量。对于高中漏洞风险的问题我们会优先处理!
收集到漏洞风险问题分为:
- DCloud代码漏洞风险 :我们可以操作修改问题。或提供配置让用户抉择。
- 三方SDK模块漏洞风险问题 :由于没有源码。无法操作修改。用户可以抉择是否使用该模块来规避风险问题
修复方案
目前会根据漏洞风险等级优先处理中高级,但可能每次版本修复的问题与开发者遇到的风险问题不一致。建议开发者同学先使用APK加固
来加强应用的安全性使应用可以安全上架!等待后续版本修复相关问题。
对于三方SDK漏洞问题,需要开发者同学积极反馈敦促相关平台提供修复漏洞风险问题的SDK。我们也会留意平台更新。并及时更新到模块中!
DCloud代码漏洞风险 修复记录
HX3.1.14版本
修复以下漏洞风险问题
修复已知WebView File域同源策略绕过漏洞
问题
修复已知Android平台WebView控件跨域访问高危漏洞
问题
修复已知Webview绕过证书校验漏洞
问题 需配置后生效
修复已知Android主机名\证书弱校验风险
问题 需配置后生效
<a id="web_untrustedca"></a>
Webview绕过证书校验漏洞
与Android主机名\证书弱校验风险
需要配置才能生效。具体如下:
配置 manifest.json 中的untrustedca节点信息,设置"refuse"或者"warning"即可去除漏洞问题。默认值为"accept"
untrustedca值域说明:
配置应用中https请求时,如果服务器返回非受信证书的处理逻辑,字符串类型,可取值:
"accept" - 接受此非受信证书,继续访问;
"refuse" - 拒绝此非 受信证书,停止访问;
"warning" - 弹出警告提示框提醒用户,由用户确定是否继续访问。仅针对webview内部请求
默认值为"accept"。需要注意如果设置refuse、warning后可能现有应用这的网络请求无法正常运行!
配置如下 修复漏洞问题:
"plus": { //uni-app项目对应节点名称为"app-plus"
"ssl": {
"untrustedca": "warning/refuse"
},
// ...
}
怎么区分DCloud代码漏洞还是三方SDK的问题呢?
你通常可以 通过漏洞详情中的漏洞代码
类名进行区分,DCloud代码多数使用io.dcloud开头,其它为三方SDK漏洞。
DCloud代码漏洞风险:
三方SDK漏洞风险:
关于组件导出风险
3.1.14+版本目前DCloud代码并没有组件导出风险的问题。基本上都是三方模块的配置具体可参考:
微信SDK: 分享、支付、登录都会有要求设置WXEntryActivity、WXPayEntryActivity并设置组件导出!
unipush模块:pushSDK内部会涉及到部分组件CustomGTService、PushReceiver、GActivity、NotificationServic等都会涉及到组件导出。
如果您的项目因为组建导出风险而无法上架只能去除以上模块的集成。
关于应用签名未校验风险
app内部记录打包时的签名SHA1。启动时可通过plus.navigator.getSignature()获取android平台的签名SHA1,两者进行比对。如果不一致表示可能被重签名存在风险选择是否退出。实现应用重签名校验逻辑。
APK可被反编译后取得源代码
请对APK进行加固,推荐使用腾讯加固平台。
WebView远程代码执行漏洞
风险描述:Android系统通过WebView.addJavascriptInterface方法注册可供JavaScript调用的Java对象,以用于增强JavaScript的功能。但是系统并没有对注册Java类的方法调用的限制。导致攻击者可以利用反射机制调用未注册的其它任何Java类,最终导致JavaScript能力的无限增强。攻击者利用该漏洞可以根据客户端能力实现远程任意代码执行攻击。 WebView 远程代码执行漏洞触发前提条件: 1)使用addJavascriptInterface方法注册可供JavaScript调用的Java对象; 2)使用WebView加载外部网页或者本地网页; 3)Android系统版本低于4.2。
目前3.1.14+版本的代码配置的minSdkVersion最低是19 也就是4.4系统。也就是说4.4系统并不存在该漏洞问题。如果你的项目minSdkVersion低于19 请修改为19或更高。
密钥硬编码漏洞
请使用3.1.14+版本打包。
-->
此文档已迁移,Android安全漏洞风险问题请参考uni-app官方文档
<!--
陆续收到开发者同学反馈在腾讯云、百度云、爱加密等平台检测到 uni-app 或 5+ App 的漏洞风险问题。我们也在不断跟进和修复。
目前收集到的相关漏铜风险分为高风险、中风险和低风险三种危险程度。
高风险:可被恶意程序利用 且几乎不需要认证
中风险:可能被中级入侵经验者利用、且不一定需要认证
低风险:仅可能被本地利用且需要认证
风险问题多数为低风险。理论上这些低风险也不会影响应用的安全质量。对于高中漏洞风险的问题我们会优先处理!
收集到漏洞风险问题分为:
- DCloud代码漏洞风险 :我们可以操作修改问题。或提供配置让用户抉择。
- 三方SDK模块漏洞风险问题 :由于没有源码。无法操作修改。用户可以抉择是否使用该模块来规避风险问题
修复方案
目前会根据漏洞风险等级优先处理中高级,但可能每次版本修复的问题与开发者遇到的风险问题不一致。建议开发者同学先使用APK加固
来加强应用的安全性使应用可以安全上架!等待后续版本修复相关问题。
对于三方SDK漏洞问题,需要开发者同学积极反馈敦促相关平台提供修复漏洞风险问题的SDK。我们也会留意平台更新。并及时更新到模块中!
DCloud代码漏洞风险 修复记录
HX3.1.14版本
修复以下漏洞风险问题
修复已知WebView File域同源策略绕过漏洞
问题
修复已知Android平台WebView控件跨域访问高危漏洞
问题
修复已知Webview绕过证书校验漏洞
问题 需配置后生效
修复已知Android主机名\证书弱校验风险
问题 需配置后生效
<a id="web_untrustedca"></a>
Webview绕过证书校验漏洞
与Android主机名\证书弱校验风险
需要配置才能生效。具体如下:
配置 manifest.json 中的untrustedca节点信息,设置"refuse"或者"warning"即可去除漏洞问题。默认值为"accept"
untrustedca值域说明:
配置应用中https请求时,如果服务器返回非受信证书的处理逻辑,字符串类型,可取值:
"accept" - 接受此非受信证书,继续访问;
"refuse" - 拒绝此非 受信证书,停止访问;
"warning" - 弹出警告提示框提醒用户,由用户确定是否继续访问。仅针对webview内部请求
默认值为"accept"。需要注意如果设置refuse、warning后可能现有应用这的网络请求无法正常运行!
配置如下 修复漏洞问题:
"plus": { //uni-app项目对应节点名称为"app-plus"
"ssl": {
"untrustedca": "warning/refuse"
},
// ...
}
怎么区分DCloud代码漏洞还是三方SDK的问题呢?
你通常可以 通过漏洞详情中的漏洞代码
类名进行区分,DCloud代码多数使用io.dcloud开头,其它为三方SDK漏洞。
DCloud代码漏洞风险:
三方SDK漏洞风险:
关于组件导出风险
3.1.14+版本目前DCloud代码并没有组件导出风险的问题。基本上都是三方模块的配置具体可参考:
微信SDK: 分享、支付、登录都会有要求设置WXEntryActivity、WXPayEntryActivity并设置组件导出!
unipush模块:pushSDK内部会涉及到部分组件CustomGTService、PushReceiver、GActivity、NotificationServic等都会涉及到组件导出。
如果您的项目因为组建导出风险而无法上架只能去除以上模块的集成。
关于应用签名未校验风险
app内部记录打包时的签名SHA1。启动时可通过plus.navigator.getSignature()获取android平台的签名SHA1,两者进行比对。如果不一致表示可能被重签名存在风险选择是否退出。实现应用重签名校验逻辑。
APK可被反编译后取得源代码
请对APK进行加固,推荐使用腾讯加固平台。
WebView远程代码执行漏洞
风险描述:Android系统通过WebView.addJavascriptInterface方法注册可供JavaScript调用的Java对象,以用于增强JavaScript的功能。但是系统并没有对注册Java类的方法调用的限制。导致攻击者可以利用反射机制调用未注册的其它任何Java类,最终导致JavaScript能力的无限增强。攻击者利用该漏洞可以根据客户端能力实现远程任意代码执行攻击。 WebView 远程代码执行漏洞触发前提条件: 1)使用addJavascriptInterface方法注册可供JavaScript调用的Java对象; 2)使用WebView加载外部网页或者本地网页; 3)Android系统版本低于4.2。
目前3.1.14+版本的代码配置的minSdkVersion最低是19 也就是4.4系统。也就是说4.4系统并不存在该漏洞问题。如果你的项目minSdkVersion低于19 请修改为19或更高。
密钥硬编码漏洞
请使用3.1.14+版本打包。
-->

如何把 uni-id 插件用于H5页面
一、准备
将整套uni-id
插件导入编辑器,上传所有云函数及公用函数。
pages
页面全部删除,自建主页,登录页,注册页,修改密码页。
保留全部VUEX
部分
1.1 主页设置
onLoad()
即开始检测token
uni_id_token
保存着token
信息,分为两种情况:有token
和没有token
- 本地
token
正确也未过期,修改VUEX
状态this.login(uni.getStorageSync('username'));
- 本地
token
错误或者过期了,跳转登录页面uni.reLaunch({url: '../login/login'});
- 本地没有
token
,跳转至登录页面
import { mapState, mapMutations } from 'vuex';
export default {
computed: mapState(['userName', 'hasLogin']),
data() {
return {
};
},
onLoad() {
let uid = { uid: `${uni.getStorageSync('user_id')}` };
let uniIdToken = uni.getStorageSync('uni_id_token');
if (uniIdToken) {
//有 token 的情况下,开始检测
uniCloud.callFunction({
name: 'user-center',
data: {
action: 'checkToken'
},
success: e => {
if (e.result.code > 0) {
//检测 token 有问题,返回登录页
uni.reLaunch({
url: '../login/login'
});
}
this.login(uni.getStorageSync('username'));
},
fail(e) {
uni.showModal({
content: e.msg,
showCancel: false
});
}
});
} else {
//没有 token 的情况下,返回登录页
uni.showModal({
content: '请登录后再浏览',
showCancel: false,
success: res => {
if (res.confirm) {
uni.reLaunch({
url: '../login/login'
});
}
}
});
}
},
methods: {
...mapMutations(['login']),
login_out() {
uni.reLaunch({
url: '../login/login'
});
}
}
};
1.2 登录页面设置
- 登录后,保存了
token
、username
、user_id
,然后跳转到主页 user_id
将用于删除云数据库uni-id-log
中过多的、重复的登录记录。 文末有删除记录的云函数方法。
methods: {
...mapMutations(['login']),
loginByPwd() {
const data = {
username: this.username,
password: this.password
};
uniCloud.callFunction({
name: 'user-center',
data: {
action: 'login',
params: data
},
success: (e) => {
if (e.result.code == 0) {
// 密码验证成功
uni.setStorageSync('uni_id_token', e.result.token);
uni.setStorageSync('username', e.result.username);
uni.setStorageSync('user_id', e.result.uid);
this.toMain(this.username);
} else {
// 密码验证失败
uni.showModal({
content: e.result.message,
showCancel: false
})
}
},
fail: (e) => {
uni.showModal({
content: JSON.stringify(e),
showCancel: false
})
}
})
},
toMain(userName) {
this.login(userName);
uni.reLaunch({
url: '../main/main',
});
}
}
1.3 注册请求
- 对于用户名和密码的格式要求,需自己写。
- 注意:注册时发送给云函数
user-center
的data
数据的格式data:{action:'register',params: 账号,密码}
gotoRegister() {
uni.showLoading({
title: '正在提交,请稍后'
});
const data = {
username: this.username,
password: this.password
};
uniCloud.callFunction({
name: 'user-center',
data: {
action: 'register',
params: data
},
success(e) {
uni.hideLoading();
if (e.result.code === 0) {
uni.showToast({
title: '注册成功'
});
uni.setStorageSync('uni_id_token', e.result.token);
uni.setStorageSync('username', e.result.username);
uni.reLaunch({
url: '../main/main'
});
} else {
uni.showModal({
content: JSON.stringify(e.result),
showCancel: false
});
}
},
fail(e) {
uni.hideLoading();
uni.showModal({
content: JSON.stringify(e),
showCancel: false
});
}
});
}
1.4 补充
- 删除多余登录记录的云函数
- 获取当前
user_id
的全部登录记录数据,筛选出create_date
值不是最大的全部记录,逐一删除。 - 云函数
removeID
'use strict';
const db = uniCloud.database();
const dbCmd = db.command;
exports.main = async (event, context) => {
const {user_id} = event;
let resList = await db.collection('uni-id-log')
.where({
user_id: `${user_id}`
})
.get();
let res = resList.data;
if (res.length > 1) {
let num = 0;
res.forEach((item) => {
if (item.create_date > num) {
num = item.create_date;
}
})
let resObjArr = await db.collection('uni-id-log')
.where({
create_date: dbCmd.neq(num)
})
.get();
resObjArr.data.forEach(async (item) => {
let resSucc = await db.collection('uni-id-log').doc(`${item._id}`).remove();
console.log(resSucc);
});
const date = new Date().getTime();
res = await db.collection('uni-id-log')
.where({
user_id: `${user_id}`
})
.get();
return {
msg: '删除成功',
code: 200
}
} else {
return {
msg: `当前账号仅有一次登录记录`,
code: 200
}
}
};
主页onLoad()
内调用
onLoad() {
const user_id = {user_id:`${uni.getStorageSync('user_id')}`}
uniCloud.callFunction({
name:'removeID',
data:user_id,
success: (e) => {
console.log(e);
},
fail: (err) => {
console.log(err);
}
})
}
一、准备
将整套uni-id
插件导入编辑器,上传所有云函数及公用函数。
pages
页面全部删除,自建主页,登录页,注册页,修改密码页。
保留全部VUEX
部分
1.1 主页设置
onLoad()
即开始检测token
uni_id_token
保存着token
信息,分为两种情况:有token
和没有token
- 本地
token
正确也未过期,修改VUEX
状态this.login(uni.getStorageSync('username'));
- 本地
token
错误或者过期了,跳转登录页面uni.reLaunch({url: '../login/login'});
- 本地没有
token
,跳转至登录页面
import { mapState, mapMutations } from 'vuex';
export default {
computed: mapState(['userName', 'hasLogin']),
data() {
return {
};
},
onLoad() {
let uid = { uid: `${uni.getStorageSync('user_id')}` };
let uniIdToken = uni.getStorageSync('uni_id_token');
if (uniIdToken) {
//有 token 的情况下,开始检测
uniCloud.callFunction({
name: 'user-center',
data: {
action: 'checkToken'
},
success: e => {
if (e.result.code > 0) {
//检测 token 有问题,返回登录页
uni.reLaunch({
url: '../login/login'
});
}
this.login(uni.getStorageSync('username'));
},
fail(e) {
uni.showModal({
content: e.msg,
showCancel: false
});
}
});
} else {
//没有 token 的情况下,返回登录页
uni.showModal({
content: '请登录后再浏览',
showCancel: false,
success: res => {
if (res.confirm) {
uni.reLaunch({
url: '../login/login'
});
}
}
});
}
},
methods: {
...mapMutations(['login']),
login_out() {
uni.reLaunch({
url: '../login/login'
});
}
}
};
1.2 登录页面设置
- 登录后,保存了
token
、username
、user_id
,然后跳转到主页 user_id
将用于删除云数据库uni-id-log
中过多的、重复的登录记录。 文末有删除记录的云函数方法。
methods: {
...mapMutations(['login']),
loginByPwd() {
const data = {
username: this.username,
password: this.password
};
uniCloud.callFunction({
name: 'user-center',
data: {
action: 'login',
params: data
},
success: (e) => {
if (e.result.code == 0) {
// 密码验证成功
uni.setStorageSync('uni_id_token', e.result.token);
uni.setStorageSync('username', e.result.username);
uni.setStorageSync('user_id', e.result.uid);
this.toMain(this.username);
} else {
// 密码验证失败
uni.showModal({
content: e.result.message,
showCancel: false
})
}
},
fail: (e) => {
uni.showModal({
content: JSON.stringify(e),
showCancel: false
})
}
})
},
toMain(userName) {
this.login(userName);
uni.reLaunch({
url: '../main/main',
});
}
}
1.3 注册请求
- 对于用户名和密码的格式要求,需自己写。
- 注意:注册时发送给云函数
user-center
的data
数据的格式data:{action:'register',params: 账号,密码}
gotoRegister() {
uni.showLoading({
title: '正在提交,请稍后'
});
const data = {
username: this.username,
password: this.password
};
uniCloud.callFunction({
name: 'user-center',
data: {
action: 'register',
params: data
},
success(e) {
uni.hideLoading();
if (e.result.code === 0) {
uni.showToast({
title: '注册成功'
});
uni.setStorageSync('uni_id_token', e.result.token);
uni.setStorageSync('username', e.result.username);
uni.reLaunch({
url: '../main/main'
});
} else {
uni.showModal({
content: JSON.stringify(e.result),
showCancel: false
});
}
},
fail(e) {
uni.hideLoading();
uni.showModal({
content: JSON.stringify(e),
showCancel: false
});
}
});
}
1.4 补充
- 删除多余登录记录的云函数
- 获取当前
user_id
的全部登录记录数据,筛选出create_date
值不是最大的全部记录,逐一删除。 - 云函数
removeID
'use strict';
const db = uniCloud.database();
const dbCmd = db.command;
exports.main = async (event, context) => {
const {user_id} = event;
let resList = await db.collection('uni-id-log')
.where({
user_id: `${user_id}`
})
.get();
let res = resList.data;
if (res.length > 1) {
let num = 0;
res.forEach((item) => {
if (item.create_date > num) {
num = item.create_date;
}
})
let resObjArr = await db.collection('uni-id-log')
.where({
create_date: dbCmd.neq(num)
})
.get();
resObjArr.data.forEach(async (item) => {
let resSucc = await db.collection('uni-id-log').doc(`${item._id}`).remove();
console.log(resSucc);
});
const date = new Date().getTime();
res = await db.collection('uni-id-log')
.where({
user_id: `${user_id}`
})
.get();
return {
msg: '删除成功',
code: 200
}
} else {
return {
msg: `当前账号仅有一次登录记录`,
code: 200
}
}
};
主页onLoad()
内调用
onLoad() {
const user_id = {user_id:`${uni.getStorageSync('user_id')}`}
uniCloud.callFunction({
name:'removeID',
data:user_id,
success: (e) => {
console.log(e);
},
fail: (err) => {
console.log(err);
}
})
}
收起阅读 »