生成指定页面带参数的微信小程序码

使用 Postman GET 请求

https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=xxxxxxxxxxxxx&secret=xxxxxxxxxxxxx

保存下 access_token,然后 POST 请求,配置如下,请求获得小程序码

https://api.weixin.qq.com/wxa/getwxacode?access_token=xxxxxxxxxx

或请求生成 QR 二维码

https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode?access_token=xxxxxxxxxx

封装一个微信小程序 多功能模态框组件 Modal

自定义一个微信小程序多功能模态框,可以输入文本 textarea,可以多选列表 CheckBox

组件

/components/modal/index.js

const app = getApp()
Component({
  properties: {
    show: {
      type: Boolean,
      value: true
    },
    title: {
      type: String,
      value: '标题'
    },
    moreRight: {
      type: String,
      value: ''
    },
    moreLeft: {
      type: String,
      value: ''
    },
    content: {
      type: String,
      value: '内容'
    },
    list: {
      type: Array,
      value: [
        {
          title: '测试',
          id: ''
        }]
    },
    bg:{
      type:Boolean,
      value:true
    },
    mode: {
      type: String,
      value: 'content'
    },
    btnLeft: {
      type: String,
      value: '取消'
    },
    btnRight: {
      type: String,
      value: '确定'
    }    
  },
  data: {

  },
  
  methods: {
    _none:function(){
      //nothing
    },
    _hideDialog(){
      this.setData({
        show:false
      })
      console.log('hide')
      this.triggerEvent('tapBg')
    },

    _showDialog() {
      this.setData({
        show: true
      })
    },
    _textarea(e){
      console.log(e.detail.value)
      var value = e.detail.value
      this.triggerEvent("myInput",value)
    },
    _tapLeft() {
      this.triggerEvent("tapLeft")
    },
    _tapRight() {
      this.triggerEvent("tapRight")
    },
    _moreRight() {
      this.triggerEvent("moreRight")
    },
    _moreLeft() {
      this.triggerEvent("moreLeft")
    },
    _checkboxChange(e){
      // console.log(e.detail.value)
      var value = e.detail.value
      this.triggerEvent("select", value)
    },
  },  
})

/components/modal/index.json

{
  "component": true
}

/components/modal/index.wxml

<view class="mask" style="{{show?'z-index: 1;opacity:0.7':''}}"></view>
<view class='modalBg' bindtap='_hideDialog' wx:if="{{show}}">
  <view class="modalDlg" catchtap='_none'>
    <view class='_modalTitle'>
      <view catchtap='_moreLeft' class='_more-left'>{{moreLeft}}</view>
      <text class='_title'>{{title}}</text>
      <view catchtap='_moreRight' class='_more-right'>{{moreRight}}</view>
    </view>
    <view wx:if="{{mode=='content'}}">{{content}}</view>
    <textarea wx:if="{{mode=='textarea'}}" bindinput='_textarea' value="{{content}}">
    </textarea>
    <scroll-view scroll-y="{{true}}" wx:if="{{mode=='list'}}">
      <view class='_check-container'>
        <checkbox-group bindchange="_checkboxChange">
          <label class="checkbox" wx:for="{{list}}" wx:key="{{key}}">
            <checkbox value="{{item.id}}" />
            <text class='checkbox-text'>{{item.title}}</text>
          </label>
        </checkbox-group>
      </view>
    </scroll-view>
    <view class='button-container'>
      <button catchtap="_tapLeft">{{btnLeft}}</button>
      <button catchtap="_tapRight">{{btnRight}}</button>
    </view>
  </view>
</view>

/components/modal/index.wxss

/* 自定义模态框 */

.mask {
  width: 100%;
  height: 100%;
  position: fixed;
  top: 0;
  left: 0;
  background: #000;
  z-index: -99999;
  opacity: 0;
  transition: opacity 0.5s ease;
}

.modalBg {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  position: fixed;
  left: 0;
  top: 0;
  z-index: 2;
}

.modalDlg {
  width: 580rpx;
  min-height: 200rpx;
  max-height: 900rpx;
  position: relative;
  z-index: 9999;
  background-color: #fff;
  border-radius: 10rpx;
  /* display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between; */
  overflow: hidden;
}

