
公众号开发(三)系统分析和数据表结构设计
可视化插件安装
在开始增加数据表之前,我们先安装一个可视化管理表结构插件:https://ext.dcloud.net.cn/plugin?id=4800
它可以非常直观又高效地完成表字段的创建。
安装后可能需要重启下HBuilder X
系统分析
从上一篇的界面看虽然只有两个界面,但客户要实现的需求如下:
- 文章的标题和内容需要在后台可以配置。
- 表单里的协议内容需要也在后台可以配置。
- 表单的内容需要在后台列出来,并需要加一个状态管理(待处理,处理中,已处理)。
- 还有一个隐藏需求,就是带分享分销(一级)功能,后台设置分销地址(一个地址对应一个人),用户通过这个分销地址进入的下的单记录这个分享人上。
因此,我们需要创建的数据表结构设计如下
数据表结构设计
表单数据表 bctos-order
字段名 | 类型 | 说明 |
---|---|---|
string | 邮箱 | |
mobile | string | 手机号码 |
idcard_0 | file | 身份证正面 |
idcard_1 | file | 身份证反面 |
uid | string | 用户id, 它关联外键:uni-id-users._id, 即用户表里的_id |
share_uid | string | 分享人, 它关联外键:bctos-share._id, 即下表里的_id |
is_pay | int | 支付状态, 默认为0,[{"text":"未支付","value":0},{"text":"已支付","value":1}] |
out_trade_no | string | 微信支付订单号 |
status | int | 处理状态, 默认为0,[{"text":"未处理","value":0},{"text":"进行中","value":1},{"text":"已完成","value":2}] |
后台配置/云函数缓存表 bctos-cache
字段名 | 类型 | 说明 |
---|---|---|
key | string | 键名 |
value | string | 缓存数据,对象和数组会在保存时会自动使用JSON.stringify传成字符串保存 |
expired | int | 过期时间,为-1时表示不限制,后台配置一般为-1,云函数缓存token一般为7200秒 |
分销用户数据表 bctos-share
字段名 | 类型 | 说明 |
---|---|---|
username | string | 分享人姓名 |
mobile | string | 手机号码 |
remark | string | 备注 |
我们开发主要围绕上面这个业务表进行,因此需要先熟悉这三表的字段。
可视化插件安装
在开始增加数据表之前,我们先安装一个可视化管理表结构插件:https://ext.dcloud.net.cn/plugin?id=4800
它可以非常直观又高效地完成表字段的创建。
安装后可能需要重启下HBuilder X
系统分析
从上一篇的界面看虽然只有两个界面,但客户要实现的需求如下:
- 文章的标题和内容需要在后台可以配置。
- 表单里的协议内容需要也在后台可以配置。
- 表单的内容需要在后台列出来,并需要加一个状态管理(待处理,处理中,已处理)。
- 还有一个隐藏需求,就是带分享分销(一级)功能,后台设置分销地址(一个地址对应一个人),用户通过这个分销地址进入的下的单记录这个分享人上。
因此,我们需要创建的数据表结构设计如下
数据表结构设计
表单数据表 bctos-order
字段名 | 类型 | 说明 |
---|---|---|
string | 邮箱 | |
mobile | string | 手机号码 |
idcard_0 | file | 身份证正面 |
idcard_1 | file | 身份证反面 |
uid | string | 用户id, 它关联外键:uni-id-users._id, 即用户表里的_id |
share_uid | string | 分享人, 它关联外键:bctos-share._id, 即下表里的_id |
is_pay | int | 支付状态, 默认为0,[{"text":"未支付","value":0},{"text":"已支付","value":1}] |
out_trade_no | string | 微信支付订单号 |
status | int | 处理状态, 默认为0,[{"text":"未处理","value":0},{"text":"进行中","value":1},{"text":"已完成","value":2}] |
后台配置/云函数缓存表 bctos-cache
字段名 | 类型 | 说明 |
---|---|---|
key | string | 键名 |
value | string | 缓存数据,对象和数组会在保存时会自动使用JSON.stringify传成字符串保存 |
expired | int | 过期时间,为-1时表示不限制,后台配置一般为-1,云函数缓存token一般为7200秒 |
分销用户数据表 bctos-share
字段名 | 类型 | 说明 |
---|---|---|
username | string | 分享人姓名 |
mobile | string | 手机号码 |
remark | string | 备注 |
我们开发主要围绕上面这个业务表进行,因此需要先熟悉这三表的字段。
收起阅读 »
公众号开发(二)公众号H5项目创建与域名配置
项目创建
后面系列文章以当前项目为准进行介绍说明,本次项目命名为:bctos-weixin-h5,项目配置如下
一定要启用uniCloud,默认选择阿里云即可
项目生成后,在uniCloud目录上右键,创建云空间
创建同名云空间
创建成功回到HBuilder X工具,继续在在uniCloud目录上右键,选择关联云空间
选择刚创建的云空间
即可完成项目创建
前端网页托管域名配置
由于是H5页面,域名是必须,虽然后面我们使用DCloud的前端网页托管
作为网页存放的地址,它提供了默认域名,但这个默认域名还是有限制的,因此必须先有一个自己的备案过的域名。
后面我们将以 weixin-h5.bctos.cn 这个域名为例子进行说明,同时由于是演示,后面我们也是使用测试公众号来演示,如果要在手机上体验,先关注我的测试号(测试完成后建议取消关注,我们也会每隔一段时间清理一次)
打开uniCloud后台
进入前端网页托管界面,先开通
开通成功后,点参数配置
增加域名
把页面刷新下就可以把审核中的状态变成配置中,并能得到CHAME的值
在自己的域名管理那边增加CHAME解析,如我的域名在阿里云是这样配置的:
配置完成,稍等一会刷新前端网页托管配置即可看到部署完成
最后把域名配置到跨域配置中
项目创建
后面系列文章以当前项目为准进行介绍说明,本次项目命名为:bctos-weixin-h5,项目配置如下
一定要启用uniCloud,默认选择阿里云即可
项目生成后,在uniCloud目录上右键,创建云空间
创建同名云空间
创建成功回到HBuilder X工具,继续在在uniCloud目录上右键,选择关联云空间
选择刚创建的云空间
即可完成项目创建
前端网页托管域名配置
由于是H5页面,域名是必须,虽然后面我们使用DCloud的前端网页托管
作为网页存放的地址,它提供了默认域名,但这个默认域名还是有限制的,因此必须先有一个自己的备案过的域名。
后面我们将以 weixin-h5.bctos.cn 这个域名为例子进行说明,同时由于是演示,后面我们也是使用测试公众号来演示,如果要在手机上体验,先关注我的测试号(测试完成后建议取消关注,我们也会每隔一段时间清理一次)
打开uniCloud后台
进入前端网页托管界面,先开通
开通成功后,点参数配置
增加域名
把页面刷新下就可以把审核中的状态变成配置中,并能得到CHAME的值
在自己的域名管理那边增加CHAME解析,如我的域名在阿里云是这样配置的:
配置完成,稍等一会刷新前端网页托管配置即可看到部署完成
最后把域名配置到跨域配置中

