uni-app-优购-小程序

1.创建微信小程序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lDOMM8ah-1609254750458)(img/image-20201125095041347.png)]

微信小程序页面结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KgvT2SbA-1609254750471)(img/image-20201125102531691.png)]

js文件用于:控制逻辑代码,相当于vue的script

wxml 控制页面结构,就相当于vue里面的tempplate

wxss 控制样式,相当于vue的style

全局配置文件

app.json 用于控制全局配置

{
    
    
  "pages":[
    "pages/index/index",
    "pages/logs/logs",
    "pages/heima/index"
  ],
  "window":{
    
    
    "backgroundTextStyle":"dark",
    "backgroundColor": "#f0f",
    "navigationBarBackgroundColor": "#000",
    "navigationBarTitleText": "黑马优购",
    "navigationBarTextStyle":"white",
    "enablePullDownRefresh": true
  },
  "style": "v2",
  "sitemapLocation": "sitemap.json"
}

pages: 控制页面路由,接收一个数组,数组是页面路径,如果路径不存在,会自动创建

window:控制页面窗口信息。

background前缀的用于控制背景配置

navigationBar前缀控制导航栏

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bAfxVFhZ-1609254750474)(img/config.344358b1.jpg)]

Page局部配置

特点:当page配置的属性和全局app.json相同的时候,会使用局部配置覆盖全局配置

全局样式与局部样式

app.wxss 全局样式

每个文件下page.wxss 局部样式

全局样式可以给所有页面使用,局部样式优先级高于全局样式,如果出现相同属性,局部样式覆盖全局样式

数据绑定语法

<view>{
   
   {变量值}}</view>
<view>{
   
   { gender === '男' ? '精神小伙':  '表妹'}}</view>

循环语法

wx:for="{ {数组}}" 默认数据内容 item,默认索引index

改变内容变量 wx:for-item='变量名' 改变索引变量名 wx:for-index=“变量名”

wx:key="字符串" wx:key="*this"

  1. 字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
  2. 保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字。

block是一个空标签,可以用来写循环标签,或者判断标签都可以,他不会出现在页面上

<!-- block标签,这是一个空标签,页面不会显示 -->
<!-- wx:key接受的是一个字符串 -->
<!-- wx:key 传入*this表示循环item的本身 -->
<view>
  <block wx:for="{
     
     {hobby}}" wx:key="*this">
    {
   
   {item}}
  </block>
</view>

循环对象

<view wx:for="{
     
     {对象}}" wx:key="index">
	表示对象的属性值:{
   
   {item}}
    表示对象的key值:{
   
   {index}}
</view>

判断语法

wx:if="{ {条件}}"

<view wx:if="{
     
     {条件一}}"></view>
<view wx:elif="{
     
     {条件二}}"></view>
<view wx:else>其他条件</view>

hidden

<!-- wx:if和hidden -->
<!-- hidden的条件为真的时候,隐藏,否则显示 -->
<view hidden="{
     
     { gender === '' }}">隐藏的模块</view>

事件处理

关键字:bind事件名="函数名"

如果需要给函数传递参数,需要通过data-参数名进行传递

如果需要接受,需要在e.currentTarget.dataset

<!-- 事件绑定 -->
<view data-name="heima" data-id="100" bindtap="heimaTap">点击事件测试</view>

定义函数:

Page({
    
    
  heimaTap: function(e) {
    
    
    const id = e.currentTarget.dataset.id;
    const name = e.currentTarget.dataset.name;
    console.log(id, name)
  },
})

输入框绑定事件

第一种方法:通过输入事件获取输入内容

<!-- 监听输入框事件 -->
<input bindinput="userInput" type="text" style="border: 2px solid #ccc; margin: 10px 10px"/>
userInput(e) {
    
    
    // 用户输入的内容
    console.log(e.detail.value)
},

第二种方法:简易的双向绑定

