基本结构 < template> < divclass = " cart" > < van-nav-bartitle = " 购物车" fixed  /> < divclass = " cart-title" > < spanclass = " all" > < i> </ i> </ span> < spanclass = " edit" > < van-iconname = " edit" /> </ span> </ div> < divclass = " cart-list" > < divclass = " cart-item" v-for = " item in 10" :key = " item" > < van-checkbox> </ van-checkbox> < divclass = " show" > < imgsrc = " http://cba.itlike.com/public/uploads/10001/20230321/a072ef0eef1648a5c4eae81fad1b7583.jpg" alt = " " > </ div> < divclass = " info" > < spanclass = " tit text-ellipsis-2" > </ span> < spanclass = " bottom" > < divclass = " price" > < span> </ span> </ div> < divclass = " count-box" > < buttonclass = " minus" > </ button> < inputclass = " inp" :value = " 4" type = " text" readonly > < buttonclass = " add" > </ button> </ div> </ span> </ div> </ div> </ div> < divclass = " footer-fixed" > < divclass = " all-check" > < van-checkboxicon-size = " 18" > </ van-checkbox> </ div> < divclass = " all-total" > < divclass = " price" > < span> </ span> < span> < iclass = " totalPrice" > </ i> </ span> </ div> < divv-if = " true" class = " goPay" > </ div> < divv-else  class = " delete" > </ div> </ div> </ div> </ div> </ template> < script> 
export  default  { name :  'CartPage' 
} 
 </ script> < stylelang = " less" scoped > 
