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


Posted in Javascript onNovember 14, 2018

前言

在做微信小程序时,觉得小组里对购物车的实现不是很完美,就自己尝试的写了下,然后用到了父子组件传值,父子组件传值的话,和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.部分想法

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

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

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

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

总结

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

Javascript 相关文章推荐
ajaxControlToolkit AutoCompleteExtender的用法
Oct 30 Javascript
jquery怎样实现ajax联动框(一)
Mar 08 Javascript
如何创建一个JavaScript弹出DIV窗口层的效果
Sep 25 Javascript
Egret引擎开发指南之视觉编程
Sep 03 Javascript
基于zepto.js实现仿手机QQ空间的大图查看组件ImageView.js详解
Mar 05 Javascript
JS文字球状放大效果代码分享
Aug 19 Javascript
JavaScript事件处理的方式(三种)
Apr 26 Javascript
简单实现的JQuery文本框水印插件
Jun 14 Javascript
JavaScript实现刷新不重记的倒计时
Aug 10 Javascript
ES6实现的遍历目录函数示例
Apr 07 Javascript
Vue.js子组件向父组件通信的方法实例代码详解
Dec 10 Javascript
微信小程序的授权实现过程解析
Aug 02 Javascript
微信小程序中遇到的iOS兼容性问题小结
Nov 14 #Javascript
javascript中一些奇葩的日期换算方法总结
Nov 14 #Javascript
element vue Array数组和Map对象的添加与删除操作
Nov 14 #Javascript
ES6 fetch函数与后台交互实现
Nov 14 #Javascript
vue-cli3全面配置详解
Nov 14 #Javascript
详解IOS微信上Vue单页面应用JSSDK签名失败解决方案
Nov 14 #Javascript
laydate时间日历插件使用方法详解
Nov 14 #Javascript
You might like
PHP 上传文件的方法(类)
2009/07/30 PHP
innerhtml用法 innertext用法 以及innerHTML与innertext的区别
2009/10/26 Javascript
jquery与google map api结合使用 控件,监听器
2010/03/04 Javascript
SyntaxHighlighter语法高亮插件使用说明
2011/08/14 Javascript
web的各种前端打印方法之jquery打印插件PrintArea实现网页打印
2013/01/09 Javascript
JavaScript 对任意元素,自定义右键菜单的实现方法
2013/05/08 Javascript
jquery监听div内容的变化具体实现思路
2013/11/04 Javascript
node.js中的fs.fchmodSync方法使用说明
2014/12/16 Javascript
基于javascript实现九九乘法表
2016/03/27 Javascript
完美JQuery图片切换效果的简单实现
2016/07/21 Javascript
JS 调用微信扫一扫功能
2016/12/22 Javascript
详解vue渲染从后台获取的json数据
2017/07/06 Javascript
React Native使用百度Echarts显示图表的示例代码
2017/11/07 Javascript
Vue.js 的移动端组件库mint-ui实现无限滚动加载更多的方法
2017/12/23 Javascript
Vue项目使用CDN优化首屏加载问题
2018/04/01 Javascript
Windows下安装 node 的版本控制工具 nvm
2020/02/06 Javascript
python 运算符 供重载参考
2009/06/11 Python
Python实现短网址ShortUrl的Hash运算实例讲解
2015/08/10 Python
Python使用Pycrypto库进行RSA加密的方法详解
2016/06/06 Python
python中import reload __import__的区别详解
2017/10/16 Python
python 简单备份文件脚本v1.0的实例
2017/11/06 Python
linux中如何使用python3获取ip地址
2019/07/15 Python
python中类的输出或类的实例输出为这种形式的原因
2019/08/12 Python
Python远程方法调用实现过程解析
2020/07/28 Python
python 利用matplotlib在3D空间中绘制平面的案例
2021/02/06 Python
阻止移动设备(手机、pad)浏览器双击放大网页的方法
2014/06/03 HTML / CSS
使用html2canvas.js实现页面截图并显示或上传的示例代码
2018/12/18 HTML / CSS
Boden英国官网:英国知名原创时装品牌
2018/11/06 全球购物
校园餐饮创业计划书
2014/01/10 职场文书
十岁生日父母答谢词
2014/01/18 职场文书
全民健身日活动方案
2014/01/29 职场文书
公司聘任书模板
2014/03/29 职场文书
幼儿教师暑期培训方案
2014/08/27 职场文书
2015年全国爱眼日活动小结
2015/02/27 职场文书
2016年端午节寄语
2015/12/04 职场文书
2019下半年英语教师的教学工作计划(3篇)
2019/09/25 职场文书