1
头图

是的,在这个框架满天飞的年代,我既然有有幸使用了原生小程序开发项目,除了麻烦些,倒也不是一无所获,耕耘总有收货嘛,写博客本身不是为了炫技还是什么,单纯的是记性不好,有些知识点 自己是花了时间去查找的,时间久了,下次会忘,所以仅做记录的成份高一些,前言不搭后语莫怪。然后早上看到一句诗也不错:追风赶月莫停留,平芜尽头是春山。
回正题吧:

上传头像

changeAvatar() {    
var that = this;    
wx.chooseImage({      
count: 1, // 最多可以选择的图片张数,默认9     
 sizeType: ['original', 'compressed'], 
// original 原图,compressed 压缩图,默认二者都有     
 sourceType: ['album', 'camera'],
 // album 从相册选图,camera 使用相机,默认二者都有     
 success: async function (res) {       
 var avatar = res.tempFilePaths;       
 await that.filebBatchDeletes()        
let resImage = await uploadImage(avatar[0])       
 if (resImage.data.code == 200) {          
that.setData({            
avatar: avatar[0],           
 picId: resImage.data.result         
 })       
 }     
 },      
fail: function () {        
// console.log("上传图片没有成功");    
  },      
complete: function () {       
 // complete      
}    })  },


export const uploadImage = (uploadFile: string) => { 
 return new Promise((resolve, reject) => {   
 wx.uploadFile({     
 url: config.otherBaseUrl + 'load/file-upload',  
    filePath: uploadFile,     
 header: {      
  "Content-Type": "multipart/form-data",     
   'token': wx.getStorageSync('accessToken'),   
   },    
  formData: {       
 'caseNum': wx.getStorageSync('userId'),      
  "caseType": 'image',     
 },     
 name: 'file',    
  success: (res) => {     
   const data = JSON.parse(res.data) 
       resolve({ data })    
  },      
fail: (err) => {     
   reject(err)      
}    
})  
})}

前端小程序拿到图片流处理成Base64async

 readImageByDataIds(id: string) {   
 let res = await getPicStream(`/readImageByDataId/${id}`) 
   this.setData({     
 processPic: res  
  })  },


export const getPicStream = (url:string) => {
  return new Promise((resolve,reject) => {   
 wx.request({    
  url: config.baseUrl + url,    
  header: {        
'X-Access-Token': wx.getStorageSync('token'),  
      'X-TIMESTAMP': getDateTimeToString(),    
  },    
  responseType: 'arraybuffer',    
  success: res => {        
let url ='data:image/png;base64,'+ wx.arrayBufferToBase64(res.data)    
    resolve(url)    
  },     
 fail: err => {   
     reject(err)      
}    
}) 
 })}

返回上一个页面并触发上一个页面的方法因为小程序不像浏览器会自动刷新,所以需要手动刷新

var pages = getCurrentPages();    
  var beforePage = pages[pages.length - 2];  
    wx.navigateBack({      
  delta: 1,        
 success: function () {     
     beforePage.getInfo(); // 执行前一个页面的getInfo方法      
  }     
 })

Base64形式的文件做预览

preClick(event: any) {   
 if (event.detail.type == 'file') {  
    const fileSystemManager = wx.getFileSystemManager() 
  try {    
    let strPath = event.detail.url.substring(event.detail.url.indexOf(',')     + 1)  
fileSystemManager.writeFileSync(
  wx.env.USER_DATA_PATH + `/${event.detail.materialName}`, strPath,"base64");    
wx.openDocument({          
     filePath: wx.env.USER_DATA_PATH + `/${event.detail.materialName}`       
 })      
} catch (err) {       
 console.log("调用失败", err);   
}   
 }  
  return true 
 }

v-button自定义透明用于绑定或获取用户信息

<button bind:getuserinfo="onGetUserInfo" open-type='{{openType}}'  plain='{{true}}' class="container"> 
 <slot name="img"></slot>
</button>

Component({  /**   * 组件的属性列表   */  
options: {  
  multipleSlots: true // 在组件定义时的选项中启用多slot支持  
},  
// externalClasses: ['ex-btn-class'],  
properties: {    
openType: {    
  type: String 
   },   
 imageSrc: {   
   type: String   
 },  
  bindgetuserinfo: {   
   type: String    
}  }, 
 /**   * 组件的初始数据   */  
data: {  },  
  /**   * 组件的方法列表   */  
methods: {    
onGetUserInfo(event) { 
     this.triggerEvent('getuserinfo', event.detail, {})    
},  
}})
.container{  padding: 0 !important;  border:none !important;}

