vant 实现全选功能

9 篇文章 0 订阅

今天遇到了一个问题。实现类似淘宝的购物车全选/全不选效果。记录下这里面的问题:

这里面有这样几成逻辑:(我们把图中的京东,网易,天猫等看成一个模块)

1每一个单独模块有一个控制该模块是否全选的按钮,点击全选/全不选切换,该模块下的所有复选框要被勾选/全不勾选

同时每一个模块的全选/全不选按钮也会影响到整个页面最底部的全选按钮状态,当每个模块都会全选状态时,整个页面底部的全选按钮将被勾选

2每一个单独模块下的复选框被逐一勾选时,它会影响到两层元素,其一是它所在的模块。当全部被勾选时,该复选框所在模块的全选按钮将被勾选,其二,所有复选框被逐一勾选时,整个页面底部的全选按钮将被勾选

所以对于每一个复选框按钮,它将都会影响到两层元素。

我们先看一看大致结构:

注意:单个模块复选框我们利用van-check-group进行包裹。再看一看数据结构

每一个模块对应一条json,其中有两个非常重要的属性:isChecked,它用来控制当前模块的选中状态,另一个就是result:它用来记录单个模块中有多少个复选框被勾选了。我们在van-checkbox-group中通过v-model进行了绑定。

代码如下:

这里有2个地方需要解释:

1 result用途到底是啥?

van-checkbox-group的change事件会有一个v-model绑定的值。它的值是一个数组,记录了当前哪些复选框被选中了。我们不用官方写法。把result写在data里。因为所有的模块是循环出来,每一个模块我们都需要知道该模块下到底选中了哪些复选框,所以我们把result写在了arr数组的每一个json里。

2 如下代码到底是啥意思?

item.list.forEach(j=>{

item.result.push(j.title)

})

我们可以这么理解。它就是全选。因为对每一个模块来说,勾选一个,当前模块的result数组就添加了一个。反过来,当当前模块的result都被添满。也就是result的长度和当前模块(arr下的json下list)长度一致的时候。就表明全选了

这个效果已经实现了。但是它需要有两个依赖条件:

1 数据结构需要用这种格式

2 我们对van-checkbox-group需要通过v-model绑定result,而不是其他

知道了基本的套路。我们接下来需要完成类似淘宝的购物车的完整功能。截图如下:

新的功能点包括如下几个:

1当只勾选一个模块内的商品,比如只勾选了京东里的商品。直接跳转至结算页面,如果至少勾选了2个模块,比如同时勾选了京东和天猫的商品,则有一个弹出层,提示单独结算。

2底部结算和合计的数字需要实时统计。实时计算(注意:结算按钮的数字不是统计所有勾选项,而是统计品类,比如本例中网易,天猫,京东等模块。如果有京东商品被勾选,则结算显示的数字是1.如果京东,天猫两个类目商品被勾选则结算按钮的值是2,依次类推)

3删除按钮点击时需要知道当前被勾选的那个品类的那个商品

针对第一个功能。先理清下思路:

(1)弹出层出现的条件就是至少勾选2个大类模块。那如何知道至少勾选了至少2个大类呢?

每次勾选商品。都会被van-checkbox-group里change事件所监听。我们知道,每个大类模块(比如京东)映射的json下都有一个

result:[],表示当前模块下哪个商品被勾选。也就是说只要有大类模块下有商品被勾选,其对应的json下的result数组的长度是不为空的。所有我们其实可以遍历整个大的数组,再去判断每一个json下的result的值是否有值。同时定义一个局部变量num,在遍历的过程中,只要当前json下的result数组有值,就累加。这样如果京东,网易和天猫这三个模块里都有商品被勾选,那么说明这三个json里的result的值都是不为空的。那累加的num就是3,那这个num就和结算的值是吻合的。

而合计的值又怎么计算呢?合计的值其实就是每一个商品的数量乘其单价,最后把所有勾选项的这些值累加。

同样的道理:我们需要一个条件。我们需要知道哪些商品被勾选了,如果能提供一个所有被勾选商品的集合,我们在他里面做文章,这个事情就好办了。

