HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

【app+sockjs】app下,stompjs不支持uniapp环境?

在uniapp下,用浏览器和h5调试,使用sockjs+stompjs没有任何问题。但是app下,无法使用stompjs,貌似不兼容。只好用sockjs,然后模拟stompjs的订阅,发送消息给websocket了。

在uniapp下,用浏览器和h5调试,使用sockjs+stompjs没有任何问题。但是app下,无法使用stompjs,貌似不兼容。只好用sockjs,然后模拟stompjs的订阅,发送消息给websocket了。

uniapp 蓝牙打印机 打印图片、二维码、条码、文字等 最新解决方案【兼容 小程序、android App】无偿分享出

蓝牙打印 经验分享

参考连接:
使用native.js 在uni app中 连接经典蓝牙 任意打印图片,摆脱ble低功耗的低速传输

便携式蓝牙打印,研究了很长时间,终于打印出来了,现在分享经验,省的少走弯路

微信小程序连接蓝牙打印机打印图片示例

热敏打印机编程 ESC/POS指令 【尤其这篇对图片处理讲解的非常好】

两个页面:

使用native.js连接蓝牙的demo -- 快速打印(只兼容app)

使用BLE低功耗蓝牙的demo -- 低速打印(可兼容小程序)

努力研究了三天,熬夜两个通宵终于打印出来了
【如果你们打印出现偏差,请检查图片的宽度是不是8的倍数,如果不是就用画图调整一下】
其他的问题请 私信我

demo地址 gitee开源中国:BluetoothPrinter 欢迎star

继续阅读 »

参考连接:
使用native.js 在uni app中 连接经典蓝牙 任意打印图片,摆脱ble低功耗的低速传输

便携式蓝牙打印,研究了很长时间,终于打印出来了,现在分享经验,省的少走弯路

微信小程序连接蓝牙打印机打印图片示例

热敏打印机编程 ESC/POS指令 【尤其这篇对图片处理讲解的非常好】

两个页面:

使用native.js连接蓝牙的demo -- 快速打印(只兼容app)

使用BLE低功耗蓝牙的demo -- 低速打印(可兼容小程序)

努力研究了三天,熬夜两个通宵终于打印出来了
【如果你们打印出现偏差,请检查图片的宽度是不是8的倍数,如果不是就用画图调整一下】
其他的问题请 私信我

demo地址 gitee开源中国:BluetoothPrinter 欢迎star

收起阅读 »

公众号开发(五)公众号域名配置及参数配置

公众号平台配置

