Vue商品控件与购物车联动效果的实例代码


Posted in Javascript onJuly 21, 2019

本篇我们将构建商品控件与购物车联动。

商品控件

商品控件的结构编写

Vue商品控件与购物车联动效果的实例代码 

在商品组件的<template>标签内完成项目结构,以及数据,事件的绑定,与判断逻辑的书写。

<template>
 <div class="goods">
  <div class="menu-wrapper" ref="menuScroll">
   <ul>
    <!--专场-->
    <li class="menu-item" :class="{'current':currentIndex===0}" @click="selectMenu(0)">
     <p class="text">
      <img :src="container.tag_icon" v-if="container.tag_icon" class="icon">
      {{container.tag_name}}
     </p>
    </li>
    <li
     class="menu-item"
     v-for="(item,index) in goods"
     :class="{'current':currentIndex===index+1}"
     @click="selectMenu(index+1)"
    >
     <p class="text">
      <img :src="item.icon" v-if="item.icon" class="icon">
      {{item.name}}
     </p>
    </li>
   </ul>
  </div>
  <!-- 右侧商品列表 -->
  <div class="foods-wrapper" ref="foodScroll">
   <!--专场-->
   <ul>
    <li class="container-list food-list-hook">
     <div v-for="item in container.operation_source_list">
      <img :src="item.pic_url">
     </div>
    </li>
    <!-- 具体分类-->
    <li v-for="item in goods" class="food-list food-list-hook">
     <h3 class="title">{{item.name}}</h3>
     <!--具体商品列表-->
     <ul>
      <li v-for="food in item.spus" class="food-item">
       <div class="icon" :style="head_bg(food.picture)"></div>

       <div class="content">
        <h3 class="name">{{food.name}}</h3>
        <p class="desc" v-if="food.description">{{food.description}}</p>
        <div class="extra">
         <span class="saled">{{food.month_saled_content}}</span>
         <span class="praise">{{food.praise_content}}</span>
        </div>
        <img
         class="product"
         :src="food.product_label_picture"
         v-show="food.product_label_picture"
        >
        <p class="price">
         <span class="text">¥{{food.min_price}}</span>
         <span class="unit">/{{food.unit}}</span>
        </p>
       </div>
       <div class="cartcontrol-wrapper">
        <Cartcontrol :food="food"></Cartcontrol>
       </div>
      </li>
     </ul>
    </li>
   </ul>
  </div>
  <Shopcart :poiInfo="poiInfo" :selectFoods="selectFoods"></Shopcart>
 </div>
</template>

Shopcart组件是Goods组件的子组件,在Shopcart组件初始化的时候我们可以传入给其参数poiInfo selectFoods.

请求数据,声明方法与计算属性

<script>
import BScroll from "better-scroll";//滚动组件
import Shopcart from "components/Shopcart/Shopcart";//购物车
import Cartcontrol from "components/Cartcontrol/Cartcontrol";//控制商品数量按钮

