HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

假如没有mac电脑,我们又如何将ipa文件上传去苹果开发者中心呢?

Appstore上传 ipa iOS打包

随着h5技术和vue技术的流行,现在越来越多人喜欢试用hbuilder、uniapp或apicloud这些框架或工具来生成ios的app,这些工具会帮我们生成一个ipa文件。

假如我们有mac电脑,我们可以使用xcode这些工具将这个ipa文件上传去苹果开发者中心。

但是我们

假如没有mac电脑,我们又如何将ipa文件上传去苹果开发者中心呢?

下面,我们介绍下如何使用初雪云上传ipa文件:


工具的地址如下:https://www.chuxueyun.com/ipawen/index.html
步骤如下:
1.上传ipa

2.输入开发者账号,和专用密码
3.提交任务

> 1.我们解决了混合开发者彻底摆脱Mac系统

> 2.解决了在Mac系统下上传IPA卡住的问题(正在通过App Store进行认证或正在验证 App一直没动静)

> 3.服务器千兆宽带支持,上传速度更快,代替mac的application loader, 一键上传构建文件到开发者中心

> 4.专用密码详解:专用密码不是开发者的账号密码,需要在开发者中心右上角账号名里点"Email Settings"->再点"My Apple ID",登录apple Id的安全管理页面里获取专用密码,这里的专用密码,仅适用于上传ipa构造版本,无法登陆其他网站或者第三方应用!

> 简单三步搞定;地址:https://www.chuxueyun.com/ipaup/index.html

继续阅读 »

随着h5技术和vue技术的流行,现在越来越多人喜欢试用hbuilder、uniapp或apicloud这些框架或工具来生成ios的app,这些工具会帮我们生成一个ipa文件。

假如我们有mac电脑,我们可以使用xcode这些工具将这个ipa文件上传去苹果开发者中心。

但是我们

假如没有mac电脑,我们又如何将ipa文件上传去苹果开发者中心呢?

下面,我们介绍下如何使用初雪云上传ipa文件:


工具的地址如下:https://www.chuxueyun.com/ipawen/index.html
步骤如下:
1.上传ipa

2.输入开发者账号,和专用密码
3.提交任务

> 1.我们解决了混合开发者彻底摆脱Mac系统

> 2.解决了在Mac系统下上传IPA卡住的问题(正在通过App Store进行认证或正在验证 App一直没动静)

> 3.服务器千兆宽带支持,上传速度更快,代替mac的application loader, 一键上传构建文件到开发者中心

> 4.专用密码详解:专用密码不是开发者的账号密码,需要在开发者中心右上角账号名里点"Email Settings"->再点"My Apple ID",登录apple Id的安全管理页面里获取专用密码,这里的专用密码,仅适用于上传ipa构造版本,无法登陆其他网站或者第三方应用!

> 简单三步搞定;地址:https://www.chuxueyun.com/ipaup/index.html

收起阅读 »

mui技术点02.双击header头部,画面回至顶部

mui

做的项目若干画面页面展示数据很长,如想回到画面顶部需要用户手动划瓶到顶端,于是添加双击header头部,画面回至顶部的功能。

header上添加ID

<header class="mui-bar mui-bar-nav" id="header">  
    <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
    <h1 id="title" class="mui-title"></h1>  
</header>

开启双击事件

doubletap默认为关闭,如果是双击操作,需要开启;单击就不用了。

//mui初始化  
mui.init({  
    gestureConfig:{  
        doubletap: true,  
    }  
});

添加JS监听代码

添加到mui.plusReady()中。

//双击回到顶部  
document.getElementById("header").addEventListener("tap",function(){  
    //mui.toast("doubletap");  
    //判断当前的视图的位置   如果滚动了,才能返回顶部  
    if(window.pageYOffset >= window.innerHeight){  
        mui.scrollTo(0,300);  
    }  
})

画面最底端添加提示

<p style="text-align: center;">双击标题栏,画面自动返回顶部呦!</p>

继续阅读 »

做的项目若干画面页面展示数据很长,如想回到画面顶部需要用户手动划瓶到顶端,于是添加双击header头部,画面回至顶部的功能。

header上添加ID

<header class="mui-bar mui-bar-nav" id="header">  
    <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
    <h1 id="title" class="mui-title"></h1>  
</header>

开启双击事件

doubletap默认为关闭,如果是双击操作,需要开启;单击就不用了。

//mui初始化  
mui.init({  
    gestureConfig:{  
        doubletap: true,  
    }  
});

添加JS监听代码

添加到mui.plusReady()中。

//双击回到顶部  
document.getElementById("header").addEventListener("tap",function(){  
    //mui.toast("doubletap");  
    //判断当前的视图的位置   如果滚动了,才能返回顶部  
    if(window.pageYOffset >= window.innerHeight){  
        mui.scrollTo(0,300);  
    }  
})

画面最底端添加提示

<p style="text-align: center;">双击标题栏,画面自动返回顶部呦!</p>

收起阅读 »

mui初相识04:mui提示框

作为提示用户,同用户进行交互的媒介,提示框是很重要的,编程并不复杂,所以直接粘贴学习的demo代码,详细的解释直接参照官方文档即可。

<!DOCTYPE html>  
<html>  
<head>  
    <meta charset="utf-8">  
    <meta name="viewport"  
        content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
    <title></title>  
    <link href="css/mui.min.css" rel="stylesheet" />  
</head>  
<body>  

    <header class="mui-bar mui-bar-nav">  
        <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
        <h1 class="mui-title">提示框</h1>  
    </header>  

    <div class="mui-content">  
        <div class="mui-content-padded">  
            <button type="button" class="mui-btn mui-btn-primary" onclick="showAlertInfo()">成功</button>  
            <button type="button" class="mui-btn mui-btn-primary" onclick="showAlertError()">失败</button>  
            <button type="button" class="mui-btn mui-btn-primary" onclick="showAlert()">回调函数</button>  
        </div>  
        <div class="mui-content-padded">  
            <button type="button" class="mui-btn mui-btn-primary" onclick="showConfirm()">确认1</button>  
            <button type="button" class="mui-btn mui-btn-primary" onclick="showConfirm2()">确认2</button>  
        </div>  
        <div class="mui-content-padded">  
            <button type="button" class="mui-btn mui-btn-primary" onclick="showPrompt()">输入框</button>  
        </div>  
    </div>  
    <script src="js/mui.min.js"></script>  
    <script type="text/javascript" charset="utf-8">  
        mui.init();  
        // 正常  
        function showAlertInfo() {  
            mui.alert('数据库更新成功!');  
        }  
        // 异常  
        function showAlertError() {  
            mui.alert('数据库更新失败!', '错误', '返回');  
        }  

        // 回调函数  
        function showAlert() {  
            mui.alert('数据库更新失败!', '错误', '返回', callback);  
        }  

        function callback() {  
            mui.toast('OK');  
        }  

        // 默认  
        function showConfirm() {  
            mui.confirm('message');  
        }  
        // 自定义  
        function showConfirm2() {  
            mui.confirm('message', 'title', ['取消', '确认'], function(e) {  
                if (e.index == 1) {  
                    mui.toast('确认');  
                } else {  
                    mui.toast('取消');  
                }  
            })  
        }  

        // 输入  
        function showPrompt() {  
            mui.prompt('text', 'defaultText', 'title', ['取消', '确认'], function(e) {  
                if (e.index == 1) {  
                    mui.toast(e.value);  
                } else {  
                    mui.toast('取消');  
                }  
            })  
        }  
    </script>  