.modalDlg>view {
  font-size: 30rpx;
  min-height: 60rpx;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}

.modalDlg>view:first-child {
  height: 80rpx;
  font-weight: 700;
  width: 100%;
  border-bottom: 1rpx solid #ccc;
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
}

.modalDlg>view:nth-child(2) {
  height: 100rpx;
  margin: 0 40rpx;
}

.modalDlg>view {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}

.modalDlg>view>button {
  width: 290rpx;
  height: 80rpx;
  font-size: 30rpx;
  border-radius: 0;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  border-top: 1rpx solid #ccc;
}

.modalDlg>view>button:first-child {
  border-right: 1rpx solid #ccc;
}

.modalDlg>view>button::after {
  border-radius: 0;
  border: none;
}

._modalTitle {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
  position: relative;
}

._title {
  margin: 10rpx;
  text-align: center;
  width: 560rpx;
  padding: 10rpx;
}

._more-right {
  position: absolute;
  right: 20rpx;
  font-size: 20rpx;
  font-weight: 400;
}

._more-left {
  position: absolute;
  left: 20rpx;
  font-size: 25rpx;
  font-weight: 400;
}

scroll-view {
  height: inherit;
  width: inherit;
  overflow: hidden;
  max-height: 735rpx;
}

._check-container checkbox-group {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: flex-start;
  background-color: #fefefe;
  padding-left: 20rpx;
}

使用方法

/pages/test/test.json

{
  "usingComponents": {
    "my-modal": "/components/modal/index"
  }
}

/pages/test/test.wxml

<my-modal title="{{modal.title}}" mode="{{modal.mode}}" show="{{myModalShow}}" content="{{modal.content}}" btn-left="{{modal.btnLeft}}" btn-right='{{modal.btnRight}}'  bind:tapLeft="{{modal.tapLeft}}" bind:tapRight="{{modal.tapRight}}" bind:tapBg="tapBg"></my-modal>

/pages/test/test.js

  myModalShow: function() {
    console.log('myModalShow')
    this.setData({
      myModalShow: true
    })
  },

  myModalHide: function() {
    this.setData({
      myModalShow: false,
      modal: {},
    })
  },
  showit:function(){
    var modal = {
      mode: 'content',
      content: '这是内容',
      title: '提示',
      btnLeft: '取消',
      btnRight: '确定',
      tapLeft: 'myModalHide',
      tapRight: 'do',
    }
    that.setData({
      modal: modal,
    })
    that.myModalShow()
  },

/pages/test/test.wxss


checkbox-group {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
}

.checkbox {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}
.checkbox-text {
  font-size: 25rpx;
}

/*  重写 checkbox 样式  */

checkbox-group{
  
  font-size:28rpx;
}
checkbox-group label{
  line-height:60rpx;
}
checkbox-group label:first-child{
  margin-top:10rpx;
}

/* 未选中的 背景样式 */

checkbox .wx-checkbox-input {
  border-radius: 50%; /* 圆角 */
  width: 40rpx; /* 背景的宽 */
  height: 40rpx; /* 背景的高 */
  margin-top: -8rpx;
}

/* 选中后的 背景样式 (红色背景 无边框 可根据UI需求自己修改) */

checkbox .wx-checkbox-input.wx-checkbox-input-checked {
  /* border: none;  */
  background: rgb(217, 50, 124);
}

/* 选中后的 对勾样式 (白色对勾 可根据UI需求自己修改) */

checkbox .wx-checkbox-input.wx-checkbox-input-checked::before {
  display: flex;
  border-radius: 50%; /* 圆角 */
  width: 20rpx; /* 选中后对勾大小,不要超过背景的尺寸 */
  height: 25rpx; /* 选中后对勾大小,不要超过背景的尺寸 */
  line-height: 25rpx;
  text-align: center;
  font-size: 20rpx; /* 对勾大小 30rpx */
  color: #fff; /* 对勾颜色 白色 */
  background: transparent;
  transform: translate(-50%, -50%) scale(1);
  -webkit-transform: translate(-50%, -50%) scale(1);
}

微信小程序 左上角返回主页按钮 自定义顶部导航栏

小程序设置 “navigationStyle”: “custom”后在哪里自行定义导航栏样式?

