微信小程式之購物車和父子元件傳值及calc的注意事項
阿新 • • 發佈:2018-12-17
在做微信小程式時,覺得小組裡對購物車的實現不是很完美,就自己嘗試的寫了下,然後用到了父子元件傳值,父子元件傳值的話,和vue框架上是非常相似的,以及calc這個css函式,calc有個注意點,自己不怎麼用,一時間有差點忘了,這裡記錄下
1.效果圖
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.部分想法
- 其實在樣式上還是挺快就完成了,就是在計算商品價格的時候,想了挺久
- 在計算價格時,當時就有點蒙圈,總是想著要怎麼判斷他是增加數量還是減少數量,然後就陷入死迴圈的之中。
- 其實不用想她是增加還是減少數量,因為你都是傳的是商品的數量,而且在計算時,也是判斷了商品是否選中,所以,直接點,計算價格乘以數量就可以了
- 然後選中的商品數量的統計就和計算價格的思路是一樣的了