</body>  
</html>
继续阅读 »

作为提示用户,同用户进行交互的媒介,提示框是很重要的,编程并不复杂,所以直接粘贴学习的demo代码,详细的解释直接参照官方文档即可。

<!DOCTYPE html>  
<html>  
<head>  
    <meta charset="utf-8">  
    <meta name="viewport"  
        content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
    <title></title>  
    <link href="css/mui.min.css" rel="stylesheet" />  
</head>  
<body>  

    <header class="mui-bar mui-bar-nav">  
        <a class="mui-action-back mui-icon mui-icon-left-nav mui-pull-left"></a>  
        <h1 class="mui-title">提示框</h1>  
    </header>  

    <div class="mui-content">  
        <div class="mui-content-padded">  
            <button type="button" class="mui-btn mui-btn-primary" onclick="showAlertInfo()">成功</button>  
            <button type="button" class="mui-btn mui-btn-primary" onclick="showAlertError()">失败</button>  
            <button type="button" class="mui-btn mui-btn-primary" onclick="showAlert()">回调函数</button>  
        </div>  
        <div class="mui-content-padded">  
            <button type="button" class="mui-btn mui-btn-primary" onclick="showConfirm()">确认1</button>  
            <button type="button" class="mui-btn mui-btn-primary" onclick="showConfirm2()">确认2</button>  
        </div>  
        <div class="mui-content-padded">  
            <button type="button" class="mui-btn mui-btn-primary" onclick="showPrompt()">输入框</button>  
        </div>  
    </div>  
    <script src="js/mui.min.js"></script>  
    <script type="text/javascript" charset="utf-8">  
        mui.init();  
        // 正常  
        function showAlertInfo() {  
            mui.alert('数据库更新成功!');  
        }  
        // 异常  
        function showAlertError() {  
            mui.alert('数据库更新失败!', '错误', '返回');  
        }  

        // 回调函数  
        function showAlert() {  
            mui.alert('数据库更新失败!', '错误', '返回', callback);  
        }  

        function callback() {  
            mui.toast('OK');  
        }  

        // 默认  
        function showConfirm() {  
            mui.confirm('message');  
        }  
        // 自定义  
        function showConfirm2() {  
            mui.confirm('message', 'title', ['取消', '确认'], function(e) {  
                if (e.index == 1) {  
                    mui.toast('确认');  
                } else {  
                    mui.toast('取消');  
                }  
            })  
        }  

        // 输入  
        function showPrompt() {  
            mui.prompt('text', 'defaultText', 'title', ['取消', '确认'], function(e) {  
                if (e.index == 1) {  
                    mui.toast(e.value);  
                } else {  
                    mui.toast('取消');  
                }  
            })  
        }  
    </script>  
</body>  
</html>
收起阅读 »

mui初相识03:mui基础控件 单选框&多选框

单选框

按照原生JS方法,通过document.getElementsByName获取。

html

<h5 class="mui-content-padded">性别</h5>  
<div class="mui-card">  
    <form class="mui-input-group">  
        <div class="mui-input-row mui-radio mui-left">  
            <label>男</label>  
            <input name="radio" type="radio" value="1" checked>  
        </div>  
        <div class="mui-input-row mui-radio mui-left">  
            <label>女</label>  
            <input name="radio" type="radio" value="2">  
        </div>  
        <div class="mui-input-row mui-radio mui-left mui-disabled">  
            <label>人妖</label>  
            <input name="radio" type="radio" value="0" disabled="disabled">  
        </div>  
    </form>  
</div>

JS

// ①单选框的事件实现  
function getVal1() {  
    var rdsObj = document.getElementsByName("radio");  
    //alert(rdsObj.length);  

    var checkVal = null;  
    for (var i = 0; i < rdsObj.length; i++) {  
        if (rdsObj[i].checked) {  
            checkVal = rdsObj[i].value;  
        }  
    }  
    mui.toast(checkVal);  
}

多选框

html

<h5 class="mui-content-padded">爱好</h5>  
<div class="mui-card">  
    <form class="mui-input-group">  
        <div class="mui-input-row mui-checkbox">  
            <label>吃饭</label>  
            <input name="checkbox" type="checkbox" value="1">  
        </div>  
        <div class="mui-input-row mui-checkbox">  
            <label>睡觉</label>  
            <input name="checkbox" type="checkbox" value="2">  
        </div>  
        <div class="mui-input-row mui-checkbox mui-disabled">  
            <label>打豆豆</label>  
            <input name="checkbox" type="checkbox" value="3" disabled="disabled">  
        </div>  
    </form>  
</div>

JS

// ③创建单选框的选择事件,调用实现  
function getVal3() {  
    var res = getCheckboxRes('checkbox');  
    if (res.length < 1) {  
        mui.toast('请选择爱好!');  
        return;  
    }  
    mui.toast(res);  
}  

function getCheckboxRes(name) {  
    var rdsObj = document.getElementsByName(name);  
    //alert(rdsObj.length);  

    var checkVal = new Array();  
    var k = 0;  
    for (var i = 0; i < rdsObj.length; i++) {  
        if (rdsObj[i].checked) {  
            checkVal[k] = rdsObj[i].value;  
            k++;  
        }  
    }  
    return checkVal;  
}
继续阅读 »

单选框

按照原生JS方法,通过document.getElementsByName获取。

html

<h5 class="mui-content-padded">性别</h5>  
<div class="mui-card">  
    <form class="mui-input-group">  
        <div class="mui-input-row mui-radio mui-left">  
            <label>男</label>  
            <input name="radio" type="radio" value="1" checked>  
        </div>  
        <div class="mui-input-row mui-radio mui-left">  
            <label>女</label>  
            <input name="radio" type="radio" value="2">  
        </div>  
        <div class="mui-input-row mui-radio mui-left mui-disabled">  
            <label>人妖</label>  
            <input name="radio" type="radio" value="0" disabled="disabled">  
        </div>  
    </form>  
</div>

JS

// ①单选框的事件实现  
function getVal1() {  
    var rdsObj = document.getElementsByName("radio");  
    //alert(rdsObj.length);  

    var checkVal = null;  
    for (var i = 0; i < rdsObj.length; i++) {  
        if (rdsObj[i].checked) {  
            checkVal = rdsObj[i].value;  
        }  
    }  
    mui.toast(checkVal);  
}

多选框

html

<h5 class="mui-content-padded">爱好</h5>  
<div class="mui-card">  
    <form class="mui-input-group">  
        <div class="mui-input-row mui-checkbox">  
            <label>吃饭</label>  
            <input name="checkbox" type="checkbox" value="1">  
        </div>  
        <div class="mui-input-row mui-checkbox">  
            <label>睡觉</label>  
            <input name="checkbox" type="checkbox" value="2">  
        </div>  
        <div class="mui-input-row mui-checkbox mui-disabled">  
            <label>打豆豆</label>  
            <input name="checkbox" type="checkbox" value="3" disabled="disabled">  
        </div>  
    </form>  
</div>

JS

// ③创建单选框的选择事件,调用实现  
function getVal3() {  
    var res = getCheckboxRes('checkbox');  
    if (res.length < 1) {  
        mui.toast('请选择爱好!');  
        return;  
    }  
    mui.toast(res);  
}  