export default {
 data() {
  return {
   container: {},
   goods: [],
   poiInfo: {},
   listHeight: [],
   menuScroll: {},
   foodScroll: {},
   scrollY: 0
  };
 },
 components: {
  BScroll,//引入组件
  Shopcart,
  Cartcontrol

 },
 created() {
  this.$axios
   .get("api/goods")
   .then(response => {
    let json = response.data;
    if (json.code === 0) {
     // 重点
     this.container = json.data.container_operation_source;
     this.goods = json.data.food_spu_tags;
     this.poiInfo = json.data.poi_info;
     this.$nextTick(function() {
      this.initScroll();
      // 左右联动
      // 右->左
      // 计算区间
      this.caculateHeight();
     });
    }
   })
   .catch(function(error) {
    // handle error
    console.log(error);
   });
 },
 computed: {
  // 根据右侧判断左侧index
  currentIndex() {
   for (let i = 0; i < this.listHeight.length; i++) {
    let start = this.listHeight[i];
    let end = this.listHeight[i + 1];
    if (this.scrollY >= start && this.scrollY < end) {
     return i;
    }
   }
   return 0;
  },
  selectFoods() {
   let foods = [];
     this.goods.forEach(good => {
     good.spus.forEach(food => {
      if (food.count > 0) {
       foods.push(food);
      }
     }); 
    });
   return foods;
  }
 },
 methods: {
  head_bg(imgName) {
   return "background-image: url(" + imgName + ");";
  },
  initScroll() {
   this.menuScroll = new BScroll(this.$refs.menuScroll, {
    click: true
   });
   this.foodScroll = new BScroll(this.$refs.foodScroll, {
    probeType: 3,
    click: true
   });
   this.foodScroll.on("scroll", pos => {
    this.scrollY = Math.abs(Math.round(pos.y));
   });
  },
  caculateHeight() {
   let foodList = this.$refs.foodScroll.getElementsByClassName(
    "food-list-hook"
   );//获取到dom元素
   let height = 0;
   this.listHeight.push(height);
   for (let i = 0; i < foodList.length; i++) {
    height += foodList[i].clientHeight;
    this.listHeight.push(height);
   }
   // [0, 215, 1343, 2425, 3483, 4330, 5823, 7237, 8022, 8788, 9443]
  },
  selectMenu(index) {
   let foodlist = this.$refs.foodScroll.getElementsByClassName(
    "food-list-hook"
   );
   // 根据下标,滚动到相对应的元素
   let el = foodlist[index];
   // 滚动到对应元素的位置
   this.foodScroll.scrollToElement(el, 100);
  }
 }
};
</script>

定义商品组件的样式

<style scoped>
.goods {
 display: -webkit-box;
 display: -ms-flexbox;
 display: flex;
 position: absolute;
 top: 190px;
 bottom: 51px;
 overflow: hidden;
 width: 100%;
}
.goods .menu-wrapper {
 -webkit-box-flex: 0;
 -ms-flex: 0 0 85px;
 flex: 0 0 85px;
 background: #f4f4f4;
}
.goods .menu-wrapper .menu-item {
 padding: 16px 23px 15px 10px;
 border-bottom: 1px solid #e4e4e4;
 position: relative;
}
.goods .menu-wrapper .menu-item.current {
 background: white;
 font-weight: bold;
 margin-top: -1px;
}
.goods .menu-wrapper .menu-item:first-child.current {
 margin-top: 1px;
}
.goods .menu-wrapper .menu-item .text {
 font-size: 13px;
 color: #333333;
 line-height: 17px;
 vertical-align: middle;
 -webkit-line-clamp: 2;
 display: -webkit-box;
 -webkit-box-orient: vertical;

 overflow: hidden;
}
.goods .menu-wrapper .menu-item .text .icon {
 width: 15px;
 height: 15px;
 vertical-align: middle;
}
.goods .menu-wrapper .menu-item .num {
 position: absolute;
 right: 5px;
 top: 5px;
 width: 13px;
 height: 13px;
 border-radius: 50%;
 color: white;
 background: red;
 text-align: center;
 font-size: 7px;
 line-height: 13px;
}

.goods .foods-wrapper {
 -webkit-box-flex: 1;
 -ms-flex: 1;
 flex: 1;
 /* background: blue; */
}
.goods .foods-wrapper .container-list {
 padding: 11px 11px 0 11px;
 border-bottom: 1px solid #e4e4e4;
}
.goods .foods-wrapper .container-list img {
 width: 100%;
 margin-bottom: 11px;
 border-radius: 5px;
}
.goods .foods-wrapper .food-list {
 padding: 11px;
}
.goods .foods-wrapper .food-list .title {
 height: 13px;
 font-size: 13px;
 background: url(btn_yellow_highlighted@2x.png) no-repeat left center;
 background-size: 2px 10px;
 padding-left: 7px;
 margin-bottom: 12px;
}

