原生微信小程序全流程(基础知识+项目全流程)

news/2024/7/20 2:40:59 标签: 微信小程序, 小程序

小程序的基本使用

小程序文件类型

小程序主要提供了 4 种文件类型:

类型名称作用是否必须存在
.wxml用于页面的布局结构,相当于网页中 .html 文件
.wxss用于页面的样式,相当于网页中的 .css 文件
.js用于页面的逻辑
.json用于页面的配置

文件作用

文件名作用是否必须存在
app.js小程序入口(首先执行的文件)
app.json小程序全局配置
app.wxss小程序全局样式
project.config.json小程序开发者工具配置是(会自动创建)
sitemap.json小程序搜索优化

要点

  • data 初始化页面中的数据
  • setData 更新数据
  • {{}} 插值语法可以实现数据的渲染
  • bind:事件类型=事件回调
Page({  //index.js
  // 约定格式,使用data定义数据
  data: {
    message: 'nihao!'
  },
  changeMessage() {
    // this.setData修改数据
    this.setData({
      message: 'new!'
    })
  }
})

index.wxml

<view>{{message}}</view>
<button bind:tap="changeMessage" type="primary" size="mini">点我试试</button>

如何注册小程序事件监听?

Page({
处理函数() {}

})

<button bind:事件名=“处理函数”>

配置文件

在这里插入图片描述

分类:

全局 app.json

页面 page.json

全局配置

app.json 是当前小程序的全局配置,包括了:

  • 小程序首页
  • 界面表现
  • 网络超时时间
  • 底部 tab
  • …等配置
全局配置pages

用于指定小程序由哪些页面组成。

  1. 每一项都对应一个页面的 路径(含文件名) 信息。
  2. 小程序中的每一个页面都必须在pages下登记一下。
  3. 文件名不需要写文件后缀,框架会自动去寻找对应位置的 .json, .js, .wxml, .wxss 四个文件进行处理。

是一个数组,每一项表示一个页面

{
  pages:[
    "pages/index/index",
    "pages/logs/logs"
  ]
}

数组中的第一个元素表示小程序启动时默认打开的页面-主页

页面跳转

<navigator url="/pages/logs/logs">跳转到log页面</navigator>

新建页面(自动优先)

新建页面-手动

共三步:

  1. 新建空目录
  2. 新建页面(4个文件)
  3. 在pages下手动补充页面地址

要点:

  1. 页面都放在pages下,一个页面一个文件夹。
  2. 新建page时,不需要写文件后缀名。
新建页面-自动

在pages下补充一项 pages/page1/page1 ,然后保存,则会自动添加一个页面。

app.json

{
  pages:[
    "pages/index/index",
    "pages/logs/logs",
    "pages/page1/page1"
  ]
}

小程序配置-全局配置—window

用于设置小程序的状态栏、导航条、标题、窗口背景色。

https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html#window

常见配置:

在这里插入图片描述

小程序配置-全局配置—tabBar

在这里插入图片描述

tabBar

tabBar 定义小程序 tab 栏的表现,如下图即所谓的 tab 栏:

在这里插入图片描述

常见配置属性

属性类型默认值是否必须说明
listarraytab 的列表,详见 list 属性说明,最少 2 个、最多 5 个 tab
color16 进制颜色tab 上的文字默认颜色,仅支持十六进制颜色
selectedColor16 进制颜色tab 上的文字选中时的颜色,仅支持十六进制颜色
backgroundColor16 进制颜色tab 的背景色,仅只持 16 进制颜色
borderStylestringblacktabbar 上边框的颜色, 仅支持 black / white
positionstringbottomtabBar 的位置,仅支持 bottom / top

上述配置中 list 具体又包含以下内容:

属性类型默认值是否必须说明
pagePathstring页面路径,必须在 pages 中先定义
textstringtab 上按钮文字
iconPathstring图片路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px,不支持网络图片,当 position 为 top 时,不显示 icon
selectedIconPathstring选中时的图片路径,icon 大小限制为 40kb,建议尺寸为 81px * 81px,不支持网络图片,当 position 为 top 时,不显示 icon

参考代码(app.josn)

{
  ......
  "tabBar": {
    "color": "#999",
    "selectedColor": "#e93b3d",
    "backgroundColor": "#fff",
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "首页",
        "iconPath": "static/tabbar/home-default.png",
        "selectedIconPath": "static/tabbar/home-active.png"
      },
      {
        "pagePath": "pages/logs/logs",
        "text": "日志",
        "iconPath": "static/tabbar/video-default.png",
        "selectedIconPath": "static/tabbar/video-active.png"
      },
      {
        "pagePath": "pages/index/demo",
        "text": "示例",
        "iconPath": "static/tabbar/face-default.png",
        "selectedIconPath": "static/tabbar/face-active.png"
      }
    ]
  }
}

小程序配置-页面配置

  • 页面配置只针对某个页面生效
    • 如 index.json 是针对 index 页面生效,demo.json 只针对页面 demo 生效
  • 不用写window字段
  • 优先级比全局配置高

常用配置:

属性类型默认值是否必须说明
navigationBarTitleTextstring空白导航栏标题文字内容
navigationBarTextStylestringblack导航栏标题颜色,仅支持 black / white
navigationBarBackgroundColor16 进制颜色#00000导航栏背景颜色,如 #000000
navigationStylestringdefault导航栏样式,仅支持 default / custom
enablePullDownRefreshbooleanfalse是否开启全局的下拉刷新

小程序适配-响应式单位rpx

rpx ,responsive pixel:

小程序中的单位rpx,它的特点是能够自动地适配置不同尺寸的手机屏幕。

原理:不管手机屏幕具体多宽,100%的屏幕宽度就是750rpx

100%屏幕的宽度 = 750rpx

  1. 所有的设备宽度都是750rpx
  2. 在实际使用中只需要将设计稿调整为 750px 宽,然后 1:1 的比例来写长度(单位使用 rpx),如:设计稿中某个区域(盒子)的大小为 18090px ,写成小程序的尺寸为 18090rpx。

注:上述的规则仅适用于设计稿宽度为 750px

rpx (responsive pixel):规定不管屏幕为多少px,100%的屏幕宽度就是750rpx
100%屏幕的宽度 = 750rpx

内置组件—navigator(跳转)

navigator 是小程序中的导航标签,类似以前web中的a标签。通过 url 来指定跳转的页面

  • url: 页面路径
    • 支持相对和绝对路径
    • 路径为空会报错
    • 还可以跳到其他小程序
  • hover-class:点击态的样式
    • none 禁用点击效果
  • open-type:跳转方式
    • navigate。默认值
    • switchTab。跳转到tabbar页
属性名类型默认值说明
urlstring当前小程序内的跳转链接
open-typestringnavigate跳转方式
targetStringself在哪个目标上发生跳转,默认当前小程序

内置组件-image(图片)

在这里插入图片描述

<image 
src="图片资源地址" 
mode="图片裁剪,缩放方式"></image> 

image组件是一个有默认大小(320*240)的盒子。

  • src: 图片资源地址。相对地址,绝对地址(外网地址)。
  • mode: 默认值为scaleToFill,用来设置图片裁剪、缩放的模式。
    • scaleToFill。不保证缩放比,图片拉伸填满容器
    • aspectFit。保证缩放比,使图片的长边显示出来
    • aspectFill。保证缩放比,使图片的短边显示出啦
  • lazy-load:默认为false,是否开启懒加载模式。(3屏)

内置组件—swiper(轮播图)

swiper可以理解为小程序内置的轮播图标签,可以让方便快速地实现轮播功能。

<swiper>
  <swiper-item>1屏的内容 </swiper-item>
  <swiper-item>2屏的内容 </swiper-item>
  <swiper-item>3屏的内容 </swiper-item>
  <swiper-item>4屏的内容 </swiper-item>
</swiper>
  1. swiper:滑块容器。内只能写swiper-item。它的默认高度是150px;
  2. swiper-item:滑块单元。它的大小是: 宽度 和 高度 为 100% * 100%;

表单相关

  1. 输入框:input
    1. password密码类型,placeholder占位文字
  2. 单选框:radio-group和radio
    1. value指定表单数据,checked选中状态
  3. 复选框:checked-group和checkbox
    1. value指定表单数据,checked选中状态
  4. 选择框:picker
    1. mode:指定不同类型的选择框

