IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 黑马微信小程序项目实战 -> 正文阅读

[JavaScript知识库]黑马微信小程序项目实战

1.小程序的第三方框架

  1. 腾讯 wepy 类似于vue
  2. 美团 mpvue 类似于vue
  3. 京东 tao 类似于react
  4. 滴滴 chameleon
  5. uni-app 类似于vue
  6. 原生框架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. ??

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方式

  1. 在request的目录下创建index.js
export const request=(params)=>{

    return new Promise((resolve,reject)=>{
        wx.request({
            ...params,
            success:(result)=>{
                resolve(result);
            },
            fail: (err)=>{
                reject(err)
            }
        });
    } )
}
  1. 在pages/index/index.js中加入request.js的路径
import {request} from "../../request/index.js"
  1. 请求路径的改写
  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 商品列表的实现

需求分析

  1. 用户上滑页面 滚动条触底 开始加载下一页数据

    1. 找到滚动条触底事件
    2. 判断还有没有下一页数据
      1. 获取总页数 总页数=math.ceil(总条数/页容量)
      2. 获取当前页的页码
      3. 判断当前;页面是否大于总页数
    3. 假如没有下一页数据会弹出一个提示
    4. 假如有下一页数据,加载下一页数据
      当前的页码++
      重新发送请求
      数据请求回来 要对data中的数据进行拼接而不是全部替换
  2. 下拉刷新页面

    1. 触发下拉刷新事件 需要在页面的Json文件中开始一个配置项
    2. 重置数据 数组
    3. 重置页码 设置为1
    4. 重新发送请求
    5. 数据请求回来了 需要手动关闭 等待效果

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. 点击轮播图预览大图
    1. 给轮播图绑定点击事件
    2. 调用小程序的api priviewUmage

  2. 点击加入购物车

    1. 绑定点击事件
    2. 获取缓存中的购物车数据 数组格式
    3. 先判断当前的商品是否存在购物车中
    4. 已经存在 修改购物车中的商品数量 购物车中的商品数量++ 重新把购物车中的数组填充回缓存中
    5. 不存在于购物车的数组中,直接给购物车数组添加一个新元素 新元素带上购买属性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;
}

  1. 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 重新把购物车数组填充回缓存中
 * 
 * 
 * 3. 商品收藏
 *  1. 页面onshow的时候 加载缓存中的商品收藏数据
 *  2. 判断该商品是不是被收藏
 *    是 改变页面的图标
 * 3. 点击商品收藏按钮
 *    1. 判断该商品是否存在商品缓存数组中 
 *    2. 已存在 把该商品删除
 *    3. 不存在 把商品添加到收藏数组中 
*/

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.购物车页面

需求分析

  1. 获取用户的收获地址

    1. 绑定点击事件
    2. 调用小程序内置api 获取用户是收货地址 wx.choseAddress
  2. 获取用户对小程序所授予获取地址的权限状态 scope

    1. 假设用户点击获取收货地址的提示框选择确定 authSetting scope,当 scope值为true 可以直接调用收货地址api
    2. 假设用户点击获取收货地址的提示框选择取消 scope值为true 诱导用户自己打开授权设置页面 当用户重新给收货地址授权的时候 获取收货地址
    3. 假设用户没有调用过获取收货地址的api scopewei undefined 可以之际调用收货地址的api
    4. 把收货地址放入本地缓冲中
    5. 页面加载完毕
    6. 获取本地存储中的地址数据
    7. 把数据设置给data中的一个变量
  3. onshow
    1. 获取缓存中的购物车数组
    2. 把购物车中的数据填充到data中

  4. 全选的实现
    1. onshow获取缓存中的购物车数据
    2. 根据购物车中的商品数据 所有的商品都被选中 checked=true 全选就被选中

  5. 总价格 总数量

    1. 需要商品被选中,我们才拿他来计算
    2. 获取购物车数组
    3. 遍历
    4. 判断商品是否选中
    5. 总价格+=商品单价*商品数量
    6. 总数量+=商品数量
    7. 把计算后的单价和数量 设置回data即可
  6. 商品属性

    1. 绑定change事件
    2. 获取被修改的商品对象
    3. 商品对象的选择状态取反
    4. 重新将数据填充回data中和缓存中
    5. 重新计算全选 总价格 总数量
  7. 全选和反选

    1. 全选 复选框都绑定事件change
    2. 获取data中的全选变量 allchecked
    3. 直接取反 allchecked
    4. 遍历购物车数组和allchecked 重新设置回data 把购物车重新设置回缓存中
  8. 商品数量的编辑

    1. “+”,"-"按钮 绑定同一个点击事件 区分的关键 自定义属性: “+” +1, “-” -1
    2. 传递被点击商品的id goods_id
    3. 获取data中的购物车数组 来获取需要被修改的商品的对象
    4. 直接修改商品对象的数据 num
      当购物车的数量为=1 同时点击的为"-"
      弹框(showMNodel)提示用户是否删除
      1 确定直接删除
      2 取消 什么也不做

    5.把cart数组 重新设置回缓存中和data中

  9. 点击结算

    1. 判断有没有收货地址信息
    2. 判断用户有没有选购商品
    3. 经过以上认证跳转到支付页面

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 需求分析

  1. 输入绑定 值改变事件 input事件

    1. 获取输入框的值
    2. 判断合法性
    3. 校验通过 把输入框的值发送到后台
    4. 返回数据打印的页面上
  2. 防抖 (防止抖动) 定时器 节流

    • 防抖一般用于防止重复输入 重复请求 节流一般用于页面上拉和下拉,可以定义全局变量定时器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 需求分析

  1. 点击+号 触发tap点击事件

    1. 调用小程序内置的选择图片的api
    2. 获取到图片的路径 数组
    3. 把图片路径存到data的变量中
    4. 页面可以根据 图片数组 进行循环显示自定义组件
  2. 点击自定义图片 组件

    1. 获取被点击元素的索引
    2. 获取data中的图片数组
    3. 根据索引=数组中删除对应的元素
    4. 把数组重新设置到data中
  3. 点击提交

    1. 获取文本域的内容
      1. data中定义变量表示输入框的内容
      2. 文本域绑定输入事件 事件触发的时候 把输入框的值存入到变量中
    2. 对这些内容合法性验证
    3. 验证通过用户选择图片 上传到我们专门的图片服务器 返回图片的外网链接
      1. 遍历图片数组
      2. 挨个上传
      3. 维护一个图数组 存放;图片上传后的外网路径
    4. 文本域和外网的图片链接一起提交到服务器 前端模拟
    5. 清空当前页面
    6. 返回上一页

11.3.2 具体实现

由于需要使用图片上传的组件,先引入图片上传的组件

11.3.2.1 引入图片上传的组件

  1. 在components下面创建一个名为Upimg的文件夹,用于文件上传组件
  2. 在components/Upimg下面的Upimg文件下声明组件
{
  "component": true,
  "usingComponents": {}
}
  1. components/Upimg/Upimg.wxml
<view class="up_img_wrap">
    <image src="{{src}}"></image>
    <icon class="" type="clear" size="23" color="red">
        
    </icon>
      
</view>

  1. 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;
}
  1. 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  
    })
  }
})

说明:由于是个人用户,小程序的订单功能未实现,但小程序中的大部分功能已经实现

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-01-28 11:49:06  更:2022-01-28 11:49:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 13:54:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码