HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

Android平台解决使用UniPush和个推推送违反谷歌应用商店(GooglePlay)个人和敏感信息政策无法上架的说明

GooglePlay unipush

HBuilderX2.8.4+版本调整为默认不再弹出隐私政策提示框,建议使用Android平台隐私与政策提示框配置方法,参考:https://ask.dcloud.net.cn/article/36937

最近谷歌应用商店(GooglePlay)加强对应用的审查,如果违反个人和敏感信息政策无法通过审核上架,甚至已经上架的应用也可能被下架。
提示信息如下:

sue: Violation of Personal and Sensitive Information policy  

We’ve identified that your app is using an SDK or library that facilitates the collection and transmission of installed packages information without meeting the prominent disclosure guidelines.  

If necessary, you can consult your SDK provider(s) for further information.  

Next steps: Submit your app for another review  

Read through the Personal and Sensitive Information policy and make the appropriate changes to your app. Your app is using the Igexin SDK, which is uploading users installed packages information to http://sdk.open.phone.igexin.com/api.php without a prominent disclosure. Prior to the collection and transmission, it must prominently highlight how the user data will be used, describe the type of data being collected and have the user provide affirmative consent for such use. Your app must handle user data securely, including transmitting it using modern cryptography (for example, over HTTPS).

这是因为UniPush和个推推送使用的SDK需要获取设备的唯一标识等用于下发推送消息,但没有提示用户确认导致违反谷歌的个人和敏感政策。

HBuilderX2.3.4+版已经更新个推SDK解决此问题
使用UniPush和个推推送时会在应用启动时会弹出隐私政策提示框

以上提示框可能会影响用户的使用体验,因此仅会在打GooglePlay渠道包时生效

提交云端打包时请在渠道包项中选择“GooglePlay”

<a id="noprompt"/>

配置不弹出隐私政策提示框

HBuilder2.6.13+版本支持配置不弹出隐私政策提示框
打开manifest.json文件,切换到源码视图:

  • 5+ App项目
    在 "plus" -> "distribute" -> "plugins" -> "push" -> "unipush" 下添加"__privacy_prompt__"字段
  • uni-app项目
    在 "app-plus" -> "distribute" -> "sdkConfigs" -> "push" -> "unipush" 下添加"__privacy_prompt__"字段
    "push" : {  
          "unipush" : {  
              "__privacy_prompt__" : "none"    //值为"show"则显示隐私政策提示框  
          }  
   },

提交云端打包后生效

离线打包配置

在AndroidManifest.xml文件中添加以下meta-data数据

<meta-data android:name="DCLOUD_PUSH_PRIVACY" android:value="false"/>
继续阅读 »

HBuilderX2.8.4+版本调整为默认不再弹出隐私政策提示框,建议使用Android平台隐私与政策提示框配置方法,参考:https://ask.dcloud.net.cn/article/36937

最近谷歌应用商店(GooglePlay)加强对应用的审查,如果违反个人和敏感信息政策无法通过审核上架,甚至已经上架的应用也可能被下架。
提示信息如下:

sue: Violation of Personal and Sensitive Information policy  

We’ve identified that your app is using an SDK or library that facilitates the collection and transmission of installed packages information without meeting the prominent disclosure guidelines.  

If necessary, you can consult your SDK provider(s) for further information.  

Next steps: Submit your app for another review  

Read through the Personal and Sensitive Information policy and make the appropriate changes to your app. Your app is using the Igexin SDK, which is uploading users installed packages information to http://sdk.open.phone.igexin.com/api.php without a prominent disclosure. Prior to the collection and transmission, it must prominently highlight how the user data will be used, describe the type of data being collected and have the user provide affirmative consent for such use. Your app must handle user data securely, including transmitting it using modern cryptography (for example, over HTTPS).

这是因为UniPush和个推推送使用的SDK需要获取设备的唯一标识等用于下发推送消息,但没有提示用户确认导致违反谷歌的个人和敏感政策。

HBuilderX2.3.4+版已经更新个推SDK解决此问题
使用UniPush和个推推送时会在应用启动时会弹出隐私政策提示框

以上提示框可能会影响用户的使用体验,因此仅会在打GooglePlay渠道包时生效

提交云端打包时请在渠道包项中选择“GooglePlay”

<a id="noprompt"/>

配置不弹出隐私政策提示框

HBuilder2.6.13+版本支持配置不弹出隐私政策提示框
打开manifest.json文件,切换到源码视图:

  • 5+ App项目
    在 "plus" -> "distribute" -> "plugins" -> "push" -> "unipush" 下添加"__privacy_prompt__"字段
  • uni-app项目
    在 "app-plus" -> "distribute" -> "sdkConfigs" -> "push" -> "unipush" 下添加"__privacy_prompt__"字段
    "push" : {  
          "unipush" : {  
              "__privacy_prompt__" : "none"    //值为"show"则显示隐私政策提示框  
          }  
   },

提交云端打包后生效

离线打包配置

在AndroidManifest.xml文件中添加以下meta-data数据

<meta-data android:name="DCLOUD_PUSH_PRIVACY" android:value="false"/>
收起阅读 »

uniapp H5端不能够定位的问题

定位 h5 uniapp
  1. H5 端获取定位信息,需要部署在 https 服务上,本地预览(localhost)仍然可以使用 http 协议。(照搬官方文档)
  2. 在manifest.json 下面需要配置腾讯地图的key(可自己申请)
"sdkConfigs" : {  
       "maps" : {  
            "qqmap" : {  
                  "key" : "xxxxxxxxxx"  
            }  
       }  
}
继续阅读 »
  1. H5 端获取定位信息,需要部署在 https 服务上,本地预览(localhost)仍然可以使用 http 协议。(照搬官方文档)
  2. 在manifest.json 下面需要配置腾讯地图的key(可自己申请)
"sdkConfigs" : {  
       "maps" : {  
            "qqmap" : {  
                  "key" : "xxxxxxxxxx"  
            }  
       }  
}
收起阅读 »

准则2.1-性能、运行Wi-Fi在iPad上一个或多个错误问题

上架 iOS打包

很多开发者上架遇到这个问题,苹果那边打不开APP,加载不出来内容!

很多人以为是没有兼容ipad,其实是苹果审核都用ipad,跟有没有支持兼容没有关系。

如果自己在国内测试加载正常,要看APP服务器是否支持国外访问,因为苹果审核在国外,自己用国外网络测试看下!!

具体反馈翻译内容

准则2.1-性能-应用程序完整性

我们在运行Wi-Fi iOS 12.4.1的iPad上查看您的应用程序时发现一个或多个错误。

具体来说,我们无法在应用程序中加载某些内容。

下一步

若要解决此问题,请在设备上运行应用程序以确定任何问题,然后修订并重新提交应用程序以供审阅。

如果我们误解了您的应用程序的预期行为,请在Resolution Center中回复此消息,以提供有关这些功能的预期工作方式的信息。

对于新应用程序,请从设备中卸载所有以前版本的应用程序,然后安装并按照步骤重现问题。对于更新,请将新版本安装为对以前版本的更新,然后按照步骤重现问题。

资源

有关测试应用程序并准备进行审阅的信息,请参阅技术说明TN2431:应用程序测试指南。

有关网络概述,请查看有关网络的信息

iOS APP上架被拒重新提交审核教程

继续阅读 »

很多开发者上架遇到这个问题,苹果那边打不开APP,加载不出来内容!

很多人以为是没有兼容ipad,其实是苹果审核都用ipad,跟有没有支持兼容没有关系。

如果自己在国内测试加载正常,要看APP服务器是否支持国外访问,因为苹果审核在国外,自己用国外网络测试看下!!

具体反馈翻译内容

准则2.1-性能-应用程序完整性

我们在运行Wi-Fi iOS 12.4.1的iPad上查看您的应用程序时发现一个或多个错误。

具体来说,我们无法在应用程序中加载某些内容。

下一步

若要解决此问题,请在设备上运行应用程序以确定任何问题,然后修订并重新提交应用程序以供审阅。

如果我们误解了您的应用程序的预期行为,请在Resolution Center中回复此消息,以提供有关这些功能的预期工作方式的信息。

对于新应用程序,请从设备中卸载所有以前版本的应用程序,然后安装并按照步骤重现问题。对于更新,请将新版本安装为对以前版本的更新,然后按照步骤重现问题。

资源

有关测试应用程序并准备进行审阅的信息,请参阅技术说明TN2431:应用程序测试指南。

有关网络概述,请查看有关网络的信息

iOS APP上架被拒重新提交审核教程

收起阅读 »

一个页面多个视频,控制只播放一个

视频
vue:  
<video :id="'video'+index" :src="item.photo_list[0]" @play="videoPlayHandle('video'+index)"></video>
data:  
video:null
methods:  
// 视频播放需要暂停其他视频,也支持当前视频播放暂停  
            videoPlayHandle(id) {  
                let newVideo = uni.createVideoContext(id);  
                if (!this.video) {  
                    this.video = newVideo;  
                    this.video.play();  
                    return  
                }  
                if (this.video.id !== newVideo.id) {  
                    newVideo.play();  
                    this.video.pause();  
                    this.video = newVideo;  
                }  
            },