<!-- 利用双向绑定实现数据获取 -->
<view>
  用户名:<input type="text" style="border: 2px solid #ccc; margin: 10px 10px" model:value="{
     
     {userName}}"/>
  <button bindtap="getUserName">获取用户名</button>
</view>
getUserName() {
    
    
    // 读取data下的数据
    console.log(this.data.userName)
},

WXSS 样式页

兼容css大多数特征,而且在css的基础上进行扩展

1、响应式单位 rpx 响应式布局单位

屏幕的宽度就是750rpx

2、样式导入

使用@import语句可以导入外联样式表,@import后跟需要导入的外联样式表的相对路径,用;表示语句结束。

示例代码:

/** common.wxss **/
.small-p {
  padding:5px;
}
/** app.wxss **/
@import "common.wxss";
.middle-p {
  padding:15px;
}

2.常用组件

图片组件 image

<image src="路径"></image>

mode:表示图片显示的方式

只要记住一个:widthFix 这个模式和html图片显示方式是一样的。

swiper组件

用于图片或者元素的轮播。

注意:swiper默认高度为150px

使用方法:

  <!-- swiper组件默认高度为150px -->
  <!-- circular 表示衔接滑动 -->
  <!-- autoplay 表示自动播放 -->
  <!-- interval 滑动间隔时间 -->
  <!-- indicator-dots 是否显示指示点 -->
  <!-- indicator-active-color 激活点的颜色 -->
<swiper>
	<swiper-item></swiper-item>
    <swiper-item></swiper-item>
    <swiper-item></swiper-item>
</swiper>

navigator 组件

作用:用于页面的跳转

open-type 用于控制跳转的方式,redirect 表示替换前一个页面,相当于vue-router的replace

<!-- 通过绝对路径跳转到其他页面 -->
<navigator url="/pages/index/index">跳转到别的页面</navigator>
<!-- 通过相对路径跳转 -->
<navigator url="../index/index">使用相对路径跳转到首页</navigator>
<!-- 设置其他open-type -->
<navigator url="/pages/index/index" open-type="redirect">通过redirect方式跳转</navigator>

另外,还可以通过编程式跳转

wx.navigateTo({
    
    
    url: '/pages/index/index',
})

rich-text

<!-- rich-text 富文本 -->
<!-- 如果服务端返回html片段,可以用该组件直接解析 -->
<rich-text nodes="<h1>测试一个大标题</h1>"></rich-text>

button

一般按钮可以设置open-type获取微信开放能力

<button open-type="contact">打开客服功能</button>

<button open-type="share">分享赚红包</button>

<!-- 获取用户手机 -->
<button bindgetphonenumber="getPhone" open-type="getPhoneNumber">获取手机号</button>

<!-- 获取用户信息 -->
<button bindgetuserinfo="getUserInfo" open-type="getUserInfo">获取用户信息</button>

单选框

<!-- 如果需要互斥,需要使用radio-group包裹起来 -->
<radio-group bindchange="getGender">
    <radio checked="{
     
     {true}}" value="man"></radio>
    <!-- 如果value值不存在,选中时会得到空字符串 -->
    <radio value="women"></radio>
</radio-group>

复选框

<!-- 复选框 -->
<view>
  <checkbox-group bindchange="getHobby">
    <checkbox value="game">王者</checkbox>
    <checkbox value="movie">抖音</checkbox>
    <checkbox value="eat">吃饭</checkbox>
  </checkbox-group>
</view>

配置tab栏

在全局配置文件app.json

注意:

pagePath 接收的路径前面不要斜杠

list 最少两个,最多五个

selectedColor 是放在tabBar对象下的

  "tabBar": {
    "selectedColor": "#E03440",
    "color": "#8C8D8D",
    "list": [{
      "pagePath": "pages/demo3/index",
      "text": "首页",
      "iconPath": "/icons/index.png",
      "selectedIconPath": "/icons/index_selected.png"
    },{
      "pagePath": "pages/demo2/index",
      "text": "分类",
      "iconPath": "/icons/category.png",
      "selectedIconPath": "/icons/category_selected.png"
    }]
  },