function getCheckboxRes(name) {  
    var rdsObj = document.getElementsByName(name);  
    //alert(rdsObj.length);  

    var checkVal = new Array();  
    var k = 0;  
    for (var i = 0; i < rdsObj.length; i++) {  
        if (rdsObj[i].checked) {  
            checkVal[k] = rdsObj[i].value;  
            k++;  
        }  
    }  
    return checkVal;  
}
收起阅读 »

跨平台移动端组件库 FirstUI 1.3.0 正式发布!

组件

FirstUI(https://www.firstui.cn/)是基于uni-app开发的一款轻量、全面可靠的跨平台移动端组件库。包括框架、组件、模板、功能插件几个部分。FirstUI开发者、设计师不断精心打磨,持续发布新的组件、模板等新功能,力求为用户提供更高品质的产品,节约用户时间与成本。

一、FirstUI特性

● 多端支持。一套代码,多端适用,支持iOS(vue和Nvue)、Android(vue和Nvue)、微信小程序、支付宝小程序、QQ小程序、百度小程序、字节跳动小程序、H5平台
● 完善的组件。目前共规划118款,已上线70款,涵盖基础组件、表单组件、导航组件、布局组件、常用布局、扩展组件、操作反馈、数据组件、JS、图表、画布。
● 丰富实用的布局、模板。基于FirstUI提供的组件,针对常用场景、行业,提供丰富实用的布局和模板。
● 专属社区。我们打造了FirstU专属社区,用户可以在社区交流分享FirstUI的使用经验、提问。有其他组件、模板需求,也可以在社区中反馈。

二、本次更新内容

● 新增 Calendar 日历 组件(VIP组件)。
● 新增 Cascader 级联选择器 组件(VIP组件)。
● 新增 Slider 滑块 组件(VIP组件)。
● 新增 Rate 评分 组件(VIP组件)。
● 新增 Select 选择器 组件(VIP组件)。
● 新增 Upload 图片上传 组件(VIP组件)。
● 新增 Gallery 画廊 组件(VIP组件)。
● 新增 BubbleBox 气泡框 组件(VIP组件)。
● 新增 BottomNavbar 底部导航栏 组件(VIP组件)。
● 新增 CountDown 倒计时 组件(VIP组件)。
● 新增 CopyText 长按复制 组件(VIP组件)。
● 新增 Timer 计时器 组件(VIP组件)。
● 新增 Qrcode 二维码 组件(VIP组件)。
● 新增 Barcode 条形码 组件(VIP组件)。
● 新增 Autograph 手写签名 组件(VIP组件)。
● 修复 Textarea 多行输入框 组件回车无法换行的问题。
● 优化 Request 网络请求 组件,新增加载中提示信息配置项。
● 优化 Icon 图标 组件,新增部分图标。
● 优化若干已知问题。

三、扫码体验FirstUI

考虑快速预览,所以暂未上架App应用,后续待功能完善再进行上架。

四、开源版与商业版

FirstUI分为开源版与商业版,部分组件为商业版专属使用。

1、开源版

● github: https://github.com/FirstUI/FirstUI (欢迎star :-D)
● gitee: https://gitee.com/firstui/FirstUI (欢迎star :-D)
● 文档地址: https://doc.firstui.cn

2、VIP会员权益:

● 完整版源码
● 全部组件
● 物料商城享VIP折扣
● 专属会员群指导、答疑
● 新特性优先体验
● VIP专属文档
会员权益详情: https://www.firstui.cn/right

3、新版优惠

新版发布,优惠期内框架可5折¥150元购买(原价¥300元),2022年01月31日截止。购买框架产品即升级为VIP会员,享受VIP会员权益。
立即购买:https://www.firstui.cn/store/detail/1

继续阅读 »

FirstUI(https://www.firstui.cn/)是基于uni-app开发的一款轻量、全面可靠的跨平台移动端组件库。包括框架、组件、模板、功能插件几个部分。FirstUI开发者、设计师不断精心打磨,持续发布新的组件、模板等新功能,力求为用户提供更高品质的产品,节约用户时间与成本。

一、FirstUI特性

● 多端支持。一套代码,多端适用,支持iOS(vue和Nvue)、Android(vue和Nvue)、微信小程序、支付宝小程序、QQ小程序、百度小程序、字节跳动小程序、H5平台
● 完善的组件。目前共规划118款,已上线70款,涵盖基础组件、表单组件、导航组件、布局组件、常用布局、扩展组件、操作反馈、数据组件、JS、图表、画布。
● 丰富实用的布局、模板。基于FirstUI提供的组件,针对常用场景、行业,提供丰富实用的布局和模板。
● 专属社区。我们打造了FirstU专属社区,用户可以在社区交流分享FirstUI的使用经验、提问。有其他组件、模板需求,也可以在社区中反馈。

二、本次更新内容

● 新增 Calendar 日历 组件(VIP组件)。
● 新增 Cascader 级联选择器 组件(VIP组件)。
● 新增 Slider 滑块 组件(VIP组件)。
● 新增 Rate 评分 组件(VIP组件)。
● 新增 Select 选择器 组件(VIP组件)。
● 新增 Upload 图片上传 组件(VIP组件)。
● 新增 Gallery 画廊 组件(VIP组件)。
● 新增 BubbleBox 气泡框 组件(VIP组件)。
● 新增 BottomNavbar 底部导航栏 组件(VIP组件)。
● 新增 CountDown 倒计时 组件(VIP组件)。
● 新增 CopyText 长按复制 组件(VIP组件)。
● 新增 Timer 计时器 组件(VIP组件)。
● 新增 Qrcode 二维码 组件(VIP组件)。
● 新增 Barcode 条形码 组件(VIP组件)。
● 新增 Autograph 手写签名 组件(VIP组件)。
● 修复 Textarea 多行输入框 组件回车无法换行的问题。
● 优化 Request 网络请求 组件,新增加载中提示信息配置项。
● 优化 Icon 图标 组件,新增部分图标。
● 优化若干已知问题。

三、扫码体验FirstUI

考虑快速预览,所以暂未上架App应用,后续待功能完善再进行上架。

四、开源版与商业版

FirstUI分为开源版与商业版,部分组件为商业版专属使用。

1、开源版

● github: https://github.com/FirstUI/FirstUI (欢迎star :-D)
● gitee: https://gitee.com/firstui/FirstUI (欢迎star :-D)
● 文档地址: https://doc.firstui.cn

2、VIP会员权益:

● 完整版源码
● 全部组件
● 物料商城享VIP折扣
● 专属会员群指导、答疑
● 新特性优先体验
● VIP专属文档
会员权益详情: https://www.firstui.cn/right

3、新版优惠

新版发布,优惠期内框架可5折¥150元购买(原价¥300元),2022年01月31日截止。购买框架产品即升级为VIP会员,享受VIP会员权益。
立即购买:https://www.firstui.cn/store/detail/1

收起阅读 »

完美解决 uniapp 发布h5上传头像base64图片 又拍云

uni_app

众多app都有修改个人头像需求,最近项目需要发布H5端,却发现上传头像图片文件不行。原因是再H5端,调用uni.canvasToTempFilePath方法后,得到的是base64图片数据,而不是图片缓存路径。而uni.uploadFile需要的是图片缓存路径。至此陷入漫长的寻找解决方案的过程。。。。

网上一些资料都是说做成formData的方式上传,这个方向是OK的,但是uniapp不支持new FormData(); 和 new window.FormData() 。 new window.FormData() 虽然不报错,但是上传的时候却把formData解析成对象,服务器(又拍云)拿不到base64的图片数据。

经过三天不断地尝试,结果发现,不用new window.FormData() 也行,直接用对象存储,作为formData的参数值上传即可。下面直接上代码:

在canvasToTempFilePath的成功回调里调用下面这个方法:

        upGoodsloadImgs(imageSrc) {  
            uni.showLoading({  
                title: '图片上传中。。。',  
                mask: "true"  
            });  
            var that = this;  
            console.log("图片源路径:" + imageSrc)  
            var blobData = '';  
            var myFormdata = {};  

            // #ifdef APP-PLUS  
            var hz = imageSrc.substring(imageSrc.lastIndexOf('.'));  
            // #endif  
            // #ifdef H5  
            var arr = imageSrc.split(',');  
            var mime = arr[0].match(/:(.*?);/)[1]  
            var bstr = atob(arr[1]);//这个网上抄的,不太明白什么作用,没有它还不行,有知道的请不吝指教  
            var n = bstr.length;  
            var u8arr = new Uint8Array(n);  
            while (n--) {  
                u8arr[n] = bstr.charCodeAt(n);//  
            }  
            blobData = new Blob([u8arr], {  
                type: mime  
            });  
            myFormdata.file = blobData;  
            console.log("that.imageName = " + that.imageName);  
            var hz = mime.split('/')[1] //获得后缀  
            imageSrc = '';  
            // #endif  

            var nowd = new Date();  
            var imgName = 'avatar' + nowd.getFullYear().toString() + (nowd.getMonth() + 1) + nowd.getDate() + nowd  
                .getHours() + nowd.getMinutes() + nowd.getSeconds() + (nowd.getTime() % 1000).toString() + '.' + hz;  
            var imgUrl = '/samxiche/' + imgName;  

            that.upyun.upload({  
                localPath: imageSrc,  
                remotePath: imgUrl,  
                success: function(res) {  
                    console.log("图片上传成功:图片链接 = " + imgUrl);  
                    console.log("返回的上传结果:", res);  
                    var resImgInfo = JSON.parse(res.data);  
                    var url = that.imgBaseHost + resImgInfo.url;  
                    console.log("图片链接:", url);  

                    let pages = getCurrentPages(); //获取所有页面栈实例列表  
                    let nowPage = pages[pages.length - 1]; //当前页页面实例  
                    let prevPage = pages[pages.length - 2]; //上一页页面实例  
                    prevPage.$vm.avatarurl = url; //修改上一页datad的变量值  
                    uni.navigateBack({ //uni.navigateTo跳转的返回,默认1为返回上一级  
                        delta: 1  
                    });  
                },  

                fail: function(errMsg) {  
                    console.log("图片上传失败:", errMsg);  
                }  
            }, myFormdata ? myFormdata : '')  

        },  

下面是又拍云的上传代码:
Upyun.prototype.upload = function (options, formParams) {
var self = this
console.log("this.bucket = " + this.bucket)
console.log("this.operator = " + this.operator)
console.log("this.getSignatureUrl = " + this.getSignatureUrl)
console.log("options.remotePath = " + options.remotePath)
var date = (new Date()).toGMTString()
var opts = {
'save-key': options.remotePath,
bucket: self.bucket,
expiration: Math.round(new Date().getTime() / 1000) + 3600,
date: date
}
var policy = Base64.encode(JSON.stringify(opts))
var data = [ 'POST', '/' + self.bucket, date, policy ].join('&')
self.getSignature(data, function (err, signature) {
if (err) {
console.log("获取图片上传签名失败!!!", err)
options.fail && options.fail(err)
options.complete && options.complete(err)
return
}

// #ifdef APP-PLUS  
console.log("APP方式上传。。。。。。")  
uni.uploadFile({  
  // url: `https://v0.api.upyun.com/${self.bucket}`,  
  url: `https://v0.api.upyun.com/${self.bucket}`,  
  filePath: options.localPath,  
  name: 'file',  
  formData: {  
    authorization: `UPYUN ${self.operator}:${signature}`,  
    policy: policy,  

  },  
  success: options.success,  
  fail: options.fail,  
  complete: options.complete  
})  
// #endif  
// #ifdef H5  
console.log("H5方式上传。。。。。。")  

// formParams.append("authorization", `UPYUN ${self.operator}:${signature}`);  
// formParams.append('policy', policy);  
  //这个formParams就是一个JSON对象,上面FormData方式的不行  
formParams.authorization = `UPYUN ${self.operator}:${signature}`;  
formParams.policy = policy;  
console.log("要上传的formData:", formParams);  

//uploadFIle接口直接不要filePath参数,就放一个formData就行。
uni.uploadFile({
// url: https://v0.api.upyun.com/${self.bucket},
url: https://v0.api.upyun.com/${self.bucket},
name: 'file',
formData: formParams,
success: options.success,
fail: options.fail,
complete: options.complete
})
// #endif
})
}

附件压缩包是完整的代码,两个js文件。配置好自己的又拍云图片空间 和 获取访问密钥的接口就行。

继续阅读 »

众多app都有修改个人头像需求,最近项目需要发布H5端,却发现上传头像图片文件不行。原因是再H5端,调用uni.canvasToTempFilePath方法后,得到的是base64图片数据,而不是图片缓存路径。而uni.uploadFile需要的是图片缓存路径。至此陷入漫长的寻找解决方案的过程。。。。

网上一些资料都是说做成formData的方式上传,这个方向是OK的,但是uniapp不支持new FormData(); 和 new window.FormData() 。 new window.FormData() 虽然不报错,但是上传的时候却把formData解析成对象,服务器(又拍云)拿不到base64的图片数据。

经过三天不断地尝试,结果发现,不用new window.FormData() 也行,直接用对象存储,作为formData的参数值上传即可。下面直接上代码:

在canvasToTempFilePath的成功回调里调用下面这个方法:

        upGoodsloadImgs(imageSrc) {  
            uni.showLoading({  
                title: '图片上传中。。。',  
                mask: "true"  
            });  
            var that = this;  
            console.log("图片源路径:" + imageSrc)  
            var blobData = '';  
            var myFormdata = {};  

            // #ifdef APP-PLUS  
            var hz = imageSrc.substring(imageSrc.lastIndexOf('.'));  
            // #endif  
            // #ifdef H5  
            var arr = imageSrc.split(',');  
            var mime = arr[0].match(/:(.*?);/)[1]  
            var bstr = atob(arr[1]);//这个网上抄的,不太明白什么作用,没有它还不行,有知道的请不吝指教  
            var n = bstr.length;  
            var u8arr = new Uint8Array(n);  
            while (n--) {  
                u8arr[n] = bstr.charCodeAt(n);//  
            }  
            blobData = new Blob([u8arr], {  
                type: mime  
            });  
            myFormdata.file = blobData;  
            console.log("that.imageName = " + that.imageName);  
            var hz = mime.split('/')[1] //获得后缀  
            imageSrc = '';  
            // #endif  

            var nowd = new Date();  
            var imgName = 'avatar' + nowd.getFullYear().toString() + (nowd.getMonth() + 1) + nowd.getDate() + nowd  
                .getHours() + nowd.getMinutes() + nowd.getSeconds() + (nowd.getTime() % 1000).toString() + '.' + hz;  
            var imgUrl = '/samxiche/' + imgName;  

            that.upyun.upload({  
                localPath: imageSrc,  
                remotePath: imgUrl,  
                success: function(res) {  
                    console.log("图片上传成功:图片链接 = " + imgUrl);  
                    console.log("返回的上传结果:", res);  
                    var resImgInfo = JSON.parse(res.data);  
                    var url = that.imgBaseHost + resImgInfo.url;  
                    console.log("图片链接:", url);  

                    let pages = getCurrentPages(); //获取所有页面栈实例列表  
                    let nowPage = pages[pages.length - 1]; //当前页页面实例  
                    let prevPage = pages[pages.length - 2]; //上一页页面实例  
                    prevPage.$vm.avatarurl = url; //修改上一页datad的变量值  
                    uni.navigateBack({ //uni.navigateTo跳转的返回,默认1为返回上一级  
                        delta: 1  
                    });  
                },  

                fail: function(errMsg) {  
                    console.log("图片上传失败:", errMsg);  
                }  
            }, myFormdata ? myFormdata : '')  

        },  

下面是又拍云的上传代码:
Upyun.prototype.upload = function (options, formParams) {
var self = this
console.log("this.bucket = " + this.bucket)
console.log("this.operator = " + this.operator)
console.log("this.getSignatureUrl = " + this.getSignatureUrl)
console.log("options.remotePath = " + options.remotePath)
var date = (new Date()).toGMTString()
var opts = {
'save-key': options.remotePath,
bucket: self.bucket,
expiration: Math.round(new Date().getTime() / 1000) + 3600,
date: date
}
var policy = Base64.encode(JSON.stringify(opts))
var data = [ 'POST', '/' + self.bucket, date, policy ].join('&')
self.getSignature(data, function (err, signature) {
if (err) {
console.log("获取图片上传签名失败!!!", err)
options.fail && options.fail(err)
options.complete && options.complete(err)
return
}

// #ifdef APP-PLUS  
console.log("APP方式上传。。。。。。")  
uni.uploadFile({  
  // url: `https://v0.api.upyun.com/${self.bucket}`,  
  url: `https://v0.api.upyun.com/${self.bucket}`,  
  filePath: options.localPath,  
  name: 'file',  
  formData: {  
    authorization: `UPYUN ${self.operator}:${signature}`,  
    policy: policy,  

  },  
  success: options.success,  
  fail: options.fail,  
  complete: options.complete  
})  
// #endif  
// #ifdef H5  
console.log("H5方式上传。。。。。。")  

// formParams.append("authorization", `UPYUN ${self.operator}:${signature}`);  
// formParams.append('policy', policy);  
  //这个formParams就是一个JSON对象,上面FormData方式的不行  
formParams.authorization = `UPYUN ${self.operator}:${signature}`;  
formParams.policy = policy;  
console.log("要上传的formData:", formParams);  

//uploadFIle接口直接不要filePath参数,就放一个formData就行。
uni.uploadFile({
// url: https://v0.api.upyun.com/${self.bucket},
url: https://v0.api.upyun.com/${self.bucket},
name: 'file',
formData: formParams,
success: options.success,
fail: options.fail,
complete: options.complete
})
// #endif
})
}

附件压缩包是完整的代码,两个js文件。配置好自己的又拍云图片空间 和 获取访问密钥的接口就行。

收起阅读 »

关于uniapp双向认证https的经验分享

大佬请绕道,emmm.....小白可以看一下
我在使用过程中遇到以下问题:

  1. uni.configMTLS如何使用的问题
  2. 如何使用域名访问的问题
  3. 证书生成的问题
  4. 证书引用的问题
  5. 使用报错的问题
    首先说一下,我后台用的是springboot,因为是测试所以随便写了一个接口,直接通过uniapp调用
  6. uni.configMTLS如何使用的问题
    uni.configMTLS是配置证书用按照官方文档配置即可,可以放到App.vue文件的生命周期函数中调用一次即可,发请求还是用uni.request正常发
  7. 如何使用域名访问的问题
    我用的是夜神模拟器,修改模拟器hosts文件配置域名映射到springboot所在机器ip,我随便起了个域名www.unihttps.com
  8. 证书生成的问题
    我用的是keytool,具体怎么用请自行百度,很大程度借助了这篇文章https://www.jianshu.com/p/cabf4b759d8f
  9. 证书引用的问题
    把生成的client.p12和server.cer直接扔到uni工程下的static下,按官方demo引用的方式用就行了
  10. 使用报错的问题
    碰到的错误,走了各种弯路,遇到各种奇葩错,当遇到如下错误时我感觉自己要成功了
    "errMsg": "request:fail abort statusCode:-1 Hostname www.unihttps.com not verified:\n certificate: sha256/4cIBWyy+2BlD/ME12B4hIRVEefrl5X0nL0/3DGISfKA=\n DN: CN=www.unihttps.com\n subjectAltNames: []"

    但是还是有毛病,此时我的postman和浏览器都可以正常使用证书访问后台,但是uni工程还是不可以,本来以为是uniapp的bug,经过长时间百度“keytool DN”关键词,找到了问题所在,server证书没有配置-SAN参数,配置后即可正常访问
    为了实现uniapp的双向认证废了我这个菜鸡2个星期,一度认证官方uni.configMTLS是逗我玩的,功夫不负有心人啊,希望后来人看到此帖能够早点下班,谢谢过程中各位大佬赐教

继续阅读 »

大佬请绕道,emmm.....小白可以看一下
我在使用过程中遇到以下问题:

  1. uni.configMTLS如何使用的问题
  2. 如何使用域名访问的问题
  3. 证书生成的问题
  4. 证书引用的问题
  5. 使用报错的问题
    首先说一下,我后台用的是springboot,因为是测试所以随便写了一个接口,直接通过uniapp调用
  6. uni.configMTLS如何使用的问题
    uni.configMTLS是配置证书用按照官方文档配置即可,可以放到App.vue文件的生命周期函数中调用一次即可,发请求还是用uni.request正常发
  7. 如何使用域名访问的问题
    我用的是夜神模拟器,修改模拟器hosts文件配置域名映射到springboot所在机器ip,我随便起了个域名www.unihttps.com
  8. 证书生成的问题
    我用的是keytool,具体怎么用请自行百度,很大程度借助了这篇文章https://www.jianshu.com/p/cabf4b759d8f
  9. 证书引用的问题
    把生成的client.p12和server.cer直接扔到uni工程下的static下,按官方demo引用的方式用就行了
  10. 使用报错的问题
    碰到的错误,走了各种弯路,遇到各种奇葩错,当遇到如下错误时我感觉自己要成功了
    "errMsg": "request:fail abort statusCode:-1 Hostname www.unihttps.com not verified:\n certificate: sha256/4cIBWyy+2BlD/ME12B4hIRVEefrl5X0nL0/3DGISfKA=\n DN: CN=www.unihttps.com\n subjectAltNames: []"

    但是还是有毛病,此时我的postman和浏览器都可以正常使用证书访问后台,但是uni工程还是不可以,本来以为是uniapp的bug,经过长时间百度“keytool DN”关键词,找到了问题所在,server证书没有配置-SAN参数,配置后即可正常访问
    为了实现uniapp的双向认证废了我这个菜鸡2个星期,一度认证官方uni.configMTLS是逗我玩的,功夫不负有心人啊,希望后来人看到此帖能够早点下班,谢谢过程中各位大佬赐教

收起阅读 »

AppStore推广内购、SKPaymentTransactionObser

AppStore推广内购、SKPaymentTransactionObser:https://ext.dcloud.net.cn/plugin?id=7092

AppStore推广内购、SKPaymentTransactionObser:https://ext.dcloud.net.cn/plugin?id=7092

重写获取当前的地理位置wgs84 转 gcj02

uni.getlocation

重写获取当前的地理位置

由于不使用第三方地图SDK无法返回gcj02 ,所以在获取后加上换算,对精度要求高的还是使用地图SDK比较好

官方不建议js转换

可能部分地区偏差较大,对精度有要求还是交钱使用地图SDK吧

继续阅读 »

重写获取当前的地理位置

由于不使用第三方地图SDK无法返回gcj02 ,所以在获取后加上换算,对精度要求高的还是使用地图SDK比较好

官方不建议js转换

可能部分地区偏差较大,对精度有要求还是交钱使用地图SDK吧

收起阅读 »

mui技术点01.手机网络连接的判断

问题说明

手机未连接网络,app访问后台接口时出现错误。

解决方案

每个画面初始时,判断网络是否连接,网络如果处于断网阶段,弹出窗口信息直接退出app,或者画面显示[未联网]消息提示,提示用户刷新网络(比如:点击刷新按钮,或者下拉等操作)。

详细代码

直接退出app

  1. 检测是否连接网络

    //mui检测是否连接网络  
    function getSysInfo() {  
    //  var str = "";  
    //  str += "名称:" + plus.os.name + "\n";  
    //  str += "版本:" + plus.os.version + "\n";  
    //  str += "语言:" + plus.os.language + "\n";  
    //  str += "厂商:" + plus.os.vendor + "\n";  
    //  str += "网络类型:";  
    
    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 str = types[plus.networkinfo.getCurrentType()];  
    if (str == '未知' || str == '未连接网络') {  
        return false;  
    } else {  
        return true;  
    }  
    }
  2. 调用及处理
    mui.plusReady(function() {  
    //如果未连接网络,退出app(针对mui框架)  
    if (!(getSysInfo())) {  
        alert('网络连接失败,请退出并重置网络!');  
        plus.runtime.quit();//退出app(针对mui框架)  
        return;  
    }  
    });

画面显示提示按钮,让用户手动刷新

  1. 点击按钮,进行画面刷新

    <!DOCTYPE html>  
    <html>  
    <head>  
    <meta charset="utf-8">  
    <meta name="viewport"  
        content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
    <title>网络未连接</title>  
    <link href="css/mui.min.css" rel="stylesheet" />  
    <style>  
        .mui-btn {  
            display: block;  
            width: 60px;  
            margin: 10px auto;  
        }  
    
        #info {  
            padding: 10px 5px;  
        }  
    </style>  
    </head>  
    <body>  
    
    <header class="mui-bar mui-bar-nav">  
        <h1 class="mui-title">按钮点击刷新</h1>  
    </header>  
    
    <div id="content" class="mui-content">  
        <div class="mui-content-padded" style="margin: 50px;text-align: center;">  
            <div id="info"></div>  
            <button id="button" type="button" class="mui-btn mui-btn-outlined">刷新</button>  
        </div>  
    </div>  
    
    <script src="js/mui.min.js"></script>  
    <script type="text/javascript" charset="utf-8">  
        mui.init();  
        var info = document.getElementById("info");  
        (function($) {  
            mui.toast('画面初始化!');  
            info.innerText = '网络未连接,请连接网络后刷新!';  
        })(mui);  
    
        document.getElementById("button").addEventListener('tap', function() {  
            location.reload();  
        });  
    </script>  
    </body>  
    </html>
  2. 下划画面刷新
<!DOCTYPE html>  
<html>  
<head>  
    <meta charset="utf-8">  
    <meta name="viewport"  
        content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
    <title>网络未连接</title>  
    <link href="css/mui.min.css" rel="stylesheet" />  
    <style>  
        #info {  
            padding: 10px 5px;  
        }  
    </style>  