一个小程序跳转到另一个小程序

async getPayInfos(item:any) {   
 const { buildId, id, estateId } = item
    let params = {    
  buildId,   
   estateId,  
    houseId: id, 
   }    
let infos = await getPayInfo(params) 
   let that = this   
 wx.navigateToMiniProgram({   
   appId: 'wxe7550',  //appid   
   path: `pages/index/index?token=${infos.data.payData}`,//path   
   success(res) {     
   that.data.isPay = true 
   that.data.mchOrderNo = infos.data.mchOrderNo   
   }    
}) 
 },

wxs

wxs是小程序的一套脚本语言 ,结合WXML,可以构建出页面的结构  wxs不依赖于运行时的基础库版本  可以在所有版本的小程序中运行wxs与javascript是不同的语言  有自己的语法 并不和javascript一致wxs的运行环境和其他javascript代码是隔离的  wxs不能调用其他JavaScript文件中定义的函数  也不能调用小程序提供的APIwxs函数不能作为组件的事件回调与es5相似,不能用es6的语法这里作为单独文件使用
图片

图片

图片
也可直接写在wxml文件中

<block wx:for="{{util.limit(comments,15)}}">  
      <tag-cmp class="tag" text="{{item.content}}">        
  <text class="num" slot="after">{{'+' + item.nums}}</text>       
 </tag-cmp>  
</block>
<wxs module="util"> 
 var limit = function(array, length) {  
  return array.slice(0, length)  
} 
 var format = function(text){ 
   if(!text){      return    }    
var reg = getRegExp('\\\\n','g')
    var text = text.replace(reg,'\n&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;')  
  return text  }  
module.exports = {    limit: limit,    format:format  }
</wxs>

属于wxs的正则
图片

