文章目录
1.小程序的第三方框架
- 腾讯 wepy 类似于vue
- 美团 mpvue 类似于vue
- 京东 tao 类似于react
- 滴滴 chameleon
- uni-app 类似于vue
- 原生框架MINA
2. 帮助文件
接口文档地址:https://www.showdoc.com.cn/128719739414963/2513235043485226
 阿里巴巴字体iconfont:https://www.iconfont.cn/
 即本项目的后台已经写好了,直接调用接口即可
3.项目的搭建
3.1新建小程序项目
填入自己的openID
3.2搭建目录结构
录名
作
styles
存放公共样式
components
存放组件
lib
存放第三库
utils
的帮助库
request
的接帮助库

3.3. 搭建项目的页面
名称
名称
index
分类
category
商品列表
goods_list
商品详情
goods_detail
购物
cart
收藏
collect
订单
order
搜索
search
个中
user
意反馈
feedback
登录
login
授权
auth
结算
pay
3.4. 引字体图标
1. 打开阿巴巴字体图标 站
 https://www.iconfont.cn/
 2. 选择的图标
 选择自己想要的图标添加入库
 
3. 添加项
 选择购物车图标
 
 点击添加至项目(我选择了三个购物车的图标)
 
 可以将其添加到一个已有的项目或者新建一个项目
 
 本次项目所需要的图标
 
 点击Font class生成代码
 
 会生成一个class地址
 
 4. 点击链接,复制样式,将其放入styles目录下的iconfont.wxss中
 
@font-face {
    font-family: "iconfont"; /* Project id 3078102 */
    src: url('//at.alicdn.com/t/font_3078102_um8ol0i9ny.woff2?t=1640609932945') format('woff2'),
         url('//at.alicdn.com/t/font_3078102_um8ol0i9ny.woff?t=1640609932945') format('woff'),
         url('//at.alicdn.com/t/font_3078102_um8ol0i9ny.ttf?t=1640609932945') format('truetype');
  }
  
  .iconfont {
    font-family: "iconfont" !important;
    font-size: 16px;
    font-style: normal;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
  }
  
  .icon-weibiaoti2fuzhi08:before {
    content: "e625";
  }
  
  .icon-shoucangxuanzhong:before {
    content: "e62b";
  }
  
  .icon-fenxiang:before {
    content: "e86e";
  }
  
  .icon-kefu:before {
    content: "e88f";
  }
  
  .icon-dingdan:before {
    content: "e645";
  }
  
  .icon-fukuantongzhi:before {
    content: "e60c";
  }
  
  .icon-tuihuotuikuan_dianpu:before {
    content: "e773";
  }
  
  .icon-shoucang:before {
    content: "e8b9";
  }
  
  .icon-gouwucheman:before {
    content: "e600";
  }
  
  .icon-gouwuchekong:before {
    content: "e601";
  }
  
  .icon-gouwucheman1:before {
    content: "e602";
  }
5. 在app.wxss中导入要使用的样式
@import "./styles/iconfont.wxss"
3.5 创建tabbar页面
1. 在项目中创建icon文件夹,并将素材中的相关图标拷贝进入
 
 2. 常见tabbar相关页面和样式
 app.json中
{
  "pages":[
    "pages/index/index",
    "pages/category/index",
    "pages/goods_list/index",
    "pages/goods_detail/index",
    "pages/cart/index",
    "pages/collect/index",
    "pages/order/index",
    "pages/search/index",
    "pages/user/index",
    "pages/feedback/index",
    "pages/login/index",
    "pages/auth/index",
    "pages/pay/index"
  ],
  "tabBar": {
    "color": "#999",
    "selectedColor": "#ff2d4a",
    "backgroundColor": "#fafafa",
    "position": "bottom",
    "borderStyle": "black",
    "list": [
      {
        "pagePath": "pages/index/index",
        "text": "首页",
        "iconPath": "icons/home.png",
        "selectedIconPath": "icons/home-o.png"
      },
      {
        "pagePath": "pages/category/index",
        "text": "分类",
        "iconPath": "icons/category.png",
        "selectedIconPath": "icons/category-o.png"
      },
      {
        "pagePath": "pages/cart/index",
        "text": "购物车",
        "iconPath": "icons/cart.png",
        "selectedIconPath": "icons/cart-o.png"
      },
      {
        "pagePath": "pages/user/index",
        "text": "我的",
        "iconPath": "icons/my.png",
        "selectedIconPath": "icons/my-o.png"
      }
    ]
  },
  
    
  "window":{
    "backgroundTextStyle":"light",
    "navigationBarBackgroundColor": "#eb4450",
    "navigationBarTitleText": "黑马优购",
    "navigationBarTextStyle":"white"
  },
  "style": "v2",
  "sitemapLocation": "sitemap.json"
}
2.初始化页面
 在app.wxss中
@import "./styles/iconfont.wxss";
/**在微信小程序中 不支持通配符* **/
page,view,text,swiper-item,image,navigator{
    padding: 0;
    margin: 0;
    box-sizing: border-box;
}
/**
 主题颜色 通过变量来实现
 less中存在变量这个知识点
 原生的css和wxss也支持变量
**/
page{
    /**定义主题颜色**/
    --themeColor:#eb4450;
    /**
    统一字体大小
    1px = 2rpx
    **/
    font-size: 20rpx;
}
==
4.1. 效果

4.2 使定义组件的式实现头部搜索框
1. 在components下面创建一个SearchInput组件,实现搜索的功能
 
 2. 在SearchInput.wxml创建搜索导航
<view class="search_input">
    <navigator url="/pages/search/index" open-type="navigate">
        搜索
    </navigator>
</view>
3. SearchInput.wxss中的样式
/* components/SearchInput/SearchInput.wxss */
.search_input {
  height: 90rpx;
  padding: 10rpx;
  background-color: var(--themeColor);
}
.search_input navigator {
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: white;
  border-radius: 15rpx;
  color: #666;
}
4.引入所使用的组件
 在pages/index/index.json中引入所使用的组件
 
{
  "usingComponents": {
    "SearchInput":"../../components/SearchInput/SearchInput"
  },
  "navigationBarTitleText": "优购首页"
}
5.使用组件
 在pages/index/index.wxml
<view class="pyg_index">
    <!--搜索框开始-->
    <SearchInput></SearchInput>
    <!--搜索框结束-->
</view>  
4.3 首页轮播图
1. 网络请求获取轮播图的数据
 在pages/index/index.js下
Page({
  /**
   * 页面的初始数据
   */
  data: {
    //轮播图数组
    swipperList:[]
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    //异步请求获取轮播图的数据
    wx.request({
      url: 'https://api-hmugo-web.itheima.net/api/public/v1/home/swiperdata',
      success:(result)=>{
        this.setData({
          swipperList:result.data.message
          })
      }
    });
    
  }
  })
接口调用结果显示
 
 没有结果显示解决办法
-  没有添加appid,点击详情按钮,选择不校验合法域名 
  
-  为了后面的开发管理和部署上线,选择开发管理中的服务设置,设置服务器的域名 
  
 2. 轮播图动态渲染
 在pages/index/index/wxml中<!--轮播图开始--> <view class="index_swiper"> <!-- 1.swiper标签存在默认高度和宽度 100%*150px 2.image标签也存在默认宽度和高度 320px*240px 3.设计轮播图 先看原图的高度 750*340 让图片高度自适应 宽度等于100% 让swiper的高度和图片高度一样即可 4.图片标签 mode属性渲染模式 widthfix 让标签的宽高和标签内容的宽高发生等比例的变化 --> <swiper autoplay indicator-dots circular> <swiper-item wx:for="{{swipperList}}" wx:key="goods_id"> <navigator> <image mode="widthfix" src="{{item.image_src}}"></image> </navigator> </swiper-item> </swiper> </view> <!--轮播图结束-->