而恰好。result为我们提供了这个桥梁。所有道理是一样的。每次商品被勾选,每个大类json下的result就会记录当前被勾选的数据。如果我们循环整个大的数组,再遍历每一个json的下result,最后把他们push到一个数组里。那这个数组不就记录了所有被勾选的对象。但是有一点,此时我们需要做一点改变。van-checkbox-group下的van-checkbox的name不再和某一个属性,比如id等进行双向绑定,而是直接绑定item。这样result数组里出现的就是所有被勾选的item,每一个item记录了所有的数据,当然就有数量和价格。那总价就出来了。

而如何在勾选的时候合计的值及时更新呢?因为每次勾选都会被van-checkbox-group的change实践监听到。所以只需要在这个事件中再执行下我们上面的逻辑即可?如何在点击+、-按钮时同步更新合计值呢?道理一样。

代码如下:

 

完整代码如下:

<template>

    <div class='shoppingCartpage'>

        <div class='header'>

            <NavBar title='购物车' backgroundColor='var(--themeBgColor)' zitiColor='#fff'>

                <div slot='right' @click='clickNavBarTxt'>{{navBarTxt}}</div>

            </NavBar>

        </div>

        <div class='main'>

            <van-list

            v-model="loading"

            :finished="finished"

            finished-text="没有更多了"

            :immediate-check='false'

            :error.sync="error"

            error-text="请求失败,点击重新加载"

            @load="onLoad"

            >

                <div class='item  p24 boxSize mt24 br20' v-for='(item,i) in arr' :key='i'>

                    <div class='title'>

                        <div class='classify'>                  

                            <van-checkbox v-model="item.isChecked" checked-color='var(--themeBgColor)'

                                        @click='changeGroupAll(item)'>{{item.title}}

                            </van-checkbox>

                        </div> 

                        <div class='freeInfo'>

                                <!-- <slot name='tipInfo'> -->

                                    <span class='fs26 fw500 mr10'>满99包邮</span>

                                    <van-icon name="warning-o" class='mr34'/>

                                    <span class='theme'>去凑单</span>

                                    <i class='icon icon-jinru fs20 theme'></i>

                                <!-- </slot> -->

                        </div>

                    </div>

                    <section class='mt50'>

                        <van-checkbox-group v-model="item.result"  @change='changeGroup(item)' >

                            <div v-for='(innerItem,j) in item.list' :key='j'>

                                <van-row class='mt34'>

                                    <van-col span='3'>

                                        <van-checkbox :name="innerItem" checked-color='var(--themeBgColor)' ></van-checkbox>

                                    </van-col>

                                    <van-col span='9'>

                                        <img :src="innerItem.defaultImage" alt="" class='thumbnail'>

                                    </van-col>

                                    <van-col span='12'>

                                        <p class='ellipsisTwo'>{{innerItem.goodsName}}</p>

                                        <van-row class='mt50'>

                                            <van-col span='10'>

                                                <div class='numBox '><span class='fs26 theme num'>{{innerItem.goodsPrice}}</span><span class='fs22 gray'>积分</span></div>

                                            </van-col>

                                            <van-col span='14' >

                                                <div class='btnOuter'>

                                                    <div class='btnInner'>

                                                        <span class='fs28 btn w dis texCenter' @click="decrease(i,j,innerItem)">-</span><input type="text" v-model='innerItem.num' class='fs28 input w texCenter'/><span class='fs28 btn w dis texCenter' @click='increase(i,j,innerItem)'>+</span>

                                                    </div>

                                                </div>

                                            </van-col>

                                        </van-row>

                                    </van-col>

                                </van-row>

                            </div>

                        </van-checkbox-group>

                    </section>

                </div>

            </van-list>

        </div>

        <section class='footer w boxSize'>

            <div class='checkAll'>                  

                <van-checkbox v-model="isCheckAll" checked-color='var(--themeBgColor)' class='h' @click='checkTotal'><div class='fs26'>全选</div></van-checkbox>

           </div> 

           <div class='accountBox'>

                <div slot='tipInfo' class='tipBox'>

                    <van-row v-if='navBarTxtStatus' >

                        <van-col span='12'><div class='btn move'>移入关注</div></van-col>

                        <van-col span='12'><div class='btn del' @click='del'>删除</div></van-col>

                    </van-row>

                    <van-row v-else>

                        <van-col span='4'><div class=' num'>合计:</div></van-col>

                        <van-col span='8'><div class='theme num'><span class='fsBold point fs32'>{{accountTotal}}</span><span class='fs24 '>积分</span></div></van-col>

                        <van-col span='12'> <div class='account texCenter fs30 fw500 white center' @click='account'>结算<span>({{accoutNum}})</span></div></van-col>

                    </van-row>

                </div>

            </div>

        </section>

        <van-popup 

            v-model="showPup"

             position="bottom"

             round

             closeable

            

            >

                <section class='pupTitle'>

                    <p class='tit fs36 fsBold'>请分别结算</p>

                    <p class='fs28'>京东与其他商品处理规则不同,请分别结算</p>

                </section>

                <div class='p24 boxSize pupCont'>

                    <section>

                        <van-row class='mt24'>

                            <van-col span='18'>

                                <p>京东商品</p>

                                <div><span>共2件</span>合计:<span class='theme ml16'>486.00积分</span></div>

                            </van-col>

                            <van-col span='6'>

                                <van-button plain type="info" hairline size='small' color='var(--themeBgColor)'>去结算</van-button>

                            </van-col>

                        </van-row>

                        <van-row class='mt24'>

                            <van-col span='18'>

                                <p>京东商品</p>

                                <div><span>共2件</span>合计:<span class='theme ml16'>486.00积分</span></div>

                            </van-col>

                            <van-col span='6'>

                                <van-button plain type="info" hairline size='small' color='var(--themeBgColor)'>去结算</van-button>

                            </van-col>

                         </van-row>

                         <div class='center h100 fs34 fsBold p24 boxSize'>取消</div>

                     </section>

                </div>          

        </van-popup>

    </div>