登录公众号平台(https://mp.weixin.qq.com/)

公众号设置--功能设置页面,先设置好业务,JS,网页三个地方的安全域名。

在测试号设置安全域名(正式公众号设置的地方在: 公众号设置--功能设置 里):

设置网页授权域名

确认保存

公众号参数配置到uni-config-center

由于公众号的参数(如appid,appsecret等)都是比较机密的信息,直接写到前端uniapp里不大安全,可以像上一节增加uni-id配置一样增加公众号配置到uni-config-center

uni_modules\uni-config-center\uniCloud\cloudfunctions\common\uni-config-center目录下创建bctos-weixin-h5目录,然后在其下面创建config.json文档,要根据自己公众号的实际的参数填写

{  
    "appId": "公众号appid",  
    "appSecret": "公众号密钥",  
    "mchId": "微信支付商户ID",  
    "mchKey": "微信支付API密钥",  
    "notify_url": "微信支付回调地址"  
}

增加读取配置的云函数

由于前端uniapp无法直接读取uni-config-center里的配置,需要一个云函数帮我们实现参数的读取。

在uniCloud/cloudfunctions目录上右键新键云函数,函数名为:bctos-config-center,在自动生成的index.js文件写入以下代码

'use strict';  
const createConfig = require('uni-config-center')  

exports.main = async (event, context) => {  
    //event为客户端上传的参数  
    console.log('event : ', event)  

    if (typeof(event.pluginId) == "undefined") event.pluginId = 'bctos-weixin-h5';  

    const uniIdConfig = createConfig({  
        pluginId: event.pluginId  
    })  

    if (typeof(event.key) == "undefined" || event.key == '') {  
        console.log('获取全部配置');  
        return uniIdConfig.config();  
    } else {  
        console.log('获取指定配置');  
        return uniIdConfig.config(event.key);  
    }  
};  

由于这个云函数使用了uni-config-center,因此需要右键bctos-config-center目录管理公共模块依赖

之前由于对云函数理解不深,以为直接引用就可以,其实每个云函数在运行都是在相互独立的环境中,因此资源是不互相共用的,云函数每次引用别的资源时,都需要重新执行管理公共模块依赖以便更新依赖。

测试前端是否能获取到配置

打开pages/index/index文件,直接onLoad方法里加入测试代码看看能不能取到配置数据

        onLoad() {  
            uniCloud.callFunction({  
                name: "bctos-config-center",  
                data: {}  
            }).then(res => {  
                console.log('config', res);  
            })  
        },

在HBuilder X的调试里选择连接本地云函数,因为云函数和配置还没上传云端,所以要选择本地调试

然后运行项目到内置浏览器看看

运行起来后我们可以看到配置信息

说明我们已经能正常读取公众号配置信息

继续阅读 »

公众号平台配置

登录公众号平台(https://mp.weixin.qq.com/)

公众号设置--功能设置页面,先设置好业务,JS,网页三个地方的安全域名。

在测试号设置安全域名(正式公众号设置的地方在: 公众号设置--功能设置 里):

设置网页授权域名

确认保存

公众号参数配置到uni-config-center

由于公众号的参数(如appid,appsecret等)都是比较机密的信息,直接写到前端uniapp里不大安全,可以像上一节增加uni-id配置一样增加公众号配置到uni-config-center

uni_modules\uni-config-center\uniCloud\cloudfunctions\common\uni-config-center目录下创建bctos-weixin-h5目录,然后在其下面创建config.json文档,要根据自己公众号的实际的参数填写

{  
    "appId": "公众号appid",  
    "appSecret": "公众号密钥",  
    "mchId": "微信支付商户ID",  
    "mchKey": "微信支付API密钥",  
    "notify_url": "微信支付回调地址"  
}

增加读取配置的云函数

由于前端uniapp无法直接读取uni-config-center里的配置,需要一个云函数帮我们实现参数的读取。

在uniCloud/cloudfunctions目录上右键新键云函数,函数名为:bctos-config-center,在自动生成的index.js文件写入以下代码

'use strict';  
const createConfig = require('uni-config-center')  

exports.main = async (event, context) => {  
    //event为客户端上传的参数  
    console.log('event : ', event)  

    if (typeof(event.pluginId) == "undefined") event.pluginId = 'bctos-weixin-h5';  

    const uniIdConfig = createConfig({  
        pluginId: event.pluginId  
    })  

    if (typeof(event.key) == "undefined" || event.key == '') {  
        console.log('获取全部配置');  
        return uniIdConfig.config();  
    } else {  
        console.log('获取指定配置');  
        return uniIdConfig.config(event.key);  
    }  
};  

由于这个云函数使用了uni-config-center,因此需要右键bctos-config-center目录管理公共模块依赖

之前由于对云函数理解不深,以为直接引用就可以,其实每个云函数在运行都是在相互独立的环境中,因此资源是不互相共用的,云函数每次引用别的资源时,都需要重新执行管理公共模块依赖以便更新依赖。

测试前端是否能获取到配置

打开pages/index/index文件,直接onLoad方法里加入测试代码看看能不能取到配置数据

        onLoad() {  
            uniCloud.callFunction({  
                name: "bctos-config-center",  
                data: {}  
            }).then(res => {  
                console.log('config', res);  
            })  
        },

在HBuilder X的调试里选择连接本地云函数,因为云函数和配置还没上传云端,所以要选择本地调试

然后运行项目到内置浏览器看看

运行起来后我们可以看到配置信息

说明我们已经能正常读取公众号配置信息

收起阅读 »

公众号开发(四)导入uni-id及相关配置

安装uni-id

如果你的项目在创建里就选择uni-id的模板,安装这一步可省略,但配置部分一样是需要的。

打开插件库:https://ext.dcloud.net.cn/plugin?id=2116

点击导入到当前项目中

配置uni-id

在uni-config-center公用模块下创建uni-id目录

这部分官网描述不是很清楚,如果不知道公用模块的目录在哪里,很容易搞糊涂,我也是通过其示例代码才知道是要在uni_modules\uni-config-center\uniCloud\cloudfunctions\common\uni-config-center这个目录下创建uni-id目录

在创建的uni-id目录下再创建config.json文件

config.json内容以下

{  
    "passwordSecret": "bctos-weixin-h5",  
    "tokenSecret": "bctos-weixin-h5",  
    "tokenExpiresIn": 7200,  
    "tokenExpiresThreshold": 600,  
    "passwordErrorLimit": 6,  
    "passwordErrorRetryTime": 3600,  
    "autoSetInviteCode": false,  
    "forceInviteCode": false,  
    "app-plus": {  
        "tokenExpiresIn": 2592000,  
        "oauth" : {  
            "weixin" : {  
                "appid" : "weixin appid",  
                "appsecret" : "weixin appsecret"  
            },  
            "apple":    {  
                "bundleId": "your APP bundleId"  
            }  
        }  
    },  
    "mp-weixin": {  
        "oauth" : {  
            "weixin" : {  
                "appid" : "weixin appid",  
                "appsecret" : "weixin appsecret"  
            }  
        }  
    },  
    "mp-alipay": {  
        "oauth" : {  
            "alipay" : {  
                "appid" : "alipay appid",  
                "privateKey" : "alipay privateKey"  
            }  
        }  
    },  
    "service": {  
        "sms": {  
            "name": "your app name",  
            "codeExpiresIn": 180,  
            "smsKey": "your sms key",  
            "smsSecret": "your sms secret"  
        },  
        "univerify": {  
            "appid":"your appid",  
            "apiKey": "your apiKey",  
            "apiSecret": "your apiSecret"  
        }  
    }  
}  

只需要配置passwordSecret和tokenSecret两个参数即可,其它参数用不到,全部使用官方默认

在cloudfunctions/common下上传uni-id模块

cloudfunctions/common目前有好几个,确切的完整目录应该是uniCloud\cloudfunctions\common

先上传uni-config-center公共模块

否则会报获取公共模块 uni-config-center 云端代码文件失败

然后在uni-id目录上右键,选择上传公共模块

云数据库增加uni-id数据表

在uniCloud目录上右键,打开数据库管理界面

点+号增加

使用OpenDB表模板创建

回到HBuilder X工具,在database目录下右键下载新增加的表

至此uni-id全部配置完毕,后面需要用到的uni-id的云函数还有一步操作,就是要在所需uni-id的云函数目录上右键选择管理公共模块依赖添加uni-id到云函数里,后面讲到云函数开发时也会说到。

继续阅读 »

安装uni-id

如果你的项目在创建里就选择uni-id的模板,安装这一步可省略,但配置部分一样是需要的。

打开插件库:https://ext.dcloud.net.cn/plugin?id=2116

点击导入到当前项目中

配置uni-id

在uni-config-center公用模块下创建uni-id目录

这部分官网描述不是很清楚,如果不知道公用模块的目录在哪里,很容易搞糊涂,我也是通过其示例代码才知道是要在uni_modules\uni-config-center\uniCloud\cloudfunctions\common\uni-config-center这个目录下创建uni-id目录

在创建的uni-id目录下再创建config.json文件

config.json内容以下

{  
    "passwordSecret": "bctos-weixin-h5",  
    "tokenSecret": "bctos-weixin-h5",  
    "tokenExpiresIn": 7200,  
    "tokenExpiresThreshold": 600,  
    "passwordErrorLimit": 6,  
    "passwordErrorRetryTime": 3600,  
    "autoSetInviteCode": false,  
    "forceInviteCode": false,  
    "app-plus": {  
        "tokenExpiresIn": 2592000,  
        "oauth" : {  
            "weixin" : {  
                "appid" : "weixin appid",  
                "appsecret" : "weixin appsecret"  
            },  
            "apple":    {  
                "bundleId": "your APP bundleId"  
            }  
        }  
    },  
    "mp-weixin": {  
        "oauth" : {  
            "weixin" : {  
                "appid" : "weixin appid",  
                "appsecret" : "weixin appsecret"  
            }  
        }  
    },  
    "mp-alipay": {  
        "oauth" : {  
            "alipay" : {  
                "appid" : "alipay appid",  
                "privateKey" : "alipay privateKey"  
            }  
        }  
    },  
    "service": {  
        "sms": {  
            "name": "your app name",  
            "codeExpiresIn": 180,  
            "smsKey": "your sms key",  
            "smsSecret": "your sms secret"  
        },  
        "univerify": {  
            "appid":"your appid",  
            "apiKey": "your apiKey",  
            "apiSecret": "your apiSecret"  
        }  
    }  
}  

只需要配置passwordSecret和tokenSecret两个参数即可,其它参数用不到,全部使用官方默认

在cloudfunctions/common下上传uni-id模块

cloudfunctions/common目前有好几个,确切的完整目录应该是uniCloud\cloudfunctions\common

先上传uni-config-center公共模块

否则会报获取公共模块 uni-config-center 云端代码文件失败

然后在uni-id目录上右键,选择上传公共模块

云数据库增加uni-id数据表

在uniCloud目录上右键,打开数据库管理界面

点+号增加

使用OpenDB表模板创建

回到HBuilder X工具,在database目录下右键下载新增加的表

至此uni-id全部配置完毕,后面需要用到的uni-id的云函数还有一步操作,就是要在所需uni-id的云函数目录上右键选择管理公共模块依赖添加uni-id到云函数里,后面讲到云函数开发时也会说到。

收起阅读 »

公众号开发(三)系统分析和数据表结构设计

可视化插件安装

在开始增加数据表之前,我们先安装一个可视化管理表结构插件:https://ext.dcloud.net.cn/plugin?id=4800
它可以非常直观又高效地完成表字段的创建。

安装后可能需要重启下HBuilder X

系统分析

从上一篇的界面看虽然只有两个界面,但客户要实现的需求如下:

  1. 文章的标题和内容需要在后台可以配置。
  2. 表单里的协议内容需要也在后台可以配置。
  3. 表单的内容需要在后台列出来,并需要加一个状态管理(待处理,处理中,已处理)。
  4. 还有一个隐藏需求,就是带分享分销(一级)功能,后台设置分销地址(一个地址对应一个人),用户通过这个分销地址进入的下的单记录这个分享人上。

因此,我们需要创建的数据表结构设计如下

数据表结构设计

表单数据表 bctos-order

字段名 类型 说明
email 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

系统分析

从上一篇的界面看虽然只有两个界面,但客户要实现的需求如下:

  1. 文章的标题和内容需要在后台可以配置。
  2. 表单里的协议内容需要也在后台可以配置。
  3. 表单的内容需要在后台列出来,并需要加一个状态管理(待处理,处理中,已处理)。
  4. 还有一个隐藏需求,就是带分享分销(一级)功能,后台设置分销地址(一个地址对应一个人),用户通过这个分销地址进入的下的单记录这个分享人上。

因此,我们需要创建的数据表结构设计如下

数据表结构设计

表单数据表 bctos-order

字段名 类型 说明
email 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,应该不会被别人放入广告。就这样。

收起阅读 »

写个app下载官网

外包接单

写个app下载官网

能做的联系qq 1034888862

写个app下载官网

能做的联系qq 1034888862