公众号开发(一)前言及使用dcloud全家桶开发介绍
本次公众号开发系列文章中介绍的代码已经以插件的方式上传到市场: https://ext.dcloud.net.cn/plugin?id=4829
开发背景
本作者是十几年的PHP后端开发者,偶尔前端忙不过来的话也兼一些前端工作,后来微信小程序出来后直接上手小程序开发,也发过几个小程序开发教程。一个偶然的机会我们的前端使用uniapp开发了一个项目,然后我们也都开始了uniapp开发之旅。但也仅是作为一个前端框架使用,后端还是使用我们自己的PHP系统。
直到最近看到uniCloud的逆天介绍:[什么是uniCloud](https://uniapp.dcloud.io/uniCloud/README "什么是uniCloud")
关键有三点:免费,免费,免费,云函数,云数据还有前端网页托管,都是免费
这对大部分创业前期的项目来说很重要,因此萌生了要把公众号需要所的后端全部修改为云函数的想法,看能不能不依赖服务器的情况完成公众号的H5全部功能
最近刚好需要一个实际运营公众号项目,决定使用uniCloud作为后端开发,虽然目前插件库很丰富,号称能减少很多重复造轮子的工作,但这些造好的轮子大多是给小程序用的,开发公众号的话还是要自己造轮子,很多基础的功能都没有现成的插件可用。
本着人人为我,我为人人的原则,给大家提供这个插件,这将大大减少大家的开发时间,大家不用再重复造轮子了
技术框架介绍
前端使用uniapp开发,这是一定的。
后台使用uniCloud的云函数,数据库也是uniCloud的云数据库
公众号H5前端有使用到uni-id用户体系,用到uni-center-config配置中心
为是免费到底,代码放到uniCloud里的前端网页托管,据说它不经过web server,页面和资源直接上cdn,就近访问,速度贼快!!^_^
要开发,我只认dcloud系列,哦耶!^_^
功能介绍
本次要开发只有一个表单界面:
功能非常简单,没有复杂的业务逻辑,因此很适合大家学习使用。但它却包含了公众号H5开发都会涉及以下几个功能:
1、在网页中获取微信公众号的用户openid
2、通过静默获取用户openid并在uni-id中实现自动完成注册登录等功能,即用户无感知进入系统时就完成了登录操作
3、从微信中获取用户昵称头像等信息,并保存到uni-id中
4、云函数实现缓存机制,并用来缓存微信access_token等数据
5、实现JS-SDK配置(这是重点和难点),实现自定义分享内容
6、导入uni-ui实现前端用户界面
7、微信支付配置及在网页中实现支付功能
总之,公众号开发涉及的主要功能都涉及了,包括签名失败的问题也搞了两天,各种填坑也没少。因此这个项目模板能省大家很多精力和时间,下载地址:https://ext.dcloud.net.cn/plugin?id=4829
本次公众号开发系列文章中介绍的代码已经以插件的方式上传到市场: https://ext.dcloud.net.cn/plugin?id=4829
开发背景
本作者是十几年的PHP后端开发者,偶尔前端忙不过来的话也兼一些前端工作,后来微信小程序出来后直接上手小程序开发,也发过几个小程序开发教程。一个偶然的机会我们的前端使用uniapp开发了一个项目,然后我们也都开始了uniapp开发之旅。但也仅是作为一个前端框架使用,后端还是使用我们自己的PHP系统。
直到最近看到uniCloud的逆天介绍:[什么是uniCloud](https://uniapp.dcloud.io/uniCloud/README "什么是uniCloud")
关键有三点:免费,免费,免费,云函数,云数据还有前端网页托管,都是免费
这对大部分创业前期的项目来说很重要,因此萌生了要把公众号需要所的后端全部修改为云函数的想法,看能不能不依赖服务器的情况完成公众号的H5全部功能
最近刚好需要一个实际运营公众号项目,决定使用uniCloud作为后端开发,虽然目前插件库很丰富,号称能减少很多重复造轮子的工作,但这些造好的轮子大多是给小程序用的,开发公众号的话还是要自己造轮子,很多基础的功能都没有现成的插件可用。
本着人人为我,我为人人的原则,给大家提供这个插件,这将大大减少大家的开发时间,大家不用再重复造轮子了
技术框架介绍
前端使用uniapp开发,这是一定的。
后台使用uniCloud的云函数,数据库也是uniCloud的云数据库
公众号H5前端有使用到uni-id用户体系,用到uni-center-config配置中心
为是免费到底,代码放到uniCloud里的前端网页托管,据说它不经过web server,页面和资源直接上cdn,就近访问,速度贼快!!^_^
要开发,我只认dcloud系列,哦耶!^_^
功能介绍
本次要开发只有一个表单界面:
功能非常简单,没有复杂的业务逻辑,因此很适合大家学习使用。但它却包含了公众号H5开发都会涉及以下几个功能:
1、在网页中获取微信公众号的用户openid
2、通过静默获取用户openid并在uni-id中实现自动完成注册登录等功能,即用户无感知进入系统时就完成了登录操作
3、从微信中获取用户昵称头像等信息,并保存到uni-id中
4、云函数实现缓存机制,并用来缓存微信access_token等数据
5、实现JS-SDK配置(这是重点和难点),实现自定义分享内容
6、导入uni-ui实现前端用户界面
7、微信支付配置及在网页中实现支付功能
总之,公众号开发涉及的主要功能都涉及了,包括签名失败的问题也搞了两天,各种填坑也没少。因此这个项目模板能省大家很多精力和时间,下载地址:https://ext.dcloud.net.cn/plugin?id=4829
收起阅读 »
关于mui扫描二维码不能扫反码的解决方案
可以使用拍照+jsQR.js实现将反码、正码二维码拍照解析。只是会有点慢,但至少可以解析出数据。
jsQR.js下载地址:https://github.com/cozmo/jsQR/tree/master/dist
本篇文章代码案例:
<!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" />
<style type="text/css">
.sizes {
width: 100%;
height: 420px;
margin-top: 10px;
border: 1px solid black;
}
</style>
</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">
<canvas id="canvas1" class="sizes"></canvas>
<!--<img class="sizes" id="imgs" />-->
</div>
<script src="../js/mui.min.js"></script>
<script src="../js/apps.js" type="text/javascript" charset="utf-8"></script>
<script src="../js/jquery-1.11.0.js" type="text/javascript" charset="utf-8"></script>
<script src="../js/jsqr/jsQR.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var getQrCodeValueNum = 0;
mui.init();
if(window.plus) {
console.log("window.plus");
plusready();
} else {
document.addEventListener('plusready', plusready, false);
console.log("addEventListener")
}
function plusready() {
getQrCodeValueNum = 0;
//处理返回事件
plus.key.addEventListener('backbutton', function() {
plus.nativeUI.closeWaiting();
plus.webview.currentWebview().close();
})
var canvas1 = document.getElementById('canvas1');
var context1 = canvas1.getContext('2d');
// 页面加载后,延迟调用摄像机拍照操作
var cmr = plus.camera.getCamera();
setTimeout(function() {
cmr.captureImage(successCB, errorCB, {});
}, 1000);
// 成功回执
function successCB(capturedFile) {
// 拍照图片保存路径
console.log(JSON.stringify(capturedFile));
if(capturedFile) {
console.log("6666");
plus.io.resolveLocalFileSystemURL(capturedFile, function(entry) {
console.log("successCB-->"+entry);
compressImage(entry.toLocalURL(),entry.name);
}, function(e) {
plus.nativeUI.toast("读取拍照文件错误:" + e.message);
});
}
}
// 失败回执
function errorCB(error) {
var code = error.code; // 错误编码
var message = error.message; // 错误描述信息
console.log("code:" + code + " msg:" + message);
mui.alert("拍照失败!", "温馨提示", "确定", function() {
plus.webview.currentWebview().close();
}, "div");
}
// 图片压缩
function compressImage(url,filename){
// var name="_doc/upload/"+filename;
// plus.zip.compressImage({
// src:url,//src: (String 类型 )压缩转换原始图片的路径
// dst:name,//压缩转换目标图片的路径
// quality:100,//quality: (Number 类型 )压缩图片的质量.取值范围为1-100
// overwrite:true,//overwrite: (Boolean 类型 )覆盖生成新文件
// width:'300',
// height:'420'
//
// },
// function(zip) {
// //页面显示图片
// showPics(zip.target,name);
// },function(error) {
// plus.nativeUI.toast("压缩图片失败,请稍候再试");
// });
showPics(url,name);
}
// 图片显示
function showPics(url,name){
//根据路径读取到文件
plus.io.resolveLocalFileSystemURL(url,function(entry){
entry.file( function(file){
plus.nativeUI.closeWaiting();
plus.nativeUI.showWaiting("获取图片中。。。");
var fileReader = new plus.io.FileReader();
fileReader.readAsDataURL(file);
fileReader.onloadend = function(e) {
var picUrl = e.target.result.toString();
console.log("picUrl-->\n"+picUrl);
// var img1 = $("#imgs").attr("src");//获取页面存放图片标签的值
// if(img1 =="" || img1 == undefined){
// $("#imgs").attr("src",picUrl);//将图片base64编码赋值给img标签
// console.log($("#imgs").attr("src"));
// }
// var code = jsQR($("#imgs").attr("src"), $("#imgs").width, $("#imgs").height, {
// inversionAttempts: "invertFirst"
// });
// console.log("code->"+code);
var imgs = new Image();
imgs.src = picUrl;
imgs.onload = function() {
console.log("imgs.onload");
plus.nativeUI.closeWaiting();
plus.nativeUI.showWaiting("正在加载。。。");
var ratio = getPixelRatio(context1);
console.log("ratio-->"+ratio);
canvas1.width = canvas1.width*ratio;
canvas1.height = canvas1.height*ratio;
// canvas1.width/2 必须 /2 不然解析不出来
context1.drawImage(imgs,0,0,canvas1.width/2,canvas1.height);
plus.nativeUI.closeWaiting();
plus.nativeUI.showWaiting("正在解析。。。");
var imageData = context1.getImageData(0, 0, canvas1.width, canvas1.height);
var code = jsQR(imageData.data, imageData.width, imageData.height, {
inversionAttempts: "invertFirst"
});
console.log("code->"+code);
if(code){
plus.nativeUI.closeWaiting();
plus.nativeUI.showWaiting("解析成功");
console.log(code.data);
drawLine(code.location.topLeftCorner, code.location.topRightCorner, "#FF3B58");
drawLine(code.location.topRightCorner, code.location.bottomRightCorner, "#FF3B58");
drawLine(code.location.bottomRightCorner, code.location.bottomLeftCorner, "#FF3B58");
drawLine(code.location.bottomLeftCorner, code.location.topLeftCorner, "#FF3B58");
if(getQrCodeValueNum <= 0) {
var qrCodeValue = code.data;
plus.nativeUI.closeWaiting();
plus.nativeUI.showWaiting("服务器请求中。。。");
// 请求服务器,保证只会请求一次
setTimeout(function(){
getInfaByQRResult(qrCodeValue);
},1500);
}
getQrCodeValueNum = getQrCodeValueNum + 1;
}else{
plus.nativeUI.closeWaiting();
mui.alert("解析失败,请重试!", "温馨提示", "确定", function() {
plus.webview.currentWebview().close();
}, "div");
}
}
}
});
});
}
// 画出二维码的部位
function drawLine(begin, end, color) {
context1.beginPath();
context1.moveTo(begin.x, begin.y);
context1.lineTo(end.x, end.y);
context1.lineWidth = 4;
context1.strokeStyle = color;
context1.stroke();
}
//获取canvas应该放大的倍数的方法(这样拍照的图片放入画布,能针对不同手机适配,提高清晰度)
function getPixelRatio(context) {
var backingStore = context.backingStorePixelRatio
|| context.webkitBackingStorePixelRatio
|| context.mozBackingStorePixelRatio
|| context.msBackingStorePixelRatio
|| context.oBackingStorePixelRatio
|| context.backingStorePixelRatio || 1;
return (window.devicePixelRatio || 1) / backingStore;
};
}
</script>
</body>
</html>
具体可以参考我的博客:
https://blog.csdn.net/qq_38322527/article/details/116033962
可以使用拍照+jsQR.js实现将反码、正码二维码拍照解析。只是会有点慢,但至少可以解析出数据。
jsQR.js下载地址:https://github.com/cozmo/jsQR/tree/master/dist
本篇文章代码案例:
<!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" />
<style type="text/css">
.sizes {
width: 100%;
height: 420px;
margin-top: 10px;
border: 1px solid black;
}
</style>
</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">
<canvas id="canvas1" class="sizes"></canvas>
<!--<img class="sizes" id="imgs" />-->
</div>
<script src="../js/mui.min.js"></script>
<script src="../js/apps.js" type="text/javascript" charset="utf-8"></script>
<script src="../js/jquery-1.11.0.js" type="text/javascript" charset="utf-8"></script>
<script src="../js/jsqr/jsQR.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
var getQrCodeValueNum = 0;
mui.init();
if(window.plus) {
console.log("window.plus");
plusready();
} else {
document.addEventListener('plusready', plusready, false);
console.log("addEventListener")
}
function plusready() {
getQrCodeValueNum = 0;
//处理返回事件
plus.key.addEventListener('backbutton', function() {
plus.nativeUI.closeWaiting();
plus.webview.currentWebview().close();
})
var canvas1 = document.getElementById('canvas1');
var context1 = canvas1.getContext('2d');
// 页面加载后,延迟调用摄像机拍照操作
var cmr = plus.camera.getCamera();
setTimeout(function() {
cmr.captureImage(successCB, errorCB, {});
}, 1000);
// 成功回执
function successCB(capturedFile) {
// 拍照图片保存路径
console.log(JSON.stringify(capturedFile));
if(capturedFile) {
console.log("6666");
plus.io.resolveLocalFileSystemURL(capturedFile, function(entry) {
console.log("successCB-->"+entry);
compressImage(entry.toLocalURL(),entry.name);
}, function(e) {
plus.nativeUI.toast("读取拍照文件错误:" + e.message);
});
}
}
// 失败回执
function errorCB(error) {
var code = error.code; // 错误编码
var message = error.message; // 错误描述信息
console.log("code:" + code + " msg:" + message);
mui.alert("拍照失败!", "温馨提示", "确定", function() {
plus.webview.currentWebview().close();
}, "div");
}
// 图片压缩
function compressImage(url,filename){
// var name="_doc/upload/"+filename;
// plus.zip.compressImage({
// src:url,//src: (String 类型 )压缩转换原始图片的路径
// dst:name,//压缩转换目标图片的路径
// quality:100,//quality: (Number 类型 )压缩图片的质量.取值范围为1-100
// overwrite:true,//overwrite: (Boolean 类型 )覆盖生成新文件
// width:'300',
// height:'420'
//
// },
// function(zip) {
// //页面显示图片
// showPics(zip.target,name);
// },function(error) {
// plus.nativeUI.toast("压缩图片失败,请稍候再试");
// });
showPics(url,name);
}
// 图片显示
function showPics(url,name){
//根据路径读取到文件
plus.io.resolveLocalFileSystemURL(url,function(entry){
entry.file( function(file){
plus.nativeUI.closeWaiting();
plus.nativeUI.showWaiting("获取图片中。。。");
var fileReader = new plus.io.FileReader();
fileReader.readAsDataURL(file);
fileReader.onloadend = function(e) {
var picUrl = e.target.result.toString();
console.log("picUrl-->\n"+picUrl);
// var img1 = $("#imgs").attr("src");//获取页面存放图片标签的值
// if(img1 =="" || img1 == undefined){
// $("#imgs").attr("src",picUrl);//将图片base64编码赋值给img标签
// console.log($("#imgs").attr("src"));
// }
// var code = jsQR($("#imgs").attr("src"), $("#imgs").width, $("#imgs").height, {
// inversionAttempts: "invertFirst"
// });
// console.log("code->"+code);
var imgs = new Image();
imgs.src = picUrl;
imgs.onload = function() {
console.log("imgs.onload");
plus.nativeUI.closeWaiting();
plus.nativeUI.showWaiting("正在加载。。。");
var ratio = getPixelRatio(context1);
console.log("ratio-->"+ratio);
canvas1.width = canvas1.width*ratio;
canvas1.height = canvas1.height*ratio;
// canvas1.width/2 必须 /2 不然解析不出来
context1.drawImage(imgs,0,0,canvas1.width/2,canvas1.height);
plus.nativeUI.closeWaiting();
plus.nativeUI.showWaiting("正在解析。。。");
var imageData = context1.getImageData(0, 0, canvas1.width, canvas1.height);
var code = jsQR(imageData.data, imageData.width, imageData.height, {
inversionAttempts: "invertFirst"
});
console.log("code->"+code);
if(code){
plus.nativeUI.closeWaiting();
plus.nativeUI.showWaiting("解析成功");
console.log(code.data);
drawLine(code.location.topLeftCorner, code.location.topRightCorner, "#FF3B58");
drawLine(code.location.topRightCorner, code.location.bottomRightCorner, "#FF3B58");
drawLine(code.location.bottomRightCorner, code.location.bottomLeftCorner, "#FF3B58");
drawLine(code.location.bottomLeftCorner, code.location.topLeftCorner, "#FF3B58");
if(getQrCodeValueNum <= 0) {
var qrCodeValue = code.data;
plus.nativeUI.closeWaiting();
plus.nativeUI.showWaiting("服务器请求中。。。");
// 请求服务器,保证只会请求一次
setTimeout(function(){
getInfaByQRResult(qrCodeValue);
},1500);
}
getQrCodeValueNum = getQrCodeValueNum + 1;
}else{
plus.nativeUI.closeWaiting();
mui.alert("解析失败,请重试!", "温馨提示", "确定", function() {
plus.webview.currentWebview().close();
}, "div");
}
}
}
});
});
}
// 画出二维码的部位
function drawLine(begin, end, color) {
context1.beginPath();
context1.moveTo(begin.x, begin.y);
context1.lineTo(end.x, end.y);
context1.lineWidth = 4;
context1.strokeStyle = color;
context1.stroke();
}
//获取canvas应该放大的倍数的方法(这样拍照的图片放入画布,能针对不同手机适配,提高清晰度)
function getPixelRatio(context) {
var backingStore = context.backingStorePixelRatio
|| context.webkitBackingStorePixelRatio
|| context.mozBackingStorePixelRatio
|| context.msBackingStorePixelRatio
|| context.oBackingStorePixelRatio
|| context.backingStorePixelRatio || 1;
return (window.devicePixelRatio || 1) / backingStore;
};
}
</script>
</body>
</html>
具体可以参考我的博客:
https://blog.csdn.net/qq_38322527/article/details/116033962

logger.js (改自:https://ext.dcloud.net.cn/plugin?id=3938#rating)
/**
* js 日期格式化
* 传时间戳参数进行时间戳转换,不传时返回当前时间(返回年月日时分秒格式)
* @param {Object} timeStamp 时间戳参数 非必传
* @param {Object} format 格式化样式 非必传
*/
function getTimeStampDatetime(format = 'yyyy-MM-dd HH:mm:ss', timeStamp) {
let nowDatetime = new Date()
timeStamp ? nowDatetime = new Date(timeStamp) : nowDatetime = new Date()
return nowDatetime.Format(format)
}
Date.prototype.Format = function(fmt) {
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"H+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[
k]).substr(("" + o[k]).length)));
return fmt;
}
// 获取日志文件信息
function getLogFileInfo() {
// 今日日期
let nowDate = getTimeStampDatetime("yyyy-MM-dd")
// 当前时间精确到秒
let nowTime = getTimeStampDatetime("HH:mm:ss")
// 文件名称
const fileName = 'log.' + nowDate
// 内容前缀
const linePrefix = nowTime + ' '
const lineSuffix = "\r\n"
return {
fileName,
linePrefix,
lineSuffix
}
}
/**
* 日志内容输出并写入文件
* 文件写入手机地址,华为Android/data/应用包名/documents/app-log/xxxx
* @param {string} text 写入日志的内容
* @param {boolean} isError 是否错误
*/
function log(text, isError) {
const fileInfo = getLogFileInfo()
let content = fileInfo.linePrefix + text + fileInfo.lineSuffix
isError && (content = '#error# ' + content)
isError ? console.error(text) : console.log(text)
//#ifndef APP-PLUS
return
//#endif
plus.io.requestFileSystem(plus.io.PUBLIC_DOCUMENTS, function(fs) {
// 可通过fs操作PUBLIC_DOCUMENTS文件系统
// 创建日志文件夹
fs.root.getDirectory("app-log", {
create: true,
exclusive: false
}, function(dir) {
// 创建或写入文件
console.log("Directory Entry Name: " + dir.fullPath + fileInfo.fileName);
dir.getFile(fileInfo.fileName, {
create: true,
}, function(fileEntry) {
// 找到文件准备写入操作
fileEntry.file(function(file) {
// create a FileWriter to write to the file
fileEntry.createWriter(function(writer) {
// Write data to file.
writer.seek(file.size)
writer.write(content)
}, function(e) {
console.error("日志写入错误", error)
});
});
});
}, function(err) {
console.error("文件夹创建失败", err)
});
}, function(error) {
console.error("文件系统进入错误", error)
});
}
function logFileNames() {
const func = (resolve, reject) => {
plus.io.requestFileSystem(plus.io.PUBLIC_DOCUMENTS, function(fs) {
fs.root.getDirectory("app-log", {
create: false,
exclusive: false
}, function(dir) {
const directoryReader = dir.createReader();
directoryReader.readEntries( function( entries ){
const names = entries.map(item => {
return item.name
})
resolve(names)
}, function ( e ) {
// alert( "Read entries failed: " + e.message );
console.error("Read entries failed", e)
reject(e)
} );
}, function(err) {
console.error("文件夹读取失败", err)
reject(err)
});
}, function(error) {
console.error("文件系统进入错误", error)
reject(error)
});
}
const promise = new Promise(func)
return promise
}
function logFileContent(fileName) {
const func = (resolve, reject) => {
plus.io.requestFileSystem(plus.io.PUBLIC_DOCUMENTS, function(fs) {
fs.root.getDirectory("app-log", {
create: false,
exclusive: false
}, function(dir) {
dir.getFile(fileName, {
create: false
}, function(fileEntry) {
fileEntry.file(function(file) {
console.log('file', JSON.stringify(file))
const reader = new plus.io.FileReader()
console.log('reader', reader)
reader.onloadend = function (e) {
console.log( "Read success")
// Get data
console.log(e.target.result)
resolve(e.target.result)
};
reader.onerror = function (e) {
console.error(e)
reject(e)
}
reader.onabort = function(e) {
console.error(e)
reject(e)
}
reader.readAsText(file) // 'utf-8'
}, function(err) {
console.error("获取文件数据对象失败", err)
reject(err)
});
}, function(err) {
console.error("获取文件数据失败", err)
reject(err)
});
}, function(err) {
console.error("文件夹创建失败", err)
reject(err)
});
}, function(error) {
console.error("文件系统进入错误", error)
reject(error)
});
}
return new Promise(func)
}
export default {
log,
logFileNames,
logFileContent,
}
/**
* js 日期格式化
* 传时间戳参数进行时间戳转换,不传时返回当前时间(返回年月日时分秒格式)
* @param {Object} timeStamp 时间戳参数 非必传
* @param {Object} format 格式化样式 非必传
*/
function getTimeStampDatetime(format = 'yyyy-MM-dd HH:mm:ss', timeStamp) {
let nowDatetime = new Date()
timeStamp ? nowDatetime = new Date(timeStamp) : nowDatetime = new Date()
return nowDatetime.Format(format)
}
Date.prototype.Format = function(fmt) {
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"H+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
for (var k in o)
if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[
k]).substr(("" + o[k]).length)));
return fmt;
}
// 获取日志文件信息
function getLogFileInfo() {
// 今日日期
let nowDate = getTimeStampDatetime("yyyy-MM-dd")
// 当前时间精确到秒
let nowTime = getTimeStampDatetime("HH:mm:ss")
// 文件名称
const fileName = 'log.' + nowDate
// 内容前缀
const linePrefix = nowTime + ' '
const lineSuffix = "\r\n"
return {
fileName,
linePrefix,
lineSuffix
}
}
/**
* 日志内容输出并写入文件
* 文件写入手机地址,华为Android/data/应用包名/documents/app-log/xxxx
* @param {string} text 写入日志的内容
* @param {boolean} isError 是否错误
*/
function log(text, isError) {
const fileInfo = getLogFileInfo()
let content = fileInfo.linePrefix + text + fileInfo.lineSuffix
isError && (content = '#error# ' + content)
isError ? console.error(text) : console.log(text)
//#ifndef APP-PLUS
return
//#endif
plus.io.requestFileSystem(plus.io.PUBLIC_DOCUMENTS, function(fs) {
// 可通过fs操作PUBLIC_DOCUMENTS文件系统
// 创建日志文件夹
fs.root.getDirectory("app-log", {
create: true,
exclusive: false
}, function(dir) {
// 创建或写入文件
console.log("Directory Entry Name: " + dir.fullPath + fileInfo.fileName);
dir.getFile(fileInfo.fileName, {
create: true,
}, function(fileEntry) {
// 找到文件准备写入操作
fileEntry.file(function(file) {
// create a FileWriter to write to the file
fileEntry.createWriter(function(writer) {
// Write data to file.
writer.seek(file.size)
writer.write(content)
}, function(e) {
console.error("日志写入错误", error)
});
});
});
}, function(err) {
console.error("文件夹创建失败", err)
});
}, function(error) {
console.error("文件系统进入错误", error)
});
}
function logFileNames() {
const func = (resolve, reject) => {
plus.io.requestFileSystem(plus.io.PUBLIC_DOCUMENTS, function(fs) {
fs.root.getDirectory("app-log", {
create: false,
exclusive: false
}, function(dir) {
const directoryReader = dir.createReader();
directoryReader.readEntries( function( entries ){
const names = entries.map(item => {
return item.name
})
resolve(names)
}, function ( e ) {
// alert( "Read entries failed: " + e.message );
console.error("Read entries failed", e)
reject(e)
} );
}, function(err) {
console.error("文件夹读取失败", err)
reject(err)
});
}, function(error) {
console.error("文件系统进入错误", error)
reject(error)
});
}
const promise = new Promise(func)
return promise
}
function logFileContent(fileName) {
const func = (resolve, reject) => {
plus.io.requestFileSystem(plus.io.PUBLIC_DOCUMENTS, function(fs) {
fs.root.getDirectory("app-log", {
create: false,
exclusive: false
}, function(dir) {
dir.getFile(fileName, {
create: false
}, function(fileEntry) {
fileEntry.file(function(file) {
console.log('file', JSON.stringify(file))
const reader = new plus.io.FileReader()
console.log('reader', reader)
reader.onloadend = function (e) {
console.log( "Read success")
// Get data
console.log(e.target.result)
resolve(e.target.result)
};
reader.onerror = function (e) {
console.error(e)
reject(e)
}
reader.onabort = function(e) {
console.error(e)
reject(e)
}
reader.readAsText(file) // 'utf-8'
}, function(err) {
console.error("获取文件数据对象失败", err)
reject(err)
});
}, function(err) {
console.error("获取文件数据失败", err)
reject(err)
});
}, function(err) {
console.error("文件夹创建失败", err)
reject(err)
});
}, function(error) {
console.error("文件系统进入错误", error)
reject(error)
});
}
return new Promise(func)
}
export default {
log,
logFileNames,
logFileContent,
}
收起阅读 »

