uniapp实现购物车功能
周六我看见一个有个公司招聘需要试岗3天,并使用uniapp完成购物车,直播间,地图,首页四个功能方能通过,于是乎,我趁手上没事就打算自己写一遍,虽然我的项目没用到,但是多掌握点总没错。今天就来第一个——购物车
因为没写接口,上面的数据我都是使用的模拟数据搭建而成,主要实现的是它件数的增加减少与支付总价格的逻辑关系,还有左边的全选和全不选。
首先第一步,我们把页面先搭建出来
<template>
<view>
//购物车没商品出现的页面
<view class="empty" v-if="show==false">
<image src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.51yuansu.com%2Fpic3%2Fcover%2F01%2F82%2F40%2F596fa6dc00bb4_610.jpg&refer=http%3A%2F%2Fpic.51yuansu.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1633499781&t=d37222e32213957ddbdd01d62e071309" mode="widthFix" style="width: 400rpx;"></image>
<view class="empty-text">空空如也的购物</view>
<view class="empty-button" @click="goshopping">去选购</view>
</view>
//购物车加购商品的东西
<view v-if="show==true">
//通过循环商品呈现出每个商品
<view class="goods-detail" v-for="(item,index) in goods" :key="index">
<view class="detail-left">
<view class="goods-left">
//商品的选择框
<checkbox-group @change="selected(item)">
<label>
<checkbox class="selected" color="#555555" :checked="checked"/>
</label>
</checkbox-group>
<image :src="item.goodsImage" mode="widthFix" style="width: 150rpx;"></image>
</view>
<view class="size">
<text style="font-size: 25rpx;">尺码:{{item.size}}</text>
<text class="goods-price">¥{{item.price}}/件</text>
</view>
</view>
<view class="detail-right">
<text class="subtract" @click="reduce(item)">-</text>
<text class="num">{{item.num}}</text>
<text @click="add(item)" class="add">+</text>
</view>
</view>
</view>
//全选总计
<view class="end">
<view class="end-left">
<checkbox-group @change="selectgoods()">
<label>
<checkbox :checked="allchecked" />全选
</label>
</checkbox-group>
<view>
总计:<text style="color: #f00;font-weight: bold;">¥ {{totalPrice}}</text>
</view>
</view>
<view class="end-right">
结算({{totalNum}})
</view>
</view>
</view>
</template>
这是我们页面的结构
其中最开始有一个v-if,大家都知道这是用于条件判断的
<view class="empty" v-if="show==false">
<image src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.51yuansu.com%2Fpic3%2Fcover%2F01%2F82%2F40%2F596fa6dc00bb4_610.jpg&refer=http%3A%2F%2Fpic.51yuansu.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1633499781&t=d37222e32213957ddbdd01d62e071309" mode="widthFix" style="width: 400rpx;"></image>
<view class="empty-text">空空如也的购物</view>
<view class="empty-button" @click="goshopping">去选购</view>
</view>
这一块就是当我们的购物车是空的时候,页面应该呈现的样子。我也给大家截图出来了
最开始的那个标题栏是因为我觉得没商品的时候黄色和它更搭,有商品的时候红色更美。毕竟我们做前端的,样式还是很重要的。哈哈哈。
结构搭好了,接下来是样式的完成。
<style lang="scss">
.goods{
line-height: 80rpx;
background-color: #FFFFFF;
&-detail{
display: flex;
padding: 30rpx 15rpx 30rpx 30rpx;
background-color: #fff;
justify-content: space-between;
border-bottom: 5rpx solid #F1F1F1;
align-items: center;
.detail-left{
display: flex;
.goods-left{
display: flex;
align-items: center;
}
}
.size{
display: flex;
justify-content: space-around;
flex-direction: column;
margin-left: 30rpx;
.goods-price{
font-size: 25rpx;
color: #F44545;
}
}
.detail-right{
text{
width: 50rpx;
line-height: 50rpx;
text-align: center;
display: inline-block;
background-color: #F7F7F7;
margin-right: 10rpx;
}
.add {
color: #FA4305;
border-radius: 10rpx 30rpx 30rpx 10rpx;
margin-right: 20rpx;
}
.subtract{
border-radius: 30rpx 10rpx 10rpx 30rpx;
}
}
}
}
.empty{
position: relative;
top: 220rpx;
text-align: center;
display: flex;
align-items: center;
flex-direction: column;
&-text{
color: #808080;
margin-bottom: 50rpx;
}
&-button{
width: 300rpx;
height: 90rpx;
color:orange;
border: 1rpx solid orange;
text-align: center;
line-height: 90rpx;
border-radius: 48rpx;
}
}
.end{
width: 100%;
height: 90rpx;
background-color:#fff;
position: fixed;
bottom: 100rpx;
left: 0;
display: flex;
align-items: center;
&-left{
width: 70%;
display: flex;
justify-content: space-between;
padding: 0 30rpx;
.end-flex{
display: flex;
align-items: center;
}
}
&-right{
width: 30%;
line-height: 90rpx;
background-color: #F44545;
text-align: center;
color: #fff;
}
}
</style>
基本结构样式写完以后就变成刚刚我们看到的第一张图了,接下来就是对功能的实现了。
先把我们页面需要的假数据写出来
<script>
export default{
data(){
return{
//购物车有无商品
show:true,
//全选是否选中
allchecked:true,
checked:true,
//商品
goods:[{
size:"女款-M",
num:1,
flag:true,
price:149,
goodsImage:"https://img0.baidu.com/it/u=4158246207,3235707994&fm=26&fmt=auto&gp=0.jpg",
},
{
size:"女款-xs",
num:1,
flag:true,
price:219,
goodsImage:"https://img0.baidu.com/it/u=811765333,1656843554&fm=11&fmt=auto&gp=0.jpg",
},
{
size:"女款-L",
num:1,
flag:true,
price:240,
goodsImage:"https://img1.baidu.com/it/u=233755383,2522308225&fm=26&fmt=auto&gp=0.jpg",
},
{
size:"女款-XXL",
num:1,
flag:true,
price:410,
goodsImage:"https://img0.baidu.com/it/u=3894000947,2570065196&fm=26&fmt=auto&gp=0.jpg",
},
{
size:"女款-XXL",
num:1,
flag:true,
price:500,
goodsImage:"https://img2.baidu.com/it/u=1001625387,3275765924&fm=26&fmt=auto&gp=0.jpg",
},
],
}
},
method:{},
computed:{}
}
</script>
接下来就是实现各个功能了,在方法中定义我们需要的
首先页面上有个跳转,在购物车没有东西的时候,我们需要跳转页面去挑选东西
goshopping(){
uni.navigateTo({
url:'../contact/contact'
})
},
地址根据自己需要的页面地址书写。
接下来就要处理我们每个商品旁边的复选框选中状态与我们全选的关系。
如果所有商品都是选中状态,那我们的全选框也应该被选中。
-
先来判断单个商品的selected(item)
1.如果商品都没被选中,则全选为空。
2.通过循环所有商品,如果全部的flag==true,则表示都被选中,则全选选中,有一个没被选中,全选都应该处于没选中状态。
代码如下:selected(item){
item.flag=!item.flag
if(!item.flag){
this.allchecked=false}else{ const a=this.goods.forEach((item)=>{ return item.flag===true }) if(a){ this.allchecked=true }else{ this.allchecked=false } } },
通过判断单选来实现全选的功能后,接下来我们也要通过全选按钮来控制所有商品的选中状态
-
再来注册全选按钮事件selectgoods()
1.点击全选按钮,所有商品全部选中
2.取消全选选中状态,商品全部处于没选中。selectgoods(){
this.allchecked=!this.allchecked
if(this.allchecked){
this.goods.map(item=>{
this.checked=true
item.flag=true
})
}else{
this.checked=false
this.goods.map(item=>{
item.flag=false
})
}
},
处理完左边的商品选中状态以后,接下来我们要实现对商品的数量的处理,
首先要实现商品数量的增减,一般来说,商品数量是不可能的为0的,为0则表示商品已经不在,所以该商品数量至少为1。
-
商品数量的增加add,
点击+号数量增加一add(item){
let num =item.num
item.num=num+1
} -
商品数量的减少
1.点击则数量减少1
2.数量最少为1,为1后则不能递减reduce(item){
let num=item.num
if(num>1){
num-=1
}else if(num=1){
uni.showToast({
title:“该宝贝不能减少了哟~”
})
}
item.num=num
},
在处理完商品的数量后,本节的重头戏来了,我们还剩下我们的结算金额还没有计算出来,还有购买的总计商品数量也没有显示。
结算的总金额=(每个商品的单价*每个商品的数量)的总和
-
先来计算购买商品的数量totalNum()
1.商品数量初始量为0
2.从上到下将商品进行遍历
3.通过判断flag,商品有数量的就把数量num加入总计数,没有的就返回总数量totalNum(){
let totalNum = 0;
this.goods.map(item => {
item.flag ? totalNum += item.num : totalNum += 0
})
return totalNum
},
在这儿不得不说我的最爱,三元表达式,真的是太喜欢了。
总数量解决好后,来计算我们的总价格,也就是我们的支付价格。
-
总价格=商品1的单价_商品1的数量+商品2的单价_商品2的价格+…+商品n的单价*商品n的价格
totalPrice() { let totalPrice = 0; this.goods.map(item => { item.flag ? totalPrice += item.num * item.price : totalPrice += 0 }) return totalPrice }
就这样我们的简单的购物车功能就完成了,是不是很简单呢,里面的逻辑性都不是很强。
最后我再附上全部的完整的代码,个人经验上面代码分散的真的不是很好看。
<template>
<view>
<view class="empty" v-if="show==false">
<image src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.51yuansu.com%2Fpic3%2Fcover%2F01%2F82%2F40%2F596fa6dc00bb4_610.jpg&refer=http%3A%2F%2Fpic.51yuansu.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1633499781&t=d37222e32213957ddbdd01d62e071309" mode="widthFix" style="width: 400rpx;"></image>
<view class="empty-text">空空如也的购物</view>
<view class="empty-button" @click="goshopping">去选购</view>
</view>
<view v-if="show==true">
<view class="goods-detail" v-for="(item,index) in goods" :key="index">
<view class="detail-left">
<view class="goods-left">
<checkbox-group @change="selected(item)">
<label>
<checkbox class="selected" color="#555555" :checked="checked"/><text></text>
</label>
</checkbox-group>
<image :src="item.goodsImage" style="width: 150rpx;height: 140rpx;"></image>
</view>
<view class="size">
<text style="font-size: 25rpx;">尺码:{{item.size}}</text>
<text class="goods-price">¥{{item.price}}/件</text>
</view>
</view>
<view class="detail-right">
<text class="subtract" @click="reduce(item)">-</text>
<text class="num">{{item.num}}</text>
<text @click="add(item)" class="add">+</text>
</view>
</view>
</view>
<view class="end">
<view class="end-left">
<checkbox-group @change="selectgoods()">
<label>
<checkbox :checked="allchecked" />全选
</label>
</checkbox-group>
<view>
总计:<text style="color: #f00;font-weight: bold;">¥ {{totalPrice}}</text>
</view>
</view>
<view class="end-right">
结算({{totalNum}})
</view>
</view>
</view>
</template>
<script>
export default{
data(){
return{
show:true,
allchecked:true,
checked:true,
goods:[{
size:"女款-M",
num:1,
flag:true,
price:149,
goodsImage:"https://img0.baidu.com/it/u=4158246207,3235707994&fm=26&fmt=auto&gp=0.jpg",
},
{
size:"女款-xs",
num:1,
flag:true,
price:219,
goodsImage:"https://img0.baidu.com/it/u=811765333,1656843554&fm=11&fmt=auto&gp=0.jpg",
},
{
size:"女款-L",
num:1,
flag:true,
price:240,
goodsImage:"https://img1.baidu.com/it/u=233755383,2522308225&fm=26&fmt=auto&gp=0.jpg",
},
{
size:"女款-XXL",
num:1,
flag:true,
price:410,
goodsImage:"https://img0.baidu.com/it/u=3894000947,2570065196&fm=26&fmt=auto&gp=0.jpg",
},
{
size:"女款-XXL",
num:1,
flag:true,
price:500,
goodsImage:"https://img2.baidu.com/it/u=1001625387,3275765924&fm=26&fmt=auto&gp=0.jpg",
},
],
}
},
methods:{
goshopping(){
uni.navigateTo({
url:'../contact/contact'
})
},
change(e){
console.log(e)
},
selected(item){
item.flag=!item.flag
if(!item.flag){
this.allchecked=false
}else{
const a=this.goods.forEach((item)=>{
return item.flag===true
})
if(a){
this.allchecked=true
}else{
this.allchecked=false
}
}
},
selectgoods(){
this.allchecked=!this.allchecked
if(this.allchecked){
this.goods.map(item=>{
this.checked=true
item.flag=true
})
}else{
this.checked=false
this.goods.map(item=>{
item.flag=false
})
}
},
reduce(item){
let num=item.num
if(num>1){
num-=1
}else if(num=1){
uni.showToast({
title:"该宝贝不能减少了哟~"
})
}
item.num=num
},
add(item){
let num =item.num
item.num=num+1
}
},
computed:{
totalNum(){
let totalNum = 0;
this.goods.map(item => {
item.flag ? totalNum += item.num : totalNum += 0
})
return totalNum
},
totalPrice() {
let totalPrice = 0;
this.goods.map(item => {
item.flag ? totalPrice += item.num * item.price : totalPrice += 0
})
return totalPrice
}
}
}
</script>
<style lang="scss">
.goods{
line-height: 80rpx;
background-color: #FFFFFF;
&-detail{
display: flex;
padding: 30rpx 15rpx 30rpx 30rpx;
background-color: #fff;
justify-content: space-between;
border-bottom: 5rpx solid #F1F1F1;
align-items: center;
.detail-left{
display: flex;
.goods-left{
display: flex;
align-items: center;
}
}
.size{
display: flex;
justify-content: space-around;
flex-direction: column;
margin-left: 30rpx;
.goods-price{
font-size: 25rpx;
color: #F44545;
}
}
.detail-right{
text{
width: 50rpx;
line-height: 50rpx;
text-align: center;
display: inline-block;
background-color: #F7F7F7;
margin-right: 10rpx;
}
.add {
color: #FA4305;
border-radius: 10rpx 30rpx 30rpx 10rpx;
margin-right: 20rpx;
}
.subtract{
border-radius: 30rpx 10rpx 10rpx 30rpx;
}
}
}
}
.empty{
position: relative;
top: 220rpx;
text-align: center;
display: flex;
align-items: center;
flex-direction: column;
&-text{
color: #808080;
margin-bottom: 50rpx;
}
&-button{
width: 300rpx;
height: 90rpx;
color:orange;
border: 1rpx solid orange;
text-align: center;
line-height: 90rpx;
border-radius: 48rpx;
}
}
.end{
width: 100%;
height: 90rpx;
background-color:#fff;
position: fixed;
bottom: 100rpx;
left: 0;
display: flex;
align-items: center;
&-left{
width: 70%;
display: flex;
justify-content: space-between;
padding: 0 30rpx;
.end-flex{
display: flex;
align-items: center;
}
}
&-right{
width: 30%;
line-height: 90rpx;
background-color: #F44545;
text-align: center;
color: #fff;
}
}
</style>