相应的wxss
.index_swiper swiper {
  width: 750rpx;
  height: 340rpx;
}
.index_swiper swiper image {
  width: 100%;
}
3. 将原生的请求改为promise方式
-  在request的目录下创建index.js export const request=(params)=>{ return new Promise((resolve,reject)=>{ wx.request({ ...params, success:(result)=>{ resolve(result); }, fail: (err)=>{ reject(err) } }); } )} 
-  在pages/index/index.js中加入request.js的路径 import {request} from “…/…/request/index.js” 
-  请求路径的改写 onLoad: function (options) { 
 //异步请求获取轮播图的数据
 // wx.request({
 // url: ‘https://api-hmugo-web.itheima.net/api/public/v1/home/swiperdata’,
 // success:(result)=>{
 // this.setData({
 // swipperList:result.data.message
 // })
 // }// }); request({url:'https://api-hmugo-web.itheima.net/api/public/v1/home/swiperdata'}) .then(result=>{ this.setData({ swipperList:result.data.message }) })}, 
4.4 首页分类导航模块
1. 请求接口数据
 在pages/index/index.js中获取导航数据
  //获取导航数据
  getCatesList(){
    request({url:'https://api-hmugo-web.itheima.net/api/public/v1/home/catitems'})
    .then(result=>{
      this.setData({
        catesList:result.data.message
      })
    })
  }
2. 页面进行导航数据的显示
 在pages/index/indedx.wxml中进行导航数据的显示
    <!--导航开始-->
    <view class="index_cate">
        <navigator 
        wx:for="{{catesList}}"
        wx:key="name"
        >
        <image  mode="widthFix" src="{{item.image_src}}">
            
        </image>
        </navigator>
    </view>
      
    <!--导航结束-->
3. css样式
.index_cate {
  display: flex;
  padding: 20rpx;
}
.index_cate navigator {
  flex: 1;
}
.index_cate navigator image {
  width: 100%;
}
4.5 首页楼层模块
1. 获取接口数据
 pages/index/index.js
  //获取楼层的接口数据
  getFloorList(){
    request({url:'https://api-hmugo-web.itheima.net/api/public/v1/home/floordata'})
    .then(result=>{
      this.setData({
        floorList:result.data.message
      })
    })
  }
2. 内容显示
 pages/index/index.wxml
  <!--楼层开始-->
    <view class="index_floor">
    <view >
        <view wx:for="{{floorList}}"
        wx:for-item="item1"
        wx:for-index="index1"
        wx:key="floor_title"
        class="floor_group"
        >
        <!--标题-->
        <view class="floor_title" >
            <image src="{{item1.floor_title.image_src}}" mode="widthFix" ></image>
        </view>
        <!--内容-->
        <view class="floor_list">
        <navigator 
        wx:for="{{item1.product_list}}"
        wx:for-item="item2"
        wx:for-index="index2"
        wx:key="name">
            <image src="{{item2.image_src}}" mode="{{index2===0?'widthFix':'scaleToFill'}}"/>
              
        </navigator>
          
        </view>
        </view>
    </view>
    </view>
      
    <!--楼层结束-->
3. 导入样式
 pages/index/index.wxss
.index_floor .floor_group .floor_title {
  padding: 10rpx 0;
}
.index_floor .floor_group .floor_title image {
  width: 100%;
}
.index_floor .floor_group .floor_list {
  overflow: hidden;
}
.index_floor .floor_group .floor_list navigator {
  float: left;
  width: 33.3%;
  /*后面四个的超链接*/
  /*2.3超链接*/
}
.index_floor .floor_group .floor_list navigator:nth-last-child(-n+4) {
  /**
                原图 232*386
                232/286 = 33.3vw/height
                **/
  height: 27.72711207vw;
  border-left: 10rpx solid #fff;
}
.index_floor .floor_group .floor_list navigator:nth-child(2),
.index_floor .floor_group .floor_list navigator:nth-child(3) {
  border-bottom: 10rpx solid #fff;
}
.index_floor .floor_group .floor_list navigator image {
  width: 100%;
  height: 100%;
}
首页效果
 
5. 分类页面
5.1 请求相关的数据
在pages/category/index.js
// pages/category/index.js
import {request} from "../../request/index.js";
Page({
  /**
   * 页面的初始数据
   */
  data: {
      //左侧的菜单数据
      leftMenuList:[],
      //右侧的商品数据
      rightContent:[],
      //被点击的左侧菜单
      currentIndex:0
  },
      //接口的返回数据
      Cates:[],
  onLoad:function(options){
      this.getCates()
  },
  //获取分类菜单
  getCates(){
    request({
      url:"https://api-hmugo-web.itheima.net/api/public/v1/categories"
    })
    .then(result=>{
      this.Cates=result.data.message
      //构造左侧的大菜单数据
      let leftMenuList=this.Cates.map(v=>v.cat_name)
      //构造右侧的商品数据
      let rightContent=this.Cates[0].children;
      this.setData({
        leftMenuList,
        rightContent
      })
    })
  },
  //左侧菜单的点击事件
  handleItemTap(e)
  {
    /**
     * 1.获取别点击的索引
     * 2.getdata中currentIndex赋值
     * 3.根据不同的索引渲染右侧的菜单
     */
   const {index}=e.currentTarget.dataset;
   let rightContent=this.Cates[index].children;
   this.setData({
    currentIndex:index,
    rightContent
   })
  }
})
5.2 页面的编写
在pages/category/index.wxml
<view class="cates">
    <SearchInput></SearchInput>
    <view class="cates_container">
        <!--左侧菜单-->
        <scroll-view class="left_menu" scroll-y>
          <view class="menu_item {{index===currentIndex?'active':''}}"
          wx:for="{{leftMenuList}}"
          wx:key="this"
          bindtap="handleItemTap"
          data-index="{{index}}"
          >
              {{item}}
          </view>
            
        </scroll-view>
        <!--右侧商品内容-->
        <scroll-view class="right_content" scroll-y>
            <view class="goods_group"
            wx:for="{{rightContent}}"
            wx:for-index="index1"
            wx:for-item="item1"
            >
             <view class="goods_title">
                 <text class="delimiter"></text>
                 <text class="title">{{item1.cat_name}}</text>
                 <text class="delimiter"></text>
             </view>
             <view class="goods_list">
                 <navigator 
                 wx:for="{{item1.children}}"
                 wx:for-index="index2"
                 wx:for-item="item2"
                 wx:key="cat_id"
                 >
                  <image src="{{item2.cat_icon}}" mode="widthFix"></image>
                  <view class="goods_name">{{item2.cat_name}}</view>
                 </navigator>
             </view>
            </view>
        </scroll-view>
    </view>
      
</view>
5.3 相关的样式
在pages/category/index.wxss
page {
  height: 100%;
}
.cates {
  height: 100%;
}
.cates .cates_container {
  display: flex;
  height: cac(100vh - 90rpx);
}
.cates .cates_container .left_menu {
  flex: 2;
}
.cates .cates_container .left_menu .menu_item {
  height: 80rpx;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 30rpx;
}
.cates .cates_container .left_menu .active {
  color: var(--themeColor);
  border-left: 5rpx solid currentColor;
}
.cates .cates_container .right_content {
  flex: 5;
}
.cates .cates_container .right_content .goods_group .goods_title {
  height: 80rpx;
  display: flex;
  justify-content: center;
  align-items: center;
}
.cates .cates_container .right_content .goods_group .goods_title .delimiter {
  color: #ccc;
  padding: 0 10rpx;
}
.cates .cates_container .right_content .goods_group .goods_list {
  display: flex;
  flex-wrap: wrap;
}
.cates .cates_container .right_content .goods_group .goods_list navigator {
  width: 33.33%;
  text-align: center;
}
.cates .cates_container .right_content .goods_group .goods_list navigator image {
  width: 50%;
}
5.3 添加缓存
先判断本地是否有没有过期的旧数据,如果有就使用旧数据,没有就重新从接口中获取
 在pages/category/index.js中修改的部分代码
onLoad:function(options){
    /**
     * 1. 先判断本地存储中有没有旧数据
     * {tine:Date.now,data[...]}
     * 2. 没有旧数据直接发送新请求
     * 有旧数据同时旧数据也没有过期 就是要本地存储中的旧数据即可
     */
    //1.获取本地存储中的数据(小程序中也是本地存储)
    const Cates=wx.getStorage("cates");
    this.getCates()
    //2.判断
    if(!Cates)
    {
      //不存在 发送请求获取数据
      this.getCates();
    }
    else
    {
      // //有旧的数据 定义过期时间 
        if(Date.now()-Cates.time>1000*10)
        {
          //重新发送数据
          this.getCates()
        }
        else{
          //可以使用旧数据
          this.Cates=Cates.data;
            //构造左侧的大菜单数据
      let leftMenuList=this.Cates.map(v=>v.cat_name)
      //构造右侧的商品数据
      let rightContent=this.Cates[0].children;
      this.setData({
        leftMenuList,
        rightContent
      })
        }
    }
  },
  //获取分类菜单
  getCates(){
    request({
      url:"https://api-hmugo-web.itheima.net/api/public/v1/categories"
    })
    .then(result=>{
      this.Cates=result.data.message
      //把接口中的数据存入本地存储中
    wx.setStorageSync("cates", {time:Date.now(),data:this.Cates});
      //构造左侧的大菜单数据
      let leftMenuList=this.Cates.map(v=>v.cat_name)
      //构造右侧的商品数据
      let rightContent=this.Cates[0].children;
      this.setData({
        leftMenuList,
        rightContent
      })
    })
  },
5.4 点击菜单左侧列表置顶
在scrocll-view中有设置滚动条的位置
 在页面中添加属性scroll-top并绑定值
 
 在data中添加scrollTop
 
 修改点击事件,点击一次距离顶部的距离为0
  //左侧菜单的点击事件
  handleItemTap(e)
  {
    /**
     * 1.获取别点击的索引
     * 2.getdata中currentIndex赋值
     * 3.根据不同的索引渲染右侧的菜单
     */
   const {index}=e.currentTarget.dataset;
   let rightContent=this.Cates[index].children;
   this.setData({
    currentIndex:index,
    //距离顶部的位置
    scrollTop:0,
    rightContent
   })
  }
5.5 提取公共接口路径

 发现这些接口有一些共同的路径,我们将其提取处理
 在request/index.js下提取公共路径
 
 然后在其他的请求接口中删除https://api-hmugo-web.itheima.net/api/public/v1/即可
6.商品列表
6.1 定义tab组件

 1. tab/tab.wxml
<view class="tab">
    <view class="tab_title">
        <view class="title_item {{item.isActive?'active':''}}"
        wx:for="{{tab}}"
        wx:key="id" 
        bindtap="handleItemTap"
        data-index="{{index}}"
        >
            {{item.value}}
        </view>
    </view>
    <view class="tab_content">
        <slot></slot>
    </view>
</view>
2. tab/tab.wxss
.tab{}
.tab_title{
    display: flex;
}
.title_item{
    display: flex;
    justify-content: center;
    align-items: center;
    flex: 1;
    padding: 15rpx 0;
}
.active{
    color: var(--themeColor);
    border-bottom: 5rpx solid currentColor;
}
.tab_content{}
3. tab/tab.js
// components/tab/tab.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
      tab:{
        type:Array,
        value:[]
      }
  },
  /**
   * 组件的初始数据
   */
  data: {
  },
  /**
   * 组件的方法列表
   */
  methods: {
    handleItemTap(e)
    {
      //获取点击的索引
      const {index}=e.currentTarget.dataset;
     //触发父组件中的事件  自定义
     this.triggerEvent("tabItemChange",{index})
    }
  }
})
6.2 商品列表中使用组件
1. pages/gods_list/index.wxml
    <!--搜索框开始-->
    <SearchInput></SearchInput>
    <!--搜索框结束-->
    <tab tab="{{tab}}" bindtabItemChange="handleTabItemChange">
        <block wx:if="{{tab[0].isActive}}">1</block>
        <block wx:elif="{{tab[1].isActive}}">2</block>
        <block wx:elif="{{tab[2].isActive}}">3</block>
    </tab>
