微信小程序购物车、父子组件传值及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函数代码
Apr 17 Javascript
js中arguments的用法(实例讲解)
Nov 30 Javascript
一个简单的jQuery计算器实现了连续计算功能
Jul 21 Javascript
node.js中的fs.createWriteStream方法使用说明
Dec 17 Javascript
Javascript获取统一管理的提示语(message)
Feb 03 Javascript
Jquery实现的简单轮播效果【附实例】
Apr 19 Javascript
jQuery EasyUI API 中文帮助文档和扩展实例
Aug 01 Javascript
谈谈JavaScript中浏览器兼容问题的写法小议
Dec 17 Javascript
jQuery+pjax简单示例汇总
Apr 21 jQuery
js上传图片预览的实现方法
May 09 Javascript
jQuery 实现批量提交表格多行数据的方法
Aug 09 jQuery
Vue 监听列表item渲染事件方法
Sep 06 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下操作Linux消息队列完成进程间通信的方法
2010/07/24 PHP
PHP字符编码问题之GB2312 VS UTF-8解决方法
2011/06/23 PHP
PHP页面间参数传递的四种方法详解
2013/06/09 PHP
PHP函数rtrim()使用中的怪异现象分析
2017/02/24 PHP
IE浏览器PNG图片透明效果代码
2008/09/02 Javascript
jquery 简单图片导航插件jquery.imgNav.js
2010/03/17 Javascript
JavaScript bold方法入门实例(把指定文字显示为粗体)
2014/10/17 Javascript
nodejs实现bigpipe异步加载页面方案
2016/01/26 NodeJs
node.js连接mongoDB数据库 快速搭建自己的web服务
2016/04/17 Javascript
bootstrap基本配置_动力节点Java学院整理
2017/07/14 Javascript
vue搜索和vue模糊搜索代码实例
2019/05/07 Javascript
使用vue-cli3 创建vue项目并配置VS Code 自动代码格式化 vue语法高亮问题
2019/05/14 Javascript
小程序如何定位所在城市及发起周边搜索
2020/02/11 Javascript
原生js实现简单轮播图
2020/10/26 Javascript
python实现带验证码网站的自动登陆实现代码
2015/01/12 Python
基python实现多线程网页爬虫
2015/09/06 Python
Win7下搭建python开发环境图文教程(安装Python、pip、解释器)
2016/05/17 Python
Python彩色化Linux的命令行终端界面的代码实例分享
2016/07/02 Python
python制作企业邮箱的爆破脚本
2016/10/05 Python
Python实现字符串逆序输出功能示例
2017/06/24 Python
Python编程实现正则删除命令功能
2017/08/30 Python
使用celery和Django处理异步任务的流程分析
2020/02/19 Python
Python 开发工具PyCharm安装教程图文详解(新手必看)
2020/02/28 Python
Pytorch转keras的有效方法,以FlowNet为例讲解
2020/05/26 Python
Hunkemöller西班牙:欧洲最大的内衣连锁店
2018/08/15 全球购物
Lookfantastic美国/加拿大:英国知名美妆购物网站
2019/03/27 全球购物
毕业生多媒体设计求职信
2013/10/12 职场文书
技术人员面试提纲
2013/11/28 职场文书
店长助理岗位职责
2013/12/13 职场文书
文明风采获奖感言
2014/02/18 职场文书
暑期社会实践心得体会
2014/09/02 职场文书
庆七一宣传标语
2014/10/08 职场文书
节约用水广告语60条
2019/11/14 职场文书
使用Python的开发框架Brownie部署以太坊智能合约
2021/05/28 Python
Python将CSV文件转化为HTML文件的操作方法
2021/06/30 Python
Golang map映射的用法
2022/04/22 Golang