点击事件

  • bind事件 catch事件
  • bind事件绑定不会阻止事件冒泡   
  • catch事件可以阻止事件向上冒泡

    <view bind:tab="handleClick"></view>   
    <view catch:tab="handleClick"></view>wx:for遍历 <block wx:for="{{data}}">  
    <v-item item="{{item}}"></v-item> 
    </block>

    表单校验要自己写表单,所以也要自己写校验

    使用的是WxValidate.jsWxValidate的验证规则: 
    (1)required:true/false,是否为必填字段。
    (2)email:true/false,是否遵守电子邮件格式。
    (3)tel:true/false,是否遵守11位手机号码。
    (4)url:true/false,是否遵守域名格式。
    (5)idcarad:true/false,是否遵守18位身份证号格式。
    (6)digits: true/false,只能输入数字。
    (7)min:数值,指定最小值。
    (8)max:数值,指定最大值。
    (9)range:[min,max],指定范围。
    (10)minlength:数值,指定最少输入的字符个数。
    (11)maxlength:数值,指定最多输入的字符个数。
    (12)rangelength:[minlength,maxlength],指定输入字符个数的范围。
    (13)dateISO:true/false,说服遵守日期格式(yyyy-mm-dd、yyyy/mm/dd)。
    (14)equalTo:字符串,指定必须输入完全相同的内容。
    (15)contains:字符串,指定必须输入包含指定字符串的内容。 

  • 方法就不说了,简单且繁琐,为什么只写规则,规则不好找😄*

    小程序的跳转

    小程序的跳转分为两种:页面跳转   保留当前页面,跳转到应用内的某个页面

    wx.navigateTo({ url: "/pages/login/login" })

    关闭当前页面,跳转到应用内的某个页面
    `wx.redirectTo({
    url: ''
    }`
    关闭所有页面,打开到应用内的某个页面

    wx.reLaunch 方法则会清空当前的堆栈 通过wx.navigateBack没有可返回的页面了

    底部tab跳转跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面
    `wx.switchTab({
    url: '/index'
    })`

    小程序的事件传参

    data- 自定义属性传参,其中  代表的是参数的名字例子:

    <view class="homeItem" bindtap="goList" data-showType="add">

    事件内可通过e.currentTarget.dataset.showtype拿到值

    goList(e: any) {
    cosole.log(e.currentTarget.dataset.showtype) 
    //注意这里showtype 可不是写错,而是小程序会把小驼峰大写转成小写
    }

    onPullDownRefresh onReachBottom  下拉刷新 上拉加载

    Page({ freshList(){    
    let myList = this.selectComponent("#yg-list")    
    myList.pullList()  
    },  
    
    data: {
      list: [],
      pageIndex:-1,
      pageSize:5,
      loading: false,
      total:0,
      isMore:false
    },
    
    /* 刷新列表数据 */
    reloadList() {
      this.data.list = [];
      this.setData({
        pageIndex: -1,
      },);
      this.getData();
    },
    
    
    /**
     * 页面相关事件处理函数--监听用户下拉动作
     */
    onPullDownRefresh: function () {
      this.reloadList()
    },
    
    /**
     * 页面上拉触底事件的处理函数
     */
    onReachBottom: function () {
      this.getData()
    },
    
    getData() {
      if (this.data.pageIndex!=-1&&this.data.list.length == this.data.total) return
      if (this.data.loading) return
      this.setData({
        pageIndex: this.data.pageIndex + 1,
        loading: true
      })
      api
        .queryMyOrderForPage(app.globalData.custId,this.data.pageIndex,this.data.pageSize)
        .then((res) => {
          let list = res.result.data;
          for (let i = 0; i < list.length; i++) {
            list[i].payTime = formatUnixTimestamp(list[i].payTime);
          }
          let dataArr = [...this.data.list,...list]
          this.setData({
            list:dataArr,
            loading: false,
            total:res.result.count
          })
          if (dataArr.length != 0 && dataArr.length == res.result.count) {
            this.setData({
              isMore: true
            })
          }
        },)
        .finally(() => {
          this.setData({
            loading: false
          })
        });
    },

    也可将方法挂在滚动元素scroll-view上

<scroll-view class="scrollWrapper" scroll-y refresher-enabled model:refresher-triggered="{{loading}}"
bindrefresherrefresh="onPullDownRefresh" bindscrolltolower="onReachBottom">

可在app.json配置在距离什么位置触发

"window": {   
 "backgroundTextStyle": "light",   
 "navigationBarBackgroundColor": "#fff",    
"navigationBarTitleText": "",    
"navigationBarTextStyle": "black",   
 "enablePullDownRefresh": true,//开启下拉刷新    "onReachBottomDistance":100,//可在app.json配置在距离什么位置触发  
},

本来以为这个没有什么难的,但是还是踩坑了

  1. 若页面不小心写了两个onReachBottom, 这个事件是不会触发的
  2. 上拉加载,下拉刷新只能用在page页面组件中,在Component组件里面不触发的😌所以在page页面监听到到达页面的底部,如何通知列表组件的加载更多,引出下面的小程序中触发子组件的方式

    小程序中触发子组件的方法

    那这里用上上个例子的代码

    <yg-list applyStatus="1" id="yg-list"/>
     onReachBottom: function() {
    //滚动到底部通知子组件加载新的数据    
    let myList = this.selectComponent("#yg-list")   
    myList.getList()  
    }

    小程序组件中子组件触发父组件的的方法,

    在这里触发indexFunction函数

     <button bindTap="clickTap">点击</button>   
     clickTap:function(){      
    this.triggerEvent('indexFunction',{value:this.properties.count})    
    }

在父组件中

<yg-list bind:indexFunction="indexFunction"/>     
  Page({     
 indexFunction:function(e){        
console.log('父容器中的方法被调用了',e);     
 }  
  })

wx:if 与 hidden

*wx:if 与hidden都可以控制微信小程序中元素的显示与否。
wx:if不满足条件是不渲染,hidden是隐藏对于性能这一块,
hidden页面的隐藏代替页面跳转,不会重新触发ready  或者created生命周期。
所以具体用法与vue的v-if和v-show相似*

小程序组件

如果写小程序每个页面纯手写重复的列表,表单,太臃肿,
所以肯定要写组件,组件的使用方式:
外层创建components文件夹,在里面写组件,
如果其中一个页面需要使用这个组件,可在当前页面的json中配置

{  "usingComponents": {    "yg-list":"/components/yg-list/yg-list"  }}

组件传值 properties 类似于vue的props

//在properties里面定义我们要的属性
 properties: {
    btText: {
      value: '默认值',//value表示默认值
      type: String   //type是我们定义的类型,这里是String字符串类型
    }
  },

组件自身的方法定义在methods,同vue一样

methods: {
   showLog:function(){
   }
 }
组件使用插槽 
Comment({      
options:{        
muitipleSlots:true  //开启插槽
}   
})


<view class="container">    
<slot name="before"></slot>
<text>{{text}}</text>  
 <slot name="after"></slot> 
</view>
使用
<v-tag> 
<text text="哈哈" slot="after">{{text}}</text>
</v-tag>

组件的外部样式externalClasses的使用

*子组件定义一个外部样式名字 
子组件的元素用上这个类名*

Comment({     
externalClasses:['my-class']   
 })


<view class="my-class">{{text}}</view>

在父组件使用这个组件的时候 是在父组件中定义的样式

<yg-list my-class="yg-class"></yg-list>
.yg-class{color:red;}

*注意:若是当前子组件的元素上还有别的样式,那组件内的样式会覆盖外部样式,
所以在父容器编写样式的时候,后面可加上impotant,提升等级关系。*

组件的behaviorbehaviors

  • 是小程序中,用于实现组件间代码共享的特性,
  • 类似于 Vue.js 中的 “mixins”
  • 每个 behaviors 可以包含一组属性、数据、生命周期函数和方法。
  • 组件引用它时,它的属性、数据和方法会被合并到组件中,每个组件可以引用多个behaviors,- - behavior也可以引用其它behavior。
  • 在外层创建一个文件夹behavior,里面可放各个用途的behavior js文件,
    创建一个behavior文件

    let classicBeh = Behavior({    
      properties:{         
     img:String,         
     content:String       
     },       
     data:{},    
      methods:{}   
     })    
    export {
    classicBeh
    }
    在组件中使用 
    import { classicBeh } from '../behaviors/classicBeh.js'   
     Comment({      
    behaviors: 
    [classicBeh]    
    })

    behavior与组件的优先级若遇到重名的情况,组件会覆盖behavior,
    但是如果是生命周期,会先执行behavior内的,再执行组件内的

    小程序中引入lodash

    在utils文件夹下新建lodash.js
    文件把压缩过的lodash.min.js文件内容放进去直接引入会报错
    再创建一个lodash-fix.js文件

    /** * 修复 微信小程序中lodash 的运行环境 */
    global.Object = Object;
    global.Array = Array;
    // global.Buffer = Buffer
    global.DataView = DataView;
    global.Date = Date;
    global.Error = Error;
    global.Float32Array = Float32Array;
    global.Float64Array = Float64Array;
    global.Function = Function;
    global.Int8Array = Int8Array;
    global.Int16Array = Int16Array;
    global.Int32Array = Int32Array;
    global.Map = Map;
    global.Math = Math;
    global.Promise = Promise;
    global.RegExp = RegExp;
    global.Set = Set;
    global.String = String;
    global.Symbol = Symbol;
    global.TypeError = TypeError;
    global.Uint8Array = Uint8Array;
    global.Uint8ClampedArray = Uint8ClampedArray;
    global.Uint16Array = Uint16Array;
    global.Uint32Array = Uint32Array;
    global.WeakMap = WeakMap;
    global.clearTimeout = clearTimeout;
    global.isFinite = isFinite;
    global.parseInt = parseInt;
    global.setTimeout = setTimeout;
    使用import "../../utils/lodash-fix.js"
    import _ from "../../utils/lodash"

    使用

     getBuildList: _.throttle(function (str: any) {   
     const { cellId, searchCell } = this.data   
     lifePayModels.getBuild({ estateId: cellId, buildName: searchCell },
     (res => {     
     let arr = res.map(item => {    
      return {         
     label: item.buildName, 
           value: item.id       
     }      
    })     
     this.setData({   
       columns: arr   
     })   
     })) 
     }, 3000),

    第三方库

    Vant  Weappvan-dialog
    van-field model:value支持双向绑定
    van-dialog给里面的输入框做校验的时候,它会关闭,所以这里用上beforeClose阻止默认关闭事件

    <van-dialog use-slot 
      data-showType="rejectShow" 
      show="{{rejectShow}}"
      show-cancel-button
      confirm-button-open-type="toReject" 
      beforeClose="beforeClose"
      bind:close="closeDialog">    <van-field 
       model:value="{{rejectComment}}"
       label="" 
       type="textarea"   
       placeholder="驳回原因" 
       autosize   
       required/>
    </van-dialog>
    Page({     
     data: {        
      beforeClose (action) {          
      return new Promise(resolve => {            
      setTimeout(() => {              
      if (action === 'confirm') {               
     // 拦截确认操作                
      resolve(false)              
      } else {                
      resolve(true)             
       }            
      }, 0)         
       })        
      }    
    
    },    
     toReject(){ 
         if(!this.data.comment){
            tips('请填写原因')         
           return      
    }        
      this.setData({     
       show:false     
     })     
       }   
     })

    微信支付

    图片
    具体的做法:

  • 打开某小程序,点击直接下单 wx.login获取用户临时登录凭证code,发送到后端服务器换取
  • openId 在下单时,小程序需要将购买的商品Id,商品数量,以及用户的openId传送到服务器
  • 在下单时,小程序需要将购买的商品Id,商品数量,以及用户的openId传送到服务器
  • 服务器在接收到商品Id、商品数量、openId后,生成服务期订单数据,同时经过一定的签名算法,向微信支付发送请求,获取预付单信息(prepay_id),同时将获取的数据再次进行相应规则的签名,向小程序端响应必要的信息 
  • 小程序端在获取对应的参数后,调用wx.requestPayment()发起微信支付,唤醒支付工作台,进行支付  
    6 接下来的一些列操作都是由用户来操作的包括了微信支付密码,指纹等验证,确认支付之后执行鉴权调起支付
    7鉴权调起支付:在微信后台进行鉴权,微信后台直接返回给前端支付的结果,前端收到返回数据后对支付结果进行展示
    8 推送支付结果:微信后台在给前端返回支付的结果后,也会向后台也返回一个支付结果,后台通过这个支付结果来更新订单的状态
    9 其中后端响应数据必要的信息则是wx.requestPayment方法所需要的参数,大致如下:``wx.requestPayment({
    // 时间戳
    timeStamp: '',
    // 随机字符串
    nonceStr: '',
    // 统一下单接口返回的 prepay_id 参数值
    package: '',
    // 签名类型
    signType: '',
    // 签名
    paySign: '',
    // 调用成功回调
    success () {},
    // 失败回调
    fail () {},
    // 接口调用结束回调
    complete () {}
    })``
    注意:以上信息中timeStamp、nonceStr、prepay_id、signType、paySign各参数均建议必须都由服务端返回(这样会尽最大可能性保证签名数据一致性),小程序端不做任何处理
    官方支付文档地址https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_...
    后端的操作 https://juejin.cn/post/7063318750790942733

    引入离线iconfont字体图标

https://blog.csdn.net/qq_15064263/article/details/126396051

域名不合法的解决

小程序刚开始开发的时候测试环境一般都是打开
不检验合法域名但是当我们发测试版本或者发生产环境的时候,
发的小程序是不请求接口的,这是因为我们没有设置合法域名,
在这里设置,注意域名必须是https的
图片

扫二维码调到小程序指定页面首先微信平台设置

设置位置: 开发管理 开发设置(最后生成连接发布的时候要确定 代码已经在线上)
图片

图片
然后可以拿着这个url去生成二维码
图片
然后在当前页面接受参数,判断是否已绑定手机号,登录

onLoad(options: any) {  
  if (options.q) {     
 let url = decodeURIComponent(options.q)   
   let obj = this.getUrlParam(url)    
  this.initPhone()    
  this.setData({      
  cellName: obj.estateName,   
     cellId: obj.estateId,      
  isRead: true     
 })   
 }
},  
getUrlParam(url) {  
  let params = url.split("?")[1].split("&");  
  let obj = {};  
  params.map(v => (obj[v.split("=")[0]] = v.split("=")[1]));    return obj 
 }

wx.nextTick

延迟一部分操作到下一个时间片再执行
使用方法同vue的this.$nextTick

 getUrlParam(url) {
    let params = url.split("?")[1].split("&");
    let obj = {};
    params.map(v => (obj[v.split("=")[0]] = v.split("=")[1]));
    return obj
  },
onLoad(options: any) {
    this.initPhone()
    if (options.q) {
      let url = decodeURIComponent(options.q)
      console.log("url", url);
      let obj = this.getUrlParam(url)
      if (Reflect.ownKeys(obj).length > 0) {
        wx.nextTick(() => {
          this.setData({
            cellName: obj.estateName,
            cellId: obj.estateId,
            isRead: true,
          })
        })
      }
    }
  },

最后如果当前文章给了你提示和帮助,还希望点赞和关注,鼓励,谢谢啦


HappyCodingTop
526 声望847 粉丝

Talk is cheap, show the code!!