3.小程序自定义组件

1、通过ide右击新建组件,组件和页面也是一样,由四个文件组成,wxml wxss json js

2、json文件中必须配置"component": true,

页面引入小程序组件

在页面的配置文件声明

key值表示组件别名,value表示组件的路径

{
    
    
  "usingComponents": {
    
    
    "heima": "/components/heima"
  }
}

组件父传子

<!-- 使用组件 -->
<heima student="67" name="{
     
     {name}}"></heima>

子组件定义属性

  properties: {
    
    
    student: {
    
    
      type: Number, // 参数的类型
      value: 0, // 默认值
    },
    // 简写形式
    name: Number
  },

组件子传父

子组件触发事件的方法:

this.triggerEvent('事件名', 参数)

父组件监听事件

<heima bind事件名="函数名"></heima>

生命周期

应用生命周期,定义app.js

页面生命周期,定义在各自页面js

应用生命周期:

//app.js
App({
    
    
  // 小程序初始化的时候执行一次
  // 使用场景:获取用户地理位置,用户授权
  onLaunch() {
    
    
    console.log('小程序初始化成功')
  },
  onShow() {
    
    
    // 当应用隐藏到后台然后重新打开,会触发onShow
    // 使用场景:例如抢购页面,当用户重新打开应用,重新刷新数据
    console.log('小程序onShow被触发')
  },
  onHide() {
    
    
    console.log('小程序onHide被触发')
  },
  onError() {
    
    
    // 使用场景:网络异常,收集异常日志
    console.log('应用出现异常')
  }
})

页面生命周期:

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    
    
    // options 表示获取页面参数,option是一个对象
    // 使用场景:在这里发送网络请求
    console.log('页面onLoad', options)
  },
  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {
    
    
    // 场景:获取页面元素
    console.log('页面 onReady触发')
  },
  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {
    
    
    console.log('页面onShow')
  },
  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {
    
    
    console.log('页面onHide')
  },
  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {
    
    
    // 场景:定时器清除
    console.log('页面onUnload')
  },


组件生命周期

  lifetimes: {
    
    
    created() {
    
    
      console.log('组件创建完毕created')
    },
    attached() {
    
    
      console.log('在组件实例进入页面节点树时执行attached')
    },
    detached() {
    
    
      // 当页面被销毁的时候,组件也会随着销毁
      console.log('在组件实例被从页面节点树移除时执行')
    }
  },

判断组件或者API是否可用

wx.canIUse('console.log')
wx.canIUse('CameraContext.onCameraFrame')
wx.canIUse('CameraFrameListener.start')
wx.canIUse('Image.src')

网络请求API

如果提示下面的错误,是因为请求域名没有加入到信任域名

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kdrURoMt-1609254750477)(img/image-20201126174455204.png)]

方法一:「小程序后台-开发-开发设置-服务器域名」 中进行配置

方法二:如果你是用测试号,可以在开发工具设置忽略检查

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1ZiNy9Ed-1609254750479)(img/image-20201126174727624.png)]

网络请求使用方法:

wx.request({
	url: '请求地址',
	method: 'post', // 默认是get请求
	success: (res) => { // 成功回调
	}
})

把回调函数改造为Promise函数

回调的写法

wx.request({
    
    
    url: 'https://api-hmugo-web.itheima.net/api/public/v1/home/catitems',
    success: (res) => {
    
    
        if(res.data.meta.status === 200) {
    
    
            this.setData({
    
     navs: res.data.message });
        }
    }
})

Promise写法

const myPromise = new Promise((reslove, reject) => {
    
    
    wx.request({
    
    
        url: 'https://api-hmugo-web.itheima.net/api/public/v1/home/catitems',
        success: (res) => {
    
    
            reslove(res);
        }
    })
})
myPromise.then(res => {
    
    
    if(res.data.meta.status === 200) {
    
    
        this.setData({
    
     navs: res.data.message });
    }
})

Promise.all方法等待所有Promise处理完毕的方法

