欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

微信小程序购物车、父子组件传值及calc的注意事项总结

程序员文章站 2022-06-06 09:35:33
前言 在做微信小程序时,觉得小组里对购物车的实现不是很完美,就自己尝试的写了下,然后用到了父子组件传值,父子组件传值的话,和vue框架上是非常相似的,以及calc这个cs...

前言

在做微信小程序时,觉得小组里对购物车的实现不是很完美,就自己尝试的写了下,然后用到了父子组件传值,父子组件传值的话,和vue框架上是非常相似的,以及calc这个css函数,calc有个注意点,自己不怎么用,一时间有差点忘了,这里记录下

下面话不多说了,来一起看看详细的介绍吧

1.效果图

微信小程序购物车、父子组件传值及calc的注意事项总结

2.子组件实现

要实现图中删除的效果,使用组件的形式更好做点,我当时本想直接在pages里实现,不过结果就是,滑动时,所有的商品都显示了删除按钮,除非用数组将每个商品要移动的距离存储起来,不过这样的话就很麻烦,所以我也是用组件来实现的

关于微信组件,可以直接点击链接访问官网查看

子组件index.wxml

<view class="commodityitem" bindtouchstart="handletouchstart" bindtouchmove="handletouchmove" style="transform:translatex({{-rightspace}}px)">
 <view class="selectedbtn" bindtap="handleselect" data-is-selected="{{commodity.isselected}}">
 <view class="noselected" wx:if="{{commodity.isselected==0}}"></view>
 <image class="selectedimg" wx:else src="/images/selected.png"></image>
 </view>
 <view class="commodityinfo">
 <view class="commodityimg">
 <image src="{{commodity.image}}"></image>  
 </view>
 <view class="commoditytitle">
 <view class="title">{{commodity.title}}</view>
 <view class="standard">规格:{{commodity.standard?commodity.standard:'无'}}</view>
 <view class="count">
 <view class="price">¥{{commodity.price}}</view>
 <view class="commoditynum">
  <i-input-number value="{{selectednum}}" min="1" max="{{commodity.stock}}" bindchange="numchange" />
 </view>
 </view>
 </view>
 </view>
 <view class="deletebtn">
 <image class="deleteimg" src="/images/delete.png"></image>
 <text class="deletetext">删除</text>
 </view>
</view>

子组件index.wxss

/* 商品 */
.commodityitem{
 display: flex;
 position: relative;
 padding: 10rpx 24rpx 20rpx 30rpx;
 box-sizing: border-box;
 background: #fff;
 transition: all .5s;
}
/* 选择按钮 */
.selectedbtn{
 display: flex;
 align-items: center;
 width: 80rpx;
}
.noselected{
 width: 46rpx;
 height: 46rpx;
 border-radius: 50%;
 border: 1px solid #ef5225;
}
.selectedbtn .selectedimg{
 width: 50rpx;
 height: 50rpx;
}
/* 商品信息 */
.commodityinfo{
 display: flex;
 width: calc(100% - 80rpx);
}
.commodityimg{
 margin-right: 18rpx;
 width: 220rpx;
 height: 220rpx;
}
.commodityimg image{
 width: 100%;
 height: 100%;
 vertical-align: middle; 
}
/* 商品title */
.commoditytitle{
 width: calc(100% - 220rpx);
}
.title{
 display: -webkit-box;
 width: 100%;
 height: 70rpx;
 line-height:35rpx;
 font-size: 24rpx;
 font-weight:600;
 overflow: hidden;
 -webkit-line-clamp: 2;
 -webkit-box-orient: vertical;
}
.standard{
 padding-top: 16rpx;
 width: 100%;
 height: 90rpx;
 box-sizing: border-box;
}
.count{
 display: flex;
 align-items: center;
 justify-content: space-between;
 width: 100%;
 height: 60rpx;
}

/* 删除按钮 */
.deletebtn{
 display: flex;
 position: absolute;
 width: 70px;
 height: 100%;
 top: 0rpx;
 right: -70px;
 flex-direction: column;
 align-items: center;
 justify-content: center;
 background: #ef5225;
}
.deleteimg{
 margin-bottom: 10rpx;
 width: 50rpx;
 height: 50rpx;
 vertical-align: middle;
}
.deletetext{
 color: #fff;
}