</head>  
<body>  

    <header class="mui-bar mui-bar-nav">  
        <h1 class="mui-title">画面下拉刷新</h1>  
    </header>  

    <div id="content" class="mui-content">  
        <div class="mui-content-padded" style="margin: 50px;text-align: center;">  
            <div id="info"></div>  
        </div>  
    </div>  

    <script src="js/mui.min.js"></script>  
    <script type="text/javascript" charset="utf-8">  
        var info = document.getElementById("info");  
        mui.init({  
        pullRefresh: {  
                container: "#content", //待刷新区域标识,querySelector能定位的css选择器均可,比如:id、.class等  
                down: { //下拉刷新  
                    style: 'circle', //必选,下拉刷新样式,目前支持原生5+ ‘circle’ 样式  
                    height: 50, //可选,默认50.触发下拉刷新拖动距离,  
                    auto: false, //可选,默认false.首次加载自动下拉刷新一次  
                    contentdown: "下拉可以刷新", //可选,在下拉可刷新状态时,下拉刷新控件上显示的标题内容  
                    contentover: "释放立即刷新", //可选,在释放可刷新状态时,下拉刷新控件上显示的标题内容  
                    contentrefresh: "正在刷新...", //可选,正在刷新状态时,下拉刷新控件上显示的标题内容  
                    callback: function() { //必选,刷新函数,根据具体业务来编写,比如通过ajax从服务器获取新数据;  
                        //模拟向服务器获取数据的等待时间  
                        sleep(1000);  
                        location.reload();  
                    }  
                }  
            }  
        });  

        (function($) {  
            info.innerText = '网络未连接,请网络连接后,下拉刷新进行页面刷新!';  
            mui.toast('画面初始化!');  
        })(mui);  

        //模拟线程等待,ms:单位毫秒  
        function sleep(ms) {  
            return new Promise(resolve => setTimeout(resolve, ms));  
        }  
    </script>  