Promise.all([ promise对象,promise对象,promise对象]).then( () => {
    
     console.log('三个promise都执行完毕了') } )

小程序实现下拉刷新

1、在页面的配置文件开启下拉刷新

"enablePullDownRefresh": true,

2、监听用户下拉刷新

onPullDownRefresh() {
    
    
}

3、停止下拉刷新动画

wx.stopPullDownRefresh();

微信小程序模块化

支持:commonjs模块导入标准

导出关键字: module.exports

导入关键字: require(‘路径’)

ESModule

导入关键字: import 变量 from ‘路径’

导出关键字: export default { }

小程序网络层封装

const BASH_URL = 'https://api-hmugo-web.itheima.net/api/public/v1';

function get(url, data) {
    
    
  const myPromise = new Promise((resolve, reject) => {
    
    
    wx.request({
    
    
      url: BASH_URL + url,
      data,
      success: (res) => {
    
    
        resolve(res);
      }
    })
  })
  return myPromise;
}

function post() {
    
    
  const myPromise = new Promise((resolve, reject) => {
    
    
    wx.request({
    
    
      method: 'POST',
      url: BASH_URL + url,
      data,
      success: (res) => {
    
    
        resolve(res);
      }
    })
  })
  return myPromise;
}

module.exports = {
    
    
  get, post
}

4.uni-app框架快速开发

工作原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MsGvgF78-1609254750480)(img/image-20201129085629827.png)]

搭建uni-app项目开发方式:

1、使用DCloud公司提供的HBuilderX来快速开发

可视化的方式比较简单,HBuilderX内置相关环境,开箱即用,无需配置nodejs。官方IDE下载地址首次安装需要配置微信开发者工具的路径
在这里插入图片描述

2、使用脚手架来快速搭建和开发

  1. 全局的环境安装安装脚手架:

  2. npm i -g @vue/cli (之前安装过可跳过此步->2)

  3. 创建项目:

    vue create -p dcloudio/uni-preset-vue my-project
    //**my-project** (你自己创建项目的名字)
    

    启动(微信小程序):

    npm run dev:mp-weixin

uniapp运行到微信小程序(也可直接导入项目)

1、在微信开发者工具启动服务端口

点击 设置–》 安全–服务端口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2ZO4JIiB-1609254750483)(img/image-20201129093104324.png)]

2、运行到微信小程序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XcXHmDWc-1609254750484)(img/image-20201129095247566.png)]

uni-app目录结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rhsp21bT-1609254750485)(img/Image(49)]-1608651084128.png)[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MSTUpZK9-1609254750486)(img/1608651119936.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OznisBNa-1609254750487)(img/image-20201129100607186.png)]

uni-app 配置文件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MMa1pCGp-1609254750488)(img/image-20201129100706653.png)]

编写注意事项

body的元素选择器请改为page,同样,div和ul和li等改为view、span和font改为text、a改为navigator、img改为image…

1.uni-app图片资源

图片资源只能放在static文件夹,否则会没编译资源到微信项目

2.删除文件

需要重启小程序,注意去微信开发者检查是否已删除成功

商品列表页面

1.实现列表滚动 使用scroll-view组件

@scrolltolower 监听滚动条是否滚动到底部

lower-threshold 距底部/右边多远时(单位px),触发 scrolltolower 事件

注意!! scroll-view 必须设置高度,另外需要设置 scroll-y实现纵向滚动

2.实现页面分页

1、监听滚动触底事件

2、在data中定义一个变量记录当前页码,如果翻页的时候,对页码进行加一的操作

3、重新的获取数据,并且把页码参数提交给服务端

4、当数据返回是,页面上原有的数据要和服务端数据进行合并,而非替换

this.products = this.products.concat(res.data.message.goods)

3.图片懒加载

目标:为了节省用户访问页面时的流量和提升访问速度

为什么需要懒加载:因为页面可能是很长的,远远超出用户可视界面,如果用户还没访问到的地方,其实可以通过懒加载的形式,节省加载资源。