unicloud后台配置ssl证书教程

看了下问答社区,图片的证书内容和密钥还是不知道怎么填,自己百度摸索了下:腾讯云下载好一年免费的SSl证书,打开压缩包,pem后缀的是证书内容,key是密钥,直接点击文件是打不开的,需要更改后缀为txt,用记事本打开,然后复制粘贴上去就行了。有ssl证书的网站是https的,没就http,http网站总是被别人加入广告,烦死,有https,应该不会被别人放入广告。就这样。
看了下问答社区,图片的证书内容和密钥还是不知道怎么填,自己百度摸索了下:腾讯云下载好一年免费的SSl证书,打开压缩包,pem后缀的是证书内容,key是密钥,直接点击文件是打不开的,需要更改后缀为txt,用记事本打开,然后复制粘贴上去就行了。有ssl证书的网站是https的,没就http,http网站总是被别人加入广告,烦死,有https,应该不会被别人放入广告。就这样。 收起阅读 »

使用native.js 在uni app中 连接经典蓝牙 任意打印图片,摆脱ble低功耗的低速传输
首先感谢 :
https://ask.dcloud.net.cn/article/1089
这段时间需要开发一个蓝牙连接打印机的app。 想法很天真,以为uniapp啥api都有了,没想到还得从打印机指令开始学......
不过还好 网上很多教程,代码直接copy改
这个项目主要用到了 html 5+,native.js 实现android原生的蓝牙连接体验,支持经典蓝牙以及大量数据传输
我根据网上的代码 封装了一个类 :
BluetoothTool.js
可以直接在 uniapp中使用 。仅支持 android app端。
还有这个demo是专门为 标签打印机 写的,小票打印机没有研究。
总共两个页面:一个是原生的蓝牙,一个是uniapp api支持的ble蓝牙 首页有跳转连接 ,只有首页会自动连接上次的设备
最后:
关于打印图片 我找到了三个方法 目前只有setBitmap3测试成功。setBitmap1,setBitmap2 貌似都不行 。(ble的setBitmap2可以)
大家自己测试吧 用的是 佳博标签打印机编程手册tspl 里面的指令
最后的最后:
我用ble低速传输打印图片就可以 ,但是原生打印图片不行 不知道为什么,有大神 知道请告知。谢谢。
首先感谢 :
https://ask.dcloud.net.cn/article/1089
这段时间需要开发一个蓝牙连接打印机的app。 想法很天真,以为uniapp啥api都有了,没想到还得从打印机指令开始学......
不过还好 网上很多教程,代码直接copy改
这个项目主要用到了 html 5+,native.js 实现android原生的蓝牙连接体验,支持经典蓝牙以及大量数据传输
我根据网上的代码 封装了一个类 :
BluetoothTool.js
可以直接在 uniapp中使用 。仅支持 android app端。
还有这个demo是专门为 标签打印机 写的,小票打印机没有研究。
总共两个页面:一个是原生的蓝牙,一个是uniapp api支持的ble蓝牙 首页有跳转连接 ,只有首页会自动连接上次的设备
最后:
关于打印图片 我找到了三个方法 目前只有setBitmap3测试成功。setBitmap1,setBitmap2 貌似都不行 。(ble的setBitmap2可以)
大家自己测试吧 用的是 佳博标签打印机编程手册tspl 里面的指令
最后的最后:
我用ble低速传输打印图片就可以 ,但是原生打印图片不行 不知道为什么,有大神 知道请告知。谢谢。