</body>  
</html>

注意

  1. 内置浏览器技术实现,但手机上未必实现,调研的结果在手机上验证之后,在进行项目代码合并。
  2. 关于下拉刷新详细的参数,请参照官网的文档说明:https://dev.dcloud.net.cn/mui/pulldown/
继续阅读 »

问题说明

手机未连接网络,app访问后台接口时出现错误。

解决方案

每个画面初始时,判断网络是否连接,网络如果处于断网阶段,弹出窗口信息直接退出app,或者画面显示[未联网]消息提示,提示用户刷新网络(比如:点击刷新按钮,或者下拉等操作)。

详细代码

直接退出app

  1. 检测是否连接网络

    //mui检测是否连接网络  
    function getSysInfo() {  
    //  var str = "";  
    //  str += "名称:" + plus.os.name + "\n";  
    //  str += "版本:" + plus.os.version + "\n";  
    //  str += "语言:" + plus.os.language + "\n";  
    //  str += "厂商:" + plus.os.vendor + "\n";  
    //  str += "网络类型:";  
    
    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 str = types[plus.networkinfo.getCurrentType()];  
    if (str == '未知' || str == '未连接网络') {  
        return false;  
    } else {  
        return true;  
    }  
    }
  2. 调用及处理
    mui.plusReady(function() {  
    //如果未连接网络,退出app(针对mui框架)  
    if (!(getSysInfo())) {  
        alert('网络连接失败,请退出并重置网络!');  
        plus.runtime.quit();//退出app(针对mui框架)  
        return;  
    }  
    });