.goods .foods-wrapper .food-list .food-item {
 display: flex;
 margin-bottom: 25px;
 position: relative;
}
.goods .foods-wrapper .food-list .food-item .icon {
 flex: 0 0 63px;
 background-position: center;
 background-size: 120% 100%;
 background-repeat: no-repeat;
 margin-right: 11px;
 height: 75px;
}
.goods .foods-wrapper .food-list .food-item .content {
 flex: 1;
}
.goods .foods-wrapper .food-list .food-item .content .name {
 font-size: 16px;
 line-height: 21px;
 color: #333333;
 margin-bottom: 10px;
 padding-right: 27px;
}
.goods .foods-wrapper .food-list .food-item .content .desc {
 font-size: 10px;
 line-height: 19px;
 color: #bfbfbf;
 margin-bottom: 8px;

 /* 超出部分显示省略号*/
 -webkit-line-clamp: 1;
 display: -webkit-box;
 -webkit-box-orient: vertical;
 overflow: hidden;
}
.goods .foods-wrapper .food-list .food-item .content .extra {
 font-size: 10px;
 color: #bfbfbf;
 margin-bottom: 7px;
}
.goods .foods-wrapper .food-list .food-item .content .extra .saled {
 margin-right: 14px;
}
.goods .foods-wrapper .food-list .food-item .content .product {
 height: 15px;
 margin-bottom: 6px;
}
.goods .foods-wrapper .food-list .food-item .content .price {
 font-size: 0;
}
.goods .foods-wrapper .food-list .food-item .content .price .text {
 font-size: 14px;
 color: #fb4e44;
}
.goods .foods-wrapper .food-list .food-item .content .price .unit {
 font-size: 12px;
 color: #bfbfbf;
}
</style>

商品数量控制组件

这里用了vue动画

cart-decrease类为商品数量减少结构。 使用指令v-show控制其显隐。有商品数量的时候会按照规定动画进行显示,反之则隐藏。

cart-count类为选中的商品数量。

cart-add类为商品数量增加结构。

通过vue全局api set进行第一次点击增加商品按钮时候的设置。

https://cn.vuejs.org/v2/api/#...

<template>
 <div class="cartcontrol">
  <transition name="move">
   <div class="cart-decrease" @click="decreaseCart" v-show="food.count">
    <span class="inner icon-remove_circle_outline"></span>
   </div>
  </transition>
  <div class="cart-count" v-show="food.count">{{food.count}}</div>
  <div class="cart-add icon-add_circle" @click="increaseCart">
   <i class="bg"></i>
  </div>
 </div>
</template>