2. pages/gods_list/index.json
{
  "usingComponents": {
    "SearchInput":"../../components/SearchInput/SearchInput",
    "tab":"../../components/tab/tab"
  },
  "navigationBarTitleText": "商品列表"
}
3. pages/gods_list/index.js
// pages/goods_list/index.js
Page({
  /**
   * 页面的初始数据
   */
  data: {
    tab:[
      {
        id:0,
        value:'综合',
        isActive:true
      },
      {
        id:1,
        value:'销量',
        isActive:false
      },
      {
        id:0,
        value:'价格',
        isActive:false
      }
    ]
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    console.log(options)
  },
  //标题的点击事件 从子组件中传递过来的
  handleTabItemChange(e)
  {
    //获取被点击标题发索引
    const {index}=e.detail
    //修改原数组
    let {tab}=this.data
    tab.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
    //赋值到data中
    this.setData({
      tab
    })
  }
})
6.3 商品列表的实现
需求分析
-  用户上滑页面 滚动条触底 开始加载下一页数据 - 找到滚动条触底事件
- 判断还有没有下一页数据 
    - 获取总页数 总页数=math.ceil(总条数/页容量)
- 获取当前页的页码
- 判断当前;页面是否大于总页数
 
- 假如没有下一页数据会弹出一个提示
- 假如有下一页数据,加载下一页数据
 当前的页码++
 重新发送请求
 数据请求回来 要对data中的数据进行拼接而不是全部替换
 
-  下拉刷新页面 - 触发下拉刷新事件 需要在页面的Json文件中开始一个配置项
- 重置数据 数组
- 重置页码 设置为1
- 重新发送请求
- 数据请求回来了 需要手动关闭 等待效果
 
1. pages/goods_list/index.js
import {request} from "../../request/index.js";
Page({
  /**
   * 页面的初始数据
   */
  data: {
    tab:[
      {
        id:0,
        value:'综合',
        isActive:true
      },
      {
        id:1,
        value:'销量',
        isActive:false
      },
      {
        id:0,
        value:'价格',
        isActive:false
      }
    ],
    goodsList:[]
  },
  //接口要的数据
  QueryParams:{
    qurty:"",
    cid:"",
    pagenum:1,
    pagesize:10
  },
  //总页数
  totalPages:1,
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
   this.QueryParams.cid=options.cid;
   this.getGoodsList();
  },
  //获取商品数据
  getGoodsList(){
    request({url:"goods/search",data:this.QueryParams})
    .then(result=>{   
      //获取总条数
      const total=  result.data.message.total
      //计算总页数
      this.totalPages=Math.ceil(total/this.QueryParams.pagesize)
      console.log(this.totalPages)
      this.setData({
        goodsList:[...this.data.goodsList,...result.data.message.goods]
        })
    })
    //关闭下拉刷新的窗口 如果没有
    wx.stopPullDownRefresh()
  },
  //标题的点击事件 从子组件中传递过来的
  handleTabItemChange(e)
  {
    //获取被点击标题发索引
    const {index}=e.detail
    //修改原数组
    let {tab}=this.data
    tab.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
    //赋值到data中
    this.setData({
      tab
    })
  },
  //页面上滑 滑动条触底事件
  onReachBottom(){
    if(this.QueryParams.pagenum>=this.totalPages)
    {
     wx.showToast({title: '没有下一页数据了' })
    }
    else{
     //还有下一页数据
     this.QueryParams.pagenum++;
     this.getGoodsList()
    }
  },
  //页面上滑刷新
  onPullDownRefresh()
  {
    //1.重置数组
    this.setData({
      goodsList:[]
    })
    //重置页码
    this.QueryParams.pagenum=1;
    //发送请求
    this.getGoodsList();
  }
})
2. pages/goods_list/index.wxss
.first_tab .goods_item {
  display: flex;
  border-bottom: 1rpx solid #ccc;
}
.first_tab .goods_item .goods_img_wrap {
  flex: 2;
  display: flex;
  justify-content: center;
  align-items: center;
}
.first_tab .goods_item .goods_img_wrap image {
  width: 70%;
}
.first_tab .goods_item .goods_info_wrap {
  flex: 3;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
}
.first_tab .goods_item .goods_info_wrap .goods_name {
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.first_tab .goods_item .goods_info_wrap .goods_price {
  color: var(--themeColor);
  font-size: 32rpx;
}
3. pages/goods_list/index.js
// pages/goods_list/index.js
import {request} from "../../request/index.js";
Page({
  /**
   * 页面的初始数据
   */
  data: {
    tab:[
      {
        id:0,
        value:'综合',
        isActive:true
      },
      {
        id:1,
        value:'销量',
        isActive:false
      },
      {
        id:0,
        value:'价格',
        isActive:false
      }
    ],
    goodsList:[]
  },
  //接口要的数据
  QueryParams:{
    qurty:"",
    cid:"",
    pagenum:1,
    pagesize:10
  },
  //总页数
  totalPages:1,
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
   this.QueryParams.cid=options.cid;
   this.getGoodsList();
  },
  //获取商品数据
  getGoodsList(){
    request({url:"goods/search",data:this.QueryParams})
    .then(result=>{   
      //获取总条数
      const total=  result.data.message.total
      //计算总页数
      this.totalPages=Math.ceil(total/this.QueryParams.pagesize)
      console.log(this.totalPages)
      this.setData({
        goodsList:[...this.data.goodsList,...result.data.message.goods]
        })
    })
    //关闭下拉刷新的窗口 如果没有
    wx.stopPullDownRefresh()
  },
  //标题的点击事件 从子组件中传递过来的
  handleTabItemChange(e)
  {
    //获取被点击标题发索引
    const {index}=e.detail
    //修改原数组
    let {tab}=this.data
    tab.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
    //赋值到data中
    this.setData({
      tab
    })
  },
  //页面上滑 滑动条触底事件
  onReachBottom(){
    if(this.QueryParams.pagenum>=this.totalPages)
    {
     wx.showToast({title: '没有下一页数据了' })
    }
    else{
     //还有下一页数据
     this.QueryParams.pagenum++;
     this.getGoodsList()
    }
  },
  //页面上滑刷新
  onPullDownRefresh()
  {
    //1.重置数组
    this.setData({
      goodsList:[]
    })
    //重置页码
    this.QueryParams.pagenum=1;
    //发送请求
    this.getGoodsList();
  }
})
7. 商品详情页面
需求分析
-  点击轮播图预览大图 
 1. 给轮播图绑定点击事件
 2. 调用小程序的api priviewUmage
-  点击加入购物车 - 绑定点击事件
- 获取缓存中的购物车数据 数组格式
- 先判断当前的商品是否存在购物车中
- 已经存在 修改购物车中的商品数量 购物车中的商品数量++ 重新把购物车中的数组填充回缓存中
- 不存在于购物车的数组中,直接给购物车数组添加一个新元素 新元素带上购买属性num 重新把购物车数组填充回缓存中
 
1.pages/goods_detail/index.wxml
<view class="detail_swiper">
    <swiper
    autoplay
    circular
    indicator-dots
    >
        <swiper-item 
        wx:for="{{goodsObj.pics}}"
        wx:key="pics_id"
        bindtap="bindlePrevewImage"
        data-url="{{item.pics_mid}}"
        >
         <image mode="widthFix" src="{{item.pics_mid}}"></image>
        </swiper-item>
          
    </swiper>
    <view class="goods_price">¥{{goodsObj.goods_price}}</view>
    <view class="goods_name_row">
        <view class="goods_name">{{goodsObj.goods_name}}</view>
        <view class="goods_collect" bindtap="handleCollect">
            <text class="iconfont {{isCollect?'icon-shoucangxuanzhong':'icon-shoucang'}} "></text>
            <view class="collect_text">收藏</view>
        </view>
    </view>