scroll-view(滚动)

``scroll-view 在页面中指定一个**可以滚动**的区域,并且这个可滚动的区域能够实现一些高级的交互,比如:下拉刷新`等。

scroll-view 中嵌套任意需要滚动的内容,要求内容必须有溢出(scroll-view有固定的尺寸),垂直滚动时 scroll-view 必须要指定高度。

属性

  • scroll-x 属性是否允许水平方面滚动
  • scroll-y 属性是否允许垂直方向滚动
  • refresher-enable 属性是否开启下拉刷新的交互

小程序样式-全局样式

app.wxss 定义全局样式,该文件中的样式会在所有的页面生效。

注:page 在每个页面中都有,它是由小程序自动添加上的,相当于网页中的 body 标签。

小程序样式-静态资源

小程序.wxss 文件中不支持使用本地路径的资源,比如背景图片是不允许使用本地图片路径的,必须使用网络路径(https:// 或 http:// 开头)或者转换成 base64 编码。

小程序样式-字体图标(iconfont)

小程序中字体图片的使用与网页中基本上是一致的,唯一的区别是小程序.wxss 文件中不支持使用本地字体文件,我们使用 iconfont 平台提供的服务生成字体文件后,直接使用其线上的字体文件地址。

资源参考:https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=4061819

小程序基本使用-请求数据并渲染

小程序模板语法-数据绑定

在js中定义数据

Page({
  data: {
    isOpen: true,
    message: 'hello world!'
  }
})

小程序的data是一个对象,不同于vue的data是一个函数

在模块中获取使用数据

小程序中使用 {{}} 实现数据与模板的绑定

  1. 内容绑定:<view>{{ 属性名 }}</view>
  2. 属性绑定: <input value="{{属性名}}" />

{{}} 内写的是表达式

简易双向绑定

  1. 小程序中提供了 model:value="{{数据名}}" 语法来实现双向的数据绑定

  2. 目前只能用在 inputtextarea 组件中。

    只能是一个单一字段的绑定,不能嵌套对象.否则出现以下报错:

    [pages/index/index] Two-way binding does not support complex data paths currently. This two-way binding is ignored.

    双向绑定目前不支持复杂的数据路径。这种双向绑定将被忽略。

小程序修改数据

格式:

<元素 bind:事件名="处理函数">
this.setData({
  属性名1: 新值1,
  属性名2: 新值2
})


this.setData({
  "属性1.属性2":})

模板语法—条件渲染

小程序中的条件渲染的方式有两种

1.wx:if

    • 小程序中,使用wx:if="{{条件}}"来判断是否需要渲染该代码块
    • 也可以用wx:elif 和 wx:else来添加else判断

2.hidden

    • 小程序中,使用hidden="{{条件}}"也能控制元素的显示与隐藏
    • 条件为true则隐藏,否则则显示

wx:if 与 hidden区别

1.区别

    1. wx:if是通过动态创建或移除元素来控制元素是否可见
    2. hidden 是通过样式(none/block)来控制元素是否可见

2.要点

  • 如果一个标签频繁切换显示,建议使用 hidden。例如:折叠面板,抽屉面板等等
  • 如果不频繁切换,建议使用wx:if,它有更好的初始化性能。

模板语法—列表渲染—基础

在这里插入图片描述

格式

<元素 wx:for="{{列表数据}}" >
   <!--  wx:for 结构内可以使用两个变量(1)item:循环项(2)index:循环索引 -->
   {{item}}, {{index}}
</元素>

手动指定索引名和当前项的变量名

<view wx:for="{{list}}" wx:for-item="value" wx:for-index="key">
	{{key}}-{{value}}
</view>

模板语法-列表渲染-wx:key

wx:key 针对不同的数组类型有不同的写法

  • 普通数组 wx:key=“*this”
  • 数组对象 wx:key=“具有唯一性的某个属性名”

小程序内置API-网络请求

网络请求

调用 wx.request 能够在小程序中发起网络请求与后端接口进行数据的交互,其语法格式如下:

wx.request({
  url: '这里是接口的地址',
  method: '这里是请求的方法',
  data: '请求时提交的数据',
  header: {/* 请求头信息 */},
  success: () => {/* 成功的回调 */},
  fail: () => {/* 失败的回调 */},
  complete: () => {/* 成功或失败的回调 */}
})

配置网络请求合法域名

域名必须是https

在这里插入图片描述
在这里插入图片描述

小程序内置api-界面交互

showLoading效果

配合网络请求来使用

wx.showLoading 显示 loading 提示框

  • title 文字提示内容
  • mask 是否显示透明蒙层,防止触摸穿透
hideLoading
  • wx.hideLoading 隐藏 loading 提示框
showToast

wx.showToast 消息提示框(轻提示)

  • title 提示的标题
  • mask 是否显示透明蒙层,防止触摸穿透
  • duration 延迟时间(提示框显示多久,单位是毫秒)
  • icon 指定图标,none 不使用图标
操作注意:
  1. 发请求之前,showLoading
  2. 请求结束之后(无论成败),hideLoading
  3. 数据渲染成功之后,showToast

小程序>微信小程序本地存储

  • wx.setStorageSync(key, value) 存入一个数据,复杂类型数据不需要 JSON.stringify 处理
  • wx.getStorageSync(key) 读取本地key数据,复杂类型数据不需要 JSON.parse 处理
  • wx.removeStorageSync(key) 删除本地key数据
  • wx.clearStorageSync()清空本地全部数据

小程序>微信小程序API的特征

API的用法分类三类:

  1. 异步的api
  2. 同步的api
  3. 支持promise的api
异步 API

绝大部分的 API 都是异步方式,通过回调函数获取 API 执行的结果

  • success API 调用成功时执行的回调
  • fail API 调用失败时执行的回调
  • complete API 调用结束时执行的回调(无论成功或失败)

基本格式:

wx.api名称({success(res){ console.log(成功执行api的结果) }})
支持promise的api

部分异步的 API 也支持以 Promise 方式返回结果,此时可以配合 asyc/await 来使用。

例如:支持Promise格式的异步api有:

  • wx.getSystemInfoSync()
  • wx.getStorage

支持Promise格式的异步api有:

  • wx.request()
同步 API

部分 API 支持以同步方式获取结果,这些 API 的名称都 **Sync** 结尾。如

  • wx.getStorageSync : 获取本地存储
  • wx.getSystemInfoSync: 获取系统信息

基本格式:

const result = wx.api名称()

事件处理-事件对象&传参

事件对象

bind:事件类型=事件回调 //回调函数第1个参数即为事件对象

事件回调传参

小程序的事件回调不支持传参数

因此要将模板中的数据传递到事件回调中就必须要通过事件对象来实现。

方式1:

  • 补充参数:

     <button bind:tap="eventHandler" **mark:属性名="值"**>点击我看看</button>
    
    
  • 获取值:

    eventHandler(ev){ console.log(**ev.mark.属性名**) 
    
    

方式2:

  • 补充参数 :

    <button bind:tap="eventHandler" **data-属性名="值"** >点击我看看</button>
    
    
  • 获取值:

    eventHandler(ev){ console.log(**ev.target.dataset.属性名**) }
    
    

事件处理-组件事件

事件类型只属于某个组件,我们将其称为组件事件

前面介绍的 tap 事件可以在绝大部分组件是监听,我们可以将其理解为通用事件类型,然而也有事件类型只属于某个组件,我们将其称为组件事件。

组件不同,支持的事件也不同

scroll-view组件中的事件

  1. bind:scrolltolower 当滚动内容到达底部或最右侧时触发
  2. bind:refresherrefresh 执行下拉操作时触发

另外,还有注意一个特别的属性

refresher-triggered 用它来控制下拉刷新状态

事件处理-表单组件中的事件

如何获取表单中,用户选择的值?

  1. input: 简易双向绑定
  2. radioGroup: 绑定change事件,在事件对象中detail.value拿到值
  3. checkboxGroup: 绑定change事件,在事件对象中detail.value拿到值
  4. picker: 绑定change事件,在事件对象中detail.value拿到值
  • change 表单数据发生改变时触发(input 不支持)

​ 5.整体表单提交

  • form: submit事件 表单提交时触发,button 按钮必须指定 form-type 属性

生命周期-页面生命周期

  • 分类
    • 应用生命周期
    • 页面生命周期
    • 组件生命周期

生命周期是一些名称固定,会自动执行的函数。

页面生命周期-基本使用

  • onLoad 在页面加载完成时执行,只会执行 1 次,常用于获取地址参数和网络请求

  • onReady页面初次渲染完成

  • onShow 在页面处于可见状态时执行,常用于动态更新数据或状态

  • onHide 在页面处于不见状态时执行,常用于销毁长时间运行的任务,如定时器

    页面生命周期-应用场景

onLoad(){ // 发起请求 }

onShow(){ // 动态更新数据或状态 }

onHide 在页面处于不见状态时执行,常用于销毁长时间运行的任务,如定时器

onReady 在页面初次渲染完成时执行,只会执行 1 次,常用于节点操作或动画交互等场景

生命周期-应用生命周期

app.js

  • onLaunch 小程序启动时执行1次,常用于获取场景值或者启动时的一些参数(如自定义分享)
  • onShow 小程序前台运行时执行,常用于更新数据或状态
  • onHide 小程序后台运行时执地,常用于销毁长时间运行的任务,如定时器。
// pages/lifetimes/index.js
Page({
  
  // 小程序转发/分享
  onShareAppMessage() {
    return {
      title: '小程序学习',
      path: '/pages/index/index?test=测试数据',
      imageUrl: '/static/images/cover.png'
    }
  }
})

小程序基础-分包加载&&自定义组件&&&项目全流程

小程序分包加载

小程序分包加载-为什么要分包加载

  • 微信平台对小程序单个包的代码体积限制为 2M,超过 2M 的情况下可以采用分包来解决
  • 即使小程序代码体积没有超过 2M 时也可以拆分成多个包来实现按需加载
  • 配置文件能忽略的只有静态资源,代码无法被忽略

在这里插入图片描述

配置忽略文件

project.config.json

{
  "description": "项目配置文件",
  "packOptions": {
    "ignore": [
      {
        "value": "static/uploads",
        "type": "folder"
      }
    ],
    "include": []
  },

type: 表示要忽略的资源类型

value: 表示具体要忽略的

小程序分包加载-使用分包配置

分类:

  1. 主包:
  • 每个小程序必定含有一个主包

  • 默认启动页面、TabBar 页面,以及公共资源/JS 脚本必须放在主包;

    2.分包 https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages.html

  • 通过subPackages来配置

  • 所有包的大小之和不超过20M

app.json

{
  // 省略其他的...
  
  "subPackages": [
      {
        "root": "subpkg_user", // 分包代码的目录,其实就是一个独立的文件夹
        "pages": [
          "pages/profile/profile"
        ]
      },
      {
        "root": "subpkg_order", // 文件夹
        "pages": [
          "pages/order_list/index", 
          "pages/order_list/index"
        ]
      }
  ]
}

注意: 写完分包之后,如果对应的文件夹和页面不存在,它会自动创建文件夹和页面

小程序分包—预加载

https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/preload.html

在打开小程序启动的时候只下载主包代码,分包并不会下载,因此能够提升小程序启动时的打开速度,但是分包的代码只有在访问到分包的页面时才去下载,这样用户就需要有一定时间的等待(一般不太影响),通过分包预加载技术可以实现提前去下载分包的代码,这样分包页面的访问速度也会得到提升。
小程序通过 preloadRule 配置需要预加载的分包。

app.json

{   ......
	"preloadRule": {
    "页面地址,进入这个页面就需要预加载分包": {       // pages/index/index 
      "network": "网络环境",                     // "wifi"
      "packages": ["要预加载的包名"]              //["goods_pkg"]
    }
  },   ......
}      //当用户访问到 pages/index/index 时,在 wifi 网络前提下预先下载 goods_pkg 分包的代码。
  • 指定某个页面路径做为 key,含义是当访问这个页面时会去预加载一个分包
  • network 预加载分包的网络条件,可选值为 all、wifi,默认为 wifi
  • packages 指定要预下载的分包名或根路径

配置完成之后,访问指定页面时,就会在控制台输出提示。

在这里插入图片描述

自定义组件—基本使用

创建组件

通常将项目中的组件都放在一个独立的目录下,并且一般就给这个文件夹取名为:components 。这个目录需要我们手动进行创建。

  1. 新建一个目录:components

  2. 在components上点击鼠标右键,选择「新建Component」

  3. 填入组件的名字。它会自动创建4个同名的文件。(这一点和创建页面是一样的)

在这里插入图片描述

组件和页面的结构区别:

  1. 组件的配置文件(.json文件)中,有一个配置项:component: true
  2. 组件的 .js 文件中调用 Component 函数,页面的.js文件中调用Page函数

注册组件

  • 页面注册是在使用组件的(xxxx.json)中通过 usingComponents 进行注册,只能在当前页面中组件
  • 全局注册是在 app.json 文件中通过 usingComponents 对自定义组件进行注册,可以在任意页面中使用
"usingComponents": {
    "my-test": "/components/MyTest/index"
}

使用组件

在wxml中,直接通过标签的方式使用即可。

自定义组件—组件样式

  1. 组件中的样式不要使用标签选择器
  2. 组件中,样式默认是隔离的: 自定义组件的样式只受到自定义组件 wxss 的影响
  3. 通过对组件的配置,可以取消这个隔离的状态

样式隔离注意点

  • app.wxss中的全局样式对组件无效
  • 只有class选择器具有样式隔离效果,id选择器、属性选择器、标签选择器不受样式隔离的影响

建议:在组件和引用组件的页面中建议使用class选择器,不要使用id、属性、标签选择器

修改组件样式的隔离选项

默认情况下,自定义组件的样式隔离特性能够防止组件内外样式互相干扰的问题。但有时,我们希望外界能够控制组件内部的样式,此时,可以通过在组件的.js文件中设置: options → addGlobalClass 为true

XX.js

Component({
  options: {
    addGlobalClass: true
  }
})

在页面中设置的同类名的选择器就能作用于子组件内部的元素。但是,组件内的class选择器,不能影响页面的元素。

自定义组件—组件样式-外部样式类

组件希望接受外部传入的样式类。此时可以在 Component 中用 externalClasses 定义若干个外部样式类。

在开发组件时,主动暴露给组件使用者,修改组件内部样式

组件 custom-component.js

/* 组件 custom-component.js */
Component({
  externalClasses: ['my-class']
});

组件 custom-component.wxml

<!-- 组件的wxml -->
<!-- 这里的my-class相当于一个占位符 -->
<view class="my-class">components/MyTest/index.wxml</view>

页面的 WXML

<!-- 页面的 WXML -->
<custom-component my-class="red-text" />
<custom-component my-class="large-text" />

页面的wxss

.red-text{ color: red; }
.large-text {font-size: 50px; }

外部样式类相当于用一个类名去当占位符,以便于在后期使用时替换成真实的类名,方便添加额外的样式。

参考:https://vant-contrib.gitee.io/vant-weapp/#/button#wai-bu-yang-shi-lei
在这里插入图片描述

自定义组件—数据方法

组件的典型结构

// borderImage.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    
  },

  /**
   * 组件的初始数据
   */
  data: {
    
  },

  /**
   * 组件的方法列表
   */
  methods: {
    
  }
})

定义数据

小程序中,用于组件模板渲染的私有数据,需要定义到data

methods方法

小程序组件中,事件处理函数和自定义方法需要定义到methods

自定义组件—组件插槽

单个插槽

小程序中,默认情况下每个自定义组件中只允许使用一个插槽进行占位。

<!--components/MyTest2/index.wxml-->
<view>
  <text>components/MyTest2/index.wxml</text>
  <!-- 对于不确定的内容,可以使用slot进行占位,具体内容交给使用者确定 -->
  <slot></slot>
</view>

使用组件

<my-test2>
  <!-- 这里的内容将被放到组件中<slot>的位置 -->
  <view>
    这里是slot里的内容
  </view>
</my-test2>

多插槽(具名插槽)

组价.js

Component({
  options: {
    multipleSlots: true // 在组件定义时的选项中启用多 slot 支持
  },
  // ... 省略其他
})

此时,可以在这个组件的 wxml 中使用多个 slot ,以不同的 name 来区分。

定义插槽

<view>
  <text>components/MyTest2/index.wxml</text>
  <!-- 对于不确定的内容,可以使用slot进行占位,具体内容交给使用者确定 -->
  <!-- <slot></slot> -->
  <slot name="before"></slot>
  <view>
    ---------这里是分割线--------
  </view>
  <slot name="after"></slot>
</view>

使用组件

<my-test2>
  <!-- 这里的内容将被放到组件中<slot>的位置 -->
  <!-- <view>
    这里是slot里的内容
  </view> -->
  <view slot="before">
    这里是before slot里的内容
  </view>
  <view slot="after">
    这里是after slot里的内容
  </view>
</my-test2>

自定义组件—生命周期

组件生命周期-lifetimes

生命周期参数描述
created在组件实例刚刚被创建时执行,此时还不能调用 setData,一般用于给组件的this添加一些自定义的属性字段
attached在组件实例进入页面节点树时执行,绝大多数初始化工作可以在这个时机进行,例如发请求获取初始数据
ready在组件在视图层布局完成后执行
moved在组件实例被移动到节点树另一个位置时执行
detached在组件实例被从页面节点树移除时执行,适合做一些清理工作
errorObject Error每当组件方法抛出错误时执行

生命周期函数要写在lifetimes里边

lifetimes: {
    created() {
      console.log('组件被created') // 这里使用setData不会引起视图的更新
      this.setData({ msg: 'abc!' })
    },
    attached() {
      this.setData({ msg: 'abcd' })
    }
  }

自定义组件-属性(父传子)

小程序中,properties是组件的对外属性,用于接收外界传递到组件中的数据

父组件传入属性值

<my-test isOpen max="9" min="1" />

子组件.js中接收

Component({
	properties: {
    isOpen: Boolean,
  	min: Number, // 直接写类型
    max: {       // 写类型 + 初始值
    	type: Number,
      value: 10 // value用于指定默认值
    }
  }
})

自定义组件-组件通讯-自定义事件triggerEvent(子传父)

在这里插入图片描述

Vant组件库

官方文档:https://vant-contrib.gitee.io/vant-weapp/#/quickstart

步骤一 通过 npm 安装

npm i @vant/weapp -S --production

步骤二 修改 app.json

将 app.json 中的 "style": "v2" 去除

步骤三 修改 project.config.json

开发者工具创建的项目,miniprogramRoot 默认为 miniprogrampackage.json 在其外部,npm 构建无法正常工作。

需要手动在 project.config.json 内添加如下配置,使开发者工具可以正确索引到 npm 依赖的位置。

{
  ...
  "setting": {
    ...
    "packNpmManually": true,
    "packNpmRelationList": [
      {
        "packageJsonPath": "./package.json",
        "miniprogramNpmDistDir": "./miniprogram/"
      }
    ]
  }
}

步骤四 构建 npm 包(重点)

开发者工具上 > “工具” > “构建npm”

在这里插入图片描述

使用

去app.json(全局注册)或页面.json(局部注册)中注册

"usingComponents": {
  "van-button": "@vant/weapp/button/index"
}

在页面中使用

<van-button type="primary">按钮</van-button>

小程序开发环境-优化目录结构

项目的根目录
├── miniprogram  // 项目相关的代码夹
├── node_modules // npm包目录
├── package.json
├── project.config.json
├── package-lock.json
└── sitemap.json

在这里插入图片描述

修改 project.config.json 中的配置项

{
  // 省略其他......
  "setting": {
    // 省略其他......
    "packNpmManually": true,
    "packNpmRelationList": [
      {
        "miniprogramNpmDistDir": "./miniprogram",
        "packageJsonPath": "package.json"
      }
    ]
  },
  "miniprogramRoot": "miniprogram/"
}

启用 less/sass

通过 less/sass 可以更好的管理 css 样式,通过 project.config.json 可以启用对 less/sass 的支持。

{
  "setting": {
    "useCompilerPlugins": ["sass"]
  }
}

然后将 .wxss 文件后缀改换成 .scss 即可。

启动项目

  1. 拉取代码
  2. 导入项目

使用小程序开发者工具导入【项目】的代码

3. 使用小程序开发者工具构建 npm

  1. 安装包:npm install
  2. 手动构建: 【工具】→【构建npm】

project.config.json的几个配置

{
  "miniprogramRoot": "miniprogram/",
  "setting": {
    "useCompilerPlugins": ["sass"],
    "packNpmManually": true,
    "packNpmRelationList": [
      {
        "packageJsonPath": "./package.json",
        "miniprogramNpmDistDir": "./miniprogram"
      }
    ],
  }
}
  • miniprogramRoot 项目的根目录为 miniprogram
  • setting.useCompilerPlugins 启用了 sass 支持
  • packNpmRelationList 指定了 npm 构建时所需的 package.json 的位置以及构建后代码的生成位置

4. 改成自己的appid

这个项目中的appid是别人的,如果我们需要改成自己的。

基础封装-消息反馈

将所有通用的工具方法封装到 utils/utils.js 中

/**
 * 用户消息反馈
 * @param {string} title 文字提示的内容
 */
export const toast = (title = '数据加载失败...') => {
  wx.showToast({
    title,
    mask: true,
    icon: 'none',
  })
}
// 挂载到全局对象 wx
wx.$toast = toast

app.js

// 在入口中执行 utils.js
import './utils/utils.js'
App({
  // ...
})

使用

 wx.$toast('//提示文字', "icon图标")

基础封装-网络请求

安装第三方的包-构建

  1. npm install wechat-http
  2. 安装完成后还必须要构建 npm后才可以使用

wechat-http用法与 axios 类似:

  • http.baseURL 配置接口基础路径
  • http.getGET 方法发起请求
  • http.postPOST 方法发起请求
  • http.putPUT 方法发起请求
  • http.deleteDELETE 方法发起请求
  • http.intercept 配置请求和响应拦截器
  • http 本身做为函数调用也能用于发起网络请求

二次封装

新建 utils/http.js 文件

// 导入 http 模块
import http from 'wechat-http'
// 基础路径
http.baseURL = 'https://live-api.itheima.net'
// 挂载到全局对象
wx.http = http
// 普通的模块导出
export default http

以全局对象方式调用时需要在入口中执行 utils/http.js

// 执行 uitls/http.js
import './utils/http.js'
App({
  // ...
})

配置响应拦截器

// 配置响应拦截器
http.intercept.response = function ({ data, config }) {

  // 检测接口是否正常返回结果
  if (data.code !== 10000) {
    wx.$toast()
    return Promise.reject(data)
  }

  // 只保留data数据,其它的都过滤掉
  return data.data
}

跳转传参

点击公告列表后将公告的ID通过地址参数传递到公告详情页面,在公告详情页 onLoad 生命周期中读取到公告 ID,然后调用接口获取公告详情的数据。

在这里插入图片描述
在这里插入图片描述

van-count-down 组件(倒计时)的应用

<van-count-down use-slot time="{{6000}}" bind:change="countDownChange">
  <text>{{timeData.seconds}}秒后重新获取</text>
</van-count-down>

time: 指定了倒计时多少毫秒

bind:change每隔一秒的回调,它会传出来当前的倒计时信息

 countDownChange(ev) {
    console.log(ev.detail)
    this.setData({
      timeData: ev.detail,
      getCodeBtnVisible: ev.detail.minutes === 0 && ev.detail.seconds === 0,
    })
  },

表单验证插件使用

先在data中设置mobile,再在模板中进行双向绑定

model:value=“{{mobile}}”

  1. 安装构建 表单验证码插件 wechat-validate
npm install wechat-validate
  1. 将插件导入到项目中
  • behaviors 将插件注入到页面中
  • rules 由插件提供的属性,用来定义数据验证的规则(类似于 Element UI)
  • validate 由插件提供的方法,根据 rules 的规则来对数据进行验证
// 导入表单验证插件
import validate from 'wechat-validate'
Page({
  data: {
    mobile: '' // 省略其他
  },
  behaviors: [validate], // 将插件注入到页面实例中
  rules: {
    mobile: [
      {required: true, message: '请填写手机号码!'},
      {pattern: /^1[3-8]\d{9}$/, message: '请填写正确的手机号码!'}
    ]
  },
  getSMSCode() {
    // 获取验证结果
    const { valid, message } = this.validate('mobile')
    // 如果验证不合法则不再执行后面的逻辑
    if (!valid) return wx.$toast(message)
    console.log('getCode')
    this.setData({ getCodeBtnVisible: false })
  },
})

保存token-跳转

  1. 在app.js中设置setToken方法,保存到本地存储
App({
  // ...
  setToken(key, token) {
 		// 将 token 记录在应用实例中   
    this[key] = token
    // 将 token 存入本地
    wx.setStorageSync(key, token)
  }
})

2.点击登录 | 注册发送请求成功之后

  const app = getApp()    //小程序中获取全局的实例对象
  app.setToken('token', res.token)
  app.setToken('refreshToken', res.refreshToken)
 //  跳转
  const url = '/pages/profile/index'
  wx.redirectTo({ url })

登录检测-鉴权组件

在这里插入图片描述

1.在根目录中创建 components 文件夹用来存放全局的组件,然后通过小程序开发者工具创建一个名为 authorization 的组件

2.接下来全局来注册这个组件,保证任何页面中都可以直接应用 authorization 组件

{
  "usingComponents": {
    "authorization": "/components/authorization/index"
  },
}

3.到用户信息页面中应用 authorization 使用做为页面根节点

<authorization>
  	...
</authorization>

4.在authorization中补充插槽和状态

<!--components/authorization/index.wxml-->
<slot wx:if="{{isLogin}}"></slot> 
  data: {
    isLogin: false
  },

读取本地存储token

读取本地存储的 token 数据,用于判断是否曾登录过

// app.js
App({
  ......
    getToken() {
    // 将 token 数据记到应用实例中
    // return this.token = wx.getStorageSync('token')
    return this.token
  }
})

在组件内读token并处理

data: {
  isLogin: false
},
lifetimes: {
  attached() {
    const isLogin = !!getApp().getToken()    
    //const app = getApp()  const isLogin = !!app.getToken()
    this.setData({ isLogin })
    if (!isLogin) {
      wx.redirectTo({ url: '/pages/login/index' })
    }
  }
},

地址重定向,登录成功后跳回到原来的页面

authoirzation 组件检测登录时获取当前页面栈实例,并在跳转到登录页面时在 URL 地址上拼凑参数:

// /components/authorization/index.js
Component({
  // ...
  lifetimes: {
    attached() {
      // 获取登录状态
      const isLogin = !!getApp().token
      // 变更登录状态
      this.setData({ isLogin })
      // 获取页面栈
      const pageStack = getCurrentPages()
      // 获取页面路径
      const currentPage = pageStack.pop()
      // 未登录的情况下跳转到登录页面
      if (!isLogin) {
        wx.redirectTo({
          url: '/pages/login/index?redirectURL=/' + currentPage.route,
        })
      }
    },
  },
})

在这里插入图片描述

用户管理-显示默认值

app.js中添加初始值

App({
  globalData: {},
  userInfo: { avatar: '', nickName: '微信用户1' }
}

在onLoad中加载值

data: {
  avatar: '',
  nickName: ''
},
onLoad() {
  const app = getApp()
  console.log(app.userInfo)
  const { avatar, nickName } = app.userInfo
  this.setData({ avatar, nickName })
  // 用户未登录时不必请求
  app.token && this.getUserProfile()
},

配置请求拦截器

将用户的登录状态通过自定义的头信息 Authorization 随接口调用时一起发送到服务端。

// 导入 wechat-http 模块
import http from 'wechat-http'
// 配置接口基础路径
http.baseURL = 'https://live-api.itheima.net'
// 配置请求拦截器

http.intercept.request = function (options) {
  console.log('请求拦截器', options.header)
  // 扩展头信息
  const defaultHeader = {}
  // 身份认证
  const token = getApp().getToken()
  if (token) {
    defaultHeader.Authorization = 'Bearer ' + getApp().getToken()
  }
  // 与默认头信息合并
  options.header = Object.assign({}, defaultHeader, options.header)
  // 处理后的请求参数
  return options
}

注:传递 token 时需要拼凑字符串前缀 "Bearer "

文件上传(例:更新用户头像)

获取用户选择的头像地址,通过 wx.uploadFile 将图片上传到服务端。

wx.uploadFile 的基本语法:

  • url 上传接口地址
  • filePath 待上传文件的临时路径(该路径只能用于小程序内部)
  • name 接口接收上传文件的数据名称(由后端指定)
  • formData 除上传文件外的其它数据
  • header 自定义头信息
  • success 上传成功的回调函数
  • fail 上传失败后的回调函数
  • complete 上传完成时的回调(无论成功或失败)

注:该 API 不支持返回 Promise,调用该 API 时,需要提前在小程序管理后台添加服务器域名。

<!-- pages/profile/index.wxml -->
<authorization>
  <view class="profile">
    <van-cell center title="头像">
      <van-icon slot="right-icon" name="arrow" size="16" color="#c3c3c5" />
      <button
        class="button"
        size="mini"
        hover-class="none"
        bind:chooseavatar="updateUserAvatar" //事件,事件名全部小写,A千万不要大写,不会触发
        open-type="chooseAvatar">    //与上边事件对应
        <image class="avatar" src="{{avatar}}"></image>
      </button>
    </van-cell>
    ...
  </view>
</authorization>
// pages/profile/index.js
const pageStack = getCurrentPages()
Page({
    ...
  // 更新用户头像
  updateUserAvatar(ev.detail.avatarUrl) {
    // 调用 API 上传文件
    wx.uploadFile({
      // 接口地址
      url: wx.$http.baseURL + '/upload',
      // 待上传的文件路径
      filePath: avatar,
      name: 'file',// wx.uploadFile 要求必传。
      header: {
        Authorization: 'Bearer ' + getApp().getToken()   // 用户登录状态
      },
      formData: { // 是我们自己的接口文档的要求。可以不传,默认就是avatar
        type: 'avatar'
      },
      success: (result) => {
        console.log(JSON.parse(result.data))
        const res = JSON.parse(result.data)
        // console.log(res.data.url)
        const avatar = res.data.url
        // 1. 在页面上显示
        this.setData({ avatar })
        // 2. 更新全局数据
        const app = getApp()
        app.userInfo.avatar = avatar
        // 3. 通过页面栈找到my/index页面,更新它的avatar信息
        const pages = getCurrentPages()
        // pages[0].data.nickName = nickName 直接修改数据不会让视图更新
        // 调用setData更新
         pages[0].setData({ avatar })
      }
    })
  }
})

上述代码中通过 wx.http.baseURL 获取接口服务器地址,通过应用实例获取 token

refresh_token使用

在这里插入图片描述

  • token:
    • 作用:在访问一些接口时,需要传入token,就是它。
    • 有效期:2小时(安全)。
  • refresh_token
    • 作用: 当token的有效期过了之后,可以使用它去请求一个特殊接口(这个接口也是后端指定的,明确需要传入refresh_token),并返回一个新的token回来(有效期还是2小时),以替换过期的那个token。
    • 有效期:14天。(最理想的情况下,一次登陆可以持续14天。)

在这里插入图片描述

1.用户在首次完成登录时会分别得到 token 和 refresh_token

2.当 token 失效后(例如2小时之后),调用接口A会返回 401 状态码(这是与后端约定好的规则)

3.检测状态码是否为 401**,如果是,则携带refreshToken去调用刷新token的接口

4.刷新 token 的接口后会返回新的 token 和 refreshToken

5.把401的接口A重新发送一遍

注意:
refresh_token也是有过期时间的,只不过一般会比token过期时间更长一些。这就是为啥如果某个应用我们天天打开,则不会提示我们登录,如果是有几周或更长时间去打开时,会再次要求我们登录。
refresh_token一个更常见的名字叫token无感刷新。

refreshToken功能-基本实现

// 响应拦截器
http.intercept.response = async ({ statusCode, data, config }) => {
  console.log(statusCode, data, config)
  // console.log(statusCode) // http 响应状态码
  // console.log(config) // 发起请求时的参数
  if (data.code === 401) {
    const app = getApp()
    // 调用接口获取新的 token
    const res = await http({
      url: '/refreshToken',
      method: 'POST',
      header: {
        Authorization: 'Bearer ' + app.getToken('refreshToken'),
      }
    })

    app.setToken('token', res.token)
    app.setToken('refreshToken', res.refreshToken)
     // 获得新的token后需要重新发送刚刚未完成的请求
    config = Object.assign(config, {
      header: {
        // 更新后的 token
        Authorization: 'Bearer ' + res.token,
      },
    })
    // 重新发请求
    return http(config)
  }
  // 拦截器处理后的响应结果
  if (data.code === 10000) {
    return data.data
  } else {
    wx.$toast(data.message || '请求失败')
    return Promise.reject(data.message)
  }
}

refreshToken也过期的特殊处理

在这里插入图片描述

完整版响应拦截器

// 响应拦截器
http.intercept.response = async ({ statusCode, data, config }) => {
  console.log(statusCode, data, config)
  // console.log(statusCode) // http 响应状态码
  // console.log(config) // 发起请求时的参数
  if (data.code === 401) {
++    if (config.url.includes('/refreshToken')) {
++      console.log('/refreshToken过期了')
++      // 获取当前页面的路径,保证登录成功后能跳回到原来页面
++      const pageStack = getCurrentPages()
++      const currentPage = pageStack.pop()
++      const redirectURL = currentPage.route
++      // 跳由跳转(登录页面)
++      wx.redirectTo({
++        url: '/pages/login/index?redirectURL=/' + redirectURL,
++      })
++      return Promise.reject('refreshToken也过期了,就只能重新登录了')
++    }
    const app = getApp()
    // 调用接口获取新的 token
    const res = await http({
      url: '/refreshToken',
      method: 'POST',
      header: {
        Authorization: 'Bearer ' + app.getToken('refreshToken'),
      }
    })

    app.setToken('token', res.token)
    app.setToken('refreshToken', res.refreshToken)
    config = Object.assign(config, {
      header: {
        // 更新后的 token
        Authorization: 'Bearer ' + res.token,
      },
    })
    // 重新发请求
    return http(config)
  }
  // 拦截器处理后的响应结果
  else if (data.code === 10000) {
    return data.data
  } else {
    wx.$toast(data.message || '请求失败')
    return Promise.reject(data.message)
  }
}

腾讯位置服务-需要提前注册

文档地址:https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/jsSdkOverview

使用步骤(共4步)

  1. 申请开发者密钥(key):申请密钥(地址:https://lbs.qq.com/dev/console/application/mine)
  2. 开通webserviceAPI服务:控制台 ->应用管理 -> 我的应用->添加key-> 勾选WebServiceAPI -> 保存(小程序SDK需要用到webserviceAPI的部分服务,所以使用该功能的KEY需要具备相应的权限)

在这里插入图片描述

3.下载小程序>微信小程序JavaScriptSDK,小程序>微信小程序JavaScriptSDK v1.1(https://mapapi.qq.com/web/miniprogram/JSSDK/qqmap-wx-jssdk1.1.zip) JavaScriptSDK v1.2(https://mapapi.qq.com/web/miniprogram/JSSDK/qqmap-wx-jssdk1.2.zip) js文件

4.安全域名设置,在小程序管理后台-> 开发 -> 开发管理 -> 开发设置 -> “服务器域名” 中设置request合法域名,添加https://apis.map.qq.com

地理定位-wx.getLocation

在这里插入图片描述

获取用户所在位置的经纬度。在小程序中调用这个接口时必须先在 app.json 中申请调用权限(开发环境可以省略)。

//app.json
{
  "requiredPrivateInfos": [
++    "getLocation"
  ],
  "permission": {
    "scope.userLocation": {
      "desc": "你的位置信息将用于小程序位置接口的效果展示"
    }
  },
}

在这里插入图片描述

Page({
  onLoad() {
    this.getLocation()
  },
  async getLocation() {
    const res = await wx.getLocation() // 要提前申请权限
    console.log(res)
  },
})

wx.getLocation返回的结果格式大致如下:

accuracy: 65
errMsg: "getLocation:ok"
horizontalAccuracy: 65
latitude: 30.88131
longitude: 114.37509
speed: -1
verticalAccuracy: 65

wx.getLocation 只能得到经纬度信息

逆地址解析-reverseGeocoder

由坐标 → 坐标所在位置的文字描述的转换,输入坐标返回地理位置信息和附近poi列表

文档地址:https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/methodReverseGeocoder

1. 导入 QQMapWX 并设置好 key

在这里插入图片描述

2.在代码中补充getPoint方法:

  1. 调用接口把经纬度转换成对应位置的文字
  2. 保存文字到address
// 导入位置服务实例
import QQMap from '../../../utils/qqmap'

Page({   ......
  onLoad() {
    this.getLocation()
  },
  async getLocation() {
    // 调用小程序API获取经纬度等信息
    const { latitude, longitude } = await wx.getLocation()  //获取用户经纬度
    this.getPoint(latitude, longitude)  
  },
  getPoint(latitude, longitude) {
    // 逆地址解析(根据经纬度来获取地址)
    QQMap.reverseGeocoder({
      location: [latitude, longitude].join(','),
      success: (result) => {
        const address = res.address
        this.setData({ address })
      },
    })
  }
})

3.渲染页面

 <van-cell-group border="{{false}}" title="当前地点">
  <van-cell title="{{address}}" border="{{false}}">  //border="{{false}}"设置无边框样式
    <text bind:tap="chooseLocation" class="enjoy-icon icon-locate">重新定位</text>
  </van-cell>
</van-cell-group>

QQMap地点搜索—search

根据当前的定位,调用 QQMap.search() 找到周边的信息。

搜索周边poi(Point of Interest),比如:“酒店” “餐饮” “娱乐” “学校” 等等
文档地址:https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/methodSearch

在这里插入图片描述

小程序中调用这个接口时必须要在 app.json 中申请调用权限

//app.json
{
  "requiredPrivateInfos": [
++    "chooseLocation"
  ]
}
// 选择新的位置
async chooseLocation() {
  // 调用小程序 API 获取新的位置
  const { latitude, longitude } = await wx.chooseLocation()
  this.getPoint(latitude, longitude)   // 获取新的位置经纬度
},

getPoint(latitude, longitude) {
  wx.showLoading({
    title: '正在加载...',      // 显示loading提示
  })

  // 逆地址解析(根据经纬度来获取地址)
  QQMap.reverseGeocoder({
    location: [latitude, longitude].join(','),
    success: ({ result: { address } }) => {
      this.setData({ address })
    },
  })

  QQMap.search({
    keyword: '住宅小区', //搜索关键词
    location: [latitude, longitude].join(','), //设置周边搜索中心点
    page_size: 5,   //只显示5条信息,不设置此项默认为10
    success: (result) => {     //success 是一个回调函数,表示搜索成功后的处理逻辑。
      const points = result.data
      this.setData({ points }) // 渲染数据
    },
    fail: (err) => {    //fail 是一个回调函数,表示搜索失败后的处理逻辑。
      console.log(err.message)
    },
    complete: () => {//complete 是一个回调函数,表示搜索结束后的处理逻辑(无论搜索成功还是失败)
      wx.hideLoading()   // 隐藏loading提示
    },
  })
},

重新定位-wx.chooseLocation

在这里插入图片描述

申请权限

获取用户指定位置的经纬度。在小程序中调用这个接口时必须要在 app.json 中申请调用权限

{
  "requiredPrivateInfos": [
    "chooseLocation"
  ]
}

示例代码:

// 选择新的位置
async chooseLocation() {
  // 调用小程序 API 获取新的位置
  const { latitude, longitude } = await wx.chooseLocation()
  // 获取新的位置附近的小区
  this.getPoint(latitude, longitude)

  console.log('起点位置:', latitude, longitude)
},

getPoint(latitude, longitude) {
  // 显示loading提示
  wx.showLoading({
    title: '正在加载...',
  })

  // 逆地址解析(根据经纬度来获取地址)
  QQMap.reverseGeocoder({
    location: [latitude, longitude].join(','),
    success: ({ result: { address } }) => {
      // console.log(address)
      // 数据数据
      this.setData({ address })
    },
  })

  QQMap.search({
    keyword: '住宅小区', //搜索关键词
    location: [latitude, longitude].join(','), //设置周边搜索中心点
    page_size: 5,
    success: (result) => {
      // console.log(result)
      // 过滤掉多余的数据
      const points = result.data.map(({ id, title, _distance }) => {
        return { id, title, _distance }
      })

      // console.log(points)
      // 渲染数据
      this.setData({ points })
    },
    fail: (err) => {
      console.log(err.message)
    },
    complete: () => {
      // 隐藏loading提示
      wx.hideLoading()
    },
  })
},

图片收集(收集身份证信息)

小程序没有input type="file"用于选择文件,要实现类似功能,用以下api:
wx.chooseMedia**:**拍摄或从手机相册中选择图片或视频。
低版本请用wx.chooseImage。

1.绑定事件选择身份证图片上传。

2.发送请求上传图片,拿到上传后的图片地址。

Page({
 ...
  async uploadPicture(ev) {
    // 获取图片临时地址
    const res = await wx.chooseMedia({
      count: 1,
      mediaType: ['image'],
      sizeType: ['compressed'],
    })
    const tempPath = res.tempFiles[0].tempFilePath
    const type = ev.mark.type
    // 上传图片到服务器
    wx.uploadFile({
      url: wx.$http.baseURL + '/upload',
      filePath: tempPath,
      name: 'file',
      header: {
        Authorization:  'Bearer ' +  getApp().getToken(),
      },
      success: (res) => {
        const res = JSON.parse(result.data)
        console.log(res.data.url) 							// 上传成功的回调
        this.setData({ [type]: res.data.url })
      },
    })
  },
})

校验表单信息

获取了全部的表单数据后再对数据进行验证,说明如下:

  • 房屋的信息是通过url地址获取的不需要验证
  • 性别可以指定默认值也不需要验证
  • 剩下的数据通过 wechat-validate 插件进行验证:
// house_pkg/pages/form/index.js
// 导入表单验证插件
import wxValidate from 'wechat-validate'
Page({
  behaviors: [wxValidate],
  data: {
    point: '',
    building: '',
    room: '',
    name: '',
    gender: 1,
    mobile: '',
    idcardFrontUrl: '',
    idcardBackUrl: '',
  },
  rules: {
    name: [
      { required: true, message: '业主姓名不能为空!' },
      { pattern: /^[\u4e00-\u9fa5]{2,5}$/, message: '业主姓名只能为中文!' },
    ],
    mobile: [
      { required: true, message: '业主手机号不能为空!' },
      { pattern: /^1[3-8]\d{9}$/, message: '请填写正确的手机号!' },
    ],
    idcardFrontUrl: [
      { required: true, message: '请上传身份证国徽面!' }
    ],
    idcardBackUrl: [
      { required: true, message: '请上传身份证照片面!' }
    ],
  },
})

表单收集—收集预约日期

1.时间选择控件:van-datetime-pickerhttps://vant-contrib.gitee.io/vant-weapp/#/datetime-picker

2.弹出层控件:van-popuphttps://vant-contrib.gitee.io/vant-weapp/#/popup

  1. 给选择日期单元绑定事件——显示选择日期弹层(van-popup)

2.给日期控件绑定确认事件confirm

3.在确认回调中获取时间戳

4.将时间戳格式化并显示

selectDate(ev) {
  // console.log(ev)
  this.setData({
    currentDate: ev.detail,
    appointment: wx.$utils.formatDate(ev.detail), // 格式化日期
  })
  this.closeDateLayer()
},

补充格式化日期的函数

formatDate(time) {
  const d = new Date(time)
  const year = d.getFullYear()
  let month = d.getMonth() + 1 // 获取月份,月份从0开始,所以加1
  let day = d.getDate()
  month = month < 10 ? '0' + month : month
  day = day < 10 ? '0' + day : day
  return `${year}-${month}-${day}`
},

wxml

<van-cell title-width="100" title="预约日期" value-class="{{appointment && 'active-cell'}}" bind:click="openDateLayer"
        is-link value="{{appointment || '请选择上门维修日期'}}" />
...省略
<van-popup bind:close="closeDateLayer" round show="{{ dateLayerVisible }}" position="bottom">
  <van-datetime-picker bind:cancel="closeDateLayer" bind:confirm="selectDate" type="date" value="{{ currentDate }}"
    min-date="{{ 1664582400000 }}" />
</van-popup>

加载更多

在scroll-view上添加 bindscrolltolower=“loadMore”

  <scroll-view 
  bindscrolltolower="loadMore"
  show-scrollbar="{{false}}" enhanced scroll-y>
    <view class="repairs">
      <view class="repairs-title">我的报修</view>
loadMore() {
    // if(是否有更多的数据)
    if (this.data.total <= this.data.list.length)
      return
    console.log('更多的数据')
    // 把页码+1,发请求,请求回来的数据要 追加 到原数组
    // [5] → [10]
    this.data.page++
    this.getList()

  },
  async getList() {
    // 发请求
    const { pageTotal, total, rows: list } = await wx.$http.get('/repair', { current: this.data.page, pageSize: this.data.pageSize })
    console.log(list, pageTotal, total)
    // 渲染数据
    // 在原来的基础上添加数据
    this.setData({ total, list: [...this.data.list, ...list] })
  },

路线规划

路线规划是常见的一个功能,它用来在地图上展示两点间路线,要使用这个功能需要用到两部分的知识:

  1. 小程序提供的 map 组件用来在页面渲染地图

map | 微信开放文档https://developers.weixin.qq.com/miniprogram/dev/component/map.html)

  1. 腾讯位置服务计算两点路线的所有坐标点(经纬度)

首先来看小程序提供的地图组件 map

  • latitude 指定地图中心点的纬度
  • longitude 指定地图中心点的经功
  • scale 指定地图初始的缩放比例,取值范围 3 - 20
  • markers 地图上的标记点
  • polyline 地图上的路线

latitude、longitude、scale 相对容易理解,重点来看一下 markers 的使用:

repqir_pkg/pages/detail/index.js

Page({
  data: {
    markers: [
      {
        id: 1,
        latitude: 40.22077,
        longitude: 116.23128,
        width: 24,
        height: 30,
      },
      {
        id: 2,
        latitude: 40.225857999999995,
        longitude: 116.23246699999999,
        iconPath: '/static/images/marker.png',
        width: 40,
        height: 40,
      },
    ],
  }
})

在定义标记点时每个标记点必须要指定 ID 属性,否则会有错误产生,通过 iconPath 可以自定义标记点的图片,width/height 定义标记点的大小尺寸。

polyline

polyline 用来在在图上展示两点间的行进路线,需要传递给它路线对应的坐标点(很多个点组成的线),获取这些坐标点需要通过位置服务计算得到。

计算两点间路线的坐标点需要用到位置服务的[路线规划]https://lbs.qq.com/miniProgram/jsSdk/jsSdkGuide/methodDirection方法

repqir_pkg/pages/detail/index.js

// repqir_pkg/pages/detail/index.js
Page({
  data: {},
  onLoad() {
    // 生成路线
    this.getPolyline()
  },
  // 调用位置服务(路线规划)
  getPolyline() {
    qqMap.direction({
      mode: 'bicycling',
      from: '40.227978,116.22998',
      to: '40.22077,116.23128',
      success: ({ result }) => {
        const coors = result.routes[0].polyline
        const points = []
        //坐标解压(返回的点串坐标,通过前向差分进行压缩)
        for (let i = 2; i < coors.length; i++) {
          coors[i] = Number(coors[i - 2]) + Number(coors[i]) / 1000000
        }
        // 获取经纬度
        for (let i = 0; i < coors.length; i += 2) {
          points.push({ latitude: coors[i], longitude: coors[i + 1] })
        }
        // 渲染数据
        this.setData({
          latitude: points[30].latitude,
          longitude: points[30].longitude,
          polyline: [
            {points, color: '#5591af', width: 4},
          ],
        })
      },
    })
  },
})

计算出来的坐标点是经过压缩的,需要按着官方指定的方式对数据进行解压才可以获取真正的坐标点,并且为了适应小程序地图组件的用法,还需要对数据进行二次的加工。

关于数据的处理大家只需要参考文档来实现就可以,可以说之所这么操作是腾讯位置服务规订好的,做为开发者按着官方提从的方法来应用即可。

自定义分享

[onShareAppMessage]https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html#onShareAppMessage-Object-object

监听用户点击页面内转发按钮(button 组件 open-type=“share”)或右上角菜单“转发”按钮的行为,并自定义转发内容。

注意:只有定义了此事件处理函数,右上角菜单才会显示“转发”按钮

Page({
  onLoad({ id, encryptedData }) {
    this.getPassport(id)
    this.getPassportShare(encryptedData)
  },
  // 获取访客详情(通行证)
  async getPassport(id) {
    // 检测是否存在 id
    if (!id) return
    // 调用接口
    const passport = await wx.$http.get('/visitor/' + id)
    // 渲染数据
    this.setData({ ...passport })
  }
  async getPassportShare(encryptedData) {
    // 检测是否存在 id
    if (!encryptedData) return
    // 调用接口
    const passport = await wx.$http.get('/visitor/share/' + this.data.encryptedData)
    // 渲染数据
    this.setData({ passport })
  },
  onShareAppMessage() {
    return {
      title: '查看通行证',
      path: '/visitor_pkg/pages/passport/index?encryptedData=' + encryptedData,
      imageUrl: 'https://enjoy-plus.oss-cn-beijing.aliyuncs.com/images/share_poster.png',
    }
  },
})

保存到本地api介绍

[saveImageToPhotosAlbum]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.saveImageToPhotosAlbum.html

保存图片到系统相册。

[getImageInfo]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.getImageInfo.html

获取图片信息。网络图片需先配置 download 域名才能生效。

在这里插入图片描述

保存到本地实现

1.给按钮绑定事件,调用getImageInfo获取图片临时路径

2.调用saveImageToPhotosAlbum传入临时路径完成保存功能

// 保存图片
  async saveQRCode() {
    try {
      // 读取图片信息
      const { path } = await wx.getImageInfo({
        src: this.data.url,
      })
      // 保存图片到相册
      wx.saveImageToPhotosAlbum({ filePath: path })
    } catch (err) {
      wx.$toast('保存图片失败,稍后重试!')
    }
  },

ush({ latitude: coors[i], longitude: coors[i + 1] })
}
// 渲染数据
this.setData({
latitude: points[30].latitude,
longitude: points[30].longitude,
polyline: [
{points, color: ‘#5591af’, width: 4},
],
})
},
})
},
})


计算出来的坐标点是经过压缩的,需要按着官方指定的方式对数据进行解压才可以获取真正的坐标点,并且为了适应小程序地图组件的用法,还需要对数据进行二次的加工。

关于数据的处理大家只需要参考文档来实现就可以,可以说之所这么操作是腾讯位置服务规订好的,做为开发者按着官方提从的方法来应用即可。

### 自定义分享

**[onShareAppMessage]https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html#onShareAppMessage-Object-object**

监听用户点击页面内转发按钮([button](https://developers.weixin.qq.com/miniprogram/dev/component/button.html) 组件 open-type="share")或右上角菜单“转发”按钮的行为,并自定义转发内容。

**注意:只有定义了此事件处理函数,右上角菜单才会显示“转发”按钮**

```js
Page({
  onLoad({ id, encryptedData }) {
    this.getPassport(id)
    this.getPassportShare(encryptedData)
  },
  // 获取访客详情(通行证)
  async getPassport(id) {
    // 检测是否存在 id
    if (!id) return
    // 调用接口
    const passport = await wx.$http.get('/visitor/' + id)
    // 渲染数据
    this.setData({ ...passport })
  }
  async getPassportShare(encryptedData) {
    // 检测是否存在 id
    if (!encryptedData) return
    // 调用接口
    const passport = await wx.$http.get('/visitor/share/' + this.data.encryptedData)
    // 渲染数据
    this.setData({ passport })
  },
  onShareAppMessage() {
    return {
      title: '查看通行证',
      path: '/visitor_pkg/pages/passport/index?encryptedData=' + encryptedData,
      imageUrl: 'https://enjoy-plus.oss-cn-beijing.aliyuncs.com/images/share_poster.png',
    }
  },
})

