折翼天使资源社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 22|回复: 0

[文档教程] 微信小程序之购物车和父子组件传值及calc的注意事项

[复制链接]

6981

主题

7363

帖子

285

积分

网站编辑

Rank: 8Rank: 8

天使之心
0
注册时间
2013-8-22
发表于 2018-11-23 11:22:32 | 显示全部楼层 |阅读模式
在做微信小程序时,觉得小组里对购物车的实现不是很完美,就自己尝试的写了下,然后用到了父子组件传值,父子组件传值的话,和vue框架上是非常相似的,以及calc这个css函数,calc有个注意点,自己不怎么用,一时间有差点忘了,这里记录下

1.效果图

13308056fc084e60beb472a67e2c7229.png

2.子组件实现


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

子组件index.wxml
  1.   
  2. {{commodity.title}}
  3. 规格:{{commodity.standard?commodity.standard:'无'}}
  4. ¥{{commodity.price}}
  5. 删除
复制代码
复制代码
子组件index.wxss
  1. /* 商品 */
  2. .commodityItem{
  3. display: flex;
  4. position: relative;
  5. padding: 10rpx 24rpx 20rpx 30rpx;
  6. box-sizing: border-box;
  7. background: #fff;
  8. transition: all .5s;
  9. }
  10. /* 选择按钮 */
  11. .selectedBtn{
  12. display: flex;
  13. align-items: center;
  14. width: 80rpx;
  15. }
  16. .noSelected{
  17. width: 46rpx;
  18. height: 46rpx;
  19. border-radius: 50%;
  20. border: 1px solid #ef5225;
  21. }
  22. .selectedBtn .selectedImg{
  23. width: 50rpx;
  24. height: 50rpx;
  25. }
  26. /* 商品信息 */
  27. .commodityInfo{
  28. display: flex;
  29. width: calc(100% - 80rpx);
  30. }
  31. .commodityImg{
  32. margin-right: 18rpx;
  33. width: 220rpx;
  34. height: 220rpx;
  35. }
  36. .commodityImg image{
  37. width: 100%;
  38. height: 100%;
  39. vertical-align: middle;
  40. }
  41. /* 商品title */
  42. .commodityTitle{
  43. width: calc(100% - 220rpx);
  44. }
  45. .title{
  46. display: -webkit-box;
  47. width: 100%;
  48. height: 70rpx;
  49. line-height:35rpx;
  50. font-size: 24rpx;
  51. font-weight:600;
  52. overflow: hidden;
  53. -webkit-line-clamp: 2;
  54. -webkit-box-orient: vertical;
  55. }
  56. .standard{
  57. padding-top: 16rpx;
  58. width: 100%;
  59. height: 90rpx;
  60. box-sizing: border-box;
  61. }
  62. .count{
  63. display: flex;
  64. align-items: center;
  65. justify-content: space-between;
  66. width: 100%;
  67. height: 60rpx;
  68. }
  69. /* 删除按钮 */
  70. .deleteBtn{
  71. display: flex;
  72. position: absolute;
  73. width: 70px;
  74. height: 100%;
  75. top: 0rpx;
  76. right: -70px;
  77. flex-direction: column;
  78. align-items: center;
  79. justify-content: center;
  80. background: #ef5225;
  81. }
  82. .deleteImg{
  83. margin-bottom: 10rpx;
  84. width: 50rpx;
  85. height: 50rpx;
  86. vertical-align: middle;
  87. }
  88. .deleteText{
  89. color: #fff;
  90. }
复制代码
复制代码
子组件index.json,这里用了iview中的数字输入框
  1. {
  2. "component": true,
  3. "usingComponents": {
  4. "i-input-number": "/component/iview/input-number/index"
  5. }
  6. }
