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

news2024/11/25 14:22:36

小程序的基本使用

小程序文件类型

小程序主要提供了 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.coloradmin.cn/o/667993.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

最新Python3.11.4版本和PyCharm开发工具安装详细教程

Python3.11.4版本安装详细教程 1. 官网下载Python安装包1.1 进入官网1.2 查看系统类型1.3 选择与主机位数相同的安装程序 2.运行安装程序2.1 Customize installation&#xff08;自定义安装&#xff09;2.2 Optional Features&#xff08;可选功能&#xff09;2.3 Advanced Opt…

synchronized 底层实现原理、重量级锁、轻量锁、锁膨胀、锁自旋、偏向锁详解

目录 0、基础知识&#xff1a;Java对象的存储格式 1. synchronized底层&#xff1a;Monitor&#xff08;重量级锁&#xff09;&#xff1a;被锁的对象与Monitor的关系 2. synchronized底层&#xff1a;轻量级锁优化&#xff0c;栈帧与被锁的对象的关系 3. 锁膨胀&#xff…

【arduino】HC-SR04超声波测距模块的驱动与使用

arduino超声波测距模块的驱动与使用 什么是超声波测距模块参数:引脚定义电路超声波传感器的控制时序驱动代码接线代码工程文件超声波是振动频率高于20KHZ的机械波。它具有频率高、波长短、绕射现象小、方向性好、能够成为射线而定向传播等特点应用广泛,适合大学生、工程师、技…

精简版Git基础操作(快速上手)

文章目录 前言一、初始化二、新建仓库三、工作区域和文件状态四、添加和提交文件五、回退到之前版本六、查看文件差异七、从版本库中删除文件八、.gitignore忽略文件九、github远程仓库--SSH配置和克隆仓库十、关联本地仓库与远程仓库十一、分支十二、解决合并冲突回退和rebase…

Go mmap 文件内存映射

Go mmap 文件内存映射 mmap是个很好用的内存映射工具&#xff0c;它可以将文件映射到内存中&#xff0c;可以方便地操作文件。使用mmap的优点是&#xff1a; 内存映射可以使得读写文件的性能更高&#xff0c;因为操作的是内存而不是磁盘。可以方便地操作文件&#xff0c;不需…

语音录音转文字的方法使用过吗

大家好&#xff01;今天我要给你们介绍一个实用的功能&#xff0c;那就是录音转文字啦&#xff01;它可以把录音中的声音内容快速且准确地转换成文字格式&#xff0c;让我们在工作和学习中变得更加高效和便利。我们在会议记录、采访访谈、语音笔记等领域&#xff0c;可以很大地…

自动化测试之稳定性测试的设计

目录 前言 压力Stress 随机Randomness 并发Concurrency 交互Interaction 时间Time 总结&#xff1a; 前言 稳定性测试是自动化测试领域最为核心的内容之一。稳定性测试设计应该考虑哪些方面&#xff1f;如何在有限的样本上最大化测试产出&#xff1f;笔者结合自动化的一…

Vivado 下 呼吸灯实验

目录 Vivado 下 呼吸灯实验 1、实验简介 2、实验环境 3、实验任务 4、硬件设计 5、程序设计 5.1、呼吸灯代码如下&#xff1a; 5.2、添加约束文件 .xdc 5.3、下载验证 Vivado 下 呼吸灯实验 呼吸灯最早由苹果公司发明并应用于笔记本睡眠提示上&#xff0c;其一经展出&…

2023年6月GESP能力等级认证C++二级真题

2023-06 GESP二级真题 题数&#xff1a;27 分数&#xff1a;100 测试时长&#xff1a;90min 一、单选题&#xff08;每题 2 分&#xff0c;共 30 分&#xff09; 1. 高级语言编写的程序需要经过以下&#xff08; &#xff09;操作&#xff0c;可以生成在计算机上运行的可执…

电商数仓(用户行为采集平台)数据仓库概念、用户行为日志、业务数据、模拟数据、用户行为数据采集模块、日志采集Flume

1、数据仓库概念 数据仓库&#xff08; Data Warehouse &#xff09;&#xff0c;是为企业制定决策&#xff0c;提供数据支持的。可以帮助企业&#xff0c;改进业务流程、提高产品质量等。 数据仓库的输入数据通常包括&#xff1a;业务数据、用户行为数据和爬虫数据等。 业务数…