app中无效,待解决

继续阅读 »
vue:  
<video :id="'video'+index" :src="item.photo_list[0]" @play="videoPlayHandle('video'+index)"></video>
data:  
video:null
methods:  
// 视频播放需要暂停其他视频,也支持当前视频播放暂停  
            videoPlayHandle(id) {  
                let newVideo = uni.createVideoContext(id);  
                if (!this.video) {  
                    this.video = newVideo;  
                    this.video.play();  
                    return  
                }  
                if (this.video.id !== newVideo.id) {  
                    newVideo.play();  
                    this.video.pause();  
                    this.video = newVideo;  
                }  
            },

app中无效,待解决

收起阅读 »

Hbuilderx开发uni-app问题及解决方案记录,持续更新~

uniapp

1、组件回调函数中,需要传自定义参数时,可以传arguments或者$event作为回调的参数,后面加上自定义参数。
注:传arguments在app端取不到value值,h5可以取到,所以最终传$event。
vue:
@change="changeFun(arguments,item)"
@change="changeFun($event,item)"

js:
changeFun(value,item){
//value为回调的参数,item为自定义参数
}

2、vue中最好不要写比较复杂的js,最好不要写js,语法类似小程序,小程序不支持html有js语法及函数,打包成app会报错,会显示undefined。
例如:.substring();格式化时间;
解决方案:可以用计算属性代替html中的计算之类的

3、uni.showModal ,app上 content参数为空值,弹窗不显示,但是h5会显示,title可为空。

4、选择图片,需要限制大小,否则上传返回的res.data为空

 uni.chooseImage({  
                    count: 9, //默认9  
                    sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有  
                    sourceType: ['album'], //从相册选择  
                    success: function(res) {  
                        let tempFilePaths = [];  
                        res.tempFiles.forEach(item => {  
                            if (item.size < 1048576) {  
                                tempFilePaths.push(item.path);  
                            }  
                        })  
                        if (tempFilePaths.length !== res.tempFiles.length) {  
                            self.$showToast({  
                                text: '请选择1M内的图片'  
                            })  
                        }  
                        if (tempFilePaths.length > 0) {  
                            uni.setStorageSync('activity_products', '');  
                            uni.setStorageSync('activity_photos', tempFilePaths);  
                            uni.navigateTo({  
                                url: './issue1'  
                            })  
                        }  
                    }  
                });  

                            uni.uploadFile({  
                        url: apiList.host + '/gateway',  
                        filePath: item,  
                        name: 'file',  
                        header: {  
                            'content-type': 'multipart/form-data', // 文件类型    
                            'method': 'file.upload.direct',  
                            'mall_code': self.$store.state.mall_code,  
                            '_app-key': 'MjIyMg==',  
                            '_authorization': self.$store.state.token || '',  
                        },  
                        success: function(res) {  
                            console.log('res.data', res.data)  
                            if (res.data) {  
                                let data = JSON.parse(res.data);  
                                if (data.code == 200) {  
                                    let url = data.data;  
                                    photos.push(url);  
                                    if (photos.length == self.activity_photos.length) {  
                                        self.params.photos = photos.join(',');  
                                        self.post();  
                                    }  
                                } else {  
                                    self.$showToast({  
                                        text: data.error  
                                    })  
                                    uni.hideLoading();  
                                }  
                            } else {  
                                self.$showToast({  
                                    text: '发布失败'  
                                })  
                                uni.hideLoading();  
                            }  
                        },  
                        fail: function(res) {  
                            self.$showToast({  
                                text: res.msg  
                            });  
                            uni.hideLoading();  
                        },  
                    })

5、app中 使用store,需要在main.js中加 Vue.prototype.$store = store;

6、app中 给变量赋值,data中必须要有此变量,否则报错。

7、app运行报错:uni-app unexpected character "
原因:页面中的class=" 之后接的数据换行了

8、app运行报错: [错误] ./pages/member/mobile_modify/mobile_modify.wxml:1:2067: Bad attr data-event-opts with message: unexpected token email.
解决:input标签中写了 v-model="a==1?a:b",这种写法不支持

9、app运行报错:TypeError: $gwx is not a function
网上方法1(无效):是那个美元符号的问题。。加个空格
解决:是语法不兼容有问题就会报这个错,至于哪里不兼容需要自己找
1.组件中的props 不能使用vue原型的全局数据,需要单独引入使用
2.localStorage.getItem()在app中不识别,不能使用,需用uni的自定义api
3.组件内不能使用data作为props
4.data()中定义的变量,若语法有问题,那么onLoad()中打印任何data中的变量,都为undefined
5.vue中使用函数@tap="autFun('btns','editStock','fun',()=>{return editStockShow(item)})",报错bad attr 'data-ecent-opts' with message:unexpected

10、如下图

11、在app端取不到value值,h5可以取到



解决:
将arguments,改为$event

12、在app端会报错。.title为undefined,h5不会


解决:
改为计算属性

13、html中最好不要做太多js操作,例如格式化时间,app中会显示undefined

14、保存图片到相册 app
1、安卓无效

         //利用plus完成保存到相册    
    let filename = Math.random() +".png"    
            plus.downloader.createDownload(this.imageURL, {    
                filename:"_downloads/"+filename    
            }, (download,status)=>{    
                if(status==200){//下载成功    
                    plus.gallery.save( download.filename, ()=>{    
                        uni.hideLoading();    
                        uni.showToast({    
                            title: '保存成功!'    
                        });    
                    })    
                }    
            }).start()  

2、ios有效 安卓无效,保存失败

 uni.downloadFile({  
    url: this.code,  
    success(res) {  
        uni.saveImageToPhotosAlbum({  
            filePath: res.tempFilePath,  
            success: function() {  
                self.$showToast({  
                    text: '保存成功'  
                })  
            },  
            fail: function(err) {  
                console.log(err)  
                self.$showToast({  
                    text: '保存失败,请截图分享'  
                })  
            }  
        });  
    },  
    fail: function(err) {  
        console.log(err)  
        self.$showToast({  
            text: '下载失败,请截图分享'  
        })  
    }  
})  

3、canvas绘图 保存到本地 ios有效 安卓 h5绘图不出来,可能是因为下载的图片是base64的;drawImg()如果是base64图片可以直接canvas绘图,不用downloadfile了,否则安卓不显示;base64直接绘图,h5不显示,安卓ios显示

<canvas style="width: 300px; height: 300px;margin:0 auto" canvas-id="myCanvas" ></canvas>
uni.downloadFile({  
    url: res.data,  
    success(res) {  
        self.drawImg(res.tempFilePath);  
    },  
    fail: function(err) {  
        console.log(err)  
        self.$showToast({  
            text: '下载失败'  
        })  
    }  
})  

drawImg(code) {  
setTimeout(() => {  
    const ctx = uni.createCanvasContext('myCanvas');  
    ctx.drawImage(code, 0, 0, 300, 300);  
    ctx.setFontSize(14);  
    ctx.setFillStyle('#6D7278');  
    ctx.fillText('长按或扫描二维码访问我的门店', 50, 280);  
    ctx.draw();  
    this.code = true;  
}, 500);  
},  
save() {  
let self = this;  
uni.canvasToTempFilePath({  
    x: 0,  
    y: 0,  
    width: 300,  
    height: 300,  
    destWidth: 600,  
    destHeight: 600,  
    canvasId: 'myCanvas',  
    success: function(res) {  
        console.log(res.tempFilePath)  
        uni.saveImageToPhotosAlbum({  
            filePath: res.tempFilePath,  
            success: function() {  
                self.$showToast({  
                    text: '保存成功'  
                })  
            },  
            fail: function(err) {  
                console.log(err)  
                self.$showToast({  
                    text: '保存失败,请截图分享'  
                })  
            }  
        });  
    }  
})  
},

15、app中,组件props为string类型,传值为空串“”,app会显示为true,若默认default为“”,其子组件的props也会显示为true

16、复制api

self.copyText = function(text) {  
        uni.setClipboardData({  
            data: text,  
            success: function(res) {  
                uni.getClipboardData({  
                    success: function(res) {  
                        // app会有默认提示“内容已复制”  
                        // self.$common.Toast({  
                        //  text: self.i18.order.copySuc  
                        // });  
                    }  
                })  
            }  
        })  
    }

解决有默认提示

17、uni.uploadFile() h5 不能写content-type参数,写了会报错,app也可以不写

18、picker:有一个app兼容问题,如果默认值为空,首次手动赋值的时候如果index是0, 页面显示不更新,我看了下文档估计是组件默认值为0的原因,H5没有,我现在处理的是index为0的话就先改为其他值,然后setTimeout赋正确值

19、页面隐藏滚动条,包括横向滚动条

//1.设置全局样式,此方法无效  
 ::-webkit-scrollbar {  
    width: 0;  
    height: 0;  
    color: transparent;  
  }
//2.设置全局样式,此方法h5页面有效,app无效  
 ::-webkit-scrollbar {  
    display: none;  
  }
