
假如没有mac电脑,我们又如何将ipa文件上传去苹果开发者中心呢?
随着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头部,画面回至顶部
做的项目若干画面页面展示数据很长,如想回到画面顶部需要用户手动划瓶到顶端,于是添加双击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图片 又拍云
众多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.....小白可以看一下
我在使用过程中遇到以下问题:
- uni.configMTLS如何使用的问题
- 如何使用域名访问的问题
- 证书生成的问题
- 证书引用的问题
- 使用报错的问题
首先说一下,我后台用的是springboot,因为是测试所以随便写了一个接口,直接通过uniapp调用 - uni.configMTLS如何使用的问题
uni.configMTLS是配置证书用按照官方文档配置即可,可以放到App.vue文件的生命周期函数中调用一次即可,发请求还是用uni.request正常发 - 如何使用域名访问的问题
我用的是夜神模拟器,修改模拟器hosts文件配置域名映射到springboot所在机器ip,我随便起了个域名www.unihttps.com - 证书生成的问题
我用的是keytool,具体怎么用请自行百度,很大程度借助了这篇文章https://www.jianshu.com/p/cabf4b759d8f - 证书引用的问题
把生成的client.p12和server.cer直接扔到uni工程下的static下,按官方demo引用的方式用就行了 - 使用报错的问题
碰到的错误,走了各种弯路,遇到各种奇葩错,当遇到如下错误时我感觉自己要成功了"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.....小白可以看一下
我在使用过程中遇到以下问题:
- uni.configMTLS如何使用的问题
- 如何使用域名访问的问题
- 证书生成的问题
- 证书引用的问题
- 使用报错的问题
首先说一下,我后台用的是springboot,因为是测试所以随便写了一个接口,直接通过uniapp调用 - uni.configMTLS如何使用的问题
uni.configMTLS是配置证书用按照官方文档配置即可,可以放到App.vue文件的生命周期函数中调用一次即可,发请求还是用uni.request正常发 - 如何使用域名访问的问题
我用的是夜神模拟器,修改模拟器hosts文件配置域名映射到springboot所在机器ip,我随便起了个域名www.unihttps.com - 证书生成的问题
我用的是keytool,具体怎么用请自行百度,很大程度借助了这篇文章https://www.jianshu.com/p/cabf4b759d8f - 证书引用的问题
把生成的client.p12和server.cer直接扔到uni工程下的static下,按官方demo引用的方式用就行了 - 使用报错的问题
碰到的错误,走了各种弯路,遇到各种奇葩错,当遇到如下错误时我感觉自己要成功了"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

mui技术点01.手机网络连接的判断
问题说明
手机未连接网络,app访问后台接口时出现错误。
解决方案
每个画面初始时,判断网络是否连接,网络如果处于断网阶段,弹出窗口信息直接退出app,或者画面显示[未联网]消息提示,提示用户刷新网络(比如:点击刷新按钮,或者下拉等操作)。
详细代码
直接退出app
-
检测是否连接网络
//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; } }
- 调用及处理
mui.plusReady(function() { //如果未连接网络,退出app(针对mui框架) if (!(getSysInfo())) { alert('网络连接失败,请退出并重置网络!'); plus.runtime.quit();//退出app(针对mui框架) return; } });
画面显示提示按钮,让用户手动刷新
-
点击按钮,进行画面刷新
<!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>
- 下划画面刷新
<!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>
注意
- 内置浏览器技术实现,但手机上未必实现,调研的结果在手机上验证之后,在进行项目代码合并。
- 关于下拉刷新详细的参数,请参照官网的文档说明:https://dev.dcloud.net.cn/mui/pulldown/
问题说明
手机未连接网络,app访问后台接口时出现错误。
解决方案
每个画面初始时,判断网络是否连接,网络如果处于断网阶段,弹出窗口信息直接退出app,或者画面显示[未联网]消息提示,提示用户刷新网络(比如:点击刷新按钮,或者下拉等操作)。
详细代码
直接退出app
-
检测是否连接网络
//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; } }
- 调用及处理
mui.plusReady(function() { //如果未连接网络,退出app(针对mui框架) if (!(getSysInfo())) { alert('网络连接失败,请退出并重置网络!'); plus.runtime.quit();//退出app(针对mui框架) return; } });
画面显示提示按钮,让用户手动刷新
-
点击按钮,进行画面刷新
<!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>
- 下划画面刷新
<!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>
注意
- 内置浏览器技术实现,但手机上未必实现,调研的结果在手机上验证之后,在进行项目代码合并。
- 关于下拉刷新详细的参数,请参照官网的文档说明:https://dev.dcloud.net.cn/mui/pulldown/

修复uni.preloadPage预加载tabBar页面,正式打包切换tabBar报错/出错
修复方法如下:
修改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 有兴趣,热爱开源,有适当的业余时间,乐于助人的朋友,欢迎一起加入到开发团队中来~
技术栈:
- uni-app
- uview 2.0
- 没有其他了
https://github.com/gooking/uni-app-mall
[码云镜像:] https://gitee.com/javazj/uni-app-mall
对 uni 有兴趣,热爱开源,有适当的业余时间,乐于助人的朋友,欢迎一起加入到开发团队中来~
技术栈:
- uni-app
- uview 2.0
- 没有其他了