uni-app中使用mapbox地图
本文分享在uni-app中如何使用mapbox地图,先了解下注意事项:
- 由于mapbox中大量操作dom元素,不能直接在uni-app中直接使用,会报错误,所以必须使用uni-app提供的render.js模式。
- 使用前请先下载mapbox-gl的npm库,如果不想下载库,那么就引入mapbox官方提供的cdn链接,实现动态导入js,css,动态导入也是uni-app建议的一种方法,不会使得打包体积过大,此文暂时只展示如何使用npm包。
- 记得自己在mapbox官网申请access token。
具体操作和代码:
- 在uni-app项目的根目录下使用npm下载mapbox-gl
npm install --save mapbox-gl
- 在项目中创建一个map页面,页面代码如下:
<template> <view class="map-wrap"> <!-- #ifdef APP-PLUS || H5 --> <view id="map-box"> <!-- 这里是mapbox的地图容器 --> </view> <!-- #endif --> </view> </template>
//renderjs 模式
<script module="mapbox" lang="renderjs">
//此处下载的是mapbox2.x版本,引入方式要按照此处书写
const mapboxgl = require('!mapbox-gl/dist/mapbox-gl.js');
//如果是1.x版本,按照下面方式书写(少了一个 '!')
//const mapboxgl = require('!mapbox-gl/dist/mapbox-gl.js');
//css也可以在下方的scss中导入,不做强求
import 'mapbox-gl/dist/mapbox-gl.css';
//let map = null //地图实例
export default {
data:{
return(){
map: null //地图实例
}
},
mounted() {
this.createdMap()
},
methods: {
createdMap() { //创建地图
mapboxgl.accessToken = '填写你自己申请到的token';
map = new mapboxgl.Map({
container: 'map-box', // container ID
style: 'mapbox://styles/mapbox/light-v9', // style URL
center: [118.810251,32.062151], // starting position [lng, lat]
zoom: 9 // starting zoom
});
map.on("load", function() {
//加载地图时触发
console.log('load')
});
},
}
}
</script>
<style lang="scss">
.map-wrap {
position: relative;
min-width: 100vw;
min-height: 100vh;
}
#map-box {
top: 0;
left: 0;
bottom: 0;
right: 0;
position: absolute;
}
#map-box /deep/ .mapboxgl-ctrl-geocoder {
min-width: 140px;
}
#map-box /deep/ .mapboxgl-ctrl-geocoder input[type="text"] {
height: 30px;
}
#map-box /deep/ .mapboxgl-ctrl-geocoder .geocoder-icon-search {
top: 5px;
}
#map-box /deep/ .mapboxgl-ctrl-geocoder .geocoder-pin-right * {
top: 5px;
}
.control-box {
padding: 10rpx 30rpx;
background-color: #00AAFF;
position: absolute;
right: 0;
top: 0;
z-index: 999px;
}
</style>
后面再写如何点击按钮控制地图交互和事件的绑定。
【好难用的markdown。。。】
https://blog.csdn.net/Hello_yihao/article/details/116055805
本文分享在uni-app中如何使用mapbox地图,先了解下注意事项:
- 由于mapbox中大量操作dom元素,不能直接在uni-app中直接使用,会报错误,所以必须使用uni-app提供的render.js模式。
- 使用前请先下载mapbox-gl的npm库,如果不想下载库,那么就引入mapbox官方提供的cdn链接,实现动态导入js,css,动态导入也是uni-app建议的一种方法,不会使得打包体积过大,此文暂时只展示如何使用npm包。
- 记得自己在mapbox官网申请access token。
具体操作和代码:
- 在uni-app项目的根目录下使用npm下载mapbox-gl
npm install --save mapbox-gl
- 在项目中创建一个map页面,页面代码如下:
<template> <view class="map-wrap"> <!-- #ifdef APP-PLUS || H5 --> <view id="map-box"> <!-- 这里是mapbox的地图容器 --> </view> <!-- #endif --> </view> </template>
//renderjs 模式
<script module="mapbox" lang="renderjs">
//此处下载的是mapbox2.x版本,引入方式要按照此处书写
const mapboxgl = require('!mapbox-gl/dist/mapbox-gl.js');
//如果是1.x版本,按照下面方式书写(少了一个 '!')
//const mapboxgl = require('!mapbox-gl/dist/mapbox-gl.js');
//css也可以在下方的scss中导入,不做强求
import 'mapbox-gl/dist/mapbox-gl.css';
//let map = null //地图实例
export default {
data:{
return(){
map: null //地图实例
}
},
mounted() {
this.createdMap()
},
methods: {
createdMap() { //创建地图
mapboxgl.accessToken = '填写你自己申请到的token';
map = new mapboxgl.Map({
container: 'map-box', // container ID
style: 'mapbox://styles/mapbox/light-v9', // style URL
center: [118.810251,32.062151], // starting position [lng, lat]
zoom: 9 // starting zoom
});
map.on("load", function() {
//加载地图时触发
console.log('load')
});
},
}
}
</script>
<style lang="scss">
.map-wrap {
position: relative;
min-width: 100vw;
min-height: 100vh;
}
#map-box {
top: 0;
left: 0;
bottom: 0;
right: 0;
position: absolute;
}
#map-box /deep/ .mapboxgl-ctrl-geocoder {
min-width: 140px;
}
#map-box /deep/ .mapboxgl-ctrl-geocoder input[type="text"] {
height: 30px;
}
#map-box /deep/ .mapboxgl-ctrl-geocoder .geocoder-icon-search {
top: 5px;
}
#map-box /deep/ .mapboxgl-ctrl-geocoder .geocoder-pin-right * {
top: 5px;
}
.control-box {
padding: 10rpx 30rpx;
background-color: #00AAFF;
position: absolute;
right: 0;
top: 0;
z-index: 999px;
}
</style>
后面再写如何点击按钮控制地图交互和事件的绑定。
【好难用的markdown。。。】
https://blog.csdn.net/Hello_yihao/article/details/116055805
收起阅读 »