子组件index.json,这里用了iview中的

{
 "component": true,
 "usingcomponents": {
 "i-input-number": "/component/iview/input-number/index"
 }
}

子组件index.js

component({

 properties: {
 commodity: object,
 },

 data: {
 touchstart: null,
 rightspace: 0,
 selectednum: 1,
 },

 methods: {
 /* 商品是否选中 */
 handleselect() {
  let selectednum = this.data.selectednum;
 let commodity = this.data.commodity;
 if(commodity.isselected == 0) {
 commodity.isselected = 1;
 } else {
 commodity.isselected = 0;
 }
  this.triggerevent('handleselect', { commodity, selectednum})
 },
 /* 处理触摸滑动开始 */
 handletouchstart(e) {
 /* 记录触摸滑动初始位置 */
 let touchstart = e.changedtouches[0].clientx;
 this.setdata({
 touchstart
 })
 },
 /* 处理触摸滑动 */
 handletouchmove(e) {
 console.log(e)
 let movespace = e.changedtouches[0].clientx;
 let touchstart = this.data.touchstart;
 if (touchstart != null) {
 if (movespace - touchstart > 70) {
  this.setdata({
  touchstart: null,
  rightspace: 0
  })
 }
 else if (movespace - touchstart < -70) {
  this.setdata({
  touchstart: null,
  rightspace: 70
  })
 }
 }
 },
 numchange(e) {
 let selectednum = e.detail.value;
 let commodity = this.data.commodity;
 this.setdata({
  selectednum
 })
 this.triggerevent('handleselect', { commodity, selectednum})
 }
 }
})

3.父组件实现

父组件index.wxml,这里用的是假数据,所以操作上会有一些是联调时不必要的操作

<view class="cart">
 <view class="item" wx:for="{{cartlist}}" wx:key="{{items.shopid}}" wx:for-item="items">
 <view class="storeinfo">
  <image class="avatar" src="{{items.logo}}"></image>
  <view class="storename">{{items.shopname}}</view>
 </view>
 <view class="discount">满¥100包邮,满10件包邮</view>
 <view class="commodity" wx:for="{{items.commodity}}" wx:key="{{item.id}}">
  <cart-item commodity="{{item}}" bind:handleselect="handleselect" />
 </view>
 </view>
 <view class="count">
  <view class="selectall" bindtap="handleselectall">
   <view class="noselected" wx:if="{{!isselectedall}}"></view>
  <image class="selectedimg" wx:else src="/images/selected.png"></image>
   <text class="selectalltext">全选</text>
  </view>
  <view class="countprice">
  <text>合计:</text>
  <text>¥{{countprice}}</text>
 </view>
  <view class="account">
  <text>结算</text>
  <text>({{countselectednum}})</text>
 </view>
 </view>
</view>

父组件index.wxss

page{
 background: #f8f8f8;
}
.cart{
 padding-bottom: 100rpx;
 font-size: 26rpx;
}
.item{
 border-bottom: 1px solid #eee;
}
/* 头部店铺信息 */
.storeinfo{
 display: flex;
 padding: 18rpx 0rpx 18rpx 30rpx;
 background: #fff;
 box-sizing: border-box;
}
.storeinfo .avatar{
 width: 56rpx;
 height: 56rpx;
 border-radius: 50%;
 vertical-align: middle;
}
.storeinfo .storename{
 margin-left: 16rpx;
 line-height: 56rpx;
}
/* 包邮信息 */
.discount{
 padding-left: 30rpx;
 height:50rpx;
 line-height: 50rpx;
 font-size:20rpx;
 color: #666;
 box-sizing: border-box;
}
/* 底部操作 */
.count{
 display: flex;
 position: fixed;
 padding-left: 30rpx;
 bottom: 0;
 left: 0;
 width: 100%;
 height: 100rpx;
 line-height: 100rpx;
 box-sizing: border-box;
 color: #232323;
 background: #eee;
}
/* 全选 */
.selectall{
 display: flex;
 padding-right: 20rpx;
 align-items: center;
 width: 25%;
 font-size: 30rpx;
}
.selectall .noselected{
 width: 46rpx;
 height: 46rpx;
 border-radius: 50%;
 border: 1px solid #ef5225;
}
.selectall .selectedimg{
 width: 50rpx;
 height: 50rpx;
}
.selectalltext{
 margin-left: 18rpx;
}