</view>
<view class="goods_info">
    <view class="goods_info_title">图文详情</view>
    <view class="goods_info_content">
        <!--富文本-->
        <rich-text nodes="{{goodsObj.goods_introduce}}">
            
        </rich-text>
    </view>
</view>
<view class="btm_tool">
    <view class="tool_item">
        <view class="iconfont icon-kefu"></view>
        <view>客服</view>   
        <button open-type="contact"></button>   
    </view>
    <view class="tool_item">
        <view class="iconfont icon-fenxiang"></view>
        <view>分享</view>  
        <button open-type="share"></button>    
    </view>
    <navigator open-type="switchTab" url="/pages/cart/index" class="tool_item">
        <view class="iconfont icon-gouwucheman"></view>
        <view>购物车</view>      
    </navigator>
    <view class="tool_item btn_cart" bindtap="handleCartAdd">
        <view>加入购物车</view>      
    </view>
    <view class="tool_item btn_buy">
        <view>立即购买</view>      
    </view>
</view>
2. pages/goods_detail/index.wxss
page {
  padding-bottom: 90rpx;
}
.detail_swiper swiper {
  height: 65vw;
  text-align: center;
}
.detail_swiper swiper image {
  width: 60%;
}
.goods_price {
  padding: 15rpx;
  font-size: 32rpx;
  font-weight: 600;
  color: var(--themeColor);
}
.goods_name_row {
  display: flex;
}
.goods_name_row .goods_name {
  border-top: 5rpx solid #dedede;
  border-bottom: 5rpx solid #dedede;
  padding: 10rpx 0;
  flex: 5;
  color: #000;
  font-size: 30rpx;
  padding: 0 10rpx;
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.goods_name_row .goods_collect {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  border-left: 1rpx solid #000;
}
.goods_name_row .goods_collect .icon-shoucangxuanzhong {
  color: orangered;
}
.goods_info .goods_info_title {
  font-size: 32rpx;
  color: var(--themeColor);
  font-weight: 600;
  padding: 20rpx;
}
.btm_tool {
  border-top: 1rpc solid #000;
  position: fixed;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 90rpx;
  background-color: #fff;
  display: flex;
}
.btm_tool .tool_item {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: 24rpx;
  position: relative;
}
.btm_tool .tool_item button {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
}
.btm_tool .btn_cart {
  flex: 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background-color: #ffa500;
  color: #fff;
  font-size: 30rpx;
  font-weight: 600;
}
.btm_tool .btn_buy {
  flex: 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  background-color: #eb4450;
  color: #fff;
  font-size: 30rpx;
  font-weight: 600;
}
-  pages/goods_detail/index.js // pages/goods_detail/index.js 
 import {request} from “…/…/request/index.js”;
 /**- 点击轮播图预览大图
- 1.给轮播图绑定点击事件
- 2.调用小程序的api priviewUmage
- 点击加入购物车
- 1.绑定点击事件
- 2.获取缓存中的购物车数据 数组格式
- 3.先判断当前的商品是否存在购物车中
- 4.已经存在 修改购物车中的商品数量 购物车中的商品数量++ 重新把购物车中的数组填充回缓存中
- 5.不存在于购物车的数组中,直接给购物车数组添加一个新元素 新元素带上购买属性num 重新把购物车数组填充回缓存中
-  
    - 商品收藏
 
-  
    - 页面onshow的时候 加载缓存中的商品收藏数据
 
-  
    - 判断该商品是不是被收藏
 
- 是 改变页面的图标
-  
    - 点击商品收藏按钮
 
-  
    - 判断该商品是否存在商品缓存数组中
 
-  
    - 已存在 把该商品删除
 
-  
    - 不存在 把商品添加到收藏数组中
 */
 
- 不存在 把商品添加到收藏数组中
 Page({ /** - 页面的初始数据
 */
 data: {
 goodsObj:{},
 //商品是否别收藏过
 isCollect:false
 }, 
 //商品对象
 GoodsInfo:{},/** - 生命周期函数–监听页面加载
 */
 onShow: function () {
 let pages= getCurrentPages();
 let currentPage=pages[pages.length-1];
 let options=currentPage.options;
 const {goods_id}=options
 this.getGoodsDetail(goods_id)
 }, 
 //获取商品详情数据
 getGoodsDetail(goods_id)
 {
 request({url:‘goods/detail’,data:{goods_id}})
 .then(result=>{
 const goodsObj=result.data.message;
 this.GoodsInfo=goodsObj
 //获取缓存中的商品收藏数组
 let collect=wx.getStorageSync(“collect”)||[];
 //判断当前商品是否别收藏
 let isCollect = collect.some(v=>v.goods_id=this.GoodsInfo.goods_id)
 this.setData({
 goodsObj:{
 goods_name:goodsObj.goods_name,
 goods_price:goodsObj.goods_price,
 //iphone部分手机不识别webp图片格式 可以在后端进行修改 临是可以自己该 确保后台存在,1.webp=>1,jpg
 goods_introduce:goodsObj.goods_introduce.replace(/.webp/g,’.jpg’),
 pics:goodsObj.pics
 },
 isCollect
 })
 })
 },
 bindlePrevewImage(e){
 //构造预览图片的数组
 const urls=this.GoodsInfo.pics.map(v=>v.pics_mid);
 //接收传递过来的url
 const current=e.currentTarget.dataset.url;
 wx.previewImage({
 current,
 urls
 })
 },
 handleCartAdd()
 {
 //1.获取缓存中的购物车数据
 let cart=wx.getStorageSync(“cart”)||[];
 //2.判断商品是否存在与购物车中
 let index=cart.findIndex(v=>v.goods_id=this.GoodsInfo.goods_id);
 if(index===-1)
 {
 //3.不存在第一次添加
 this.GoodsInfo.num=1;
 this.GoodsInfo.checked=true
 cart.push(this.GoodsInfo);
 }
 else
 {
 //4.已经存在购物车中 执行num++
 cart[index].num++
 }
 //5.把购物车重新添加到缓存中
 wx.setStorageSync(‘cart’, cart)
 wx.showToast({
 title:“加入成功”,
 icon:‘success’,
 //true 防止用户手抖疯狂点击
 mask: true
 })}, 
 //点击商品收藏图标
 handleCollect(){
 let isCollect=false
 //1. 获取缓存中商品收藏数组
 let collect=wx.getStorageSync(“collect”)||[]
 //判断该商品是否被收藏过
 let index=collect.findIndex(v=>v.goods_id=this.GoodsInfo.goods_id)
 //3 当index=1-表示已经收藏过
 if(index!-1)
 {
 //已经收藏过 应该从收藏册数组中删除
 collect.splice(index,1)
 isCollect=false;
 wx.showToast({
 title:“取消成功”,
 icon:“success”,
 mask:true
 });
 }
 else{
 //没有收藏过
 collect.push(this.GoodsInfo)
 isCollect=true
 wx.showToast({
 title:“收藏成功”,
 icon:“success”,
 mask:true
 });
 }
 wx.setStorageSync(“collect”,collect)
 this.setData({
 isCollect
 })
 }
 })
8. 商品收藏页面
1. 在 pages/collect/index.json中引入组件
{
  "usingComponents": {
    "tab":"../../components/tab/tab"
  },
  "navigationBarTitleText":"商品收藏"
}
** 2.pages/collect/index.wxml**
<tab tab="{{tabs}}" bindtabItemChange="handleTabItemChange">
    <view class="collect_main">
        <view class="collect_title">
            <text class="collect_tips active">全部</text>
            <text class="collect_tips">正在热卖</text>
            <text class="collect_tips">即将上线</text>
        </view>
        <view class="collect_content">
            <navigator class="goods_item"
                wx:for="{{collect}}"
                wx:key="goods_id"
                url="/pages/goods_detail/index?goods_id={{item.goods_id}}"
                >
                        <!--左侧图片容器-->
                        <view class="goods_img_wrap">
                            <image mode="widthFix" src="{{item.goods_small_logo?item.goods_small_logo:'http://image5.suning.cn/uimg/b2c/newcatentries/0000000000-000000000160455569_1_400x400.jpg'}}">
                            </image>
                        </view>
                        <!--右侧商品容器-->
                        <view class="goods_info_wrap" >
                            <view class="goods_name">{{item.goods_name}}</view>
                            <view class="goods_price">¥{{item.goods_price}}</view>
                        </view>
                </navigator>
        </view>
    </view>
 </tab>
3. pages/collect/index.js
// pages/collect/index.js
Page({
  /**
   * 页面的初始数据
   */
  data: {
    collect:[],
    tabs:[
      {
        id:0,
        value:"商品收藏",
        isActive:true
      },
      {
        id:1,
        value:"品牌收藏",
        isActive:false
      },
      {
        id:2,
        value:"店铺收藏",
        isActive:false
      },
      {
        id:3,
        value:"浏览足迹",
        isActive:false
      }
    ]
  },
  onShow(){
    const collect=wx.getStorageSync("collect")||[]
    this.setData({
      collect 
    })
  },
  handleTabItemChange(e)
  {
    console.log(e)
    //获取被点击标题发索引
    const {index}=e.detail
    //修改原数组
    let {tabs}=this.data
      for(let i=0;i<4;i++)
      {
        if(i===index)
        {
          tabs[i].isActive=true
        }
        else
        {
          tabs[i].isActive=false
        }
      }
    //赋值到data中
    this.setData({
      tabs
    })
  }
})
4. pages/collect/index.wxss
.collect_main {
  background-color: #f3f4f6;
}
.collect_main .collect_title {
  padding: 40rpx 0;
}
.collect_main .collect_title .collect_tips {
  padding: 15rpx;
  border: 1rpx solid #ccc;
  margin-left: 25rpx;
  background-color: #fff;
}
.collect_main .collect_title .active {
  color: var(--themeColor);
  border-color: currentColor;
}
.collect_main .collect_content .goods_item {
  display: flex;
  background-color: #fff;
  border-bottom: 1rpx solid #ccc;
}
.collect_main .collect_content .goods_item .goods_img_wrap {
  flex: 2;
  display: flex;
  justify-content: center;
  align-items: center;
}
.collect_main .collect_content .goods_item .goods_img_wrap image {
  width: 70%;
}
.collect_main .collect_content .goods_item .goods_info_wrap {
  flex: 3;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
}
.collect_main .collect_content .goods_item .goods_info_wrap .goods_name {
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.collect_main .collect_content .goods_item .goods_info_wrap .goods_price {
  color: var(--themeColor);
  font-size: 32rpx;
}
9.购物车页面
需求分析
-  获取用户的收获地址 - 绑定点击事件
- 调用小程序内置api 获取用户是收货地址 wx.choseAddress
 
-  获取用户对小程序所授予获取地址的权限状态 scope - 假设用户点击获取收货地址的提示框选择确定 authSetting scope,当 scope值为true 可以直接调用收货地址api
- 假设用户点击获取收货地址的提示框选择取消 scope值为true 诱导用户自己打开授权设置页面 当用户重新给收货地址授权的时候 获取收货地址
- 假设用户没有调用过获取收货地址的api scopewei undefined 可以之际调用收货地址的api
- 把收货地址放入本地缓冲中
- 页面加载完毕
- 获取本地存储中的地址数据
- 把数据设置给data中的一个变量
 
-  onshow 
 1. 获取缓存中的购物车数组
 2. 把购物车中的数据填充到data中
-  全选的实现 
 1. onshow获取缓存中的购物车数据
 2. 根据购物车中的商品数据 所有的商品都被选中 checked=true 全选就被选中
-  总价格 总数量 - 需要商品被选中,我们才拿他来计算
- 获取购物车数组
- 遍历
- 判断商品是否选中
- 总价格+=商品单价*商品数量
- 总数量+=商品数量
- 把计算后的单价和数量 设置回data即可
 
-  商品属性 - 绑定change事件
- 获取被修改的商品对象
- 商品对象的选择状态取反
- 重新将数据填充回data中和缓存中
- 重新计算全选 总价格 总数量
 
-  全选和反选 - 全选 复选框都绑定事件change
- 获取data中的全选变量 allchecked
- 直接取反 allchecked
- 遍历购物车数组和allchecked 重新设置回data 把购物车重新设置回缓存中
 
-  商品数量的编辑 - “+”,"-"按钮 绑定同一个点击事件 区分的关键 自定义属性: “+” +1, “-” -1
- 传递被点击商品的id goods_id
- 获取data中的购物车数组 来获取需要被修改的商品的对象
- 直接修改商品对象的数据 num
 当购物车的数量为=1 同时点击的为"-"
 弹框(showMNodel)提示用户是否删除
 1 确定直接删除
 2 取消 什么也不做
 5.把cart数组 重新设置回缓存中和data中 
-  点击结算 - 判断有没有收货地址信息
- 判断用户有没有选购商品
- 经过以上认证跳转到支付页面
 
1. pages/cart/index.wxml
<view class="revice_address_row">
    <!--当收货地址不存在时按钮显示 空对象的Boolean类型也为true-->
    <view class="address_btn" wx:if="{{!address.userName}}">
        <button bindtap="handleChoseAddress" type="primary" plain>收货地址</button>
    </view>
    <!--收货地址存在就显示-->
    <view wx:else class="user_info_row">
       <view class="user_info">
           <view>
            {{address.userName}}
           </view>
            <view>
                {{address.provinceName+address.cityName+address.countyName+address.detailInfo}}
            </view>
            <view class="user_phone">
                {{address.telNumber}}
            </view>   
       </view>
    </view>
</view>
<!--购物车内容-->
<view class="cart_content">
    <view class="cart_title">购物车</view>
    <view class="cart_main">
        <!--当购物车不为空-->
       <block wx:if="{{cart.length!==0}}">
        <view class="cart_item"
        wx:for="{{cart}}"
        wx:key="goods_id"
        >
            <!--复选框-->
            <view class="cart_chk_wrap">
                <checkbox-group bindchange="handeItemChange" data-id="{{item.goods_id}}"> 
                    <checkbox checked="{{item.checked}}"></checkbox>
                </checkbox-group>
            </view>
            <!--商品图片-->
            <navigator class="cart_img_wrap">
                <image mode="widthFix" src="{{item.goods_small_logo}}"></image>
            </navigator>
            <!--商品信息-->
            <view class="cart_info_wrap">
                <view class="goods_name">{{item.goods_name}}</view>
                <view class="goods_price_wrap">
                    <view class="goods_price">¥{{item.goods_price}}</view>
                    <view class="cart_num_tool">
                        <view bindtap="handleItemNumEdit" data-operation="{{-1}}" data-id="{{item.goods_id}}" class="num_edit" >-</view>
                        <view class="goods_num">{{item.num}}</view>
                        <view bindtap="handleItemNumEdit" data-operation="{{1}}" data-id="{{item.goods_id}}" class="num_edit">+</view>
                    </view>
                </view>
            </view>
        </view>
       </block>
       <block wx:else="{{cart.length!==0}}">
           <image mode="widthFix" src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2Fa4064bdab5f6c800ed664014f3eb7d13a4dd25b3138d0-hYHe07_fw658&refer=http%3A%2F%2Fhbimg.b0.upaiyun.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1644844807&t=a98780f2ab7a2ee2653d589ef43e9f93"></image>
       </block>
    </view>
</view>
<!--底部工具栏-->
<view class="footer_tool">
    <!--全选-->
    <view class="all_chk_wrap">
        <checkbox-group bindchange="handleItemAllCheck">
            <checkbox  checked="{{allChecked}}">全选</checkbox>
        </checkbox-group>
    </view>
    <!--总价格-->
    <view class="total_price_wrap">
        <view class="total_price">
            总计<text class="total_price_text">¥{{totalPrice}}</text>
        </view>
        <view>
            包含运费
        </view>
    </view>
    <view class="order_pay_wrap" bindtap="handlePay">
        结算{{totalNum}}
    </view>
</view>
2. pages/cart/index.wxss
page {
  padding-bottom: 90rpx;
}
.revice_address_row .address_btn {
  padding: 20rpx;
}
.revice_address_row .address_btn button {
  width: 60%;
}
.revice_address_row .user_info_row {
  display: flex;
  padding: 20rpx;
}
.revice_address_row .user_info_row .user_info {
  flex: 5;
}
.revice_address_row .user_info_row .user_phone {
  flex: 3;
  text-align: right;
}
.cart_content .cart_title {
  padding: 20rpx;
  font-size: 36rpx;
  color: var(--themeColor);
  border-top: 1rpx solid currentColor;
  border-bottom: 1rpx solid currentColor;
}
.cart_content .cart_main .cart_item {
  display: flex;
  padding: 10rpx;
  border-bottom: 1rpx solid #000;
}
.cart_content .cart_main .cart_item .cart_chk_wrap {
  flex: 1;
  display: flex;
  justify-content: center;
  align-items: center;
}
.cart_content .cart_main .cart_item .cart_img_wrap {
  flex: 2;
  display: flex;
  align-items: center;
  justify-content: center;
}
.cart_content .cart_main .cart_item .cart_img_wrap image {
  width: 80%;
}
.cart_content .cart_main .cart_item .cart_info_wrap {
  flex: 4;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
}
.cart_content .cart_main .cart_item .cart_info_wrap .goods_name {
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  color: #666;
}
.cart_content .cart_main .cart_item .cart_info_wrap .goods_price_wrap {
  display: flex;
  justify-content: space-between;
}
.cart_content .cart_main .cart_item .cart_info_wrap .goods_price_wrap .goods_price {
  color: var(--themeColor);
  font-size: 34rpx;
}
.cart_content .cart_main .cart_item .cart_info_wrap .goods_price_wrap .cart_num_tool {
  display: flex;
}
.cart_content .cart_main .cart_item .cart_info_wrap .goods_price_wrap .cart_num_tool .num_edit {
  width: 55rpx;
  height: 55rpx;
  display: flex;
  justify-content: center;
  justify-items: center;
  border: 1rpx solid #ccc;
}
.cart_content .cart_main .cart_item .cart_info_wrap .goods_price_wrap .cart_num_tool .goods_num {
  width: 55rpx;
  height: 55rpx;
  display: flex;
  justify-content: center;
  justify-items: center;
}
.footer_tool {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 90rpx;
  background-color: #fff;
  display: flex;
  border-top: 1rpx solid #ccc;
}
.footer_tool .all_chk_wrap {
  flex: 2;
  display: flex;
  justify-content: center;
  align-items: center;
}
.footer_tool .total_price_wrap {
  flex: 5;
  padding-right: 15rpx;
  text-align: right;
}
.footer_tool .total_price_wrap .total_price text.total_price_text {
  color: var(--themeColor);
  font-size: 34rpx;
  font-weight: 600;
}
.footer_tool .order_pay_wrap {
  flex: 3;
  background-color: var(--themeColor);
  color: #fff;
  font-size: 32rpx;
  font-weight: 600;
  display: flex;
  justify-content: center;
  align-items: center;
}
异步编程中的Promise
在使用微信小程序的过程中,我发现由于异步流程延迟的原因,导致不能及时的获取真实的数据,我们可以使用promise()异步请求来解决这个问题
 在utils下面创建一个名为asyncWx.js文件,后面问我们可以进行异步待遇
/**
 *promise形式的 getSetting 
 */
export const getSetting =()=>{
    return new Promise((resolve,reject)=>{
        wx.getSetting({
            success: function(result) {
                resolve(result)
            },
            fail:(err)=>{
                reject(err)
            }
        });
    })
}
/**
 *promise形式的 chooseAddress 
 */
 export const chooseAddress =()=>{
    return new Promise((resolve,reject)=>{
        wx.chooseAddress({
            success: function(result) {
                resolve(result)
            },
            fail:(err)=>{
                reject(err)
            }
        });
    })
}
/**
 *promise形式的 openSetting 
 */
 export const openSetting =()=>{
    return new Promise((resolve,reject)=>{
        wx.openSetting({
            success: function(result) {
                resolve(result)
            },
            fail:(err)=>{
                reject(err)
            }
        });
    })
}
export const showToast =({title})=>{
    return new Promise((resolve,reject)=>{
        wx.showToast({
            title: title,
            icon: 'success',
            success:(res)=>{
                resolve(res)
            },
            fail:(err)=>{
                reject(err)
            }
          })
    })
}
3.pages/cart/index.js
 调用相关的异步函数
 
// pages/cart/index.js
//调用promise
import{getSetting,chooseAddress,openSetting,showToast} from "../../utils/asyncWx.js" 
// import regeneratorRuntime from "../../lib/runtime/runtime"
Page({
  /**
   * 页面的初始数据
   */
  data: {
      address:{},
      cart:[],
      allChecked:false,
      totalPrice:0,
      totalNum:0 
  },
  onShow(){
  // 获取缓冲中的收货地址信息
  const address=wx.getStorageSync("address")
  //获取缓存中的购物车数据
  const cart=wx.getStorageSync("cart")||[]
  // 计算全选中
  // every 数组方法 遍历会接收一个回调函数  如果每一个回调函数都返回true every方法的返回值就为true 只有有一个返回false every方法不在执行 防护false
    //空数组返回值为true
  // const allChecked=cart.length?cart.every(v=>v.checked):false 
        this.setCart(cart)
        this.setData({
          address
        })
  },
  //点击获取收货地址
  async handleChoseAddress()
  {
      
  //1.获取权限状态
  const res1=await getSetting();
  const scopeAddress=res1.authSetting['scope.address']
  //2 。判断权限状态
    if(scopeAddress===false)
    {
      await openSetting()
    }
    //调用获取收货地址的api
    const address=await chooseAddress();
    console.log(address)
    //放入缓存中
    wx.setStorageSync('address', address)
  },
  //商品选中
  handeItemChange(e)
  {
      //获取选中商品的id
      const goods_id=e.currentTarget.dataset.id
      //获取购物车数组
      let {cart}=this.data;
      //找到被修改商品的对象
      let index = cart.findIndex(v=>v.goods_id===goods_id)
      //修改商品的选中事件
      cart[index].checked=!cart[index].checked
      this.setCart(cart)
  },
  //商品全选和反选
  handleItemAllCheck(e)
  {
      //获取data中的数据和选中状态
      let {cart,allChecked}=this.data
      //选中状态取反
      allChecked=!allChecked
      //循环修改cart中商品的选中状态
      cart.forEach(v=>v.checked=allChecked)
      //把修改后的值填充回data或者缓存中
      this.setCart(cart)
  },
  //商品数量的编辑
  handleItemNumEdit(e)
  {
    //获取传递过来的参数
    const {operation,id}=e.currentTarget.dataset
    //获取购物车数组
    let {cart}=this.data;
    //找到需要获取商品的索引
    const index=cart.findIndex(v=>v.goods_id===id)
    if(cart[index].num===1&&operation===-1)
    {
      //弹框提示
      wx.showModal({
        title: '提示',
        content: '你是否删除',
        success: (res)=> {
          if (res.confirm) {
            cart.splice(index, 1);
            this.setCart(cart)
          } else if (res.cancel) {
            console.log('用户点击取消')
          }
        }
      })
    }
    //进行修改数量
    else
    {
    cart[index].num+=operation
    this.setCart(cart)
    }
  },
  //点击结算功能
  async handlePay(){
   //判断收货地址
   const {address,totalNum}=this.data
   if(!address.userName)
   {
     
     await showToast({title:"您还没有选择地址"})
     return
   }
   //判断用户是否选购了商品
   if(totalNum===0)
   {
     await showToast({title:"您还没有选购商品"})
     return 
   }
   //跳转到支付页面
   wx.navigateTo({
     url: '/pages/pay/index'
   });
  },
//设置购物车状态同时 重新计算底部工具栏的数据 全选中 价格 购买数量
  setCart(cart){
    let allChecked=true
      //总价格 总数量
      let totalPrice=0
      let totalNum=0
      cart.forEach(v=>{
        if(v.checked)
        {
          totalPrice+=v.num*v.goods_price
          totalNum+=v.num
        }
        else{
            allChecked=false
        }
      })
      if(cart.length==0)
      {
        allChecked=false
      }
      //将数据设置回data和缓存中
      this.setData({
        cart,
        totalNum,
        totalPrice,
        allChecked
      })
      wx.setStorageSync("cart",cart)
  } 
})
10.搜索中心
10.1 需求分析
-  输入绑定 值改变事件 input事件 - 获取输入框的值
- 判断合法性
- 校验通过 把输入框的值发送到后台
- 返回数据打印的页面上
 
-  防抖 (防止抖动) 定时器 节流 - 防抖一般用于防止重复输入 重复请求 节流一般用于页面上拉和下拉,可以定义全局变量定时器id来实现
 
10.2 具体实现
1. pages/search/index.wxml
<view class="search_row">
    <input placeholder="请输入您要搜索的商品" value="{{inputValue}}" bindinput="handleInpput"></input>
    <button hidden="{{!isFouces}}" bindtap="handleCancle">取消</button>
</view>
<view class="search_content">
        <navigator url="/pages/goods_detail/index?goods_id={{item.goods_id}}" class="search_item" wx:for="{{goods}}" wx:key="{{goods_id}}">
                {{item.goods_name}}
        </navigator>
</view>
2. pages/search/index.js
import {request} from "../../request/index.js"
Page({
  /**
   * 页面的初始数据
   */
  data: {
    goods:[],
    //取消按钮是显示
    isFouces:false,
    //输入框的值
    inputValue:''
  },
  Timeid:-1,
  handleInpput(e)
  {
     //获取输入框的值
     const {value}=e.detail
     //监测合法值
     if(!value.trim())
     {
       this.setData({
         goods:[],
         isFouces:false
       })
       //值不合法
       return ;
     }
     this.setData({
       isFouces:true
     })
     //发送获取数据
     clearTimeout(this.Timeid)
     this.Timeid=setTimeout(()=>{
       this.qsearch(value)
     },1000)
     this.qsearch(value)
  
  },
  //发送请求获取搜索数据
  async qsearch(query)
  {
    console.log(query)
    const res=await request({url:"goods/search",data:{query}});
    this.setData({
      goods:res.data.message.goods
    })
  },
  //点击取消按钮
  handleCancle()
  {
      this.setData({
        inputValue:'',
        isFouces:false,
        goods:[]
      })
  }
})
3. pages/search/index.wxss
page {
  background-color: #dedede;
  padding: 20rpx;
}
.search_row {
  height: 60rpx;
  display: flex;
}
.search_row input {
  background-color: #fff;
  flex: 4;
  height: 100%;
  padding-left: 30rpx;
}
.search_row button {
  flex: 1;
  height: 100%;
  font-size: 26rpx;
  padding: 0;
  margin: 0 10rpx;
  display: flex;
  justify-content: center;
  align-items: center;
}
.search_content {
  margin-top: 30rpx;
}
.search_content .search_item {
  background-color: #fff;
  font-size: 26rpx;
  padding: 15rpx 10rpx;
  border-bottom: 1rpx solid #ccc;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
11.个人中心
11.1 用户登录授权
1. page/login/index.wxml
<!--pages/login/index.wxml-->
<button plain type="primary" open-type="getUserInfo" bindgetuserinfo="handleGetUserInfo">登录 </button>
2. pages/login/index.js
// pages/login/index.js
Page({
  handleGetUserInfo(e)
  {
    const {userInfo} = e.detail;
    wx.setStorageSync('userInfo', userInfo)
    wx.navigateBack({
      delta: 1  
    })
  }
})
3. pages/login/index.wxss
/* pages/login/index.wxss */
button{
    margin-top: 40rpx;
    width: 70%;
}
11.2 商品收藏
1. 在pages/collect/index.json中引入组件
{
  "usingComponents": {
    "tab":"../../components/tab/tab"
  },
  "navigationBarTitleText":"商品收藏"
}
2. pages/collect/index.wxss
<tab tab="{{tabs}}" bindtabItemChange="handleTabItemChange">
    <view class="collect_main">
        <view class="collect_title">
            <text class="collect_tips active">全部</text>
            <text class="collect_tips">正在热卖</text>
            <text class="collect_tips">即将上线</text>
        </view>
        <view class="collect_content">
            <navigator class="goods_item"
                wx:for="{{collect}}"
                wx:key="goods_id"
                url="/pages/goods_detail/index?goods_id={{item.goods_id}}"
                >
                        <!--左侧图片容器-->
                        <view class="goods_img_wrap">
                            <image mode="widthFix" src="{{item.goods_small_logo?item.goods_small_logo:'http://image5.suning.cn/uimg/b2c/newcatentries/0000000000-000000000160455569_1_400x400.jpg'}}">
                            </image>
                        </view>
                        <!--右侧商品容器-->
                        <view class="goods_info_wrap" >
                            <view class="goods_name">{{item.goods_name}}</view>
                            <view class="goods_price">¥{{item.goods_price}}</view>
                        </view>
                </navigator>
        </view>
    </view>
 </tab>
3. pages/ccollect/index.wxss
.collect_main {
  background-color: #f3f4f6;
}
.collect_main .collect_title {
  padding: 40rpx 0;
}
.collect_main .collect_title .collect_tips {
  padding: 15rpx;
  border: 1rpx solid #ccc;
  margin-left: 25rpx;
  background-color: #fff;
}
.collect_main .collect_title .active {
  color: var(--themeColor);
  border-color: currentColor;
}
.collect_main .collect_content .goods_item {
  display: flex;
  background-color: #fff;
  border-bottom: 1rpx solid #ccc;
}
.collect_main .collect_content .goods_item .goods_img_wrap {
  flex: 2;
  display: flex;
  justify-content: center;
  align-items: center;
}
.collect_main .collect_content .goods_item .goods_img_wrap image {
  width: 70%;
}
.collect_main .collect_content .goods_item .goods_info_wrap {
  flex: 3;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
}
.collect_main .collect_content .goods_item .goods_info_wrap .goods_name {
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.collect_main .collect_content .goods_item .goods_info_wrap .goods_price {
  color: var(--themeColor);
  font-size: 32rpx;
}
4. pages/collect/index.js
// pages/collect/index.js
Page({
  /**
   * 页面的初始数据
   */
  data: {
    collect:[],
    tabs:[
      {
        id:0,
        value:"商品收藏",
        isActive:true
      },
      {
        id:1,
        value:"品牌收藏",
        isActive:false
      },
      {
        id:2,
        value:"店铺收藏",
        isActive:false
      },
      {
        id:3,
        value:"浏览足迹",
        isActive:false
      }
    ]
  },
  onShow(){
    const collect=wx.getStorageSync("collect")||[]
    this.setData({
      collect 
    })
  },
  handleTabItemChange(e)
  {
    console.log(e)
    //获取被点击标题发索引
    const {index}=e.detail
    //修改原数组
    let {tabs}=this.data
      for(let i=0;i<4;i++)
      {
        if(i===index)
        {
          tabs[i].isActive=true
        }
        else
        {
          tabs[i].isActive=false
        }
      }
    //赋值到data中
    this.setData({
      tabs
    })
  }
})
11.3 意见反馈
11.3.1 需求分析
-  点击+号 触发tap点击事件 - 调用小程序内置的选择图片的api
- 获取到图片的路径 数组
- 把图片路径存到data的变量中
- 页面可以根据 图片数组 进行循环显示自定义组件
 
-  点击自定义图片 组件 - 获取被点击元素的索引
- 获取data中的图片数组
- 根据索引=数组中删除对应的元素
- 把数组重新设置到data中
 
-  点击提交 - 获取文本域的内容 
    - data中定义变量表示输入框的内容
- 文本域绑定输入事件 事件触发的时候 把输入框的值存入到变量中
 
- 对这些内容合法性验证
- 验证通过用户选择图片 上传到我们专门的图片服务器 返回图片的外网链接 
    - 遍历图片数组
- 挨个上传
- 维护一个图数组 存放;图片上传后的外网路径
 
- 文本域和外网的图片链接一起提交到服务器 前端模拟
- 清空当前页面
- 返回上一页
 
- 获取文本域的内容 
    
11.3.2 具体实现
由于需要使用图片上传的组件,先引入图片上传的组件
11.3.2.1 引入图片上传的组件
-  在components下面创建一个名为Upimg的文件夹,用于文件上传组件 
-  在components/Upimg下面的Upimg文件下声明组件 { 
 “component”: true,
 “usingComponents”: {}
 }
-  components/Upimg/Upimg.wxml </icon>
-  components/Upimg/Upimg.wxss .up_img_wrap{ 
 width: 90rpx;
 height: 90rpx;
 position: relative;
 }
 .up_img_wrap image{
 width: 100%;
 height: 100%;
 border-radius: 15rpx;
 }
 .up_img_wrap icon{
 position: absolute;
 top: -22rpx;
 right: -22rpx;
 }
-  components/Upimg/Uping.js // components/Upimg/Upimg.js 
 Component({
 /**- 组件的属性列表
 */
 properties: {
 src: {
 type:String,
 value:""
 }
 }, /** - 组件的初始数据
 */
 data: {
 }, /** - 组件的方法列表
 */
 methods: {
 } 
 })
- 组件的属性列表
11.3.2.2 页面编写
1. 在pages/feedback/index.json中声明组件
{
  "usingComponents": {
    "SearchInput":"../../components/SearchInput/SearchInput",
    "tab":"../../components/tab/tab",
    "UpImg":"../../components/Upimg/Upimg"
  },
  "navigationBarTitleText":"意见反馈",
  "enablePullDownRefresh":true,
  "backgroundTextStyle":"dark"
}
2. pages/feedback/index.wxml
<tab tab="{{tab}}" bindtabItemChange="handleTabItemChange">
<view class="fb_main">
    <view class="fb_title">问题的种类</view>
    <view class="fb_tips">
        <text>功能建议</text>
        <text>购买遇到的问题</text>
        <text>性能问题</text>
        <text>其他</text>
    </view>
    <view class="fb_content">
        <textarea value="{{textVal}}" bindinput="handleTextInput" placeholder="请输入您要描述的问题"></textarea>
        <view class="fb_tool">
          <button bindtap="handleChoseImg">+</button>
         <view class="up_img_item"
         wx:for="{{chooseImgs}}"
         wx:key="this"
         bindtap="handleRemoveImage"
         data-index="{{index}}"
         >
              <UpImg src="{{item}}"></UpImg>
        </view>
        
        </view>
    </view>
    <view class="form_btn_wrap">
        <button bindtap="handleFormSubmit" type="warn">
            <icon class="" type="success_no_circle" size="23" color="white">                
            </icon>
            提交
        </button>
    </view>
</view>
</tab>
3. pages/feedback/index.wxss
page {
  background-color: #eeeeee;
}
.fb_main {
  padding: 20rpx;
}
.fb_main .fb_tips {
  display: flex;
  flex-wrap: wrap;
}
.fb_main .fb_tips text {
  width: 30%;
  padding: 10rpx;
  text-align: center;
  background-color: #fff;
  margin: 20rpx 10rpx;
}
.fb_main .fb_content {
  background-color: #fff;
  margin-top: 20rpx;
}
.fb_main .fb_content textarea {
  padding: 10rpx;
}
.fb_main .fb_content .fb_tool {
  display: flex;
  flex-wrap: nowrap;
  padding-bottom: 30rpx;
}
.fb_main .fb_content .fb_tool button {
  margin: 0;
  width: 90rpx;
  height: 90rpx;
  font-size: 60rpx;
  padding: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  margin-left: 20rpx;
  color: #ccc;
  margin-top: 20rpx;
}
.fb_main .fb_content .fb_tool .up_img_item {
  margin-top: 20rpx;
  margin-left: 20rpx;
}
.fb_main .form_btn_wrap {
  margin-top: 20rpx;
  display: flex;
  justify-content: flex-end;
}
.fb_main .form_btn_wrap button {
  background-color: red;
  color: white;
  margin: 0;
  padding: 0;
  width: 30%;
}
4. pages/feedback/index.js
Page({
  /**
   * 页面的初始数据
   */
  data: {
    tab:[
      {
        id:0,
        value:'体验问题',
        isActive:true
      },
      {
        id:1,
        value:'商品商家投诉',
        isActive:false
      }
    ],
    //被选中图片的路径 数组
    chooseImgs:[],
  //     //外网的图片路径数组
  // UploadImgs:[],
    //文本域内容
    textVal:''
  },
    //标题的点击事件 从子组件中传递过来的
    handleTabItemChange(e)
    {
      //获取被点击标题发索引
      const {index}=e.detail
      //修改原数组
      let {tab}=this.data
      tab.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
      //赋值到data中
      this.setData({
        tab
      })
    },
    //点击+号自定义选择图片
    handleChoseImg(e)
    {
      //调用小程序中内置的选择图片api
      wx.chooseImage({
          //同时选中图片的数量
          count:9,
          //图片的格式 原图 压缩
          sizeType:['original','compressed'],
          //图片的来源 相册 照相机
          sourceType:['album','camera'],
          success:(result)=>{
            console.log(result)
            this.setData({
              //图片数组进行拼接
              chooseImgs:[...this.data.chooseImgs,...result.tempFilePaths]
            })
          }
      });
    },
    //点击自定义的图片进行删除
    handleRemoveImage(e)
    {
      //获取被点击的组件的索引
      const {index}=e.currentTarget.dataset
      //获取data中的图片数组
      let {chooseImgs}=this.data
      //删除元素
      chooseImgs.splice(index,1)
      this.setData({
        chooseImgs
      })
    },
    //文本域的输入事件
    handleTextInput(e)
    {
      this.setData({
        textVal:e.detail.value 
      })
    },
    //提交按钮的点击事件
    handleFormSubmit()
    {
      //获取文本域的内容
      const {textVal,chooseImgs}=this.data
      //合法性验证
      if(!textVal.trim())
      {
        //不合法
        wx.showToast({
          title:'输入不合法',
          icon:'none',
          mask: true
        });
        return
      }
      //准备上传文件,到专门的服务器
      //上传文件的api不支持多个文件同时上传
      //显示正在等待的图片
      wx.showLoading({
        title:"正在上传",
        mask:true
      })
      if(chooseImgs.length!=0)
      {
      chooseImgs.forEach((v,i)=>{
      wx.uploadFile({
        //图片要上传到哪里
        // url: 'https://images.ac.cn/Home/Index/UploadAction/',
        url:'https://img.coolcr.cn/api/upload',
        //要上传文件的路径
        filePath:v,
        //上传文件的名称 后台获取某个文件 file
        // name:'file',
        name:"image",
        // header: {}, // 设置请求的 header
         // HTTP 请求中其他额外的 form data
        formData: {},
        success: function(res){
          let url=JSON.parse(res.data).data.url
          // this.UploadImgs.push(url)  
        }
      })
        //所有图片都上传完毕才会触发
        if(i===chooseImgs.length-1){
          wx.hideLoading()
          console.log('把文本中的内容和外网的图片数组提交到后台中')
          //提交都成功了
          //重置页面
          this.setData({
            textVal:'',
            chooseImgs:[]
          })
          //返回到上一个页面
          wx.navigateBack({
            delta: 1
            
          })
        }
    })
  }
  else
  {
    console.log("只是提交了文本")
    wx.hideLoading()
     //返回到上一个页面
     wx.navigateBack({
      delta: 1
    })
  }
    }
})
11.4 个人中心
1. pages/user/index.wxml
<view class="user_info_wrap">
    <view wx:if="{{userInfo.avatarUrl}}" class="user_img_wrap">
        <image class="user_bg" src="{{userInfo.avatarUrl}}"></image>
        <view class="user_info">
            <image class="user_icon" src="{{userInfo.avatarUrl}}"></image>
            <view class="user_name">{{userInfo.nickName}}</view>
        </view>
    </view>
    <view wx:else class="user_btn">
        <navigator url="/pages/login/index">登录</navigator>         
    </view>
</view>
<view class="user_content">
    <view class="user_main">
      <!--历史足迹-->
      <view class="history_wrap">
          <navigator>
              <view class="his_num">8</view>
              <view class="his_name">收藏的店铺</view>
          </navigator>
          <navigator url="/pages/collect/index">
            <view class="his_num">{{collectNums}}</view>
            <view class="his_name">收藏的商品</view>
        </navigator>
        <navigator>
            <view class="his_num">8</view>
            <view class="his_name">关注的商品</view>
        </navigator>
        <navigator>
            <view class="his_num">8</view>
            <view class="his_name">我的足迹</view>
        </navigator>
      </view>
      <!--我的订单-->
      <view class="orders_wrap">
          <view class="order_title">我的订单</view>
          <view class="order_content">
              <navigator url="/pages/order/index?type=1">
               <view class="iconfont icon-dingdan"></view>
               <view class="order_name">全部订单</view>   
              </navigator>
              <navigator url="/pages/order/index?type=2">
                <view class="iconfont icon-fukuantongzhi"></view>
                <view class="order_name">待付款</view>   
               </navigator>
               <navigator url="/pages/order/index?type=3">
                <view class="iconfont icon-weibiaoti2fuzhi08"></view>
                <view class="order_name">待收货</view>   
               </navigator>
               <navigator>
                <view class="iconfont icon-tuihuotuikuan_dianpu"></view>
                <view class="order_name">退货/退款</view>   
               </navigator>
          </view>
      </view>
      <!--收货地址管理-->
      <view class="address_wrap">
          收货地址管理
      </view>
      <view class="app_info_wrap">
          <view class="app_info_item app_info_contact">
              <text>联系客服</text>
              <text>400-618-4000</text>
          </view>
          <navigator class="app_info_item" url="/pages/feedback/index">意见反馈</navigator>
          <view class="app_info_item">关于我们</view>
      </view>
      <!--推荐-->
      <view class="recommend_wrap">
          把应用推荐给其他人
      </view>
    </view>
</view>
2. pages/user/index.wxss
page {
  background-color: #edece8;
}
.user_info_wrap {
  height: 45vh;
  overflow: hidden;
  background-color: var(--themeColor);
  position: relative;
}
.user_info_wrap .user_img_wrap {
  position: relative;
}
.user_info_wrap .user_img_wrap .user_bg {
  height: 50vh;
  filter: blur(10rpx);
}
.user_info_wrap .user_img_wrap .user_info {
  position: absolute;
  left: 50%;
  top: 20%;
  transform: translateX(-50%);
  text-align: center;
}
.user_info_wrap .user_img_wrap .user_icon {
  width: 150rpx;
  height: 150rpx;
  border-radius: 50%;
}
.user_info_wrap .user_img_wrap .user_name {
  color: #fff;
  margin-top: 40rpx;
}
.user_info_wrap .user_btn {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  top: 40%;
  border: 1rpx solid greenyellow;
  color: greenyellow;
  font-size: 38rpx;
  padding: 30rpx;
  border-radius: 10rpx;
}
.user_content {
  position: relative;
}
.user_content .user_main {
  padding-bottom: 100rpx;
  color: #666;
  position: absolute;
  width: 90%;
  left: 50%;
  transform: translateX(-50%);
  top: -40rpx;
}
.user_content .user_main .history_wrap {
  background-color: #fff;
  display: flex;
}
.user_content .user_main .history_wrap navigator {
  flex: 1;
  padding: 10rpx 0;
  text-align: center;
}
.user_content .user_main .history_wrap navigator .his_num {
  color: var(--themeColor);
}
.user_content .user_main .orders_wrap {
  background-color: #fff;
  margin-top: 30rpx;
}
.user_content .user_main .orders_wrap .order_title {
  padding: 20rpx;
  border-bottom: 1rpx solid #ccc;
}
.user_content .user_main .orders_wrap .order_content {
  display: flex;
}
.user_content .user_main .orders_wrap .order_content navigator {
  padding: 15rpx 0;
  flex: 1;
  text-align: center;
}
.user_content .user_main .orders_wrap .order_content navigator .iconfont {
  color: var(--themeColor);
  font-size: 40rpx;
}
.user_content .user_main .address_wrap {
  margin-top: 30rpx;
  background-color: #fff;
  padding: 20rpx;
}
.user_content .user_main .app_info_wrap {
  margin-top: 30rpx;
  background-color: #fff;
}
.user_content .user_main .app_info_wrap .app_info_item {
  padding: 20rpx;
  border-bottom: 1rpx solid #ccc;
}
.user_content .user_main .app_info_wrap .app_info_contact {
  display: flex;
  justify-content: space-between;
}
.user_content .user_main .recommend_wrap {
  margin-top: 30rpx;
  background-color: #fff;
  padding: 20rpx;
}
3. pages/user/index.js
// pages/user/index.js
Page({
  /**
   * 页面的初始数据
   */
  data: {
    userInfo:{},
    //收藏商品的数量
    collectNums:0
  },
  onShow(){
    const userInfo=wx.getStorageSync('userInfo')
    const collect=wx.getStorageSync("collect")||[]
    this.setData({
      userInfo,
      collectNums:collect.length  
    })
  }
})
说明:由于是个人用户,小程序的订单功能未实现,但小程序中的大部分功能已经实现










