微信小程序开发实战

719 阅读6分钟

一、项目初始化

1.创建一个小程序

因为之前我已经注册过小程序,所以这里使用的邮箱是小欣欣的邮箱。创建具体步骤就不多说

2.删除不必要的内容

如图所示:

1.项目无关内容.gif

3.创建项目文件夹

image.png

image.png

4.创建项目文件

image.png

image.png

5.引入字体图标

2.引入字体图标.gif

6.搭建项目tabbar结构

3.tabbar.gif

7.项目样式初始化

image.png

二、实现小程序各页面

1.首页

1.1.搜索框

(1)首先创建了一个SearchInput组件

image.png

(2) 然后在要使用的那个页面的json文件中引入,在html文件中使用即可

image.png

(3)对搜索组件进行开发即可 css文件

.search_input {
  height: 90rpx;
  padding: 10rpx;
  background-color: var(--themeColor);
}
.search_input navigator {
  display: flex;
  color: #666;
  height: 100%;
  justify-content: center;
  align-items: center;
  background-color: #fff;
  border-radius: 15rpx;
}

效果图:

4.搜索框的实现.gif

1.2 轮播图

(1)获取轮播图数据

onLoad: function (options) {
    // 1发送异步请求获取轮播图数据
    wx.request({
      url: 'https://api-hmugo-web.itheima.net/api/public/v1/home/swiperdata',
      success: (result) => {
        this.setData({
          swiperList:result.data.message
        })
        // console.log(this.data.swiperList);
        
      },

(2)渲染轮播图

 <swiper autoplay interval="3000" circular indicator-dots indicator-color='skyblue'>
    <swiper-item wx:for="{{swiperList}}"  wx:key="item.goods_id">
     <navigator>
      <image mode="widthFix" src="{{item.image_src}}"></image>
     </navigator>
    </swiper-item>
  </swiper>

轮播图效果图:

5.轮播图效果图.gif

1.3.封装请求数据函数

image.png

使用:

image.png

1.4 分类列表

请求数据:

// 获取导航分类数组
getCatesList(){
  request({url: 'https://api-hmugo-web.itheima.net/api/public/v1/home/catitems'}).then(result=>{
    // console.log(result);
    this.setData({
   catesList:result.data.message
        })
  })
},
<view class="index_cate">
    <navigator
    wx:for="{{catesList}}"
    wx:key="name"
    >
  <image mode="widthFix" src="{{item.image_src}}"></image></navigator>
  </view>

样式:

/**index.wxss**/
.index_swiper swiper {
  width: 750rpx;
  height: 340rpx;
}
.index_swiper swiper image {
  width: 100%;
}
.index_cate {
  display: flex;
}
.index_cate navigator {
  padding: 20rpx;
  flex: 1;
}
.index_cate navigator image {
  width: 100%;
}

效果图:

image.png

1.5楼层

请求获取楼层数据:

getFloorList() {
        request({ url: 'https://api-hmugo-web.itheima.net/api/public/v1/home/floordata' }).then(result => {
            console.log(result);
            this.setData({
                floorList: result.data.message
            })
        })
    },

html:

<!-- 楼层开始 -->
  <view class="index_floor">
    <view class="floor_group"
    wx:for="{{floorList}}"
    wx:for-item="item1"
    wx:for-index="index1"
    wx:key="floor_title"
    >
      <!-- 标题 -->
      <view class="floor_title">
        <image mode="widthFix" src="{{item1.floor_title.image_src}}"></image>
      </view>
      <!-- 内容 -->
      <view class="floor_content">
        <navigator 
        wx:for="{{item1.product_list}}"
        wx:for-item="item2"
    wx:for-index="index2"
        wx:key="name"
        >
        <image mode="widthFix" src="{{item2.image_src}}"></image>
      </navigator>
      </view>
    </view>
  </view>
  <!-- 楼层结束 -->

样式:

// 楼层样式
.index_floor{
  .floor_group{
    .floor_title{
      padding: 10rpx 0;
      image{
    width: 100%;
      }
    }
    .floor_content{
      // 清除浮动
      overflow: hidden;
      navigator {
        float: left;
        width: 33.33%;
        // 后四个超链接
        &:nth-last-child(-n+4){
          // 原图的宽高 232*386
          // 232/386 =33.33vw/height
          // 第一张图片的高为 33.33vw*386/232
          height:27.72711vw ;
          border-left: 10rpx solid #fff;
        }
        &:nth-child(2),
        &:nth-child(3){
          border-bottom: 10rpx solid #fff;
        }
        image{
          height: 100%;
          width: 100%;
        }
      }
    }
  }
}

效果图:

image.png

2.分类

设计图:

image.png

2.1.获取分类数据:

// pages/category/index.js
// 引入用来发送请求的封装好的方法
import { request } from "../../request/index.js"
Page({

  /**
   * 页面的初始数据
   */
  data: {
  // 左侧的菜单数据
leftMenuList:[],
// 右侧的商品数据
rightGoodsContent:[]
  },
  // 接口返回的数据
  CatesList:[],

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
this.getCates();
  },
  // 获取分类数据
  getCates(){
    request({
      url:'https://api-hmugo-web.itheima.net/api/public/v1/categories'
    }).then(result=>{
      this.CatesList=result.data.message
      // 构造左侧的大菜单数据
      let leftMenuList=this.CatesList.map(v=>v.cat_name);
      let rightGoodsContent =this.CatesList[0].children
      this.setData({
        leftMenuList,
        rightGoodsContent
      })
      console.log(result);
      
    })

  }

  
})

2.2布局+点击左侧菜单栏跳转

wxml:

<!--pages/category/index.wxml-->
<view class="cate_index">
<!-- 搜索框开始 -->
<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="{{rightGoodsContent}}"
        wx:for-index="index1"
        wx:for-item="item1"
       wx:key='cat_id'>
       <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-item="item2"
        wx:for-index="index2"
        wx:key="cat_id"
         >
        <image mode="widthFix" src="{{item2.cat_icon}}"></image>
        <view class="goods_name">{{item2.cat_name}}</view>
      </navigator>
      </view>
     </view>
    </scroll-view>
    <!-- 右侧商品内容结束 -->
  </view>
  </view>

wxss:

/* pages/category/index.wxss */
page {
  height: 100%;
}
.cate_index .cates_container {
  height: calc(100vh - 90rpx);
  display: flex;
}
.cate_index .cates_container .left_menu {
  flex: 2;
}
.cate_index .cates_container .left_menu .menu_item {
  height: 80rpx;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 30rpx;
}
.cate_index .cates_container .left_menu .active {
  color: var(--themeColor);
  border-left: 5rpx solid currentColor;
}
.cate_index .cates_container .right_content {
  flex: 5;
}
.cate_index .cates_container .right_content .goods_group .goods_title {
  height: 80rpx;
  display: flex;
  justify-content: center;
  align-items: center;
}
.cate_index .cates_container .right_content .goods_group .goods_title .delimiter {
  color: #ccc;
  padding: 0 10rpx;
}
.cate_index .cates_container .right_content .goods_group .goods_list {
  display: flex;
  flex-wrap: wrap;
}
.cate_index .cates_container .right_content .goods_group .goods_list navigator {
  width: 33.33%;
  text-align: center;
}

js:

// pages/category/index.js
// 引入用来发送请求的封装好的方法
import { request } from "../../request/index.js"
Page({
  /**
   * 页面的初始数据
   */
  data: {
  // 左侧的菜单数据
leftMenuList:[],
// 右侧的商品数据
rightGoodsContent:[],
// 被点击的左侧菜单
currentIndex:0
  },
  // 接口返回的数据
  CatesList:[],

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
this.getCates();
  },
  // 获取分类数据
  getCates(){
    request({
      url:'https://api-hmugo-web.itheima.net/api/public/v1/categories'
    }).then(result=>{
      this.CatesList=result.data.message
      // 构造左侧的大菜单数据
      let leftMenuList=this.CatesList.map(v=>v.cat_name);
      let rightGoodsContent =this.CatesList[0].children
      this.setData({
        leftMenuList,
        rightGoodsContent
      })
      // console.log(result);
      
    })

  },
  // 左侧菜单的点击事件
  handleItemTap(e){
    // console.log(e);
    // 1 获取被点击的标题上的索引
    // 2 给data中的currentIndexfuzhi
    // 3根据不同的索引渲染右侧的页面
    const {index} =e.currentTarget.dataset;
    let rightGoodsContent = this.CatesList[index].children;
    this.setData({
      currentIndex:index,
      rightGoodsContent
    })
  }
  
})

效果图:

6.商品分类页面效果图.gif

2.3.使用缓存技术

 onLoad: function (options) {
    /*
    0 web中的本地存储和小程序中的本地存储的区别
    1 写代码方式不一样
    web:localStorage.setItem("key","value") localstorage.getiItem("key")
    小程序中:wxwx.setStorageSync('key', 'value);wx.getStorageSync('key')
    2 存的时候,有没有做类型转换
    web中会把数据变为字符串
    小程序中不存在类型转换
    1 先判断一下本地存储中有没有旧的数据
    {time:Date.now(),data:[...]}
    2 没有旧数据就直接发送新请求
    3 有旧数据,并且旧数据没有过期,就使用本地存储中的旧数据即可
    */
  //  1 获取本地存储中的数据,(小程序也是存在本地存储技术)
   const Cates=wx.getStorageSync("cates");
  //  2 判断
  if(!Cates){
    this.getCates();
  }else{
    // 有旧的数据,定义过期时间
    if(Date.now()-Cates.time>1000*600){
      // 发送请求
      this.getCates();
    }else{
      // 可以使用旧数据
      this.Cates =Cates.data
       // 构造左侧的大菜单数据
       let leftMenuList=this.Cates.map(v=>v.cat_name);
       let rightGoodsContent =this.Cates[0].children
       this.setData({
         leftMenuList,
         rightGoodsContent
       })

      
    }

  }

在获取分类数据时,把数据存入本地缓存中

image.png

2.4 优化分类页面

使得点击每一个左侧分类时,右侧内容页面都是置顶显示

image.png

2.5 定义公共接口

export const request=(params)=>{
  // 定义公共的url
  // url:https://api-hmugo-web.itheima.net/api/public/v1
  const baseUrl ='https://api-hmugo-web.itheima.net/api/public/v1'
  return new Promise((resolve,reject)=>{
    wx.request({
     ...params,
     url:baseUrl+params.url,
     success:(result)=>{
     resolve(result);
     },
     fail:(err)=>{
       reject(err);
     }
    })
  })
}

2.6 让小程序中支持es7的async语法

image.png

3 商品列表

3.1 实现点击页面跳转和传递参数(分类id)

image.png

传递的参数在商品列表的onload生命周期函数的options里面

3.2 实现搜索框和tabs组件

自定义组件,方法与上面自定义组件类似,这里就以动图演示

7.自定义tab栏组件.gif

3.3.商品列表的静态结构

先用静态图片写死,调样式,然后动态处理

html:

<SearchInput></SearchInput>
<!-- 监听自定义事件 -->
<Tabs tabs="{{tabs}}" bindtabsItemChange="handleTabsItemChange" >
  
  <block wx:if="{{tabs[0].isActive}}">
    <view class="first_tab">
        <navigator class="goods_item"
        wx:for="{{goodsList}}"
        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:'https://ww1.sinaimg.cn/large/007rAy9hgy1g24by9t530j30i20i2glm.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>
  </block>
  <block wx:elif="{{tabs[1].isActive}}">销量</block>
  <block wx:elif="{{tabs[2].isActive}}">价格</block>

</Tabs>

css:

/* pages/goods_list/index.wxss */
.first_tab .goods_item {
  display: flex;
  border-bottom: 1px 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.4 加载下一页数据

image.png

效果图:

8.商品列表详情页.gif

3.5 下拉刷新

image.png

效果图:

9.下拉刷新页面.gif

3.6 添加全局正在加载图标效果

image.png

效果图:

10.请求数据时显示加载中图片.gif

4 商品详情页面

4.1 获取商品详情数据

image.png

4.2 渲染商品详情页(包括轮播图等)

<!--pages/goods_detail/index.wxml-->
<view class="goods_detail">
  <view class="detail_swiper">
    <!-- 轮播图开始 -->
    <swiper autoplay interval="3000" circular indicator-dots indicator-color='skyblue'>
    <swiper-item wx:for="{{goodsObj.pics}}"  wx:key="pics_id">
      <image mode="widthFix" src="{{item.pics_mid}}"></image>

    </swiper-item>
  </swiper>
 </view>
  <!-- 轮播图结束 -->
  <!-- 商品价格开始 -->
  <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">
        <text class="iconfont icon-shoucang"></text>
        <view class="collect_text">
            收藏
        </view>
          
    </view>
</view>
<!-- 商品名称+收藏结束 -->

<!-- 图文详情开始 -->
<view class="goods_info">
    <!-- 标题 -->
    <view class="goods_info_title">图文详情</view>
    <!-- 内容 -->
    <view class="goods_info_content">
        <!-- 富文本 -->
        <rich-text class="" nodes="{{goodsObj.goods_introduce}}">
            
        </rich-text>
          
    </view>
</view>

<!-- 图文详情结束 -->

</view>

样式:

/* pages/goods_detail/index.wxss */
.goods_detail .detail_swiper swiper {
  height: 65vw;
  text-align: center;
}
.goods_detail .detail_swiper swiper image {
  width: 60%;
}
.goods_detail .goods_price {
  padding: 15rpx;
  font-size: 32rpx ;
  font-weight: 600;
  color: var(--themeColor);
}
.goods_detail .goods_name_row {
  border-top: 5rpx solid #dedede;
  border-bottom: 5rpx solid #dedede;
  padding: 10rpx 0;
  display: flex;
}
.goods_detail .goods_name_row .goods_name {
  flex: 5;
  color: #000;
  font-size: 30rpx;
  padding: 0 10rpx;
  display: -webkit-box;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
}
.goods_detail .goods_name_row .goods_collect {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  border-left: 1rpx solid #000;
}
.goods_detail .goods_info .goods_info_title {
  font-size: 32rpx;
  color: var(--themeColor);
  font-weight: 700;
  padding: 20rpx;
}


js:

// 获取商品详情数据
    async getGoodsDetail(goods_id) {
        const goodsObj = await request({ url: "/goods/detail", data: { goods_id } })
        this.setData({
                // 因为只用到这些数据,所以为了提高性能,只需要把这些数据保存
                goodsObj: {
                    pics: goodsObj.pics,
                    goods_name: goodsObj.goods_name,
                    goods_price: goodsObj.goods_price,
                    // 转换图片格式,兼容iPhone手机
                    goods_introduce: goodsObj.goods_introduce.replace(/\.webp/g, '.jpg')

                }
            })
            // console.log(this.data.goodsObj);
    }

效果图:

11.渲染商品详情页.gif

4.4.放大预览图片

image.png

这里api里面的urls里面包含多少个链接放大就会显示多少个链接

效果图:

12.预览图片放大效果.gif

4.5.底部工具栏

<!-- 底部工具栏开始 -->
<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-yixianshi-"></view>
    <view>分享</view>
    <button open-type="share"></button>
  </view>
  <navigator open-type="switchTab" url="/pages/cart/index" class="tool_item">
    <view class="iconfont icon-gouwuche"></view>
    <view>购物车</view>
  </navigator>
  <view class="tool_item btn_cart " bindtap="handleCartAdd">
  加入购物车
  </view>
  <view class="tool_item btn_buy">
    立即购买
  </view>
</view>
<!-- 底部工具栏结束 -->

css:

//    底部工具栏样式
.btm_tool{
    border-top: 1rpx solid #ccc;
    position: fixed;
    left: 0;
    bottom: 0;
    width: 100%;
    height: 90rpx;
    background-color: #fff;
    display: flex;
    .tool_item{
      flex: 1;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      font-size: 24rpx;
      position: relative;
      button{
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        opacity: 0;
      }
    }
    .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;
    }
    .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;
    }
}

这里的button用的非常秒,为了避免button带来的样式影响,所以这样设置

效果图:

13.底部工具栏开发.gif

4.6.加入购物车

这里暂时没有接口,只有存在本地存储中

// 点击加入购物车
    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;
            cart.push(this.goodsInfo)
        } else {
            // 4.已经存在购物车数据了 执行num++
            cart[index].num++
        }
        // 5.把购物车重新添加回缓存中
        wx.setStorageSync("cart", cart);
        // 6.弹窗
        wx.showToast({
            title: '加入成功',
            // 防抖
            icon: 'sucess',
            mask: true
        });
    }

5.购物车

效果图:

14.购物车开发.gif