.countprice{
 position: absolute;
 top: 0;
 right: 270rpx;
 height: 100%;
 line-height: 100rpx;
 text-align: center;
 font-size: 30rpx;
}
.countprice text{
 margin-right: 15rpx;
}
.account{
 position: absolute;
 top: 0;
 right: 0;
 width: 270rpx;
 height: 100%;
 line-height: 100rpx;
 text-align: center;
 font-size: 30rpx;
 background: #ef5225;
 color: #fff;
}
父组件index.json,引用子组件
{
 "usingcomponents": {
 "cart-item": "/component/cart/index"
 }
}

父组件index.js

page({

 data: {
 cartlist: [
  {
  shopname: '猫咪小店',
  logo: '/images/avatar.jpeg',
  shopid: 11,
  commodity: [
   {
   id: 1,
   image:'/images/commodity.jpg',
   title: '雅诗兰黛鲜活焕亮红石榴晚霜50ml 补水保湿 滋润排浊',
   standard: '111 + 黑色',
   price: '100',
   stock: 10,
   quantity: 1,
   isselected: 0,
   }, 
   {
   id: 2,
   image:'/images/avatar7.jpg',
   title: '雅诗兰黛鲜活焕亮红石榴晚霜50ml 补水保湿 滋润排浊',
   price: '10',
   stock: 5,
   quantity: 1,
   isselected: 0,
   }
  ]
  },
  {
  shopname: '猫咪小店',
  logo: '/images/avatar5.jpg',
  shopid: 450,
  commodity: [
   {
   id: 3,
   image:'/images/commodity.jpg',
   title: '雅诗兰黛鲜活焕亮红石榴晚霜50ml 补水保湿 滋润排浊',
   price: '90',
   stock: 10,
   quantity: 1,
   isselected: 0,
   },
   {
   id: 4,
   image:'/images/avatar7.jpg',
   title: '雅诗兰黛鲜活焕亮红石榴晚霜50ml 补水保湿 滋润排浊',
   price: '100',
   stock: 5,
   quantity: 1,
   isselected: 0,
   }, 
   {
   id: 5,
   image:'/images/commodity.jpg',
   title: '雅诗兰黛鲜活焕亮红石榴晚霜50ml 补水保湿 滋润排浊',
   standard: '111 + 黑色',
   price: '100',
   stock: 2,
   quantity: 1,
   isselected: 0,
   }
  ]
  },
  {
  shopname: '猫咪小店',
  logo: '/images/avatar.jpeg',
  shopid: 550,
  commodity: [
   {
   id: 6,
   image:'/images/avatar8.jpg',
   title: '雅诗兰黛鲜活焕亮红石榴晚霜50ml 补水保湿 滋润排浊',
   standard: '111 + 黑色',
   price: '100',
   stock: 1,
   quantity: 1,
   isselected: 0,
   }
  ]
  },
 ],
  /* 商品是否全选中 */
  isselectedall: false,
  /* 已选中商品的价格 */
  countprice: 0,
 /* 统计所有选中的商品数量 */
 countselectednum: 0,
 },
 /* 处理商品选中 */
 handleselect(e) {
  let countprice = 0;
 let countselectednum = 0;
 let cartlist = this.data.cartlist;
 let length = cartlist.length;

  /* 因为是假数据,所以需要循环查找到对应的数据将其替换 */
 for(let i = 0; i < length; i++) {
  for(let j = 0; j < cartlist[i].commodity.length; j++) {
    if (cartlist[i].commodity[j].id == e.detail.commodity.id) {
   cartlist[i].commodity[j] = e.detail.commodity;
   cartlist[i].commodity[j].selectednum = e.detail.selectednum;
  }
  if (cartlist[i].commodity[j].isselected == 1) {
   /* 点击选中的时候,计算价格,要判断下设置的商品选中数量,
   * 我这里的是对点击了的商品才设置了选中的数量,所以需要对没有点击的商品数量设置为1,然后就默认的加一
   */
   if (cartlist[i].commodity[j].selectednum != undefined) {
   countprice += cartlist[i].commodity[j].price * cartlist[i].commodity[j].selectednum;
   countselectednum += cartlist[i].commodity[j].selectednum
   } else {
   countprice += cartlist[i].commodity[j].price * 1;
   countselectednum += 1;
   }
  }
  }
 }

  /* 对是否全选中进行判断 */
  let isselectedall = true;
  for (let i = 0; i < length; i++) {
   for (let j = 0; j < cartlist[i].commodity.length; j++) {
    /* 若商品中的isselecetd有为0的就终止循环,直接设置为未全选 */
    if (cartlist[i].commodity[j].isselected == 0) {
     isselectedall = false;
     break;
    }
   }
  }

 this.setdata({
  cartlist,
   isselectedall,
   countprice,
  countselectednum
 })
 },
 /* 全选中商品 */
 handleselectall() {
  let isselectedall = !this.data.isselectedall;
  let cartlist = this.data.cartlist;
  let length = cartlist.length;
 let countprice = 0;
 let countselectednum = 0;

  /* 遍历数据中的isselected来进行全选的操作 */
  for(let i = 0; i < length; i++) {
   for (let j = 0; j < cartlist[i].commodity.length; j++) {
    if(isselectedall) {
     cartlist[i].commodity[j].isselected = 1;
   /* 全选的时候,计算价格,要判断下设置的商品选中数量,
   * 我这里的是对点击了的商品才设置了选中的数量,所以需要对没有点击的商品数量设置为1,然后就默认加一
   */
   if (cartlist[i].commodity[j].selectednum != undefined) {
   countprice += parseint(cartlist[i].commodity[j].price) * cartlist[i].commodity[j].selectednum;
   countselectednum += cartlist[i].commodity[j].selectednum;
   } else {
   countprice += cartlist[i].commodity[j].price * 1; 
   countselectednum += 1;  
   }
    } else {
     cartlist[i].commodity[j].isselected = 0;
    }
   }
  }

  this.setdata({
   isselectedall,
   cartlist,
  countprice,
  countselectednum
  })
 },
})