画面显示提示按钮,让用户手动刷新

  1. 点击按钮,进行画面刷新

    <!DOCTYPE html>  
    <html>  
    <head>  
    <meta charset="utf-8">  
    <meta name="viewport"  
        content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
    <title>网络未连接</title>  
    <link href="css/mui.min.css" rel="stylesheet" />  
    <style>  
        .mui-btn {  
            display: block;  
            width: 60px;  
            margin: 10px auto;  
        }  
    
        #info {  
            padding: 10px 5px;  
        }  
    </style>  
    </head>  
    <body>  
    
    <header class="mui-bar mui-bar-nav">  
        <h1 class="mui-title">按钮点击刷新</h1>  
    </header>  
    
    <div id="content" class="mui-content">  
        <div class="mui-content-padded" style="margin: 50px;text-align: center;">  
            <div id="info"></div>  
            <button id="button" type="button" class="mui-btn mui-btn-outlined">刷新</button>  
        </div>  
    </div>  
    
    <script src="js/mui.min.js"></script>  
    <script type="text/javascript" charset="utf-8">  
        mui.init();  
        var info = document.getElementById("info");  
        (function($) {  
            mui.toast('画面初始化!');  
            info.innerText = '网络未连接,请连接网络后刷新!';  
        })(mui);  
    
        document.getElementById("button").addEventListener('tap', function() {  
            location.reload();  
        });  
    </script>  
    </body>  
    </html>
  2. 下划画面刷新