//3.page.json 页面设置此参数,仅对app有效  
{  
      "path": "pages/home/classifyDetail/classifyDetail",  
      "style": {  
        "navigationBarTitleText": "分类详情",  
        "navigationBarBackgroundColor": "#FFFFFF",  
        "app-plus": {  
          // 禁用原生导航栏  
          "titleNView": false,  
          // 手机端隐藏滚动条,保留滚动效果  
          "scrollIndicator":"none"  
        }  
      }  
  },

20、解决安卓点击两次返回按钮关闭应用问题

// 重写quit方法改为隐藏至后台  
if (!this.$common.isIOS()) {  
  let main = plus.android.runtimeMainActivity();  
  plus.runtime.quit = function () {  
    main.moveTaskToBack(false);  
  }  
}

21、跳转最好都写绝对路径,/pages/……
遇过的问题:h5中,首页写相对路径,./ ../ 首次进页面跳转成功,进入其他tab页面,再回来跳转,无效了
@tap=navigateTo(url)
navigator标签
uni.navigateTo()

继续阅读 »

1、组件回调函数中,需要传自定义参数时,可以传arguments或者$event作为回调的参数,后面加上自定义参数。
注:传arguments在app端取不到value值,h5可以取到,所以最终传$event。
vue:
@change="changeFun(arguments,item)"
@change="changeFun($event,item)"

js:
changeFun(value,item){
//value为回调的参数,item为自定义参数
}

2、vue中最好不要写比较复杂的js,最好不要写js,语法类似小程序,小程序不支持html有js语法及函数,打包成app会报错,会显示undefined。
例如:.substring();格式化时间;
解决方案:可以用计算属性代替html中的计算之类的

3、uni.showModal ,app上 content参数为空值,弹窗不显示,但是h5会显示,title可为空。

4、选择图片,需要限制大小,否则上传返回的res.data为空

 uni.chooseImage({  
                    count: 9, //默认9  
                    sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有  
                    sourceType: ['album'], //从相册选择  
                    success: function(res) {  
                        let tempFilePaths = [];  
                        res.tempFiles.forEach(item => {  
                            if (item.size < 1048576) {  
                                tempFilePaths.push(item.path);  
                            }  
                        })  
                        if (tempFilePaths.length !== res.tempFiles.length) {  
                            self.$showToast({  
                                text: '请选择1M内的图片'  
                            })  
                        }  
                        if (tempFilePaths.length > 0) {  
                            uni.setStorageSync('activity_products', '');  
                            uni.setStorageSync('activity_photos', tempFilePaths);  
                            uni.navigateTo({  
                                url: './issue1'  
                            })  
                        }  
                    }  
                });  

                            uni.uploadFile({  
                        url: apiList.host + '/gateway',  
                        filePath: item,  
                        name: 'file',  
                        header: {  
                            'content-type': 'multipart/form-data', // 文件类型    
                            'method': 'file.upload.direct',  
                            'mall_code': self.$store.state.mall_code,  
                            '_app-key': 'MjIyMg==',  
                            '_authorization': self.$store.state.token || '',  
                        },  
                        success: function(res) {  
                            console.log('res.data', res.data)  
                            if (res.data) {  
                                let data = JSON.parse(res.data);  
                                if (data.code == 200) {  
                                    let url = data.data;  
                                    photos.push(url);  
                                    if (photos.length == self.activity_photos.length) {  
                                        self.params.photos = photos.join(',');  
                                        self.post();  
                                    }  
                                } else {  
                                    self.$showToast({  
                                        text: data.error  
                                    })  
                                    uni.hideLoading();  
                                }  
                            } else {  
                                self.$showToast({  
                                    text: '发布失败'  
                                })  
                                uni.hideLoading();  
                            }  
                        },  
                        fail: function(res) {  
                            self.$showToast({  
                                text: res.msg  
                            });  
                            uni.hideLoading();  
                        },  
                    })

5、app中 使用store,需要在main.js中加 Vue.prototype.$store = store;

6、app中 给变量赋值,data中必须要有此变量,否则报错。

7、app运行报错:uni-app unexpected character "
原因:页面中的class=" 之后接的数据换行了

8、app运行报错: [错误] ./pages/member/mobile_modify/mobile_modify.wxml:1:2067: Bad attr data-event-opts with message: unexpected token email.
解决:input标签中写了 v-model="a==1?a:b",这种写法不支持

9、app运行报错:TypeError: $gwx is not a function
网上方法1(无效):是那个美元符号的问题。。加个空格
解决:是语法不兼容有问题就会报这个错,至于哪里不兼容需要自己找
1.组件中的props 不能使用vue原型的全局数据,需要单独引入使用
2.localStorage.getItem()在app中不识别,不能使用,需用uni的自定义api
3.组件内不能使用data作为props
4.data()中定义的变量,若语法有问题,那么onLoad()中打印任何data中的变量,都为undefined
5.vue中使用函数@tap="autFun('btns','editStock','fun',()=>{return editStockShow(item)})",报错bad attr 'data-ecent-opts' with message:unexpected

10、如下图

11、在app端取不到value值,h5可以取到



解决:
将arguments,改为$event

12、在app端会报错。.title为undefined,h5不会


解决:
改为计算属性

13、html中最好不要做太多js操作,例如格式化时间,app中会显示undefined

14、保存图片到相册 app
1、安卓无效

         //利用plus完成保存到相册    
    let filename = Math.random() +".png"    
            plus.downloader.createDownload(this.imageURL, {    
                filename:"_downloads/"+filename    
            }, (download,status)=>{    
                if(status==200){//下载成功    
                    plus.gallery.save( download.filename, ()=>{    
                        uni.hideLoading();    
                        uni.showToast({    
                            title: '保存成功!'    
                        });    
                    })    
                }    
            }).start()  

2、ios有效 安卓无效,保存失败

 uni.downloadFile({  
    url: this.code,  
    success(res) {  
        uni.saveImageToPhotosAlbum({  
            filePath: res.tempFilePath,  
            success: function() {  
                self.$showToast({  
                    text: '保存成功'  
                })  
            },  
            fail: function(err) {  
                console.log(err)  
                self.$showToast({  
                    text: '保存失败,请截图分享'  
                })  
            }  
        });  
    },  
    fail: function(err) {  
        console.log(err)  
        self.$showToast({  
            text: '下载失败,请截图分享'  
        })  
    }  
})  

3、canvas绘图 保存到本地 ios有效 安卓 h5绘图不出来,可能是因为下载的图片是base64的;drawImg()如果是base64图片可以直接canvas绘图,不用downloadfile了,否则安卓不显示;base64直接绘图,h5不显示,安卓ios显示

<canvas style="width: 300px; height: 300px;margin:0 auto" canvas-id="myCanvas" ></canvas>
uni.downloadFile({  
    url: res.data,  
    success(res) {  
        self.drawImg(res.tempFilePath);  
    },  
    fail: function(err) {  
        console.log(err)  
        self.$showToast({  
            text: '下载失败'  
        })  
    }  
})  

drawImg(code) {  
setTimeout(() => {  
    const ctx = uni.createCanvasContext('myCanvas');  
    ctx.drawImage(code, 0, 0, 300, 300);  
    ctx.setFontSize(14);  
    ctx.setFillStyle('#6D7278');  
    ctx.fillText('长按或扫描二维码访问我的门店', 50, 280);  
    ctx.draw();  
    this.code = true;  
}, 500);  
},  
save() {  
let self = this;  
uni.canvasToTempFilePath({  
    x: 0,  
    y: 0,  
    width: 300,  
    height: 300,  
    destWidth: 600,  
    destHeight: 600,  
    canvasId: 'myCanvas',  
    success: function(res) {  
        console.log(res.tempFilePath)  
        uni.saveImageToPhotosAlbum({  
            filePath: res.tempFilePath,  
            success: function() {  
                self.$showToast({  
                    text: '保存成功'  
                })  
            },  
            fail: function(err) {  
                console.log(err)  
                self.$showToast({  
                    text: '保存失败,请截图分享'  
                })  
            }  
        });  
    }  
})  
},

15、app中,组件props为string类型,传值为空串“”,app会显示为true,若默认default为“”,其子组件的props也会显示为true

16、复制api

self.copyText = function(text) {  
        uni.setClipboardData({  
            data: text,  
            success: function(res) {  
                uni.getClipboardData({  
                    success: function(res) {  
                        // app会有默认提示“内容已复制”  
                        // self.$common.Toast({  
                        //  text: self.i18.order.copySuc  
                        // });  
                    }  
                })  
            }  
        })  
    }

解决有默认提示

17、uni.uploadFile() h5 不能写content-type参数,写了会报错,app也可以不写

18、picker:有一个app兼容问题,如果默认值为空,首次手动赋值的时候如果index是0, 页面显示不更新,我看了下文档估计是组件默认值为0的原因,H5没有,我现在处理的是index为0的话就先改为其他值,然后setTimeout赋正确值

19、页面隐藏滚动条,包括横向滚动条