5+APP,使用HbuilderX打包IOS包,切换WKWebview内核后启动白屏问题解决分享
一、项目介绍
使用vue开发的的项目,通过Hbuilder X打包称APP。Andriod版本的包,安装后一直没什么问题。
在打IOS包时遇到了大问题。
二、问题描述
打好的ipa包,安装到测试设备,启动图标显示完之后,直接白屏。
开始真机调试,报错信息如下:
11:12:35.725 同步手机端程序文件完成
11:12:37.631 联机调试并非打包,调试基座 HBuilder 是默认的测试包,权限、图标都不可自定义。只有在点菜单"发行-发行为原生安装包"时才能自定义这些设置
11:12:37.652 iOS9.0及以上系统需要在"设置"-"通用"-"设备管理"(或"描述文件")中信任DCloud企业证书(Digital Heaven开头的证书)才可以正常使用
11:12:37.673 如手机上HBuilder调试基座未启动,请手动启动。如应用未更新,请在手机上杀掉基座进程重启
11:13:07.571 Script error.
11:13:07.592 filename:
11:13:07.613 lineno:0
三、定位问题
结合官方论坛的其他帖子,问题方向是WKWebview内核严格的跨域限制导致的。
问题定位历程是强行切换UIWebview,页面显示正常。
切换UIWebview步骤
-
在manifest.json文件,代码视图plus下新增
"kernel" : { "ios" : "UIWebview", //或者 "WKWebview" },
最初以为是整个文件跨域
参考链接https://ask.dcloud.net.cn/article/36348
后来将index.html引用的js全删掉换成只有静态文字,发现不是index.html跨域。
然后开始从入口js排查,vue项目,入口main.js里引用的js一点一点删除测试。
结果:
const requireComponents = require.context('./components', true, /\.vue/)
requireComponents.keys().forEach(fileName => {
const reqCom = requireComponents(fileName)
const reqComName = reqCom.default['name']
Vue.component(reqComName, reqCom.default || reqCom)
})
定位出是require.context这个批量引入组件导致的。
参考链接https://www.cnblogs.com/cai-xin/p/12410297.html
require是运行时调用,所以require理论上可以运用在代码的任何地方
import是编译时调用,所以必须放在文件开头
也就是运行时去加载文件导致本地文件跨域。
结论:因require.context导致的本地文件跨域,最后更改组件批量引入方式 使用import导入。
一、项目介绍
使用vue开发的的项目,通过Hbuilder X打包称APP。Andriod版本的包,安装后一直没什么问题。
在打IOS包时遇到了大问题。
二、问题描述
打好的ipa包,安装到测试设备,启动图标显示完之后,直接白屏。
开始真机调试,报错信息如下:
11:12:35.725 同步手机端程序文件完成
11:12:37.631 联机调试并非打包,调试基座 HBuilder 是默认的测试包,权限、图标都不可自定义。只有在点菜单"发行-发行为原生安装包"时才能自定义这些设置
11:12:37.652 iOS9.0及以上系统需要在"设置"-"通用"-"设备管理"(或"描述文件")中信任DCloud企业证书(Digital Heaven开头的证书)才可以正常使用
11:12:37.673 如手机上HBuilder调试基座未启动,请手动启动。如应用未更新,请在手机上杀掉基座进程重启
11:13:07.571 Script error.
11:13:07.592 filename:
11:13:07.613 lineno:0
三、定位问题
结合官方论坛的其他帖子,问题方向是WKWebview内核严格的跨域限制导致的。
问题定位历程是强行切换UIWebview,页面显示正常。
切换UIWebview步骤
-
在manifest.json文件,代码视图plus下新增
"kernel" : { "ios" : "UIWebview", //或者 "WKWebview" },
最初以为是整个文件跨域
参考链接https://ask.dcloud.net.cn/article/36348
后来将index.html引用的js全删掉换成只有静态文字,发现不是index.html跨域。
然后开始从入口js排查,vue项目,入口main.js里引用的js一点一点删除测试。
结果:
const requireComponents = require.context('./components', true, /\.vue/)
requireComponents.keys().forEach(fileName => {
const reqCom = requireComponents(fileName)
const reqComName = reqCom.default['name']
Vue.component(reqComName, reqCom.default || reqCom)
})
定位出是require.context这个批量引入组件导致的。
参考链接https://www.cnblogs.com/cai-xin/p/12410297.html
require是运行时调用,所以require理论上可以运用在代码的任何地方
import是编译时调用,所以必须放在文件开头
也就是运行时去加载文件导致本地文件跨域。
结论:因require.context导致的本地文件跨域,最后更改组件批量引入方式 使用import导入。
收起阅读 »