实现方法:lazy-load设置为 true即可

4.图片预览API

图片预览功能的作用:用于给用户放大缩小查看图片的细节。

uni.previewImage

接收一个urls参数,该参数不能为空

urls必须是一个数组,数组里放图片的链接。

uni.previewImage({
	urls: urls // urls必须是一个数组,数组里面放图片路径
})

5.自定义分享标题或者图片

// 通过该方法设置分享的标题,跳转地址,或者图片
onShareAppMessage() {
    
    
    return {
    
    
        // 分享标题
        title: '黑马56期黑马优购项目',
        // 用户打开点击分享内容是跳转的页面
        path: '/pages/index/index'
    }
},

购物车

1、修改商品数量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JBB6NKfZ-1609254750489)(img/image-20201203143748330.png)]

实现思路:

1、给按钮添加事件

2、事件中修改商品的amount属性

3、如果商品数量为0时,提示用户是否删除商品

4、如果用户选择是,直接把商品删除

// 减少数量
reduce(i) {
    
    
    // 获取当前修改的商品对象
    const product = this.cart[i];
    if (product.amount === 1) {
    
    
        // 提示是否删除商品
        uni.showModal({
    
    
            title: '提醒',
            content: '是否真的删除该商品',
            confirmText: '删除', // 修改确认按钮的文字
            success: (e) => {
    
    
                // e 会得到用户点击按钮的状态,如果cancel为true表示点击取消,confirm为true表示点击删除
                // 表示点击删除按钮
                if (e.confirm) {
    
    
                    // 把商品从购物车移除
                    this.cart.splice(i, 1);
                }
            }
        })
    } else {
    
    
        product.amount--;
    }
},

注意:购物车隐藏时保存购物车到本地存储

因为购物车修改数量的是this.cart对象,但是当页面进来的时候,会获取本地存储的购物车对象,会导致购物车被覆盖,所以当页面隐藏的时候,需要先把购物车存到本地存储。

onHide() {
    
    
    // 当页面隐藏的时候,把购物车写入到本地缓存
    uni.setStorage({
    
    
        key: 'cart',
        data: this.cart
    })
},

2、实现购物车商品选择按钮

实现思路:

1、通过购物车商品对象的checked属性判断商品是否选中,显示不同的勾选状态

2、实现点击切换选中状态:给按钮绑定点击事件,点击时,切换checked属性值

// 切换商品选中状态
toggleChecked(i) {
    
    
    console.log('toggleChecked', i)
    this.cart[i].checked = !this.cart[i].checked;
    console.log(this.cart[i].checked)
}

注意:因为给商品对象增加了checked属性,新增的属性是不具备响应性(数据的变更会触发页面的重新渲染)特性,所以我们可以通过在商品详情页加入商品的时候,默认给他checked属性。

第二种解决方案

// 切换商品选中状态
toggleChecked(i) {
    
    
    this.cart[i].checked = !this.cart[i].checked;
    // 第二种解决方案
    this.cart.splice(i, 1, this.cart[i])
}

3、实现购物车全选

实现思路:

1、使用计算属性监听购物车商品选择状态

computed: {
    
    
    // 全选状态
    allChecked() {
    
    
        let isAllCheck = true
        // 遍历购物车,判断商品是否全选或者非全选
        this.cart.forEach(item => {
    
    
            if (!item.checked) {
    
    
                isAllCheck = false;
            }
        })
        return isAllCheck;
    }
}

2、点击购物车全选按钮切换单个商品的选择状态

​ 实现思路:1、给全选按钮绑定点击事件

​ 2、当点击的时候,把购物车商品checked属性切换状态

// 切换全选状态
toggleAllChecked() {
    
    
    const cart = this.cart.map((item, index) => {
    
    
        // item.checked = !this.allChecked;
        return {
    
     ...item , checked: !this.allChecked}
    })
    this.cart = cart;
}

4、实现总价的计算