https://segmentfault.com/q/1010000015377537

在 app.json 里进行更改 “navigationStyle”:”custom” 即可全屏显示,然后自定义顶部样式

{
  "pages":[
    "pages/index/index",
    "pages/logs/logs"
  ],
  "window":{
    "backgroundTextStyle":"light",
    "navigationBarBackgroundColor": "#44c9fb",
    "navigationBarTitleText": "小程序名字",
    "navigationBarTextStyle":"#fff",
    "navigationStyle":"custom"
  }
}

封装成组件可参考这篇文章

微信小程序 自定义头部导航栏 navigationStyle

https://www.jianshu.com/p/7393c800ba09

微信小程序 尝试 WebSocket

服务端开启 WebSocket,使用 WorkerMan + phpSocket.io

开启的端口为 2120,访问为 ws://wanaioa.unetu.net:2120/

由于微信小程序只能使用 443 端口,需将域名的 443 端口进行转发,同时为了保证原 https 的正常运作,参照网上的教程,将域名的 uri 匹配  xxx.xxx/wss 进行转发,其余不进行转发

location /wss
  {
    proxy_pass http://0.0.0.0:2120;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header X-Real-IP $remote_addr;
  }

参考文献

https://www.kancloud.cn/walkor/workerman/315297

以下为 WorkerMan 的原文

创建wss服务

问:

Workerman如何创建一个wss服务,使得客户端可以用过wss协来连接通讯,比如在微信小程序中连接服务端。

答:

wss协议实际是websocket+SSL,就是在websocket协议上加入SSL层,类似https(http+SSL)。
所以只需要在websocket协议的基础上开启SSL即可支持wss协议。

方法一 ,直接用Workerman开启SSL

准备工作:

1、Workerman版本不小于3.3.7

2、PHP安装了openssl扩展

3、已经申请了证书(pem/crt文件及key文件)放在磁盘任意目录

代码:

<?php
require_once __DIR__ . '/Workerman/Autoloader.php';
use Workerman\Worker;

// 证书最好是申请的证书
$context = array(
    // 更多ssl选项请参考手册 http://php.net/manual/zh/context.ssl.php
    'ssl' => array(
        // 请使用绝对路径
        'local_cert'                 => '磁盘路径/server.pem', // 也可以是crt文件
        'local_pk'                   => '磁盘路径/server.key',
        'verify_peer'                => false,
        // 'allow_self_signed' => true, //如果是自签名证书需要开启此选项
    )
);
// 这里设置的是websocket协议(端口任意,但是需要保证没被其它程序占用)
$worker = new Worker('websocket://0.0.0.0:443', $context);
// 设置transport开启ssl,websocket+ssl即wss
$worker->transport = 'ssl';
$worker->onMessage = function($con, $msg) {
    $con->send('ok');
};

Worker::runAll();

通过以上的代码,Workerman就监听了wss协议,客户端就可以通过wss协议来连接workerman实现安全即时通讯了。

测试

打开chrome浏览器,按F12打开调试控制台,在Console一栏输入(或者把下面代码放入到html页面用js运行)

// 证书是会检查域名的,请使用域名连接
ws = new WebSocket("wss://域名");
ws.onopen = function() {
    alert("连接成功");
    ws.send('tom');
    alert("给服务端发送一个字符串:tom");
};
ws.onmessage = function(e) {
    alert("收到服务端的消息:" + e.data);
};

注意:

1、如果无法启动,则一般是443端口被占用,请改成其它端口,注意改成其它端口后客户端连接时需要带上端口号,客户端连接时地址类似wss://domain.com:xxx ,xxx为端口号。如果必须使用443端口请使用方法二代理的方式实现wss。

2、wss端口只能通过wss协议访问,ws无法访问wss端口。

3、证书一般是与域名绑定的,所以测试的时候客户端请使用域名连接,不要使用ip去连。

4、如果出现无法访问的情况,请检查服务器防火墙。

5、此方法要求PHP版本>=5.6,因为微信小程序要求tls1.2,而PHP5.6以下版本不支持tls1.2。

方法二、利用nginx/apache代理wss

除了用Workerman自身的SSL,也可以利用nginx/apache作为wss代理转发给workerman(注意此方法workerman部分千万不要设置ssl,否则将无法连接)。