<script>
import Vue from 'vue'
export default {
 props:{
  food:{
   type:Object
  }
 },
 methods:{
  decreaseCart(){
   this.food.count--;
  },
  increaseCart(){
   if(!this.food.count){
    Vue.set(this.food,'count',1);
   }else{
    this.food.count++;
   }
  }
  
 }
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.cartcontrol {
 font-size: 0;
}

.cartcontrol .cart-decrease {
 display: inline-block;
 width: 26px;
 height: 26px;
 font-size: 26px;
 color: #b4b4b4;
}

.cartcontrol .cart-count {
 display: inline-block;
 width: 25px;
 text-align: center;
 font-size: 12px;
 line-height: 26px;
 vertical-align: top;
}

.cartcontrol .cart-add {
 display: inline-block;
 width: 26px;
 height: 26px;
 font-size: 26px;
 color: #ffd161;
 position: relative;
}
.cartcontrol .cart-add .bg {
 width: 20px;
 height: 20px;
 border-radius: 50%;
 background: black;
 position: absolute;
 left: 3px;
 top: 3px;
 z-index: -1;
}

.move-enter-active,
.move-leave-active {
 transition: all 0.5s linear;
}
.move-enter,
.move-leave-to {
 transform: translateX(20px) rotate(180deg);
}
</style>

购物车组件

Vue商品控件与购物车联动效果的实例代码 

我们现在创建shopcart购物车组件。

<template>
 <div class="shopcart" :class="{'highligh':totalCount>0}">
  <div class="shopcart-wrapper">
   <div class="content-left">
    <div class="logo-wrapper" :class="{'highligh':totalCount>0}">
     <span class="icon-shopping_cart logo" :class="{'highligh':totalCount>0}"></span>
     <i class="num" v-show="totalCount">{{totalCount}}</i>
    </div>
    <div class="desc-wrapper">
     <p class="total-price" v-show="totalPrice">¥{{totalPrice}}</p>
     <p class="tip" :class="{'highligh':totalCount>0}">另需{{shipping_fee_tip}}</p>
    </div>
   </div>

   <div class="content-right" :class="{'highligh':totalCount>0}">{{payStr}}</div>
  </div>
 </div>
</template>

<script>
// 导入BScroll
import BScroll from "better-scroll";

export default {
 props: {
  min_price_tip: {
   type: String,
   default: ""
  },
  shipping_fee_tip: {
   type: String,
   default: ""
  },
  selectFoods: {
   type: Array,
   default() {
    return [
     /* {
      min_price: 10,
      count: 3
     },
     {
      min_price: 7,
      count: 1
     } */
    ];
   }
  }
 },
 computed: {
  // 总个数 将所选商品的个数累加得到总个数。
  totalCount() {
   let num = 0;
   this.selectFoods.forEach(food => {
    num += food.count;
   });

   return num;
  },
  // 总金额
  totalPrice() {
   let total = 0;
   this.selectFoods.forEach(food => {
    total += food.min_price * food.count;
   });

   return total;
  },
  // 结算按钮显示
  payStr() {
   if (this.totalCount > 0) {
    return "去结算";
   } else {
    return this.min_price_tip;
   }
  }
 },
 components: {
  BScroll
 }
};
</script>

<style>
.shopcart-wrapper {
 width: 100%;
 height: 51px;
 background: #514f4f;
 position: fixed;
 left: 0;
 bottom: 0;
 display: flex;
 z-index: 99;
}
.shopcart-wrapper.highligh {
 background: #2d2b2a;
}

.shopcart-wrapper .content-left {
 flex: 1;
}
.shopcart-wrapper .content-left .logo-wrapper {
 width: 50px;
 height: 50px;
 background: #666666;
 border-radius: 50%;
 position: relative;
 top: -14px;
 left: 10px;
 text-align: center;
 float: left;
}
.shopcart-wrapper .content-left .logo-wrapper.highligh {
 background: #ffd161;
}
.shopcart-wrapper .content-left .logo-wrapper .logo {
 font-size: 28px;
 color: #c4c4c4;
 line-height: 50px;
}
.shopcart-wrapper .content-left .logo-wrapper .logo.highligh {
 color: #2d2b2a;
}
.shopcart-wrapper .content-left .logo-wrapper .num {
 width: 15px;
 height: 15px;
 line-height: 15px;
 border-radius: 50%;
 font-size: 9px;
 color: white;
 background: red;
 position: absolute;
 right: 0;
 top: 0;
}
.shopcart-wrapper .content-left .desc-wrapper {
 float: left;
 margin-left: 13px;
}
.shopcart-wrapper .content-left .desc-wrapper .total-price {
 font-size: 18px;
 line-height: 33px;
 color: white;
}
.shopcart-wrapper .content-left .desc-wrapper .tip {
 font-size: 12px;
 color: #bab9b9;
 line-height: 51px;
}
.shopcart-wrapper .content-left .desc-wrapper .tip.highligh {
 line-height: 12px;
}

.shopcart-wrapper .content-right {
 flex: 0 0 110px;
 font-size: 15px;
 color: #bab9b9;
 line-height: 51px;
 text-align: center;
 font-weight: bold;
}
.shopcart-wrapper .content-right.highligh {
 background: #ffd161;
 color: #2d2b2a;
}

.shopcart-wrapper .shopcart-list {
 position: absolute;
 left: 0;
 top: 0;
 z-index: -1;
 width: 100%;
}
.shopcart-wrapper .shopcart-list.show {
 transform: translateY(-100%);
}

.shopcart-wrapper .shopcart-list .list-top {
 height: 30px;
 text-align: center;
 font-size: 11px;
 background: #f3e6c6;
 line-height: 30px;
 color: #646158;
}

.shopcart-wrapper .shopcart-list .list-header {
 height: 30px;
 background: #f4f4f4;
}
.shopcart-wrapper .shopcart-list .list-header .title {
 float: left;
 border-left: 4px solid #53c123;
 padding-left: 6px;
 line-height: 30px;
 font-size: 12px;
}
.shopcart-wrapper .shopcart-list .list-header .empty {
 float: right;
 line-height: 30px;
 margin-right: 10px;
 font-size: 0;
}
.shopcart-wrapper .shopcart-list .list-header .empty img {
 height: 14px;
 margin-right: 9px;
 vertical-align: middle;
}
.shopcart-wrapper .shopcart-list .list-header .empty span {
 font-size: 12px;
 vertical-align: middle;
}

.shopcart-wrapper .shopcart-list .list-content {
 max-height: 360px;
 overflow: hidden;
 background: white;
}
.shopcart-wrapper .shopcart-list .list-content .food-item {
 height: 38px;
 padding: 12px 12px 10px 12px;
 border-bottom: 1px solid #f4f4f4;
}
.shopcart-wrapper .shopcart-list .list-content .food-item .desc-wrapper {
 float: left;
 width: 240px;
}
.shopcart-wrapper
 .shopcart-list
 .list-content
 .food-item
 .desc-wrapper
 .desc-left {
 float: left;
 width: 170px;
}
.shopcart-wrapper
 .shopcart-list
 .list-content
 .food-item
 .desc-wrapper
 .desc-left
 .name {
 font-size: 16px;
 margin-bottom: 8px;

 /* 超出部分隐藏*/
 -webkit-line-clamp: 1;
 display: -webkit-box;
 -webkit-box-orient: vertical;
 overflow: hidden;
 height: 16px;
}
.shopcart-wrapper
 .shopcart-list
 .list-content
 .food-item
 .desc-wrapper
 .desc-left
 .unit {
 font-size: 12px;
 color: #b4b4b4;
}
.shopcart-wrapper
 .shopcart-list
 .list-content
 .food-item
 .desc-wrapper
 .desc-left
 .description {
 font-size: 12px;
 color: #b4b4b4;

 /* 超出部分隐藏*/
 overflow: hidden;
 height: 12px;
}
.shopcart-wrapper
 .shopcart-list
 .list-content
 .food-item
 .desc-wrapper
 .desc-right {
 float: right;
 width: 70px;
 text-align: right;
}
.shopcart-wrapper
 .shopcart-list
 .list-content
 .food-item
 .desc-wrapper
 .desc-right
 .price {
 font-size: 12px;
 line-height: 38px;
}

.shopcart-wrapper .shopcart-list .list-content .food-item .cartcontrol-wrapper {
 float: right;
 margin-top: 6px;
}

.shopcart .shopcart-mask {
 position: fixed;
 top: 0;
 right: 0;
 width: 100%;
 height: 100%;
 z-index: 98px;
 background: rgba(7, 17, 27, 0.6);
}
</style>

Vue商品控件与购物车联动效果的实例代码 

到此购物车与组件联动就结束了。下篇我们讲如何进行商品分类菜单数量提示。

总结

以上所述是小编给大家介绍的Vue商品控件与购物车联动效果的实例代码,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

Javascript 相关文章推荐
JavaScript 异步调用框架 (Part 6 - 实例 &amp; 模式)
Aug 04 Javascript
js 获取radio按钮值的实例
Aug 17 Javascript
jQuery照片伸缩效果不影响其他元素的布局
May 09 Javascript
jQuery.holdReady()方法用法实例
Dec 27 Javascript
jQuery监控文本框事件并作相应处理的方法
Apr 16 Javascript
jQuery判断checkbox选中状态
May 12 Javascript
浅谈javascript中的constructor
Jun 08 Javascript
jQuery实现产品对比功能附源码下载
Aug 09 Javascript
babel基本使用详解
Feb 17 Javascript
微信公众平台 客服接口发消息的实现代码(Java接口开发)
Apr 17 Javascript
如何换个角度使用VUE过滤器详解
Sep 11 Javascript
Vue实现手机号、验证码登录(60s禁用倒计时)
Dec 19 Vue.js
浅析Angular 实现一个repeat指令的方法
Jul 21 #Javascript
Node.js 实现简单的无侵入式缓存框架的方法
Jul 21 #Javascript
Vue中遍历数组的新方法实例详解
Jul 21 #Javascript
Vue项目中使用WebUploader实现文件上传的方法
Jul 21 #Javascript
jquery插件开发模式实例详解
Jul 20 #jQuery
JS回调函数原理与用法详解【附PHP回调函数】
Jul 20 #Javascript
JavaScript展开操作符(Spread operator)详解
Jul 20 #Javascript
You might like
php 购物车实例(申精)
2009/05/11 PHP
php数据入库前清理 注意php intval与mysql的int取值范围不同
2010/12/12 PHP
PHP第一季视频教程(李炎恢+php100 不断更新)
2011/05/29 PHP
关于PHP结束标签的使用细节探讨及联想
2013/03/04 PHP
destoon数据库表说明汇总
2014/07/15 PHP
Zend Guard使用指南及问题处理
2015/01/07 PHP
jquery获取多个checkbox的值异步提交给php的方法
2015/06/24 PHP
Smarty模板类内部原理实例分析
2019/07/03 PHP
JS的数组的扩展实例代码
2008/07/09 Javascript
JavaScript改变HTML元素的样式改变CSS及元素属性
2013/11/12 Javascript
jQuery实现的感应鼠标悬停图片色彩渐显效果
2015/03/03 Javascript
BootstrapTable与KnockoutJS相结合实现增删改查功能【二】
2016/05/10 Javascript
jQuery增加、删除及修改select option的方法
2016/08/19 Javascript
JS实现的简单轮播图运动效果示例
2016/12/22 Javascript
EasyUI学习之Combobox级联下拉列表(2)
2016/12/29 Javascript
简单谈谈require模块化jquery和angular的问题
2017/06/23 jQuery
js编写简单的计时器功能
2017/07/15 Javascript
Bootstrap栅格系统的使用详解
2017/10/30 Javascript
在 vue-cli v3.0 中使用 SCSS/SASS的方法
2018/06/14 Javascript
小程序最新获取用户昵称和头像的方法总结
2019/09/23 Javascript
详解如何在Javascript和Sass之间共享变量
2019/11/13 Javascript
小程序使用wxs解决wxml保留2位小数问题
2019/12/13 Javascript
微信小程序实现签到弹窗动画
2020/09/21 Javascript
[38:32]DOTA2上海特级锦标赛A组资格赛#2 Secret VS EHOME第二局
2016/02/26 DOTA
Python实现识别手写数字大纲
2018/01/29 Python
Python实现时间序列可视化的方法
2019/08/06 Python
如何在Oracle中查看各个表、表空间占用空间的大小
2015/10/31 面试题
运动会广播稿200字
2014/01/15 职场文书
物理专业本科生自荐信
2014/01/30 职场文书
工程安全员岗位职责
2014/03/09 职场文书
《庐山的云雾》教学反思
2014/04/22 职场文书
2014年后勤工作总结
2014/11/18 职场文书
六年级学生评语大全
2014/12/26 职场文书
三八节祝酒词
2015/08/11 职场文书
2016教师年度考核评语大全
2015/12/01 职场文书
酒店工程部的岗位职责汇总大全
2019/10/23 职场文书