// 主题 padding
.cart  { padding-top :  46px; padding-bottom :  100px; background-color :  #f5f5f5; min-height :  100vh; .cart-title  { height :  40px; display :  flex; justify-content :  space-between; align-items :  center; padding :  0 10px; font-size :  14px; .all  { i  { font-style :  normal; margin :  0 2px; color :  #fa2209; font-size :  16px; } } .edit  { .van-icon  { font-size :  18px; } } } .cart-item  { margin :  0 10px 10px 10px; padding :  10px; display :  flex; justify-content :  space-between; background-color :  #ffffff; border-radius :  5px; .show img  { width :  100px; height :  100px; } .info  { width :  210px; padding :  10px 5px; font-size :  14px; display :  flex; flex-direction :  column; justify-content :  space-between; .bottom  { display :  flex; justify-content :  space-between; .price  { display :  flex; align-items :  flex-end; color :  #fa2209; font-size :  12px; span  { font-size :  16px; } } .count-box  { display :  flex; width :  110px; .add,.minus  { width :  30px; height :  30px; outline :  none; border :  none; } .inp  { width :  40px; height :  30px; outline :  none; border :  none; background-color :  #efefef; text-align :  center; margin :  0 5px; } } } } } 
} .footer-fixed  { position :  fixed; left :  0; bottom :  50px; height :  50px; width :  100%; border-bottom :  1px solid #ccc; background-color :  #fff; display :  flex; justify-content :  space-between; align-items :  center; padding :  0 10px; .all-check  { display :  flex; align-items :  center; .van-checkbox  { margin-right :  5px; } } .all-total  { display :  flex; line-height :  36px; .price  { font-size :  14px; margin-right :  10px; .totalPrice  { color :  #fa2209; font-size :  18px; font-style :  normal; } } .goPay, .delete  { min-width :  100px; height :  36px; line-height :  36px; text-align :  center; background-color :  #fa2f21; color :  #fff; border-radius :  18px; &.disabled  { background-color :  #ff9779; } } } } 
 </ style> 按需导入组件 import  {  Checkbox }  from  'vant' 
Vue. use ( Checkbox) 
新建 modules/cart.js 模块 export  default  { namespaced :  true , state  ( )  { return  { cartList :  [ ] } } , mutations :  { } , actions :  { } , getters :  { } 
} 
挂载到 store 上面 import  Vue from  'vue' 
import  Vuex from  'vuex' 
import  user from  './modules/user' 
import  cart from  './modules/cart' Vue. use ( Vuex) export  default  new  Vuex. Store ( { getters :  { token :  state  =>  state. user. userInfo. token} , modules :  { user, cart} 
} ) 
封装 API 接口 api/cart.js 
export  const  getCartList  =  ( )  =>  { return  request. get ( '/cart/list' ) 
} 
封装 action 和 mutation mutations :  { setCartList  ( state,  newList )  { state. cartList =  newList} , 
} , 
actions :  { async  getCartAction  ( context )  { const  {  data }  =  await  getCartList ( ) data. list. forEach ( item  =>  { item. isChecked =  true } ) context. commit ( 'setCartList' ,  data. list) } 
} , 
页面中 dispatch 调用 computed :  { isLogin  ( )  { return  this . $store. getters. token} 
} , 
created  ( )  { if  ( this . isLogin)  { this . $store. dispatch ( 'cart/getCartAction' ) } 
} , 
将数据映射到页面 import  {  mapState }  from  'vuex' computed :  { ... mapState ( 'cart' ,  [ 'cartList' ] ) 
} 
动态渲染 
< divclass = " cart-list" > < divclass = " cart-item" v-for = " item in cartList" :key = " item.goods_id" > < van-checkboxicon-size = " 18" :value = " item.isChecked" > </ van-checkbox> < divclass = " show" @click = " $router.push(`/prodetail/${item.goods_id}`)" > < img:src = " item.goods.goods_image" alt = " " > </ div> < divclass = " info" > < spanclass = " tit text-ellipsis-2" > </ span> < spanclass = " bottom" > < divclass = " price" > < span> </ span> </ div> < CountBox:value = " item.goods_num" > </ CountBox> </ span> </ div> </ div> </ div> 封装 getters:商品总数 / 选中的商品列表 / 选中的商品总数 / 选中的商品总价 getters :  { cartTotal  ( state )  { return  state. cartList. reduce ( ( sum,  item,  index )  =>  sum +  item. goods_num,  0 ) } , selCartList  ( state )  { return  state. cartList. filter ( item  =>  item. isChecked) } , selCount  ( state,  getters )  { return  getters. selCartList. reduce ( ( sum,  item,  index )  =>  sum +  item. goods_num,  0 ) } , selPrice  ( state,  getters )  { return  getters. selCartList. reduce ( ( sum,  item,  index )  =>  { return  sum +  item. goods_num *  item. goods. goods_price_min} ,  0 ) . toFixed ( 2 ) } 
} 
页面中 mapGetters 映射使用 computed: {...mapGetters('cart', ['cartTotal', 'selCount', 'selPrice']),
},
< divclass = " cart-title" > < spanclass = " all" > < i> </ i> </ span> < spanclass = " edit" > < van-iconname = " edit" /> </ span> </ div> < divclass = " footer-fixed" > < divclass = " all-check" > < van-checkboxicon-size = " 18" > </ van-checkbox> </ div> < divclass = " all-total" > < divclass = " price" > < span> </ span> < span> < iclass = " totalPrice" > </ i> </ span> </ div> < divv-if = " true" :class = " { disabled: selCount === 0 }" class = " goPay" > </ div> < divv-else   :class = " { disabled: selCount === 0 }" class = " delete" > </ div> </ div> </ div> 全选 getters getters :  { isAllChecked  ( state )  { return  state. cartList. every ( item  =>  item. isChecked) } 
} ... mapGetters ( 'cart' ,  [ 'isAllChecked' ] ) , < div class = "all-check" > < van- checkbox : value= "isAllChecked"  icon- size= "18" > < / van- checkbox> 全选
< / div> 
点击小选,修改状态 < van- checkbox @click= "toggleCheck(item.goods_id)"  ... > < / van- checkbox> toggleCheck  ( goodsId )  { this . $store. commit ( 'cart/toggleCheck' ,  goodsId) 
} , mutations :  { toggleCheck  ( state,  goodsId )  { const  goods =  state. cartList. find ( item  =>  item. goods_id ===  goodsId) goods. isChecked =  ! goods. isChecked} , 
} 
点击全选,重置状态 < div @click= "toggleAllCheck"  class = "all-check" > < van- checkbox : value= "isAllChecked"  icon- size= "18" > < / van- checkbox> 全选
< / div> toggleAllCheck  ( )  { this . $store. commit ( 'cart/toggleAllCheck' ,  ! this . isAllChecked) 
} , mutations :  { toggleAllCheck  ( state,  flag )  { state. cartList. forEach ( item  =>  { item. isChecked =  flag} ) } , 
} 
封装 api 接口 
export  const  changeCount  =  ( goodsId,  goodsNum,  goodsSkuId )  =>  { return  request. post ( '/cart/update' ,  { goodsId, goodsNum, goodsSkuId} ) 
} 
页面中注册点击事件,传递数据 < CountBox : value= "item.goods_num"  @input= "value => changeCount(value, item.goods_id, item.goods_sku_id)" > < / CountBox> changeCount  ( value,  goodsId,  skuId )  { this . $store. dispatch ( 'cart/changeCountAction' ,  { value, goodsId, skuId} ) 
} , 
提供 action 发送请求, commit mutation mutations :  { changeCount  ( state,  {  goodsId,  value }  )  { const  obj =  state. cartList. find ( item  =>  item. goods_id ===  goodsId) obj. goods_num =  value} 
} , 
actions :  { async  changeCountAction  ( context,  obj )  { const  {  goodsId,  value,  skuId }  =  objcontext. commit ( 'changeCount' ,  { goodsId, value} ) await  changeCount ( goodsId,  value,  skuId) } , 
} 
data 提供数据, 定义是否在编辑删除的状态 data () {return {isEdit: false}
},
注册点击事件,修改状态 < spanclass = " edit" @click = " isEdit = !isEdit" > < van-iconname = " edit" /> </ span> 底下按钮根据状态变化 < divv-if = " !isEdit" :class = " { disabled: selCount === 0 }" class = " goPay" > </ div> < divv-else  :class = " { disabled: selCount === 0 }" class = " delete" > </ div> 监视编辑状态,动态控制复选框状态 watch :  { isEdit  ( value )  { if  ( value)  { this . $store. commit ( 'cart/toggleAllCheck' ,  false ) }  else  { this . $store. commit ( 'cart/toggleAllCheck' ,  true ) } } 
} 
查看接口,封装 API ( 注意:此处 id 为获取回来的购物车数据的 id ) 
export  const  delSelect  =  ( cartIds )  =>  { return  request. post ( '/cart/clear' ,  { cartIds} ) 
} 
注册删除点击事件 < div v- else  : class = "{ disabled: selCount === 0 }"  @click= "handleDel"  class = "delete" > 删除 ( { {  selCount } } ) 
< / div> async  handleDel  ( )  { if  ( this . selCount ===  0 )  return await  this . $store. dispatch ( 'cart/delSelect' ) this . isEdit =  false 
} , 
提供 actions actions :  { async  delSelect  ( context )  { const  selCartList =  context. getters. selCartListconst  cartIds =  selCartList. map ( item  =>  item. id) await  delSelect ( cartIds) Toast ( '删除成功' ) context. dispatch ( 'getCartAction' ) } 
} , 
外面包个大盒子,添加 v-if 判断 < divclass = " cart-box" v-if = " isLogin && cartList.length > 0" > < divclass = " cart-title" > </ div> < divclass = " cart-list" > </ div> < divclass = " footer-fixed" > </ div> </ div> < divclass = " empty-cart" v-else > < imgsrc = " @/assets/empty.png" alt = " " > < divclass = " tips" > </ div> < divclass = " btn" @click = " $router.push('/')" > </ div> </ div> 相关样式 .empty-cart  { padding :  80px 30px; img  { width :  140px; height :  92px; display :  block; margin :  0 auto; } .tips  { text-align :  center; color :  #666; margin :  30px; } .btn  { width :  110px; height :  32px; line-height :  32px; text-align :  center; background-color :  #fa2c20; border-radius :  16px; color :  #fff; display :  block; margin :  0 auto; } 
}