保存到本地api介绍

[saveImageToPhotosAlbum]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.saveImageToPhotosAlbum.html

保存图片到系统相册。

[getImageInfo]https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.getImageInfo.html

获取图片信息。网络图片需先配置 download 域名才能生效。

[外链图片转存中…(img-M8SkNwoO-1687267859406)]

保存到本地实现

1.给按钮绑定事件,调用getImageInfo获取图片临时路径

2.调用saveImageToPhotosAlbum传入临时路径完成保存功能

// 保存图片
  async saveQRCode() {
    try {
      // 读取图片信息
      const { path } = await wx.getImageInfo({
        src: this.data.url,
      })
      // 保存图片到相册
      wx.saveImageToPhotosAlbum({ filePath: path })
    } catch (err) {
      wx.$toast('保存图片失败,稍后重试!')
    }
  },

http://www.niftyadmin.cn/n/452394.html

相关文章

计算机网络——自顶向下方法(第二章学习记录)

本章学习应用层 网络应用是计算机网络存在的理由。 网络应用程序体系结构 现代网络应用程序有两种主流体系结构&#xff1a;客户—服务器体系结构和对等(P2P)体系结构 客户—服务器体系结构&#xff08;client-server )&#xff0c;在这个结构中&#xff0c;有一个总是打开的…