复制代码
复制代码
子组件index.js
  1. Component({
  2. properties: {
  3. commodity: Object,
  4. },
  5. data: {
  6. touchStart: null,
  7. rightSpace: 0,
  8. selectedNum: 1,
  9. },
  10. methods: {
  11. /* 商品是否选中 */
  12. handleSelect() {
  13. let selectedNum = this.data.selectedNum;
  14. let commodity = this.data.commodity;
  15. if(commodity.isselected == 0) {
  16. commodity.isselected = 1;
  17. } else {
  18. commodity.isselected = 0;
  19. }
  20. this.triggerEvent('handleselect', { commodity, selectedNum})
  21. },
  22. /* 处理触摸滑动开始 */
  23. handleTouchStart(e) {
  24. /* 记录触摸滑动初始位置 */
  25. let touchStart = e.changedTouches[0].clientX;
  26. this.setData({
  27. touchStart
  28. })
  29. },
  30. /* 处理触摸滑动 */
  31. handleTouchMove(e) {
  32. console.log(e)
  33. let moveSpace = e.changedTouches[0].clientX;
  34. let touchStart = this.data.touchStart;
  35. if (touchStart != null) {
  36. if (moveSpace - touchStart > 70) {
  37. this.setData({
  38. touchStart: null,
  39. rightSpace: 0
  40. })
  41. }
  42. else if (moveSpace - touchStart < -70) {
  43. this.setData({
  44. touchStart: null,
  45. rightSpace: 70
  46. })
  47. }
  48. }
  49. },
  50. numChange(e) {
  51. let selectedNum = e.detail.value;
  52. let commodity = this.data.commodity;
  53. this.setData({
  54. selectedNum
  55. })
  56. this.triggerEvent('handleselect', { commodity, selectedNum})
  57. }
  58. }
  59. })
复制代码
复制代码
3.父组件实现

父组件index.wxml,这里用的是假数据,所以操作上会有一些是联调时不必要的操作
  1. {{items.shopname}}
  2. 满¥100包邮,满10件包邮
  3. 全选
  4. 合计:
  5. ¥{{countPrice}}
  6. 结算
  7. ({{countSelectedNum}})
复制代码
复制代码
父组件index.wxss
  1. page{
  2. background: #f8f8f8;
  3. }
  4. .cart{
  5. padding-bottom: 100rpx;
  6. font-size: 26rpx;
  7. }
  8. .item{
  9. border-bottom: 1px solid #eee;
  10. }
  11. /* 头部店铺信息 */
  12. .storeInfo{
  13. display: flex;
  14. padding: 18rpx 0rpx 18rpx 30rpx;
  15. background: #fff;
  16. box-sizing: border-box;
  17. }
  18. .storeInfo .avatar{
  19. width: 56rpx;
  20. height: 56rpx;
  21. border-radius: 50%;
  22. vertical-align: middle;
  23. }
  24. .storeInfo .storeName{
  25. margin-left: 16rpx;
  26. line-height: 56rpx;
  27. }
  28. /* 包邮信息 */
  29. .discount{
  30. padding-left: 30rpx;
  31. height:50rpx;
  32. line-height: 50rpx;
  33. font-size:20rpx;
  34. color: #666;
  35. box-sizing: border-box;
  36. }
  37. /* 底部操作 */
  38. .count{
  39. display: flex;
  40. position: fixed;
  41. padding-left: 30rpx;
  42. bottom: 0;
  43. left: 0;
  44. width: 100%;
  45. height: 100rpx;
  46. line-height: 100rpx;
  47. box-sizing: border-box;
  48. color: #232323;
  49. background: #eee;
  50. }
  51. /* 全选 */
  52. .selectAll{
  53. display: flex;
  54. padding-right: 20rpx;
  55. align-items: center;
  56. width: 25%;
  57. font-size: 30rpx;
  58. }
  59. .selectAll .noSelected{
  60. width: 46rpx;
  61. height: 46rpx;
  62. border-radius: 50%;
  63. border: 1px solid #ef5225;
  64. }
  65. .selectAll .selectedImg{
  66. width: 50rpx;
  67. height: 50rpx;
  68. }
  69. .selectAllText{
  70. margin-left: 18rpx;
  71. }
  72. .countPrice{
  73. position: absolute;
  74. top: 0;
  75. right: 270rpx;
  76. height: 100%;
  77. line-height: 100rpx;
  78. text-align: center;
  79. font-size: 30rpx;
  80. }
  81. .countPrice text{
  82. margin-right: 15rpx;
  83. }
  84. .account{
  85. position: absolute;
  86. top: 0;
  87. right: 0;
  88. width: 270rpx;
  89. height: 100%;
  90. line-height: 100rpx;
  91. text-align: center;
  92. font-size: 30rpx;
  93. background: #ef5225;
  94. color: #fff;
  95. }
复制代码
复制代码
父组件index.json,引用子组件
  1. {
  2. "usingComponents": {
  3. "cart-item": "/component/cart/index"
  4. }
  5. }