Linux信号概念、认识、处理动作 ( 2 ) -【Linux通信架构系列 】

系列文章目录 C技能系列 Linux通信架构系列 C高性能优化编程系列 深入理解软件架构设计系列 高级C并发线程编程 期待你的关注哦&#xff01;&#xff01;&#xff01; 现在的一切都是为将来的梦想编织翅膀&#xff0c;让梦想在现实中展翅高飞。 Now everything is for the…

适合嵌入式开发的GUI(嵌入式学习)

嵌入式开发的GUI如何选择&#xff1f; 常见的嵌入式GUI开发方法轻量级GUI库优缺点 基于Web技术优缺点 Qt框架优缺点 原生开发优缺点 嵌入式系统的限制 常见的嵌入式GUI开发方法 嵌入式开发中的GUI&#xff08;图形用户界面&#xff09;是指在嵌入式系统中实现图形化的用户界面…

Unity核心7——2D动画

一、序列帧动画 &#xff08;一&#xff09;什么是序列帧动画 ​ 我们最常见的序列帧动画就是我们看的日本动画片&#xff0c;以固定时间间隔按序列切换图片&#xff0c;就是序列帧动画的本质 ​ 当固定时间间隔足够短时&#xff0c;我们肉眼就会认为图片是连续动态的&#…

Pandas数据处理与分析教程:从基础到实战

文章目录 前言什么是Pandas&#xff1f;Pandas的安装和导入数据结构Series&#xff08;案例1&#xff1a;创建Series&#xff09;DataFrame&#xff08;案例2&#xff1a;创建DataFrame&#xff09; 数据读取和写入从CSV文件中读取数据&#xff08;案例3&#xff1a;读取CSV文件…

一起学SF框架系列6.2-模块core-Environment

Environment是集成在容器中的抽象接口&#xff0c;它对应用程序环境的两个关键方面进行建模&#xff1a;配置文件&#xff08;profiles&#xff09;和属性&#xff08;properties&#xff09;。 配置文件&#xff08;profiles&#xff09; 配置文件为核心容器中提供了一种机制…

Redis7---单线程和多线程(一)

目录 一、几个面试题 1.Redis的单线程部分 1.2 Redis所谓的“单线程” 1.3 Redis演进变化 1.3.1 Redis 3.x 单线程时代性能很快的原因 1.3.2 Redis 4.0 之前一直采用单线程的主要原因有三个 2. Redis单线程为什么加了多线程特性 3.Redis 6/7的多线程特性和IO多路复用入…

【系统架构】第二章-计算机系统基础知识(一)

计算机硬件 1、处理器&#xff1a;CISC&#xff08;复杂指令集&#xff09;、RISC&#xff08;精简指令集&#xff09; 2、存储器&#xff1a;按照与处理器的物理距离&#xff1a;片上缓存、片外缓存、主存、外存 3、总线&#xff1a;按照总线在计算机中的位置划分&#xff1a…

大厂流出2023年最新软件测试面试题【全】

1.B/S架构和C/S架构区别 B/S 只需要有操作系统和浏览器就行&#xff0c;可以实现跨平台&#xff0c;客户端零维护&#xff0c;维护成本低&#xff0c;但是个性化能力低&#xff0c;响应速度较慢C/S响应速度快&#xff0c;安全性强&#xff0c;一般应用于局域网中&#xff0c;因…

格雷码转换电路

目录 格雷码转换电路 1、简介 1.2、格雷码转化为二进制码原理如下&#xff1a; 1.3、二进制码转化为格雷码原理如下&#xff1a; 2、实验任务 3、程序设计 3.1、格雷码转换二进制 3.2、二进制转换格雷码 4、仿真测试 5、仿真验证 格雷码转换电路 格雷码&#xff0c;…

推荐一款能够节省办公空间的显示器!

作为一名高校科研人员&#xff0c;课题组师生日常科研工作必备电子设备的维护及更新对于科研进度有着极大影响作用。近日购买了最新一代的戴尔显示器E2424HS。 以下是我的一些真实使用体验&#xff1a; 01 外观高端大气 拆箱前&#xff0c;在检查外包装没有任何破损后&#…