微信小程序购物车、父子组件传值及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 相关文章推荐
JQuery防止退格键网页后退的实现代码
Mar 23 Javascript
在JavaScript并非所有的一切都是对象
Apr 11 Javascript
jQuery之自动完成组件的深入解析
Jun 19 Javascript
jQuery 处理页面的事件详解
Jan 20 Javascript
JavaScript实现星星等级评价功能
Mar 22 Javascript
vue router2.0二级路由的简单使用
Jul 05 Javascript
微信小程序 获取javascript 里的数据
Aug 17 Javascript
基于js中style.width与offsetWidth的区别(详解)
Nov 12 Javascript
webpack4与babel配合使es6代码可运行于低版本浏览器的方法
Oct 12 Javascript
基于vue通用表单解决方案的思考与分析
Mar 16 Javascript
vue全局自定义指令-元素拖拽的实现代码
Apr 14 Javascript
element-ui中Table表格省市区合并单元格的方法实现
Aug 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
php实现mysql数据库备份类
2008/03/20 PHP
PHP速成大法
2015/01/30 PHP
php源码分析之DZX1.5字符串截断函数cutstr用法
2015/06/17 PHP
PHP aes (ecb)解密后乱码问题
2015/06/22 PHP
php自定义时间转换函数示例
2016/12/07 PHP
Javascript实例教程(19) 使用HoTMetal(4)
2006/12/23 Javascript
javascript 动态添加事件代码
2008/11/30 Javascript
jQuery AJAX回调函数this指向问题
2010/02/08 Javascript
jquery刷新页面的实现代码(局部及全页面刷新)
2011/07/11 Javascript
JavaScript实现控制打开文件另存为对话框的方法
2015/04/17 Javascript
jQuery获取某天的农历日期并判断是否除夕或新年的方法
2016/03/01 Javascript
html、css和jquery相结合实现简单的进度条效果实例代码
2016/10/24 Javascript
Vue.set() this.$set()引发的视图更新思考及注意事项
2018/08/30 Javascript
详解ESLint在Vue中的使用小结
2018/10/15 Javascript
NestJs 静态目录配置详解
2019/03/12 Javascript
使用Vue.set()方法实现响应式修改数组数据步骤
2019/11/09 Javascript
请求时token过期自动刷新token操作
2020/09/11 Javascript
vue项目实现减少app.js和vender.js的体积操作
2020/11/12 Javascript
[06:24]DOTA2 2015国际邀请赛中国区预选赛第二日TOP10
2015/05/27 DOTA
[46:55]完美世界DOTA2联赛决赛 FTD vs Phoenix 第三场 11.08
2020/11/11 DOTA
Python cx_freeze打包工具处理问题思路及解决办法
2016/02/13 Python
Python RabbitMQ消息队列实现rpc
2018/05/30 Python
Python实现App自动签到领取积分功能
2018/09/29 Python
对pandas的行列名更改与数据选择详解
2018/11/12 Python
python实现海螺图片的方法示例
2019/05/12 Python
基于django ManyToMany 使用的注意事项详解
2019/08/09 Python
MAC平台基于Python Appium环境搭建过程图解
2020/08/13 Python
python数据抓取3种方法总结
2021/02/07 Python
测绘工程个人的自我评价
2013/11/10 职场文书
大班下学期个人总结
2015/02/13 职场文书
父亲节寄语大全
2015/02/27 职场文书
白银帝国观后感
2015/06/17 职场文书
法定授权委托证明书
2015/06/18 职场文书
web前端之css水平居中代码解析
2021/05/20 HTML / CSS
python正则表达式re.search()的基本使用教程
2021/05/21 Python
MySQL之PXC集群搭建的方法步骤
2021/05/25 MySQL