//1.设置全局样式,此方法无效  
 ::-webkit-scrollbar {  
    width: 0;  
    height: 0;  
    color: transparent;  
  }
//2.设置全局样式,此方法h5页面有效,app无效  
 ::-webkit-scrollbar {  
    display: none;  
  }
//3.page.json 页面设置此参数,仅对app有效  
{  
      "path": "pages/home/classifyDetail/classifyDetail",  
      "style": {  
        "navigationBarTitleText": "分类详情",  
        "navigationBarBackgroundColor": "#FFFFFF",  
        "app-plus": {  
          // 禁用原生导航栏  
          "titleNView": false,  
          // 手机端隐藏滚动条,保留滚动效果  
          "scrollIndicator":"none"  
        }  
      }  
  },

20、解决安卓点击两次返回按钮关闭应用问题

// 重写quit方法改为隐藏至后台  
if (!this.$common.isIOS()) {  
  let main = plus.android.runtimeMainActivity();  
  plus.runtime.quit = function () {  
    main.moveTaskToBack(false);  
  }  
}

21、跳转最好都写绝对路径,/pages/……
遇过的问题:h5中,首页写相对路径,./ ../ 首次进页面跳转成功,进入其他tab页面,再回来跳转,无效了
@tap=navigateTo(url)
navigator标签
uni.navigateTo()

收起阅读 »

获取手机设备信息、app版本信息、ip地址

Device mui html5plus

获取手机设备的相关信息,如IMEI、IMSI、型号、厂商等。通过plus.device获取设备信息管理对象。
获取当前运行环境信息、与其它程序进行通讯等。通过plus.runtime可获取运行环境管理对象。

直接上demo,复制即可用。