totalPrice() {
    
    
    let totalPrice = 0;
    this.cart.length && this.cart.forEach(item => {
    
    
        if (item.checked) {
    
    
            totalPrice += item.amount * item.goods_price
        }
    })
    return totalPrice;
}

5、总数量的计算

totalCount() {
    
    
    let total = 0;
    this.cart.length && this.cart.forEach(item => {
    
    
        if (item.checked) {
    
    
            total += item.amount;
        }
    })
    return total;
}

订单确认页

1、显示已选商品列表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hqq5SOpK-1609254750491)(img/image-20201203172918718.png)]

实现思路:

1、在onLoad生命周期获取本地存储的购物车数据

2、把数据循环并传入给Product组件

响应性原理

响应性和非响应性数据的区别

响应性数据:当数据发生变更,页面会自动渲染

非响应性数据:当数据发生变更,页面不会自动渲染

为什么会出现非响应性数据?

因为Vue实现响应性使用Object.defineProperty(),但是该接口不支持对象和数组的监听。

解决访问

1、初始化的时候给初始值

2、调用Vue.set方法

3、替换掉原来对象

数组解决方案:

// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SZ1b2whp-1609254750492)(img/image-20201205143900391.png)]

async chooseAddress() {
    
    
    // 调用微信地址本
    const [error, res] = await uni.chooseAddress()
    if (!error) {
    
    
        this.isShowAddress = true;
        // 省+市+区+详细收货地址
        this.address = res.provinceName + res.cityName + res.countyName + res.detailInfo;
        this.userName = res.userName;
        this.telNumber = res.telNumber;
    }
}

2、处理隐藏电话号码

思路:使用过滤器实现

filters: {
    
    
    // 隐藏联系方式敏感信息,18661234567 =》 1866****567
    // value 过滤器前面的值
    hidePhone(value) {
    
    
        // 数据处理
        // 第一步,截取字符串的前四位
        const start = value.substr(0, 4)
        // 第二步,从字符串末尾截取三位
        const end = value.substr(-3)
        return start + '****' + end;
    }
}

3、点击去支付按钮

1、当用户没有选择地址的时候,提示用户选择地址

uni.showToast

2、当选择地址,没有登录,跳转到登录页

3、当选择地址并且登录,发起支付

4、授权登录流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mYepqmxQ-1609254750493)(img/image-20201205160204975.png)]

用户登录完整代码

async userInfoCallBack(e){
    
    
    console.log(e)
    // 记录用户信息到本地缓存
    uni.setStorage({
    
    
        key: 'userInfo',
        data: e.detail.userInfo
    })
    // 获取加密数据
    const {
    
     encryptedData, iv, rawData, signature } = e.detail;
    // 获取code属性,通过登录接口获取
    const [error, res] = await uni.login()
    const {
    
     code } = res;
    // 发送网络请求登录
    const [LoginError, loginRes] = await uni.request({
    
    
        url: 'https://api-hmugo-web.itheima.net/api/public/v1/users/wxlogin',
        method: 'POST',
        data: {
    
    
            encryptedData, iv, rawData, signature, code
        }
    })
    if (loginRes.data.meta.status === 200) {
    
    
        // 登录成功,把token存储到本地
        uni.setStorageSync('token', loginRes.data.message.token)
        // 提示用户登录成功,并且返回上一页
        uni.showToast({
    
    
            icon: 'none',
            title: '登录成功'
        })
        uni.navigateBack()
    } else {
    
    
        uni.showToast({
    
    
            icon: 'none',
            title: '服务器异常,请重新点击登录'
        })
    }

}

5、支付功能

思路:

1、判断用户是否已经选择地址,或者是否已经登录,处理三种不同状态

2、创建订单

3、创建预支付订单

4、调用支付api发起支付请求

5、支付完毕,跳转到订单页

