微信小程序购物车、父子组件传值及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 相关文章推荐
一款js和css代码压缩工具[附JAVA环境配置方法]
Apr 16 Javascript
每天一篇javascript学习小结(Date对象)
Nov 13 Javascript
基于javascript实现按圆形排列DIV元素(一)
Dec 02 Javascript
微信小程序  checkbox组件详解及简单实例
Jan 10 Javascript
利用ES6语法重构React组件详解
Mar 02 Javascript
基于JavaScript实现多级菜单效果
Jul 25 Javascript
jquery获取链接地址和跳转详解(推荐)
Aug 15 jQuery
jQuery实现表格冻结顶栏效果
Aug 20 jQuery
Vue2 配置 Axios api 接口调用文件的方法
Nov 13 Javascript
Express下采用bcryptjs进行密码加密的方法
Feb 07 Javascript
angular2中使用第三方js库的实例
Feb 26 Javascript
vue+SSM实现验证码功能
Dec 07 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
yii2.0实现创建简单widgets示例
2016/07/18 PHP
Laravel使用Queue队列的技巧汇总
2019/09/02 PHP
laravel-admin的图片删除实例
2019/09/30 PHP
PHP程序员简单的开展服务治理架构操作详解(三)
2020/05/14 PHP
javascript中对对层的控制
2006/12/29 Javascript
js实现的网页颜色代码表全集
2007/07/17 Javascript
javascript与CSS复习(三)
2010/06/29 Javascript
js函数获取html中className所在的内容并去除标签
2013/09/08 Javascript
Javascript设置对象的ReadOnly属性(示例代码)
2013/12/25 Javascript
在JavaScript中正确引用bind方法的应用
2015/05/11 Javascript
讲解JavaScript中for...in语句的使用方法
2015/06/03 Javascript
jquery实现表格隔行换色效果
2015/11/19 Javascript
jQuery实现鼠标选文字发新浪微博的方法
2016/04/02 Javascript
JavaScript中从setTimeout与setInterval到AJAX异步
2017/02/13 Javascript
Node.js  事件循环详解及实例
2017/08/06 Javascript
vue Element-ui input 远程搜索与修改建议显示模版的示例代码
2017/10/19 Javascript
浅谈Vue.js 组件中的v-on绑定自定义事件理解
2017/11/17 Javascript
js取小数点后两位四种方法
2019/01/18 Javascript
[05:05]第三天的dota2
2013/07/29 DOTA
[02:49]2014DOTA2电竞也是体育项目! 势要把荣誉带回中国!
2014/07/20 DOTA
[01:03:13]VG vs Pain 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
python中的多重继承实例讲解
2014/09/28 Python
Python读取ini文件、操作mysql、发送邮件实例
2015/01/01 Python
PyTorch搭建多项式回归模型(三)
2019/05/22 Python
Django文件存储 默认存储系统解析
2019/08/02 Python
python模块hashlib(加密服务)知识点讲解
2019/11/25 Python
python使用selenium爬虫知乎的方法示例
2020/10/28 Python
python搜索算法原理及实例讲解
2020/11/18 Python
利用CSS3 动画 绘画 圆形动态时钟
2018/03/20 HTML / CSS
HTML5 Web Database 数据库的SQL语句的使用方法
2012/12/09 HTML / CSS
阿迪达斯墨西哥官方网站:adidas墨西哥
2017/11/03 全球购物
有模特经验的简历自我评价
2013/09/19 职场文书
业绩考核岗位职责
2014/02/01 职场文书
党员三严三实心得体会
2014/10/13 职场文书
图片批量处理 - 尺寸、格式、水印等
2022/03/07 杂记
SQL Server实现分页方法介绍
2022/03/16 SQL Server