<!doctype html>  
<html>  
    <head>  
        <meta charset="utf-8">  
        <title></title>  
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
        <link href="../../css/mui.min.css" rel="stylesheet" />  
        <script src="../../js/mui.js"></script>  
        <script src="../../js/vue.js"></script>  
        <style>  
            body{max-width: 750px; min-width: 320px; margin: 0 auto; background-color: #F5F5F5;overflow-x: hidden;  
                font-family: -apple-system,Helvetica,sans-serif;}  
            div{font-size: .26rem; color: #474747;line-height: 2;}  
            span{font-size: .28rem; color: #D1021F;}  
        </style>  
        <script>  
            (function(doc, win) {  
                var w = document.documentElement.clientWidth;  
                if (w > 750) {  
                    w = 750  
                } else if (w < 320) {  
                    w = 320  
                }  
                var f = w / 750 * 100 + "px";  
                document.documentElement.style.fontSize = f;  
                var docEl = doc.documentElement,  
                    resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',  
                    recalc = function() {  
                        var clientWidth = docEl.clientWidth > 750 ? 750 : docEl.clientWidth;  
                        if (clientWidth > 750) {  
                            clientWidth = 750  
                        } else if (clientWidth < 320) {  
                            clientWidth = 320  
                        }  
                        if (!clientWidth) return;  
                        docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';  
                    };  

                if (!doc.addEventListener) return;  
                win.addEventListener(resizeEvt, recalc, false);  
                doc.addEventListener('DOMContentLoaded', recalc, false);  
            })(document, window);  
        </script>  
    </head>  

    <body>  
        <header class="mui-bar mui-bar-nav">  
            <h1 class="mui-title">我的手机信息</h1>  
        </header>  
        <div id="content" class="mui-content mui-content-padded">  
            <div class="mui-text-left" v-for="item in list">  
                {{item.title}}  
                <span>  
                    {{item.value}}  
                </span>  
            </div>  
        </div>  

        <script type="text/javascript">  
            var spans = document.getElementsByTagName('span');  
            var VM = new Vue({  
                el: ".mui-content",  
                data: {  
                    list:[]  
                },  

            })  
            mui.plusReady(function() {  
                //获取系统名称  
                var name = plus.os.name;  
                VM.list.push({  
                    "title": "系统名称",  
                    "value": name  
                })  
                //获取系统版本  
                var version = plus.os.version;  
                VM.list.push({  
                    "title": "系统版本",  
                    "value": version  
                })  
                //设备型号  
                VM.list.push({  
                    "title": "设备型号",  
                    "value": plus.device.model  
                })  
                //获取生产厂商  
                var vendor2 = plus.device.vendor  
                VM.list.push({  
                    "title": "生产厂商",  
                    "value": vendor2  
                })  
                //获取系统供应商  
                var vendor = plus.os.vendor  
                VM.list.push({  
                    "title": "系统供应商",  
                    "value": vendor  
                })  
                //获取系统语言信息  
                var language = plus.os.language;  
                VM.list.push({  
                    "title": "系统语言信息",  
                    "value": language  
                })  

                var types = {}; //网络类型  
                types[plus.networkinfo.CONNECTION_UNKNOW] = "未知";  
                types[plus.networkinfo.CONNECTION_NONE] = "未连接网络";  
                types[plus.networkinfo.CONNECTION_ETHERNET] = "有线网络";  
                types[plus.networkinfo.CONNECTION_WIFI] = "WiFi网络";  
                types[plus.networkinfo.CONNECTION_CELL2G] = "2G蜂窝网络";  
                types[plus.networkinfo.CONNECTION_CELL3G] = "3G蜂窝网络";  
                types[plus.networkinfo.CONNECTION_CELL4G] = "4G蜂窝网络";  
                var network = types[plus.networkinfo.getCurrentType()];  
                VM.list.push({  
                    "title": "网络类型",  
                    "value": network  
                })  
                //获取设备的唯一标示  
                plus.device.getInfo({  
                    success: function(e) {  
                        VM.list.push({  
                            "title": "国际移动设备身份码imei",  
                            "value": e.imei  
                        })  
                        VM.list.push({  
                            "title": "国际移动用户识别码imsi",  
                            "value": e.imsi  
                        })  
                        VM.list.push({  
                            "title": "设备的唯一标识",  
                            "value": e.uuid  
                        })  
                    },  
                    fail: function(e) {  
                        console.log('getDeviceInfo failed: ' + JSON.stringify(e));  
                    }  
                });  
                //获取APP版本信息  
                plus.runtime.getProperty(plus.runtime.appid, function(inf) {  
                    var ver = inf.version;  
                    VM.list.push({  
                        "title": "APP名称",  
                        "value": inf.name  
                    })  
                    VM.list.push({  
                        "title": "APP版本信息",  
                        "value": "版本:"+inf.version+";版本号:"+inf.versionCode  
                    })  
                    console.log(JSON.stringify(inf))  
                })  

            });  
            //获取IP地址信息  
            function addScriptTag(src) {  
                var script = document.createElement('script');  
                script.setAttribute("type", "text/javascript");  
                script.src = src;  
                document.body.appendChild(script);  
            }  

            function foo(data) {  
                var json = data.data[0];  

                VM.list.push({  
                    "title": "位置",  
                    "value": json.location  
                })  
                VM.list.push({  
                    "title": "IP地址",  
                    "value": json.origip  
                })  
                console.log("IPInfo:"+ JSON.stringify(json));  
            };  
            window.onload = function() {  
                addScriptTag(  
                    'https://sp0.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?query=ip&co=&resource_id=6006&t=1562124098965&ie=utf8&oe=gbk&cb=foo&format=json&tn=baidu'  
                );  
            }  
        </script>  
    </body>  
</html>  

注意:
获取IP地址和plus.device.getInfo都是异步的,所以在使用的时候要注意时机

运行结果:
Image text

Android和IOS获取imei、imsi、uuid时须知:

imei: (String 类型 )设备的国际移动设备身份码

如果设备不支持或无法获取(如用户未授权)则返回空字符串。 如果设备存在多个身份码,则以“,”字符分割拼接,如“862470039452950,862470039452943”。
平台支持

Android - ALL (支持): 需要用户授权才能获取,如果用户拒绝获取设备信息则返回空字符串。  
iOS - ALL (不支持): 无法获取设备身份码,返回空字符串。  

imsi: (Array[ String ] 类型 )设备的国际移动用户识别码

字符串数组类型,获取设备上插入SIM的国际移动设备身份码。 如果设备支持多卡模式则返回所有SIM身份码。 如果设备不支持或没有插入SIM卡则返回空数组。
平台支持

Android - ALL (支持): 如果无法获取国际移动用户标识(如用户未授权)则返回空数组。  
iOS - ALL (不支持): 无法获取设备移动用户识别码,返回空数组。  

uuid: (String 类型 )设备标识

设备的唯一标识号。
平台支持

Android - ALL (支持): 与设备的imei号一致。 注意:如果无法获取设备imei则使用设备wifi的mac地址,如果无法获取设备mac地址则随机生成设备标识号(不同App在同一台设备上获取的值一致)。  
iOS - ALL (不支持): 根据包名随机生成的设备标识号。 注意:设备重置(刷机)后会重新生成  

其他的属性和方法,参考html5plus官网:
http://www.html5plus.org/doc/zh_cn/device.html

继续阅读 »

获取手机设备的相关信息,如IMEI、IMSI、型号、厂商等。通过plus.device获取设备信息管理对象。
获取当前运行环境信息、与其它程序进行通讯等。通过plus.runtime可获取运行环境管理对象。

直接上demo,复制即可用。

<!doctype html>  
<html>  
    <head>  
        <meta charset="utf-8">  
        <title></title>  
        <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
        <link href="../../css/mui.min.css" rel="stylesheet" />  
        <script src="../../js/mui.js"></script>  
        <script src="../../js/vue.js"></script>  
        <style>  
            body{max-width: 750px; min-width: 320px; margin: 0 auto; background-color: #F5F5F5;overflow-x: hidden;  
                font-family: -apple-system,Helvetica,sans-serif;}  
            div{font-size: .26rem; color: #474747;line-height: 2;}  
            span{font-size: .28rem; color: #D1021F;}  
        </style>  
        <script>  
            (function(doc, win) {  
                var w = document.documentElement.clientWidth;  
                if (w > 750) {  
                    w = 750  
                } else if (w < 320) {  
                    w = 320  
                }  
                var f = w / 750 * 100 + "px";  
                document.documentElement.style.fontSize = f;  
                var docEl = doc.documentElement,  
                    resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',  
                    recalc = function() {  
                        var clientWidth = docEl.clientWidth > 750 ? 750 : docEl.clientWidth;  
                        if (clientWidth > 750) {  
                            clientWidth = 750  
                        } else if (clientWidth < 320) {  
                            clientWidth = 320  
                        }  
                        if (!clientWidth) return;  
                        docEl.style.fontSize = 100 * (clientWidth / 750) + 'px';  
                    };  

                if (!doc.addEventListener) return;  
                win.addEventListener(resizeEvt, recalc, false);  
                doc.addEventListener('DOMContentLoaded', recalc, false);  
            })(document, window);  
        </script>  
    </head>  

    <body>  
        <header class="mui-bar mui-bar-nav">  
            <h1 class="mui-title">我的手机信息</h1>  
        </header>  
        <div id="content" class="mui-content mui-content-padded">  
            <div class="mui-text-left" v-for="item in list">  
                {{item.title}}  
                <span>  
                    {{item.value}}  
                </span>  
            </div>  
        </div>  

        <script type="text/javascript">  
            var spans = document.getElementsByTagName('span');  
            var VM = new Vue({  
                el: ".mui-content",  
                data: {  
                    list:[]  
                },  

            })  
            mui.plusReady(function() {  
                //获取系统名称  
                var name = plus.os.name;  
                VM.list.push({  
                    "title": "系统名称",  
                    "value": name  
                })  
                //获取系统版本  
                var version = plus.os.version;  
                VM.list.push({  
                    "title": "系统版本",  
                    "value": version  
                })  
                //设备型号  
                VM.list.push({  
                    "title": "设备型号",  
                    "value": plus.device.model  
                })  
                //获取生产厂商  
                var vendor2 = plus.device.vendor  
                VM.list.push({  
                    "title": "生产厂商",  
                    "value": vendor2  
                })  
                //获取系统供应商  
                var vendor = plus.os.vendor  
                VM.list.push({  
                    "title": "系统供应商",  
                    "value": vendor  
                })  
                //获取系统语言信息  
                var language = plus.os.language;  
                VM.list.push({  
                    "title": "系统语言信息",  
                    "value": language  
                })  

                var types = {}; //网络类型  
                types[plus.networkinfo.CONNECTION_UNKNOW] = "未知";  
                types[plus.networkinfo.CONNECTION_NONE] = "未连接网络";  
                types[plus.networkinfo.CONNECTION_ETHERNET] = "有线网络";  
                types[plus.networkinfo.CONNECTION_WIFI] = "WiFi网络";  
                types[plus.networkinfo.CONNECTION_CELL2G] = "2G蜂窝网络";  
                types[plus.networkinfo.CONNECTION_CELL3G] = "3G蜂窝网络";  
                types[plus.networkinfo.CONNECTION_CELL4G] = "4G蜂窝网络";  
                var network = types[plus.networkinfo.getCurrentType()];  
                VM.list.push({  
                    "title": "网络类型",  
                    "value": network  
                })  
                //获取设备的唯一标示  
                plus.device.getInfo({  
                    success: function(e) {  
                        VM.list.push({  
                            "title": "国际移动设备身份码imei",  
                            "value": e.imei  
                        })  
                        VM.list.push({  
                            "title": "国际移动用户识别码imsi",  
                            "value": e.imsi  
                        })  
                        VM.list.push({  
                            "title": "设备的唯一标识",  
                            "value": e.uuid  
                        })  
                    },  
                    fail: function(e) {  
                        console.log('getDeviceInfo failed: ' + JSON.stringify(e));  
                    }  
                });  
                //获取APP版本信息  
                plus.runtime.getProperty(plus.runtime.appid, function(inf) {  
                    var ver = inf.version;  
                    VM.list.push({  
                        "title": "APP名称",  
                        "value": inf.name  
                    })  
                    VM.list.push({  
                        "title": "APP版本信息",  
                        "value": "版本:"+inf.version+";版本号:"+inf.versionCode  
                    })  
                    console.log(JSON.stringify(inf))  
                })  

            });  
            //获取IP地址信息  
            function addScriptTag(src) {  
                var script = document.createElement('script');  
                script.setAttribute("type", "text/javascript");  
                script.src = src;  
                document.body.appendChild(script);  
            }  

            function foo(data) {  
                var json = data.data[0];  

                VM.list.push({  
                    "title": "位置",  
                    "value": json.location  
                })  
                VM.list.push({  
                    "title": "IP地址",  
                    "value": json.origip  
                })  
                console.log("IPInfo:"+ JSON.stringify(json));  
            };  
            window.onload = function() {  
                addScriptTag(  
                    'https://sp0.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?query=ip&co=&resource_id=6006&t=1562124098965&ie=utf8&oe=gbk&cb=foo&format=json&tn=baidu'  
                );  
            }  
        </script>  
    </body>  
</html>  

注意:
获取IP地址和plus.device.getInfo都是异步的,所以在使用的时候要注意时机

运行结果:
Image text

Android和IOS获取imei、imsi、uuid时须知:

imei: (String 类型 )设备的国际移动设备身份码

如果设备不支持或无法获取(如用户未授权)则返回空字符串。 如果设备存在多个身份码,则以“,”字符分割拼接,如“862470039452950,862470039452943”。
平台支持

Android - ALL (支持): 需要用户授权才能获取,如果用户拒绝获取设备信息则返回空字符串。  
iOS - ALL (不支持): 无法获取设备身份码,返回空字符串。  

imsi: (Array[ String ] 类型 )设备的国际移动用户识别码

字符串数组类型,获取设备上插入SIM的国际移动设备身份码。 如果设备支持多卡模式则返回所有SIM身份码。 如果设备不支持或没有插入SIM卡则返回空数组。
平台支持

Android - ALL (支持): 如果无法获取国际移动用户标识(如用户未授权)则返回空数组。  
iOS - ALL (不支持): 无法获取设备移动用户识别码,返回空数组。  

uuid: (String 类型 )设备标识

设备的唯一标识号。
平台支持

Android - ALL (支持): 与设备的imei号一致。 注意:如果无法获取设备imei则使用设备wifi的mac地址,如果无法获取设备mac地址则随机生成设备标识号(不同App在同一台设备上获取的值一致)。  
iOS - ALL (不支持): 根据包名随机生成的设备标识号。 注意:设备重置(刷机)后会重新生成  

其他的属性和方法,参考html5plus官网:
http://www.html5plus.org/doc/zh_cn/device.html

收起阅读 »

uni-app仿微信聊天uni_chatroom实例|uniapp仿微信

uniapp模板

前几天给大家分享了一个uniapp自定义弹窗组件,今天给大家分享基于uniapp+vue+vuex等技术开发的仿微信界面App聊天室UniChatRoom,实现了发送消息、图片预览、红包、长按菜单、地图位置、仿朋友圈等功能。
uni-app自定义仿微信/ios弹出框:https://ask.dcloud.net.cn/article/36440

三端效果图如下:

使用技术:

  • 编辑器:HBuilder X
  • 技术框架:uni-app + vue
  • 状态管理:Vuex
  • iconfont图标:阿里字体图标库
  • 自定义导航栏 + 底部Tabbar
  • 弹窗组件:uniPop(基于uni-app封装模态弹窗)
  • 测试环境:H5端 + 小程序 + App端(三端均兼容)

引入公共部分组件及样式

import Vue from 'vue'  
import App from './App'  

// >>>引入css  
import './assets/fonts/iconfont.css'  
import './assets/css/reset.css'  
import './assets/css/layout.css'  

// >>>引入状态管理  
import store from './store'  
Vue.prototype.$store = store  

// >>>引入公共组件  
import headerBar from './components/header/header.vue'  
import tabBar from './components/tabbar/tabbar.vue'  
import popupWindow from './components/popupWindow.vue'  
Vue.component('header-bar', headerBar)  
Vue.component('tab-bar', tabBar)  
Vue.component('popup-window', popupWindow)  

// >>>引入uniPop弹窗组件  
import uniPop from './components/uniPop/uniPop.vue'  
Vue.component('uni-pop', uniPop)  

Vue.config.productionTip = false  
App.mpType = 'app'  

const app = new Vue({  
    ...App  
})  
app.$mount()

uniapp朋友圈功能实现

/**  
 * @tpl 朋友圈模板  
 */  

<template>  
    <view class="flexbox flex_col">  
        <header-bar :isBack="true" title="朋友圈" :bgColor="{background: headerBarBackground}" transparent>  
            <text slot="back" class="uni_btnIco iconfont icon-arrL"></text>  
            <text slot="iconfont" class="uni_btnIco iconfont icon-publish mr_5" @tap="handlePublish"></text>  
        </header-bar>  

        <view class="uni__scrollview flex1">  
            <view class="uni-friendZone">  
                ...  
            </view>  
        </view>  
    </view>  
</template>  

<script>  
    export default {  
        data() {  
            return {  
                headerBarBackground: 'transparent'  
            }  
        },  
        onPageScroll : function(e) {  
            // console.log("滚动距离为:" + e.scrollTop);  
            this.headerBarBackground = 'rgba(65,168,99,'+e.scrollTop / 200+')'  
        },  
        methods: {  
            ...  
        }  
    }  
</script>  

<style scoped>  

</style>

uniapp使用scroll-view组件,通过判断聊天内容高度和scroll-view高度的大小设置滚动距离

<scroll-view id="scrollview" scroll-y="true" :scroll-top="scrollTop" style="height: 100%;">  
    <view class="uni-chatMsgCnt" id="msglistview">  
        <view class="msgitem">xxx</view>  
        <view class="msgitem">xxx</view>  
        <view class="msgitem">xxx</view>  
        ...  
    </view>  
</scroll-view>  

export default {  
    data() {  
        return {  
            scrollTop: 0,  
            ...  
        }  
    },  
    mounted() {  
        this.scrollToBottom()  
    },  
    updated() {  
        this.scrollToBottom()  
    },  
    methods: {  
        // 滚动至聊天底部  
        scrollToBottom(t) {  
            let that = this  
            let query = uni.createSelectorQuery()  
            query.select('#scrollview').boundingClientRect()  
            query.select('#msglistview').boundingClientRect()  
            query.exec((res) => {  
                // console.log(res)  
                if(res[1].height > res[0].height){  
                    that.scrollTop = res[1].height - res[0].height  
                }  
            })  
        },  
        ...  
    }  
}

作者:xiaoyan2015
链接: https://segmentfault.com/a/1190000020636455
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

继续阅读 »

前几天给大家分享了一个uniapp自定义弹窗组件,今天给大家分享基于uniapp+vue+vuex等技术开发的仿微信界面App聊天室UniChatRoom,实现了发送消息、图片预览、红包、长按菜单、地图位置、仿朋友圈等功能。
uni-app自定义仿微信/ios弹出框:https://ask.dcloud.net.cn/article/36440

三端效果图如下:

使用技术:

  • 编辑器:HBuilder X
  • 技术框架:uni-app + vue
  • 状态管理:Vuex
  • iconfont图标:阿里字体图标库
  • 自定义导航栏 + 底部Tabbar
  • 弹窗组件:uniPop(基于uni-app封装模态弹窗)
  • 测试环境:H5端 + 小程序 + App端(三端均兼容)

引入公共部分组件及样式

import Vue from 'vue'  
import App from './App'  

// >>>引入css  
import './assets/fonts/iconfont.css'  
import './assets/css/reset.css'  
import './assets/css/layout.css'  

// >>>引入状态管理  
import store from './store'  
Vue.prototype.$store = store  

// >>>引入公共组件  
import headerBar from './components/header/header.vue'  
import tabBar from './components/tabbar/tabbar.vue'  
import popupWindow from './components/popupWindow.vue'  
Vue.component('header-bar', headerBar)  
Vue.component('tab-bar', tabBar)  
Vue.component('popup-window', popupWindow)  

// >>>引入uniPop弹窗组件  
import uniPop from './components/uniPop/uniPop.vue'  
Vue.component('uni-pop', uniPop)  

Vue.config.productionTip = false  
App.mpType = 'app'  

const app = new Vue({  
    ...App  
})  
app.$mount()

uniapp朋友圈功能实现

/**  
 * @tpl 朋友圈模板  
 */  

<template>  
    <view class="flexbox flex_col">  
        <header-bar :isBack="true" title="朋友圈" :bgColor="{background: headerBarBackground}" transparent>  
            <text slot="back" class="uni_btnIco iconfont icon-arrL"></text>  
            <text slot="iconfont" class="uni_btnIco iconfont icon-publish mr_5" @tap="handlePublish"></text>  
        </header-bar>  

        <view class="uni__scrollview flex1">  
            <view class="uni-friendZone">  
                ...  
            </view>  
        </view>  
    </view>  
</template>  

<script>  
    export default {  
        data() {  
            return {  
                headerBarBackground: 'transparent'  
            }  
        },  
        onPageScroll : function(e) {  
            // console.log("滚动距离为:" + e.scrollTop);  
            this.headerBarBackground = 'rgba(65,168,99,'+e.scrollTop / 200+')'  
        },  
        methods: {  
            ...  
        }  
    }  
</script>  

<style scoped>  

</style>

uniapp使用scroll-view组件,通过判断聊天内容高度和scroll-view高度的大小设置滚动距离

<scroll-view id="scrollview" scroll-y="true" :scroll-top="scrollTop" style="height: 100%;">  
    <view class="uni-chatMsgCnt" id="msglistview">  
        <view class="msgitem">xxx</view>  
        <view class="msgitem">xxx</view>  
        <view class="msgitem">xxx</view>  
        ...  
    </view>  
</scroll-view>  

export default {  
    data() {  
        return {  
            scrollTop: 0,  
            ...  
        }  
    },  
    mounted() {  
        this.scrollToBottom()  
    },  
    updated() {  
        this.scrollToBottom()  
    },  
    methods: {  
        // 滚动至聊天底部  
        scrollToBottom(t) {  
            let that = this  
            let query = uni.createSelectorQuery()  
            query.select('#scrollview').boundingClientRect()  
            query.select('#msglistview').boundingClientRect()  
            query.exec((res) => {  
                // console.log(res)  
                if(res[1].height > res[0].height){  
                    that.scrollTop = res[1].height - res[0].height  
                }  
            })  
        },  
        ...  
    }  
}

作者:xiaoyan2015
链接: https://segmentfault.com/a/1190000020636455
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

收起阅读 »

安卓禁止弹出软件盘

虚拟键盘

最近在做一个项目,其中有个功能需要自动输入数据(扫码枪或者是IC读卡器)。用input每次都会弹出虚拟键盘,非常不方便,然后在论坛里面找到加上readonly就行,实测不行。最后发现扫码枪(部分)、IC读卡器是模拟键盘输入的,就想到了另外一个解决方案。

问题:禁止安卓输入框弹出软键盘

解决思路:用jQuery获取按键值,然后把按键值转换回按键,之后累加到自己想要的数位后可以直接填充到相应位置或操作。

代码如下:

            card_id = "";  
            old_card_id = "";  
            $("body").on("keyup", function(event) {//获取键值  
                old_card_id = "";  
                switch(event.which){//键值转换为所按按键(下面为数字0-9键值)  
                    case 48:  
                    old_card_id = 0;  
                    break;  

                    case 49:  
                    old_card_id = 1;  
                    break;  

                    case 50:  
                    old_card_id = 2;  
                    break;  

                    case 51:  
                    old_card_id = 3;  
                    break;  

                    case 52:  
                    old_card_id = 4;  
                    break;  

                    case 53:  
                    old_card_id = 5;  
                    break;  

                    case 54:  
                    old_card_id = 6;  
                    break;  

                    case 55:  
                    old_card_id = 7;  
                    break;  

                    case 56:  
                    old_card_id = 8;  
                    break;  

                    case 57:  
                    old_card_id = 9;  
                    break;  

                    default:  
                    card_id = "";  
                    document.onkeydown=null;  
                    break;  
                };  
                card_id = card_id + old_card_id;//把转换后的按键累计成字符串  
                if (card_id.length == 10) {//需要的字符个数  
                    console.log(card_id);  
                    alert(card_id);  
                    card_id = "";  
                }if(card_id.length > 10){//本处可不需要,因为我的IC读卡器是18位的,我只需要10位数字,所以这里我过滤了超过10位数字的字符串  
                    card_id = "";  
                }  
            });

最后,也许代码有些乱或者冗余。各位需要的小伙伴看一下思路就行。

继续阅读 »

最近在做一个项目,其中有个功能需要自动输入数据(扫码枪或者是IC读卡器)。用input每次都会弹出虚拟键盘,非常不方便,然后在论坛里面找到加上readonly就行,实测不行。最后发现扫码枪(部分)、IC读卡器是模拟键盘输入的,就想到了另外一个解决方案。

问题:禁止安卓输入框弹出软键盘

解决思路:用jQuery获取按键值,然后把按键值转换回按键,之后累加到自己想要的数位后可以直接填充到相应位置或操作。

代码如下:

            card_id = "";  
            old_card_id = "";  
            $("body").on("keyup", function(event) {//获取键值  
                old_card_id = "";  
                switch(event.which){//键值转换为所按按键(下面为数字0-9键值)  
                    case 48:  
                    old_card_id = 0;  
                    break;  

                    case 49:  
                    old_card_id = 1;  
                    break;  

                    case 50:  
                    old_card_id = 2;  
                    break;  

                    case 51:  
                    old_card_id = 3;  
                    break;  

                    case 52:  
                    old_card_id = 4;  
                    break;  

                    case 53:  
                    old_card_id = 5;  
                    break;  

                    case 54:  
                    old_card_id = 6;  
                    break;  

                    case 55:  
                    old_card_id = 7;  
                    break;  

                    case 56:  
                    old_card_id = 8;  
                    break;  

                    case 57:  
                    old_card_id = 9;  
                    break;  

                    default:  
                    card_id = "";  
                    document.onkeydown=null;  
                    break;  
                };  
                card_id = card_id + old_card_id;//把转换后的按键累计成字符串  
                if (card_id.length == 10) {//需要的字符个数  
                    console.log(card_id);  
                    alert(card_id);  
                    card_id = "";  
                }if(card_id.length > 10){//本处可不需要,因为我的IC读卡器是18位的,我只需要10位数字,所以这里我过滤了超过10位数字的字符串  
                    card_id = "";  
                }  
            });

最后,也许代码有些乱或者冗余。各位需要的小伙伴看一下思路就行。

收起阅读 »

Android检查手机是否被root

Native.JS

论坛里找了好久,没找到demo,只好自己摸索,借鉴了一下,自己测试了一下是可以的,分享出来,欢迎补充

                let isRoot = false;  
                const paths = ["/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su",  
                                "/system/bin/failsafe/su", "/data/local/su"];  

                var File = plus.android.importClass("java.io.File");  
                for (let i = 0; i < paths.length; i++) {  
                    let path = paths[i];  
                    var fd = new File(path);  
                    if(fd.exists()){  
                       isRoot = true;  
                       break;  
                    }  
                }
继续阅读 »

论坛里找了好久,没找到demo,只好自己摸索,借鉴了一下,自己测试了一下是可以的,分享出来,欢迎补充

                let isRoot = false;  
                const paths = ["/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su",  
                                "/system/bin/failsafe/su", "/data/local/su"];  

                var File = plus.android.importClass("java.io.File");  
                for (let i = 0; i < paths.length; i++) {  
                    let path = paths[i];  
                    var fd = new File(path);  
                    if(fd.exists()){  
                       isRoot = true;  
                       break;  
                    }  
                }
收起阅读 »

HBuilderX初级安装使用教程

HBuilderX安装使用教程

1. 安装HBuilderX

HBuilderX官网下载地址https://www.dcloud.io/hbuilderx.html

打开网站后,点击Download按钮,弹窗如下:

  • App开发版,包含大部分App开发插件。App开发,指的是手机应用开发。
  • 如果您是初学者开发前端,建议下载 标准版 。后期如果学习App开发,可以到【插件安装】中,安装相关插件

  1. Windows版本,下载的是一个 zip包,需要解压后才能使用。解压软件可以使用:360压缩软件、或7zip压缩软件

  2. 压缩过程,不要中断。不要在压缩软件中打开exe。

  3. 解压完成后,点击HBuilderX.exe ,如下图

注意:如果您无法打开或启动报错,请参考此贴 windows启动问题排查

2. 初识HBuilderX

HBuilderX首次启动后,打开了一个HBuilderX自述文件.md, 如下图:

HBuilderX自述文件.md: 是一个markdown文件,什么是markdown呢?就是个文本语言。 有兴趣的同学,点此了解Markdown

有兴趣的同学,可以读一读这个文件。或者直接点击右上角的x,关闭此文件。

关闭后的页面窗口如下:

3. 创建项目,开始HX旅程

点击【新建项目】、或点击工具栏上第一个图标(就是带有红色+号)、或直接 Ctrl + N ,调出创建项目窗口

新建项目后,如下图:

4. 编写代码

如上图所示,HBuilderX拥有强大的代码助手提示,可以帮您少敲很多代码。

5. 查看代码效果

如下图所示,点击【预览】可以查看代码效果。

如果您首次点击【预览】,又没有安装【内置浏览器】,点击预览的时候,会提示您安装插件,点击确定即可。

6. 安装插件

点击菜单【工具】【插件安装】

7. 文件单击与文件双击

如上图,标红部分,显示效果不一样。

这是因为:单击预览文件,双击打开文件

继续阅读 »

1. 安装HBuilderX

HBuilderX官网下载地址https://www.dcloud.io/hbuilderx.html

打开网站后,点击Download按钮,弹窗如下:

  • App开发版,包含大部分App开发插件。App开发,指的是手机应用开发。
  • 如果您是初学者开发前端,建议下载 标准版 。后期如果学习App开发,可以到【插件安装】中,安装相关插件

  1. Windows版本,下载的是一个 zip包,需要解压后才能使用。解压软件可以使用:360压缩软件、或7zip压缩软件

  2. 压缩过程,不要中断。不要在压缩软件中打开exe。

  3. 解压完成后,点击HBuilderX.exe ,如下图

注意:如果您无法打开或启动报错,请参考此贴 windows启动问题排查

2. 初识HBuilderX

HBuilderX首次启动后,打开了一个HBuilderX自述文件.md, 如下图:

HBuilderX自述文件.md: 是一个markdown文件,什么是markdown呢?就是个文本语言。 有兴趣的同学,点此了解Markdown

有兴趣的同学,可以读一读这个文件。或者直接点击右上角的x,关闭此文件。

关闭后的页面窗口如下:

3. 创建项目,开始HX旅程

点击【新建项目】、或点击工具栏上第一个图标(就是带有红色+号)、或直接 Ctrl + N ,调出创建项目窗口

新建项目后,如下图:

4. 编写代码

如上图所示,HBuilderX拥有强大的代码助手提示,可以帮您少敲很多代码。

5. 查看代码效果

如下图所示,点击【预览】可以查看代码效果。

如果您首次点击【预览】,又没有安装【内置浏览器】,点击预览的时候,会提示您安装插件,点击确定即可。

6. 安装插件

点击菜单【工具】【插件安装】

7. 文件单击与文件双击

如上图,标红部分,显示效果不一样。

这是因为:单击预览文件,双击打开文件

收起阅读 »

关于MUI安卓方向急需改进的方面

mui

安卓项目很多都需要依赖Jar包各方面的扩展,但是MUI这方面支持很不好,需要自定义增加sdk的话,就必须离线打包,处理起来很麻烦

安卓项目很多都需要依赖Jar包各方面的扩展,但是MUI这方面支持很不好,需要自定义增加sdk的话,就必须离线打包,处理起来很麻烦

在uni-app中对优博讯和新大陆安卓(Android)PDA的条码扫码广播监听(带示例附件)

我们在开发的时候需要获取优博讯PDA的条码扫码内容,根据前人的分享及自己找的一些资料整理示例代码如下,希望能帮助到有需要的人。为了避免刚入门的人无从下手,特增加了附件。

以下是示例,真正在用的时候,建议在app启动之后就开始监听,并通过全局事件来推送给需要的页面,而不是在每个页面都自己去监听,有利于代码的维护和提升性能。全局事件的监听和释放可参考我的另一个帖子:关于uni-app全局事件监听和释放监听的分享

另外优博讯的PDA需要在系统的:设置->扫描->Default->关闭(键盘方式输出) 才能使用广播监听。

onLoad() {  

            page = this;  

            this.title = "开始监听!";  

            var main = plus.android.runtimeMainActivity(); //获取activity    
            var context = plus.android.importClass('android.content.Context'); //上下文    
            var receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {  
                onReceive: doReceive  
            });  
            var IntentFilter = plus.android.importClass('android.content.IntentFilter');  
            var Intent = plus.android.importClass('android.content.Intent');  
            var filter = new IntentFilter();  

            //针对优博讯安卓PDA-i6300A添加监听,其它优博讯的型号应该一样或类似  
            filter.addAction("android.intent.ACTION_DECODE_DATA"); //监听扫描  

            main.registerReceiver(receiver, filter); //注册监听    

            function doReceive(context, intent) {  

                //通过intent实例引入intent类,方便以后的‘.’操作    
                plus.android.importClass(intent);  

                //条码内容  
                var barcodeBytes = intent.getByteArrayExtra("barcode");  
                var barcode = byteToString(barcodeBytes);  

                //条码长度  
                var barcodeLength = intent.getIntExtra("length", 0);  
                //var myArray = new ArrayBuffer(0);  
                //条码类型  
                var barcodeTypeBytes = intent.getByteExtra("barcodeType", (0 | 0));  
                var barcodeType = byteToString(barcodeTypeBytes);  

                // uni.showModal({  
                //  content: '条码:' + barcode + '\r\n长度:' + barcodeLength + '\r\n类型:' + barcodeType,  
                //  showCancel: false  
                // });  
                page.title = barcode;  
                //console.log(barcode);    
                //main.unregisterReceiver(receiver);//取消监听    
            }  

            function byteToString(arr) {  
                if (typeof arr === 'string') {  
                    return arr;  
                }  
                var str = '',  
                    _arr = arr;  
                for (var i = 0; i < _arr.length; i++) {  
                    var one = _arr[i].toString(2),  
                        v = one.match(/^1+?(?=0)/);  
                    if (v && one.length == 8) {  
                        var bytesLength = v[0].length;  
                        var store = _arr[i].toString(2).slice(7 - bytesLength);  
                        for (var st = 1; st < bytesLength; st++) {  
                            store += _arr[st + i].toString(2).slice(2);  
                        }  
                        str += String.fromCharCode(parseInt(store, 2));  
                        i += bytesLength - 1;  
                    } else {  
                        str += String.fromCharCode(_arr[i]);  
                    }  
                }  
                return str;  
            }  

        }

下面是新大陆PDA的扫码方式

    //新大陆 智联天地 N7000R 获取方法  

    //#ifdef APP-PLUS  

            //this.title = "开始监听!";  

            var main = plus.android.runtimeMainActivity(); //获取activity    

            var context = plus.android.importClass('android.content.Context'); //上下文    

            var receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {  

                onReceive: doReceive  

            });  

            var IntentFilter = plus.android.importClass('android.content.IntentFilter');  

            var Intent = plus.android.importClass('android.content.Intent');  

            var filter = new IntentFilter();  

            filter.addAction("com.android.server.scannerservice.broadcast"); //监听扫描  

            // filter.addAction("android.intent.extra.SCAN_DATA"); //监听扫描  

            main.registerReceiver(receiver, filter); //注册监听    

            function doReceive(context, intent) {  

                //通过intent实例引入intent类,方便以后的‘.’操作    

                plus.android.importClass(intent);  

                var barcodeBytes1 = intent.getStringExtra("scannerdata");  

                console.log(page.openCustmerScan);  

                if (page.openCustmerScan) {  

                    page.cylinderRecovery.customerId = barcodeBytes1  

                } else {  

                    page.setval(barcodeBytes1)  

                }  

            }  
    //#ifdef APP-PLUS  

            //this.title = "开始监听!";  

            var main = plus.android.runtimeMainActivity(); //获取activity    

            var context = plus.android.importClass('android.content.Context'); //上下文    

            var receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {  

                onReceive: doReceive  

            });  

            var IntentFilter = plus.android.importClass('android.content.IntentFilter');  

            var Intent = plus.android.importClass('android.content.Intent');  

            var filter = new IntentFilter();  

            filter.addAction("com.android.server.scannerservice.broadcast"); //监听扫描  

            // filter.addAction("android.intent.extra.SCAN_DATA"); //监听扫描  

            main.registerReceiver(receiver, filter); //注册监听    

            function doReceive(context, intent) {  

                //通过intent实例引入intent类,方便以后的‘.’操作    

                plus.android.importClass(intent);  

                var barcodeBytes1 = intent.getStringExtra("scannerdata");  

                console.log(page.openCustmerScan);  

                if (page.openCustmerScan) {  

                    page.cylinderRecovery.customerId = barcodeBytes1  

                } else {  

                    page.setval(barcodeBytes1)  

                }  

            }  

            //#endif  
            //#endif
继续阅读 »

我们在开发的时候需要获取优博讯PDA的条码扫码内容,根据前人的分享及自己找的一些资料整理示例代码如下,希望能帮助到有需要的人。为了避免刚入门的人无从下手,特增加了附件。

以下是示例,真正在用的时候,建议在app启动之后就开始监听,并通过全局事件来推送给需要的页面,而不是在每个页面都自己去监听,有利于代码的维护和提升性能。全局事件的监听和释放可参考我的另一个帖子:关于uni-app全局事件监听和释放监听的分享

另外优博讯的PDA需要在系统的:设置->扫描->Default->关闭(键盘方式输出) 才能使用广播监听。

onLoad() {  

            page = this;  

            this.title = "开始监听!";  

            var main = plus.android.runtimeMainActivity(); //获取activity    
            var context = plus.android.importClass('android.content.Context'); //上下文    
            var receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {  
                onReceive: doReceive  
            });  
            var IntentFilter = plus.android.importClass('android.content.IntentFilter');  
            var Intent = plus.android.importClass('android.content.Intent');  
            var filter = new IntentFilter();  

            //针对优博讯安卓PDA-i6300A添加监听,其它优博讯的型号应该一样或类似  
            filter.addAction("android.intent.ACTION_DECODE_DATA"); //监听扫描  

            main.registerReceiver(receiver, filter); //注册监听    

            function doReceive(context, intent) {  

                //通过intent实例引入intent类,方便以后的‘.’操作    
                plus.android.importClass(intent);  

                //条码内容  
                var barcodeBytes = intent.getByteArrayExtra("barcode");  
                var barcode = byteToString(barcodeBytes);  

                //条码长度  
                var barcodeLength = intent.getIntExtra("length", 0);  
                //var myArray = new ArrayBuffer(0);  
                //条码类型  
                var barcodeTypeBytes = intent.getByteExtra("barcodeType", (0 | 0));  
                var barcodeType = byteToString(barcodeTypeBytes);  

                // uni.showModal({  
                //  content: '条码:' + barcode + '\r\n长度:' + barcodeLength + '\r\n类型:' + barcodeType,  
                //  showCancel: false  
                // });  
                page.title = barcode;  
                //console.log(barcode);    
                //main.unregisterReceiver(receiver);//取消监听    
            }  

            function byteToString(arr) {  
                if (typeof arr === 'string') {  
                    return arr;  
                }  
                var str = '',  
                    _arr = arr;  
                for (var i = 0; i < _arr.length; i++) {  
                    var one = _arr[i].toString(2),  
                        v = one.match(/^1+?(?=0)/);  
                    if (v && one.length == 8) {  
                        var bytesLength = v[0].length;  
                        var store = _arr[i].toString(2).slice(7 - bytesLength);  
                        for (var st = 1; st < bytesLength; st++) {  
                            store += _arr[st + i].toString(2).slice(2);  
                        }  
                        str += String.fromCharCode(parseInt(store, 2));  
                        i += bytesLength - 1;  
                    } else {  
                        str += String.fromCharCode(_arr[i]);  
                    }  
                }  
                return str;  
            }  

        }