// 支付按钮事件
async pay() {
    
    
    const token = uni.getStorageSync('token');
    // 第一种情况,没有选择地址
    if (!this.isShowAddress) {
    
    
        uni.showToast({
    
    
            title: '请选择收货地址',
            icon: 'none'
        })
        // 当选择地址,没有登录,跳转到登录
        // 判断token是否存在
    } else if (!token) {
    
    
        uni.navigateTo({
    
    
            url: '/pages/login/index'
        })
    } else {
    
    
        // 生成订单
        // 准备提交给服务端的商品数据
        const requestProduct = this.cart.map(item => {
    
    
            return {
    
     goods_id: item.goods_id, goods_number: item.amount, goods_price: item.goods_price }
        })
        const [orderError, orderRes] = await uni.request({
    
    
            url: 'https://api-hmugo-web.itheima.net/api/public/v1/my/orders/create',
            method: 'POST',
            header: {
    
    
                Authorization: token
            },
            data: {
    
    
                order_price: this.totalPrice,
                consignee_addr: this.address,
                goods: requestProduct
            }
        })
        if (orderRes.data.meta.status === 200) {
    
    
            // 发起支付
            const [payError, payRes] = await uni.request({
    
    
                url: 'https://api-hmugo-web.itheima.net/api/public/v1/my/orders/req_unifiedorder',
                method: 'POST',
                header: {
    
    
                    Authorization: token
                },
                data: {
    
    
                    order_number: orderRes.data.message.order_number
                }
            })
            const [requestPaymentError, requestPaymentRes] = await uni.requestPayment(payRes.data.message.pay);
            // 支付成功
            if (!requestPaymentError) {
    
    
                uni.showToast({
    
    
                    icon: 'none',
                    title: '支付成功'
                })
                uni.redirectTo({
    
    
                    url: '/pages/order/index'
                })
            }
        }

    }
}

6、购物车存储方案差异

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bb72mpjv-1609254750494)(img/image-20201205172351245.png)]

7、删除已结算购物车商品

// 清除已经结算的商品
async clearPaymentProduct() {
    
    
    // 删除已经结算的商品
    // 结算的商品列表 this.cart
    // 本地缓存的购物车 uni.getStorage('cart')
    // 把本地缓存的购物车删除已经结算的商品
    const [err, originCart] = await uni.getStorage({
    
    
        key: 'cart'
    })
    const notPayCart = originCart.data.filter(item => {
    
    
        return !item.checked;
    })
    uni.setStorage({
    
    
        key: 'cart',
        data: notPayCart
    })
}

会员中心

1、显示会员头像和昵称

1.1 通过token判断用户是否已经登录

1.2 如果token存在,就认为已经登录,读取本地缓存的userInfo,显示相应的数据

2、当会员未登录,显示登录按钮

			checkLogin() {
    
    
				// 通过Token判断是否已经登录
				const token = uni.getStorageSync('token');
				if (token) {
    
    
					// 已经登录
					const userInfo = uni.getStorageSync('userInfo');
					console.log(userInfo)
					this.nickName = userInfo.nickName;
					this.avatarUrl = userInfo.avatarUrl;
					this.isLogin = true;
				} else {
    
    
					// 未登录
					this.isLogin = false;
				}
			}

3、订单导航按钮

			// 跳转到订单列表
			goToOrderList(orderType) {
    
    
				uni.navigateTo({
    
    
					url: '/pages/order/index?orderType=' + orderType
				})
			}

4、拨打电话

callPhone() {
    
    
    uni.makePhoneCall({
    
    
        phoneNumber: '400-666-888'
    })
}

5、平台差异处理

条件编译是用特殊的注释作为标记,在编译时根据这些特殊的注释,将注释里面的代码编译到不同平台。

**写法:**以 #ifdef 或 #ifndef 加 %PLATFORM% 开头,以 #endif 结尾。

  • #ifdef:if defined 仅在某平台存在
  • #ifndef:if not defined 除了某平台均存在
  • %PLATFORM%:平台名称

例子:如果只希望在微信小程序平台显示

<!-- #ifdef MP-WEIXIN -->
<view class="info">
    <view class="iconfont icon-banben"></view>
    <view class="name">当前版本</view>
    <view class="value">v4.1.1</view>