</template>

<script>

import NavBar from "@/components/NavBar"

import CartItem from "./cartItem"

import shoppingCartApi from "@/apis/shoppingCartApi"

import { Dialog } from 'vant';

export default {

    name:'shoppingCart',

    created(){

        //查询购物车

        // this.findGoodsInCart()

    },

    data(){

        return{

            showPup:false,

            arr:[

                    {

                    code:"jd",

                    title:"京东",

                    isChecked:false,

                    result:[],

                    list:[

                            { 

                                id: 'jd001',defaultImage: "https://imgs.woliwu.com/goods/images/9000018176_default.jpg",

                                goodsCode: "JD8655www9",goodsName: "京东商品01",goodsPrice: 1,num: 1,stockStatus: "有货",supplierCode: "jd"

                            },

                            {

                                id: 'jd002',defaultImage: "https://imgs.woliwu.com/goods/images/900003072301_default.jpg",goodsCode: "JD8655123048",

                                goodsName: "京东商品02",goodsPrice: 2,num: 2,stockStatus: null,supplierCode: "jd"

                            }

                        ]

                    },

                    { 

                        code:"wy",

                        title:"网易",

                        isChecked:false,

                        result:[],

                        list:[

 

                            {   id: 'wy001',defaultImage: "https://imgs.woliwu.com/goods/images/900003072301_default.jpg",

                                goodsCode: "JD8655",goodsPrice: 1,num: 3,stockStatus: null,supplierCode: "wy"

                            },

                            {

                                id: 'wy002',defaultImage: "https://imgs.woliwu.com/goods/images/900003072301_default.jpg",

                                goodsCode: "JD865509890",

                                goodsPrice: 2,

                                num: 3,

                                stockStatus: null,

                                supplierCode: "wy"

                            }

                        ]

                    },

                    { 

                        code:"tm",

                        title:"天猫",

                        isChecked:false,

                        result:[],

                        list:[

 

                            {   id: 'tm001',defaultImage: "https://imgs.woliwu.com/goods/images/900003072301_default.jpg",

                                goodsCode: "JD8655",goodsPrice: 1,num: 3,stockStatus: null,supplierCode: "wy"

                            },

                            {

                                id: 'tm002',defaultImage: "https://imgs.woliwu.com/goods/images/900003072301_default.jpg",

                                goodsCode: "JD865509890",

                                goodsPrice: 2,

                                num: 1,

                                stockStatus: null,

                                supplierCode: "tm"

                            },

                            

                            {   id: 'tm003',defaultImage: "https://imgs.woliwu.com/goods/images/900003072301_default.jpg",

                                goodsCode: "JD8655",goodsPrice: 2,num: 1,stockStatus: null,supplierCode: "tm"

                            },

                            {

                                id: 'tm004',defaultImage: "https://imgs.woliwu.com/goods/images/900003072301_default.jpg",

                                goodsCode: "JD865509890",

                                goodsPrice: 2,

                                num: 2,

                                stockStatus: null,

                                supplierCode: "tm"

                            }

 

                        ]

                    },

            ],

            isCheckAll:false,

            navBarTxt:'编辑',

            navBarTxtStatus:false,

            loading:false,

            finished:false,

            error:false,

            idArr:[],

            accoutNum:0,

            accountTotal:0,

            groupNum:[]

        }

    },

    components:{NavBar,CartItem},

    methods:{

        getCartsData(){

        },

        clickNavBarTxt(){

            this.navBarTxtStatus=!this.navBarTxtStatus

            this.navBarTxtStatus?this.navBarTxt='完成':this.navBarTxt='编辑'

        },

        increase(i,j,item){

           this.arr[i]['list'][j].num++;

           this.accutateNum()

        },

        decrease(i,j,item){

            this.arr[i]['list'][j].num--;

            if(this.arr[i]['list'][j].num<1){

                this.arr[i]['list'][j].num=1

            }

            this.accutateNum()

        },

      

        del(){

            let self=this 

           var res=false

           //判断是否有勾选项

            res= this.arr.some(item=>{

               return item.result.length>0

           })

           if(res){

 

               this.arr.forEach(item=>{

                   item.result.forEach(j=>{

                        this.idArr.push(j)

                   })

               })

                Dialog.confirm({

                    message: '确认将此商品删除'

                }).then(() => {

                    self.deleteGoodsToCart()

                }).catch(() => {

 

                })

           }else{

                this.$toast('您尚未勾选商品')

           }

        },

        changeGroupAll(item){

            item.isChecked=!item.isChecked

            item.result=[]

            //判断当前模块是否全选

            if(item.isChecked){

                item.list.forEach(j=>{

                    // item.result.push(j.id)

                    item.result.push(j)

                })

            }else{

                item.result=[]

            }

            //判断是否整体全选

            let val=this.arr.every(i=>i.isChecked)

            if(val){

                this.isCheckAll=true

            }else{

                this.isCheckAll=false

            }

        },

        changeGroup(item){

            //判断单个模块是否全选

            console.log('item.result:',item.result)

            if(item.result.length==item.list.length){

                item.isChecked=true

            }else{

                item.isChecked=false

            }

 

            //获取结算值

            this.accutateNum()

            

 

            //判断整体是否全选

            let val=this.arr.every(item=>item.isChecked)

            if(val){

                this.isCheckAll=true

            }else{

                this.isCheckAll=false

            }            

        },

        //整体全选

        checkTotal(){

            this.isCheckAll=!this.isCheckAll

            if(this.isCheckAll){

                this.arr.forEach(item=>{

                    item.result=[]

                    item.isChecked=this.isCheckAll

                    item.list.forEach(j=>{

                        // item.result.push(j.id)

                        item.result.push(j)

                    })

                })

            }else{

                this.arr.forEach(item=>{

                    item.isChecked=this.isCheckAll

                    item.list.forEach(j=>{

                        item.result=[]

                    })

                })

            }

        },

        //查询购物车

        async findGoodsInCart (){

            try{

                let res=await shoppingCartApi.findGoodsInCart()

                let list=res.data&&res.data.data||[]

                let tempArr=list.map(item=>{

                    return {...item,result:[],isChecked:false}

                })

                this.arr=tempArr

            }catch(err){

                throw new Error(err)             

            }

        },

        async deleteGoodsToCart(){

            console.log('this.idArr:',this.idArr)

            let ids=[]

            for(i of this.idArr){

                ids.push(i.id)

            }

            console.log('ids:',ids)

            try{

                let delRes=await shoppingCartApi.deleteGoodsToCart({ids})                

                if(delRes.data.code==0){

                    self.findGoodsInCart()

                }

            }catch(err){

                throw new Error(err)

            }

        },

        //结算

        account(){

 

            let allResLen=[]

            this.arr.forEach(item=>{

                allResLen.push(item.result.length)

            })

            let val=allResLen.reduce((total, currentValue, currentIndex, arr)=>{

                return total+currentValue

            })

            console.log('val:',val)

            if(val==0){//全部未勾选

                this.$toast('您尚未勾选商品')

                return

            }else{//有勾选项

                let temp=allResLen.filter(item=>item==0)

                console.log('this.allResultLenArr:',allResLen)

                console.log('temp:',temp)

                console.log('allResLen.length-1:',allResLen.length-1)

 

                //只勾选了单个模块

                if(temp.length==allResLen.length-1){

                    this.$router.push('/submitOrder')

                }else{

                //勾选了多个模块

                    this.showPup=true

                    //统计哪些模块有勾选项

                    let num=0

                    this.arr.forEach(item=>{

                        if(item.result.length>0){

                            num+=1

                        }

                    })

                    this.accoutNum=num

                    // console.log('this.accoutNum:',this.accoutNum)

                }

            }

            

            

        },

        //统计结算数字和总积分数

        accutateNum(){

            let num=0

            let groupNum=[]

            let total=0

            this.arr.forEach(item=>{

                if(item.result.length>0){

                    num+=1

                    item.result.forEach(j=>{

                        groupNum.push(j)

                    })

                }

            })

            console.log('groupNum:',groupNum)

            //计算总数

            groupNum.forEach(item=>{

                total+=item.num*item.goodsPrice

            })

            console.log('taotal:',total)

            this.accoutNum=num

            this.accountTotal=total

        },

        //上拉刷新

        onLoad(){

            // this.loading=true

            // this.findGoodsInCart()

        }

    }

}