<!DOCTYPE html>  
<html>  
<head>  
    <meta charset="utf-8">  
    <meta name="viewport"  
        content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  
    <title>网络未连接</title>  
    <link href="css/mui.min.css" rel="stylesheet" />  
    <style>  
        #info {  
            padding: 10px 5px;  
        }  
    </style>  
</head>  
<body>  

    <header class="mui-bar mui-bar-nav">  
        <h1 class="mui-title">画面下拉刷新</h1>  
    </header>  

    <div id="content" class="mui-content">  
        <div class="mui-content-padded" style="margin: 50px;text-align: center;">  
            <div id="info"></div>  
        </div>  
    </div>  

    <script src="js/mui.min.js"></script>  
    <script type="text/javascript" charset="utf-8">  
        var info = document.getElementById("info");  
        mui.init({  
        pullRefresh: {  
                container: "#content", //待刷新区域标识,querySelector能定位的css选择器均可,比如:id、.class等  
                down: { //下拉刷新  
                    style: 'circle', //必选,下拉刷新样式,目前支持原生5+ ‘circle’ 样式  
                    height: 50, //可选,默认50.触发下拉刷新拖动距离,  
                    auto: false, //可选,默认false.首次加载自动下拉刷新一次  
                    contentdown: "下拉可以刷新", //可选,在下拉可刷新状态时,下拉刷新控件上显示的标题内容  
                    contentover: "释放立即刷新", //可选,在释放可刷新状态时,下拉刷新控件上显示的标题内容  
                    contentrefresh: "正在刷新...", //可选,正在刷新状态时,下拉刷新控件上显示的标题内容  
                    callback: function() { //必选,刷新函数,根据具体业务来编写,比如通过ajax从服务器获取新数据;  
                        //模拟向服务器获取数据的等待时间  
                        sleep(1000);  
                        location.reload();  
                    }  
                }  
            }  
        });  

        (function($) {  
            info.innerText = '网络未连接,请网络连接后,下拉刷新进行页面刷新!';  
            mui.toast('画面初始化!');  
        })(mui);  

        //模拟线程等待,ms:单位毫秒  
        function sleep(ms) {  
            return new Promise(resolve => setTimeout(resolve, ms));  
        }  
    </script>  
</body>  
</html>

注意

  1. 内置浏览器技术实现,但手机上未必实现,调研的结果在手机上验证之后,在进行项目代码合并。
  2. 关于下拉刷新详细的参数,请参照官网的文档说明:https://dev.dcloud.net.cn/mui/pulldown/
收起阅读 »

修复uni.preloadPage预加载tabBar页面,正式打包切换tabBar报错/出错

uni统计 预加载

修复方法如下:

修改uni统计源代码(修改后的uni-stat见附件)

打开HBuilderX安装目录,找到 HBuilderX安装目录\plugins\uniapp-cli\node_modules\@dcloudio\uni-stat\dist\index.js 这个文件。需注意package.json文件中version指定的版本,我的是uni-stat 2.0.0-32920211119001这个版本。

如果你的项目不是使用HBuilderX创建的,你可能需要修改 HBuilderX安装目录\plugins\uniapp-cli\node_modules\@dcloudio\vue-cli-plugin-uni\packages\uni-stat\dist 文件夹下的 uni-stat.cjs.js和uni-stat.es.js这两个文件,改造方法应该和下面的相同(因为我的项目没有用到这两个js代码,所以不清楚这两个代码在什么环境下起作用)。至于怎么查看用到的是哪个目录中的uni-stat,可以通过查看项目编译后的app-service.js文件,搜索关键字“@dcloudio/uni-stat@”, 可以定位到uni-stat的版本

下面开工改造:

1. 修改getRoute方法

const getRoute = () => {    
  var pages = getCurrentPages();    
  var page = pages[pages.length - 1];    
  if (!page) return ''    
  // preloadPage的nvue页面$vm为undefined    
  let _self = page.$vm;    
  // 为preloadPage的nvue页面尝试从$page获取route属性    
  let _page = page.$page;    
  if (getPlatformName() === 'bd' && _self) {    
    return _self.$mp && _self.$mp.page.is;    
  } else if (_self){    
    return (_self.$scope && _self.$scope.route) || (_self.$mp && _self.$mp.page.route);    
  } else if (_page){    
    return _page.route    
  } else {    
    console.error('未找到路径')    
    return ''    
  }    
};

2. 修改getPageRoute方法

const getPageRoute = (self) => {    
  var pages = getCurrentPages();    
  var page = pages[pages.length - 1];    
  if (!page) return ''    
  let _self = page.$vm;    
  let _page = page.$page;    
  let query = self._query;    
  let str = query && JSON.stringify(query) !== '{}' ? '?' + JSON.stringify(query) : '';    
  // clear    
  self._query = '';    
  if (getPlatformName() === 'bd' && _self) {    
    return _self.$mp && _self.$mp.page.is + str;    
  } else if (_self) {    
    return (_self.$scope && _self.$scope.route + str) || (_self.$mp && _self.$mp.page.route + str);    
  } else if (_page){    
    return _page.route    
  } else {    
    console.error('未找到路径')    
    return ''    
  }    
};

3. 修改class Stat的load方法

class Stat extends Util {    
  ....此处省略    
  load(options, self) {    
    if (!self.$scope && !self.$mp) {    
      // 如果是preloadPage nvue页面, 则这里拿不到对应的页面(也就是nvue页面不在CurrentPages页面栈中),需要使用route判断一下正确性    
      // 否则会把错误的页面赋值给self.$scope,进而引发Cannot read property '__call_hook' of undefined的错误    
      const page = getCurrentPages();    
      const route = page[page.length - 1].route;    
      if (self.$options && self.$options.route === route) {    
        self.$scope = page[page.length - 1];    
      }    
    }    
    this.self = self;    
    this._query = options;    
  }    
}

4. 在lifecycle前定义函数tryinterceptShare

const tryinterceptShare = function(ctx) {    
    if (ctx.$scope && ctx.$scope.onShareAppMessage) {    
        let oldShareAppMessage = ctx.$scope.onShareAppMessage;    
        ctx.$scope.onShareAppMessage = function(options) {    
          stat.interceptShare(false);    
          return oldShareAppMessage.call(ctx, options)    
        };    
    }    
}