OpenCV项目开发实战--一步一步介绍使用 OpenPose进行多人姿势估计C++/python实现

文末附基于Python和C++两种方式实现的测试代码下载链接 在我们上一篇文章中中,我们使用OpenPose模型对单个人执行人体姿态估计。在这篇文章中,我们将讨论如何进行多人姿态估计。 当一张照片中有多个人时,姿势估计会产生多个独立的关键点。我们需要弄清楚哪组关键点属于同一…

微服务 - Spring Cloud - Eureka Server单机和集群搭建、单机服务注册和集群服务注册

Eureka 服务管理 Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。 SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能 Eureka服务…

python的tqdm一些操作

主要参数 iterable: 可迭代的对象, 在手动更新时不需要进行设置 desc: str, 左边进度条的描述性文字 total: 总的项目数 leave: bool, 执行完成后是否保留进度条 file: 输出指向位置, 默认是终端, 一般不需要设置 ncols: 调整进度条宽度, 默认是根据环境自动调节长度, 如果设置…

如何在 XMind 中绘制流程图

XMind 是专业强大的思维导图软件,由于其结构没有任何限制,很多朋友特别喜欢用它来绘制流程图。禁不住大家的多次询问,今天 XMind 酱就将这简单的流程图绘图方法分享给大家。 在 XMind 中,绘制流程图的主角是「自由主题」和「联系」。它们可以打破思维导图的限制,让你自由…

[大数据 Sqoop,hive,HDFS数据操作]

目录 &#x1f957;前言: &#x1f957;实现Sqoop集成Hive,HDFS实现数据导出 &#x1f957;依赖: &#x1f957;配置文件: &#x1f957;代码实现: &#x1f957;控制器调用: &#x1f957;Linux指令导入导出: &#x1f957;使用Sqoop将数据导入到Hive表中。例如&#…

CloFormer实战:使用CloFormer实现图像分类任务(二)

文章目录 训练部分导入项目使用的库设置随机因子设置全局参数图像预处理与增强读取数据设置Loss设置模型设置优化器和学习率调整算法设置混合精度&#xff0c;DP多卡&#xff0c;EMA定义训练和验证函数训练函数验证函数调用训练和验证方法 运行以及结果查看测试热力图可视化展示…

批量选项-代码

<template> <div class"drawing" :style"{ height: maxHeight }"> <a-space :size"GLOBAL.spaceDefault"> <!-- 新增 --> <span class"plm-icon-btn" click"add"> <span class"opera…