复制代码
复制代码
父组件index.js
  1. Page({
  2. data: {
  3. cartList: [
  4. {
  5. shopname: '猫咪小店',
  6. logo: '/images/avatar.jpeg',
  7. shopid: 11,
  8. commodity: [
  9. {
  10. id: 1,
  11. image:'/images/commodity.jpg',
  12. title: '雅诗兰黛鲜活焕亮红石榴晚霜50ml 补水保湿 滋润排浊',
  13. standard: '111 + 黑色',
  14. price: '100',
  15. stock: 10,
  16. quantity: 1,
  17. isselected: 0,
  18. },
  19. {
  20. id: 2,
  21. image:'/images/avatar7.jpg',
  22. title: '雅诗兰黛鲜活焕亮红石榴晚霜50ml 补水保湿 滋润排浊',
  23. price: '10',
  24. stock: 5,
  25. quantity: 1,
  26. isselected: 0,
  27. }
  28. ]
  29. },
  30. {
  31. shopname: '猫咪小店',
  32. logo: '/images/avatar5.jpg',
  33. shopid: 450,
  34. commodity: [
  35. {
  36. id: 3,
  37. image:'/images/commodity.jpg',
  38. title: '雅诗兰黛鲜活焕亮红石榴晚霜50ml 补水保湿 滋润排浊',
  39. price: '90',
  40. stock: 10,
  41. quantity: 1,
  42. isselected: 0,
  43. },
  44. {
  45. id: 4,
  46. image:'/images/avatar7.jpg',
  47. title: '雅诗兰黛鲜活焕亮红石榴晚霜50ml 补水保湿 滋润排浊',
  48. price: '100',
  49. stock: 5,
  50. quantity: 1,
  51. isselected: 0,
  52. },
  53. {
  54. id: 5,
  55. image:'/images/commodity.jpg',
  56. title: '雅诗兰黛鲜活焕亮红石榴晚霜50ml 补水保湿 滋润排浊',
  57. standard: '111 + 黑色',
  58. price: '100',
  59. stock: 2,
  60. quantity: 1,
  61. isselected: 0,
  62. }
  63. ]
  64. },
  65. {
  66. shopname: '猫咪小店',
  67. logo: '/images/avatar.jpeg',
  68. shopid: 550,
  69. commodity: [
  70. {
  71. id: 6,
  72. image:'/images/avatar8.jpg',
  73. title: '雅诗兰黛鲜活焕亮红石榴晚霜50ml 补水保湿 滋润排浊',
  74. standard: '111 + 黑色',
  75. price: '100',
  76. stock: 1,
  77. quantity: 1,
  78. isselected: 0,
  79. }
  80. ]
  81. },
  82. ],
  83. /* 商品是否全选中 */
  84. isSelectedAll: false,
  85. /* 已选中商品的价格 */
  86. countPrice: 0,
  87. /* 统计所有选中的商品数量 */
  88. countSelectedNum: 0,
  89. },
  90. /* 处理商品选中 */
  91. handleSelect(e) {
  92. let countPrice = 0;
  93. let countSelectedNum = 0;
  94. let cartList = this.data.cartList;
  95. let length = cartList.length;
  96. /* 因为是假数据,所以需要循环查找到对应的数据将其替换 */
  97. for(let i = 0; i < length; i++) {
  98. for(let j = 0; j < cartList[i].commodity.length; j++) {
  99. if (cartList[i].commodity[j].id == e.detail.commodity.id) {
  100. cartList[i].commodity[j] = e.detail.commodity;
  101. cartList[i].commodity[j].selectedNum = e.detail.selectedNum;
  102. }
  103. if (cartList[i].commodity[j].isselected == 1) {
  104. /* 点击选中的时候,计算价格,要判断下设置的商品选中数量,
  105. * 我这里的是对点击了的商品才设置了选中的数量,所以需要对没有点击的商品数量设置为1,然后就默认的加一
  106. */
  107. if (cartList[i].commodity[j].selectedNum != undefined) {
  108. countPrice += cartList[i].commodity[j].price * cartList[i].commodity[j].selectedNum;
  109. countSelectedNum += cartList[i].commodity[j].selectedNum
  110. } else {
  111. countPrice += cartList[i].commodity[j].price * 1;
  112. countSelectedNum += 1;
  113. }
  114. }
  115. }
  116. }
  117. /* 对是否全选中进行判断 */
  118. let isSelectedAll = true;
  119. for (let i = 0; i < length; i++) {
  120. for (let j = 0; j < cartList[i].commodity.length; j++) {
  121. /* 若商品中的isselecetd有为0的就终止循环,直接设置为未全选 */
  122. if (cartList[i].commodity[j].isselected == 0) {
  123. isSelectedAll = false;
  124. break;
  125. }
  126. }
  127. }
  128. this.setData({
  129. cartList,
  130. isSelectedAll,
  131. countPrice,
  132. countSelectedNum
  133. })
  134. },
  135. /* 全选中商品 */
  136. handleSelectAll() {
  137. let isSelectedAll = !this.data.isSelectedAll;
  138. let cartList = this.data.cartList;
  139. let length = cartList.length;
  140. let countPrice = 0;
  141. let countSelectedNum = 0;
  142. /* 遍历数据中的isselected来进行全选的操作 */
  143. for(let i = 0; i < length; i++) {
  144. for (let j = 0; j < cartList[i].commodity.length; j++) {
  145. if(isSelectedAll) {
  146. cartList[i].commodity[j].isselected = 1;
  147. /* 全选的时候,计算价格,要判断下设置的商品选中数量,
  148. * 我这里的是对点击了的商品才设置了选中的数量,所以需要对没有点击的商品数量设置为1,然后就默认加一
  149. */
  150. if (cartList[i].commodity[j].selectedNum != undefined) {
  151. countPrice += parseInt(cartList[i].commodity[j].price) * cartList[i].commodity[j].selectedNum;
  152. countSelectedNum += cartList[i].commodity[j].selectedNum;
  153. } else {
  154. countPrice += cartList[i].commodity[j].price * 1;
  155. countSelectedNum += 1;
  156. }
  157. } else {
  158. cartList[i].commodity[j].isselected = 0;
  159. }
  160. }
  161. }
  162. this.setData({
  163. isSelectedAll,
  164. cartList,
  165. countPrice,
  166. countSelectedNum
  167. })
  168. },
  169. })