4.父子组件传值

较常用的都是父组件往子组件传值,所以子组件往父组件传值就会不是很熟悉

我这里的话,是因为用的假数据,在点击商品选中或者不选中时,需要改变商品里的选中属性,所以用到了子组件往父组件传值,也包括传递选中的商品数量

子组件往父组件传值的话,是通过在调用this.triggerevent()来实现的

/* 在父组件中定义方法:bind:handleselect或者也可以直接写成bindhandleselect*/
<cart-item commodity="{{item}}" bind:handleselect="handleselect" />

在子组件中调用

this.triggerevent('handleselect', { commodity, selectednum})

这个this.triggerevent('handleselect', { commodity, selectednum })方法中,handleselect的名称要与父组件中引用子组件时绑定的方法名称一样,后面的对象就是传递的值,也可以直接是以直接量的形式传递,然后再父组件中通过e.detail来获取对应的值

handleselect(e) {
 console.log(e.detail)
 console.log(e.detail.commodity)
 console.log(e.detail.selectednum)
}

5.calc的注意事项

我以前也遇到过,然后现在再用的时候,一时间把这点给忘了,在看到编译器样式的时候,才猛然想起

.user-content{
 padding: 10px 0 10px 50px;
 width: calc(100% - 50px); /* 计算宽度,'+'或'-'符号前后有空格 */
 height: 18px;
}

css中使用calc可以进行简单的运算:

单位可以是百分比,px,rem,em等单位

使用"+","-","*","/"运算符(使用"+"或者"-"符号时,符号前后必须加上空格)

在firefox浏览器上使用要加上-moz前缀

chrome浏览器上使用要加上-webkit前缀

(使用"+"或者"-"符号时,符号前后必须加上空格)

6.部分想法

其实在样式上还是挺快就完成了,就是在计算商品价格的时候,想了挺久

在计算价格时,当时就有点蒙圈,总是想着要怎么判断他是增加数量还是减少数量,然后就陷入死循环的之中。

其实不用想她是增加还是减少数量,因为你都是传的是商品的数量,而且在计算时,也是判断了商品是否选中,所以,直接点,计算价格乘以数量就可以了

然后选中的商品数量的统计就和计算价格的思路是一样的了

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。