通讯原理及流程是:

1、客户端发起wss连接连到nginx/apache

2、nginx/apache将wss协议的数据转换成ws协议数据并转发到Workerman的websocket协议端口

3、Workerman收到数据后做业务逻辑处理

4、Workerman给客户端发送消息时,则是相反的过程,数据经过nginx/apache转换成wss协议然后发给客户端

nginx配置参考

前提条件及准备工作:

1、已经安装nginx,版本不低于1.3

2、假设Workerman监听的是8282端口(websocket协议)

3、已经申请了证书(pem/crt文件及key文件)放在了/etc/nginx/conf.d/ssl下

4、打算利用nginx开启443端口对外提供wss代理服务(端口可以根据需要修改)

5、nginx一般作为网站服务器运行着其它服务,为了不影响原来的站点使用,这里使用地址 域名/wss 作为wss的代理入口。也就是客户端连接地址为 wss://域名/wss

nginx配置类似如下

server {
  listen 443;

  ssl on;
  ssl_certificate /etc/ssl/server.pem;
  ssl_certificate_key /etc/ssl/server.key;
  ssl_session_timeout 5m;
  ssl_session_cache shared:SSL:50m;
  ssl_protocols SSLv3 SSLv2 TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;

  location /wss
  {
    proxy_pass http://127.0.0.1:8282;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header X-Real-IP $remote_addr;
  }
  
  # location / {} 站点的其它配置...
}

测试

// 证书是会检查域名的,请使用域名连接
ws = new WebSocket("wss://域名/wss");

ws.onopen = function() {
    alert("连接成功");
    ws.send('tom');
    alert("给服务端发送一个字符串:tom");
};
ws.onmessage = function(e) {
    alert("收到服务端的消息:" + e.data);
};

利用apache代理wss

也可以利用apache作为wss代理转发给workerman(注意如使用apache代理SSL,则workerman部分千万不要设置ssl,否则将无法连接)。

准备工作:
1、GatewayWorker 监听 8282 端口(websocket协议)

2、已经申请了ssl证书, 放在了/server/httpd/cert/ 下

3、利用apache转发443端口至指定端口8282

4、httpd-ssl.conf 已加载

5、openssl 已安装

启用 proxy_wstunnel_module 模块

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so

配置SSL及代理

#extra/httpd-ssl.conf
DocumentRoot "/网站/目录"
ServerName 域名

# Proxy Config
SSLProxyEngine on

ProxyRequests Off
ProxyPass /wss ws://127.0.0.1:8282
ProxyPassReverse /wss ws://127.0.0.1:8282

# 添加 SSL 协议支持协议,去掉不安全的协议
SSLProtocol all -SSLv2 -SSLv3
# 修改加密套件如下
SSLCipherSuite HIGH:!RC4:!MD5:!aNULL:!eNULL:!NULL:!DH:!EDH:!EXP:+MEDIUM
SSLHonorCipherOrder on
# 证书公钥配置
SSLCertificateFile /server/httpd/cert/your.pem
# 证书私钥配置
SSLCertificateKeyFile /server/httpd/cert/your.key
# 证书链配置,
SSLCertificateChainFile /server/httpd/cert/chain.pem

测试

// 证书是会检查域名的,请使用域名连接
ws = new WebSocket("wss://域名/wss");

ws.onopen = function() {
    alert("连接成功");
    ws.send('tom');
    alert("给服务端发送一个字符串:tom");
};
ws.onmessage = function(e) {
    alert("收到服务端的消息:" + e.data);
};

微信小程序 invalid page hint

微信小程序消息推送在测试可以发送成功,正式发送失败

错误信息如下

{
"errcode": 41030,
"errmsg": "invalid page hint: [YJmNKa07043954]"
}
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/notice.html?search-key=41030

page 不正确

错误 /pages/index/index

正确 pages/index/index

微信小程序 使用 SVG

将 svg 转成 base64 后使用

如在 https://loading.io/ 右键打开图片 

https://loading.io/spinners/lava-lamp/index.svg

右键保存本地

将 svg 转换成 base64

https://www.sojson.com/image2base64.html

在小程序的 CSS 里使用 背景图片的方式调用 base64 后的 svg

或使用 image 标签