复制代码
复制代码
4.父子组件传值


  • 较常用的都是父组件往子组件传值,所以子组件往父组件传值就会不是很熟悉
  • 我这里的话,是因为用的假数据,在点击商品选中或者不选中时,需要改变商品里的选中属性,所以用到了子组件往父组件传值,也包括传递选中的商品数量子组件往父组件传值的话,是通过在调用this.triggerEvent()来实现的
  1. /* 在父组件中定义方法:bind:handleselect或者也可以直接写成bindhandleselect*/
复制代码
复制代码
在子组件中调用
  1. this.triggerEvent('handleselect', { commodity, selectedNum})
复制代码
复制代码
这个this.triggerEvent('handleselect', { commodity, selectedNum })方法中,handleselect的名称要与父组件中引用子组件时绑定的方法名称一样,后面的对象就是传递的值,也可以直接是以直接量的形式传递,然后再父组件中通过e.detail来获取对应的值
  1. handleSelect(e) {
  2. console.log(e.detail)
  3. console.log(e.detail.commodity)
  4. console.log(e.detail.selectedNum)
  5. }
复制代码
复制代码
5.calc的注意事项

我以前也遇到过,然后现在再用的时候,一时间把这点给忘了,在看到编译器样式的时候,才猛然想起
  1. .user-content{
  2. padding: 10px 0 10px 50px;
  3. width: calc(100% - 50px); /* 计算宽度,'+'或'-'符号前后有空格 */
  4. height: 18px;
  5. }
复制代码
复制代码

  • css中使用calc可以进行简单的运算:
  • 单位可以是百分比,px,rem,em等单位
  • 使用"+","-","*","/"运算符(使用"+"或者"-"符号时,符号前后必须加上空格)
  • 在Firefox浏览器上使用要加上-moz前缀
  • chrome浏览器上使用要加上-webkit前缀(使用"+"或者"-"符号时,符号前后必须加上空格)


6.部分想法


  • 其实在样式上还是挺快就完成了,就是在计算商品价格的时候,想了挺久
  • 在计算价格时,当时就有点蒙圈,总是想着要怎么判断他是增加数量还是减少数量,然后就陷入死循环的之中。
  • 其实不用想她是增加还是减少数量,因为你都是传的是商品的数量,而且在计算时,也是判断了商品是否选中,所以,直接点,计算价格乘以数量就可以了然后选中的商品数量的统计就和计算价格的思路是一样的了