5. 修改lifecycle 的 onLoad 方法

  onLoad(options) {    
    stat.load(options, this);    
    // 重写分享,获取分享上报事件    
    tryinterceptShare(this)    
    // preloadPage nvue页面,$scope可能为undefined    
    if (!this.$scope) {    
        var $scope = undefined;    
        var self = this;    
        Object.defineProperty(this, '$scope', {    
            get() {    
                return $scope    
            },    
            set(val) {    
                $scope = val;    
                setTimeout(()=>{    
                    tryinterceptShare(self)    
                }, 0)    
            }    
        })    
    }    
  }

6. try catch lifecycle 中的所有方法,防止uni-stat统计出错引发应用异常

在 lifecycle定义后,添加如下代码  

const ERROR_PREFIX = 'uni-stat@' + STAT_VERSION;    

// 把lifecycle的方法全都try catch起来,防止出什么错导致应用页面打不开    
const tryfunc = function(func, name) {    
    return function() {    
        try {    
            let args = [].slice.call(arguments);    
            func.apply(this, args)    
        } catch(e) {    
            console.error(ERROR_PREFIX + ' error in function '+ name);    
            console.error(JSON.stringify(e))    
        }    
    }    
}    
Object.keys(lifecycle).forEach(k=>{    
    lifecycle[k] = tryfunc(lifecycle[k], k)    
})  

至此改造完毕。因为我也不知道这么修改会不会在其它平台出现错误,所以大家还是在修改后调试一下再发布,不过目前我自己在安卓机子上测试没发现问题。

继续阅读 »

修复方法如下:

修改uni统计源代码(修改后的uni-stat见附件)

打开HBuilderX安装目录,找到 HBuilderX安装目录\plugins\uniapp-cli\node_modules\@dcloudio\uni-stat\dist\index.js 这个文件。需注意package.json文件中version指定的版本,我的是uni-stat 2.0.0-32920211119001这个版本。

如果你的项目不是使用HBuilderX创建的,你可能需要修改 HBuilderX安装目录\plugins\uniapp-cli\node_modules\@dcloudio\vue-cli-plugin-uni\packages\uni-stat\dist 文件夹下的 uni-stat.cjs.js和uni-stat.es.js这两个文件,改造方法应该和下面的相同(因为我的项目没有用到这两个js代码,所以不清楚这两个代码在什么环境下起作用)。至于怎么查看用到的是哪个目录中的uni-stat,可以通过查看项目编译后的app-service.js文件,搜索关键字“@dcloudio/uni-stat@”, 可以定位到uni-stat的版本

下面开工改造:

1. 修改getRoute方法

const getRoute = () => {    
  var pages = getCurrentPages();    
  var page = pages[pages.length - 1];    
  if (!page) return ''    
  // preloadPage的nvue页面$vm为undefined    
  let _self = page.$vm;    
  // 为preloadPage的nvue页面尝试从$page获取route属性    
  let _page = page.$page;    
  if (getPlatformName() === 'bd' && _self) {    
    return _self.$mp && _self.$mp.page.is;    
  } else if (_self){    
    return (_self.$scope && _self.$scope.route) || (_self.$mp && _self.$mp.page.route);    
  } else if (_page){    
    return _page.route    
  } else {    
    console.error('未找到路径')    
    return ''    
  }    
};

2. 修改getPageRoute方法

const getPageRoute = (self) => {    
  var pages = getCurrentPages();    
  var page = pages[pages.length - 1];    
  if (!page) return ''    
  let _self = page.$vm;    
  let _page = page.$page;    
  let query = self._query;    
  let str = query && JSON.stringify(query) !== '{}' ? '?' + JSON.stringify(query) : '';    
  // clear    
  self._query = '';    
  if (getPlatformName() === 'bd' && _self) {    
    return _self.$mp && _self.$mp.page.is + str;    
  } else if (_self) {    
    return (_self.$scope && _self.$scope.route + str) || (_self.$mp && _self.$mp.page.route + str);    
  } else if (_page){    
    return _page.route    
  } else {    
    console.error('未找到路径')    
    return ''    
  }    
};

3. 修改class Stat的load方法

class Stat extends Util {    
  ....此处省略    
  load(options, self) {    
    if (!self.$scope && !self.$mp) {    
      // 如果是preloadPage nvue页面, 则这里拿不到对应的页面(也就是nvue页面不在CurrentPages页面栈中),需要使用route判断一下正确性    
      // 否则会把错误的页面赋值给self.$scope,进而引发Cannot read property '__call_hook' of undefined的错误    
      const page = getCurrentPages();    
      const route = page[page.length - 1].route;    
      if (self.$options && self.$options.route === route) {    
        self.$scope = page[page.length - 1];    
      }    
    }    
    this.self = self;    
    this._query = options;    
  }    
}

4. 在lifecycle前定义函数tryinterceptShare

const tryinterceptShare = function(ctx) {    
    if (ctx.$scope && ctx.$scope.onShareAppMessage) {    
        let oldShareAppMessage = ctx.$scope.onShareAppMessage;    
        ctx.$scope.onShareAppMessage = function(options) {    
          stat.interceptShare(false);    
          return oldShareAppMessage.call(ctx, options)    
        };    
    }    
}

5. 修改lifecycle 的 onLoad 方法

  onLoad(options) {    
    stat.load(options, this);    
    // 重写分享,获取分享上报事件    
    tryinterceptShare(this)    
    // preloadPage nvue页面,$scope可能为undefined    
    if (!this.$scope) {    
        var $scope = undefined;    
        var self = this;    
        Object.defineProperty(this, '$scope', {    
            get() {    
                return $scope    
            },    
            set(val) {    
                $scope = val;    
                setTimeout(()=>{    
                    tryinterceptShare(self)    
                }, 0)    
            }    
        })    
    }    
  }

6. try catch lifecycle 中的所有方法,防止uni-stat统计出错引发应用异常

在 lifecycle定义后,添加如下代码  

const ERROR_PREFIX = 'uni-stat@' + STAT_VERSION;    

// 把lifecycle的方法全都try catch起来,防止出什么错导致应用页面打不开    
const tryfunc = function(func, name) {    
    return function() {    
        try {    
            let args = [].slice.call(arguments);    
            func.apply(this, args)    
        } catch(e) {    
            console.error(ERROR_PREFIX + ' error in function '+ name);    
            console.error(JSON.stringify(e))    
        }    
    }    
}    
Object.keys(lifecycle).forEach(k=>{    
    lifecycle[k] = tryfunc(lifecycle[k], k)    
})  

至此改造完毕。因为我也不知道这么修改会不会在其它平台出现错误,所以大家还是在修改后调试一下再发布,不过目前我自己在安卓机子上测试没发现问题。

收起阅读 »

uni-商城开源项目,寻求志同道合开发者

开源

https://github.com/gooking/uni-app-mall

[码云镜像:] https://gitee.com/javazj/uni-app-mall

对 uni 有兴趣,热爱开源,有适当的业余时间,乐于助人的朋友,欢迎一起加入到开发团队中来~

技术栈:

  1. uni-app
  2. uview 2.0
  3. 没有其他了
继续阅读 »

https://github.com/gooking/uni-app-mall

[码云镜像:] https://gitee.com/javazj/uni-app-mall

对 uni 有兴趣,热爱开源,有适当的业余时间,乐于助人的朋友,欢迎一起加入到开发团队中来~

技术栈:

  1. uni-app
  2. uview 2.0
  3. 没有其他了
收起阅读 »