</view>
<!-- #endif -->

通过运行时判断

判断是否为iphoneX

// 获取平台的信息
const sysInfo = uni.getSystemInfoSync()
this.isIphoneX = sysInfo.model === 'iPhone X';

订单日期处理

		// 使用filter实现日期的转换
		filters: {
    
    
			timestampToDisplay(timestamp) {
    
    
				// 把timestamp转换为以毫秒为单位的格式
				const date = new Date(timestamp * 1000)
				// 获取年
				const year = date.getFullYear();
				// 获取月
				const month = date.getMonth() + 1;
				// 获取日
				const day = date.getDate();
				
				// 时
				const hour = date.getHours();
				const min = date.getMinutes();
				const sec = date.getSeconds();
				
				return `${
      
      year}/${
      
      month}/${
      
      day} ${
      
      hour}:${
      
      min}:${
      
      sec}`
			}
		}

使用第三方模块

1、初始化项目

npm init -y

2、安装第三方模块

npm install dayjs

3、使用第三方模块

import dayjs from 'dayjs'

反馈页面

1、问题列表

​ 思路:定义一个数组,然后通过循环显示到页面上

2、选中状态的切换

思路:

​ 2.1 在data定义一个选中的数组

​ 2.2 定义一个方法,当点击选项,如果存在,就移除数据,否则就加入数组

​ 2.3 页面上,通过三元运算,显示active状态

select(e) {
    
    
    // 如果已经存在selectedQuestion,要去掉,否则加入
    const index = this.selectedQuestion.findIndex(item => {
    
    
        return item === e;
    })
    if (index > -1) {
    
    
        // 找到
        this.selectedQuestion.splice(index, 1);
    } else {
    
    
        // 没找到
        this.selectedQuestion.push(e);
    }
}

3、选择图片

async chooseImage() {
    
    
    // 判断是否超出选择的限制
    if (this.uploadImags.length >= 4) {
    
    
        // 已经不能再选
        uni.showToast({
    
    
            icon: 'none',
            title: '已经超出选择限制'
        })
    } else {
    
    
        // 判断用户应选择了多少张
        // 剩余还能选多少张
        const canChoose = 4 - this.uploadImags.length;
        const [error, res] = await uni.chooseImage({
    
    count: canChoose})
        this.uploadImags = this.uploadImags.concat(res.tempFilePaths);
    }

}

发布应用

线上版本:需要微信审核,审核完毕后,所有普通用户都可以使用

开发版本:开发者通过微信开发者工具,点击上传之后的版本,这个版本只有开发者权限的人员查看

体验版本:给只有体验权限的人员使用,一般给产品经理或者测试人员使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A1QDV1hJ-1609254750495)(img/image-20201206172750018.png)]

包限制

为什么微信需要限制包的大小?

当第一次访问的时候,如果包太大,会导致加载速度变慢

主包放资源策略?

主包限制是2M

非必要的(用户第一次加载的时候没用到的代码、图片资源)资源不要放在代码里

必要的资源就放在主包

分包策略

微信允许我们把代码分到不同包,分为主包分包

主包的加载时机:当用户扫描小程序二维码就会加载

分包加载时机:当用户使用到分包的内容时才会加载

把什么页面放到分包?

把使用率较低的页面放到分包

怎么实现分包?

1、创建分包目录,然后把分包页面放到分包目录下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U9BtHxyo-1609254750496)(img/image-20201206175826695.png)]

	"subPackages": [{
    
    
	    "root": "pageA",
	    "pages": [{
    
    
	        "path": "feedback/index"
	    }]
	}],

root 表示分包根目录

pages:页面数组

​ path:相对路径

分包注意事项:

原来跳转页面的路径需要更改为分包的路径

微信总包的限制?

目前小程序分包大小有以下限制:

  • 整个小程序所有分包大小不超过 16M
  • 单个分包/主包大小不能超过 2M

猜你喜欢

转载自blog.csdn.net/weixin_48371382/article/details/111938953