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 相关文章推荐
Google AJAX 搜索 API实现代码
Nov 17 Javascript
完美实现仿QQ空间评论回复特效
May 06 Javascript
Javascript实现Array和String互转换的方法
Dec 21 Javascript
用NODE.JS中的流编写工具是要注意的事项
Mar 01 Javascript
微信小程序 loading(加载中提示框)实例
Oct 28 Javascript
xmlplus组件设计系列之图标(ICON)(1)
May 05 Javascript
防止页面url缓存中ajax中post请求的处理方法
Oct 10 Javascript
vue购物车插件编写代码
Nov 27 Javascript
vue使用技巧及vue项目中遇到的问题
Jun 04 Javascript
JavaScript实现封闭区域布尔运算的示例代码
Jun 25 Javascript
详解javascript设计模式三:代理模式
Mar 25 Javascript
Vue+Node实现商品列表的分页、排序、筛选,添加购物车功能详解
Dec 07 Javascript
浅析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使用mkdir创建多级目录的方法
2015/12/22 PHP
PHP中调用C/C++制作的动态链接库的教程
2016/03/10 PHP
php指定长度分割字符串str_split函数用法示例
2017/01/30 PHP
jQuery实现仿Google首页拖动效果的方法
2015/05/04 Javascript
浅谈NodeJS中require路径问题
2015/05/07 NodeJs
浅谈javascript的Array.prototype.slice.call
2015/08/31 Javascript
js中不同的height, top的区别对比
2015/09/24 Javascript
javascript创建对象的几种模式介绍
2016/05/06 Javascript
js判断所有表单项不为空则提交表单的实现方法
2016/09/09 Javascript
浅谈Angularjs link和compile的使用区别
2016/10/21 Javascript
Node.js开发第三方微信公众平台
2017/06/05 Javascript
Nuxt升级2.0.0时出现的问题(小结)
2018/10/08 Javascript
vue下载excel的实现代码后台用post方法
2019/05/10 Javascript
纯js实现无缝滚动功能代码实例
2020/02/21 Javascript
vscode调试node.js的实现方法
2020/03/22 Javascript
vue-admin-template配置快捷导航的代码(标签导航栏)
2020/09/04 Javascript
[02:34]2016完美“圣”典风云人物:BurNIng专访
2016/12/10 DOTA
[02:03]风行者至宝清风环佩外观展示
2020/09/05 DOTA
python字符串过滤性能比较5种方法
2017/06/22 Python
机器学习python实战之决策树
2017/11/01 Python
python实现机器人行走效果
2018/01/29 Python
Python实现的绘制三维双螺旋线图形功能示例
2018/06/23 Python
深入理解python中sort()与sorted()的区别
2018/08/29 Python
用Python批量把文件复制到另一个文件夹的实现方法
2019/08/16 Python
Python使用get_text()方法从大段html中提取文本的实例
2019/08/27 Python
Python 动态导入对象,importlib.import_module()的使用方法
2019/08/28 Python
Python解释器以及PyCharm的安装教程图文详解
2020/02/26 Python
selenium+python配置chrome浏览器的选项的实现
2020/03/18 Python
python 通过pip freeze、dowload打离线包及自动安装的过程详解(适用于保密的离线环境
2020/12/14 Python
使用css3制作登录表单的步骤
2014/04/07 HTML / CSS
采购经理岗位职责
2014/02/16 职场文书
《小池塘》教学反思
2014/02/28 职场文书
信用卡结清证明怎么写
2014/09/13 职场文书
一文弄懂MySQL中redo log与binlog的区别
2022/02/15 MySQL
Go web入门Go pongo2模板引擎
2022/05/20 Golang
GPU服务器的多用户配置方法
2022/07/07 Servers