</script>

<style lang="less" scoped>

.shoppingCartpage{box-shadow:0px 2px 6px 0px rgba(51,51,51,0.04);border-radius:20px;background:#F6F6F7FF;min-height: 100vh}

.header{width: 100%; height: 170px;position: relative;overflow:hidden;padding: 0;margin: 0;}

.header::after{width: 140%;height: 170px;position: absolute;top:0;left:-20%;content: "";

;display: block;border-radius: 0 0 50% 50%;background:var(--themeBgColor);} 

.main{margin:-100px auto 0 auto;width: 702px;background: transparent;padding-bottom:273px;position: relative;z-index:1}

.footer{width: 100%; height: 101px;position: fixed;bottom:97px;left: 0;z-index: 2;padding: 10px 24px}

.checkAll{float: left;width: 40%;height: 100%}

.accountBox{float: left;width: 60%;display: flex;justify-content: flex-end}

.account {width:190px;height:80px;background:var(--themeBgColor);border-radius:40px;}

.h{height: 70px;}

.tipBox{width: 100%;}

.white{color:#fff}

.num{height: 101px;line-height: 80px}

.btn{width: 190px; height: 60px;line-height:60px;border-radius:40px;text-align: center}

.move{background: var(--themeBgColor);color:#fff}

.del{border:1px solid var(--themeBgColor);background: #fff;color:var(--themeBgColor)}

.point{color:var(--themeBgColor);margin-right:5px}

.gray{color:#999999FF}

.main{

    .item{background:#fff;} 

    .title{display: flex; height: 40px;line-height: 40px}

    .classify{flex:0 0 220px}

    .freeInfo{flex:1 0 auto;display:flex;justify-content: flex-end}

    .mt51{margin-top:51px}

    .thumbnail{width: 185px; height: 185px;display: block}

    .rd{width: 40px; height: 40px;border:1px solid #999999FF;background: #fff;position: relative;cursor: pointer;}

    .rd::after{display: block;width: 40px; height: 40px;position: absolute;top:0;left: 0;background: #fff;visibility: visible;}

    .btn{width: 40px; height: 40px;}

    .input{width:80px; height: 37px;border-right:1px solid #CCCCCCFF;;border-left:1px solid #CCCCCCFF;}

    .mt50{margin-top:50px}

    .dis{display: inline-block}

    .btnOuter{display: flex;justify-content: flex-end}

    .btnInner{width: 168px;border:1px solid #CCCCCCFF;}

    .num{margin-right:6px}

    

}

.pupTitle{  height: 180px;border-bottom: 1px solid #eee;

            .tit{height: 98px;line-height:98px}

            .mt10{margin-top:10px}

        }

.pupTitle p{text-align: center}

.texColor{color:#7B7B7BFF}

.h100{ height: 100px;}

.pupCont p{font-size:24px;color:#999999FF }

.pupCont div{font-size:26px;color:#333333FF;font-weight: 500%}

.ml16{margin-left:16px;}

.pupCont span{display: inline-block}

.theme{color:var(--themeBgColor)}

</style>

  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值