下面是新大陆PDA的扫码方式

    //新大陆 智联天地 N7000R 获取方法  

    //#ifdef APP-PLUS  

            //this.title = "开始监听!";  

            var main = plus.android.runtimeMainActivity(); //获取activity    

            var context = plus.android.importClass('android.content.Context'); //上下文    

            var receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {  

                onReceive: doReceive  

            });  

            var IntentFilter = plus.android.importClass('android.content.IntentFilter');  

            var Intent = plus.android.importClass('android.content.Intent');  

            var filter = new IntentFilter();  

            filter.addAction("com.android.server.scannerservice.broadcast"); //监听扫描  

            // filter.addAction("android.intent.extra.SCAN_DATA"); //监听扫描  

            main.registerReceiver(receiver, filter); //注册监听    

            function doReceive(context, intent) {  

                //通过intent实例引入intent类,方便以后的‘.’操作    

                plus.android.importClass(intent);  

                var barcodeBytes1 = intent.getStringExtra("scannerdata");  

                console.log(page.openCustmerScan);  

                if (page.openCustmerScan) {  

                    page.cylinderRecovery.customerId = barcodeBytes1  

                } else {  

                    page.setval(barcodeBytes1)  

                }  

            }  
    //#ifdef APP-PLUS  

            //this.title = "开始监听!";  

            var main = plus.android.runtimeMainActivity(); //获取activity    

            var context = plus.android.importClass('android.content.Context'); //上下文    

            var receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {  

                onReceive: doReceive  

            });  

            var IntentFilter = plus.android.importClass('android.content.IntentFilter');  

            var Intent = plus.android.importClass('android.content.Intent');  

            var filter = new IntentFilter();  

            filter.addAction("com.android.server.scannerservice.broadcast"); //监听扫描  

            // filter.addAction("android.intent.extra.SCAN_DATA"); //监听扫描  

            main.registerReceiver(receiver, filter); //注册监听    

            function doReceive(context, intent) {  

                //通过intent实例引入intent类,方便以后的‘.’操作    

                plus.android.importClass(intent);  

                var barcodeBytes1 = intent.getStringExtra("scannerdata");  

                console.log(page.openCustmerScan);  

                if (page.openCustmerScan) {  

                    page.cylinderRecovery.customerId = barcodeBytes1  

                } else {  

                    page.setval(barcodeBytes1)  

                }  

            }  

            //#endif  
            //#endif
收起阅读 »