JavaWEB笔记19 Vue组件开发案例--购物车

JavaWEB笔记19 Vue组件开发案例–购物车

一.案例需求演示:

在这里插入图片描述
购物车案例:使用Vue的组件式开发完成如上图效果的购物车界面,其中商品对应+和-号点击可以控制商品数量,且商品数量不能为0或小于0,并且当商品数目进行改变时,对应地总价显示价格也要发生改变;商品后面的×号,点击可以在购物车中去除该条商品信息;商品数目中的数量可以进行输入控制

二.案例给出的代码初始样板:

案例用到的js文件(自备):

  • vue.js
  • jQuery.js

代码模板:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style type="text/css">
    .container {
		padding: 0;
		margin: 0;
    }
    .container .cart {
      width: 300px;
      /*background-color: lightgreen;*/
      margin: auto;
    }
    .container .title {
      background-color: lightblue;
      height: 40px;
      line-height: 40px;
      text-align: center;
      /*color: #fff;*/  
    }
    .container .total {
      background-color: #FFCE46;
      height: 50px;
      line-height: 50px;
      text-align: right;
    }
    .container .total button {
      margin: 0 10px;
      background-color: #DC4C40;
      height: 35px;
      width: 80px;
      border: 0;
    }
    .container .total span {
      color: red;
      font-weight: bold;
    }
    .container .item {
      height: 55px;
      line-height: 55px;
      position: relative;
      border-top: 1px solid #ADD8E6;
    }
    .container .item img {
      width: 45px;
      height: 45px;
      margin: 5px;
    }
    .container .item .name {
      position: absolute;
      width: 90px;
      top: 0;left: 55px;
      font-size: 16px;
    }

    .container .item .change {
      width: 100px;
      position: absolute;
      top: 0;
      right: 50px;
    }
    .container .item .change a {
      font-size: 20px;
      width: 30px;
      text-decoration:none;
      background-color: lightgray;
      vertical-align: middle;
    }
    .container .item .change .num {
      width: 40px;
      height: 25px;
    }
    .container .item .del {
      position: absolute;
      top: 0;
      right: 0px;
      width: 40px;
      text-align: center;
      font-size: 40px;
      cursor: pointer;
      color: red;
    }
    .container .item .del:hover {
      background-color: orange;
    }
  </style>
</head>
<body>
  <div id="app">
    <div class="container">
		<!-- class="cart" 可以作为父组件 -->
      <div class="cart">
		  <!-- class="title" 标题子组件 -->
        <div class="title">我的商品</div>
		<!-- 中间的列表组件 -->
        <div>
          <div class="item">
            <img src="img/a.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href=""></a>
              <input type="text" class="num" />
              <a href=""></a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/b.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href=""></a>
              <input type="text" class="num" />
              <a href=""></a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/c.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href=""></a>
              <input type="text" class="num" />
              <a href=""></a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/d.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href=""></a>
              <input type="text" class="num" />
              <a href=""></a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/e.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href=""></a>
              <input type="text" class="num" />
              <a href=""></a>
            </div>
            <div class="del">×</div>
          </div>
        </div>
		<!--class="total" 底部的子组件  -->
        <div class="total">
          <span>总价:123</span>
          <button>结算</button>
        </div>
      </div>
    </div>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    
    var vm = new Vue({
      el: '#app',
      data: {
        
      }
    });

  </script>
</body>
</html>

img目录下提供的图片:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

三.案例实现过程详解:

步骤大致如下:将页面看成,顶部标题,中间条目,底部总价三个组件。然后定义一个全局组件作为顶部标题,中间条目,底部总价三个组件的父组件。

一.修改案例的静态页面,改成组件化

1. 定义父组件和三个子组件

<script type="text/javascript">
	//定义三个子组件
	var CarTitle={
		template:``,
	}
    var CarList={
		template:``,
	}
	var CarTotal={
		template:``,
	}
    //定义父组件
	Vue.component('my-car',{
		template:``,
		//注册子组件
		components:{
			'car-title':CarTitle,
			'car-list':CarList,
			'car-total':CarTotal
		}
	})
	
    var vm = new Vue({
      el: '#app',
      data: {
      }
    });

  </script>

2.剪贴对应的html代码放到对应的三个子组件的模板中,也就是template属性中

         //定义三个子组件
				//顶部标题组件
			var CarTitle = {
				template: `<div class="title">我的商品</div>`,
			}
            //中间条目组件
			var CarList = {
				template: `<div>
          <div class="item">
            <img src="img/a.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/b.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/c.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/d.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
          <div class="item">
            <img src="img/e.jpg"/>
            <div class="name"></div>
            <div class="change">
              <a href="">-</a>
              <input type="text" class="num" />
              <a href="">+</a>
            </div>
            <div class="del">×</div>
          </div>
        </div>`,
			}
            //底部总结组件
			var CarTotal = {
				template: `<div class="total">
						<span>总价:123</span>
						<button>结算</button>
					</div>`,
			}

3. 添加根组件模板以及使用三个子组件

//定义父组件
			Vue.component('my-car', {
				template: `
				<div class="cart">
					<car-title></car-title>
					<car-list></car-list>
					<car-total></car-total>
				</div>`,
				//注册子组件
				components: {
					'car-title': CarTitle,
					'car-list': CarList,
					'car-total': CarTotal
				}
			})

4.在 根组件中的模板中使用 my-car 父组件

        <div id="app">
			<div class="container">
				<!-- 使用组件 -->
				<my-car></my-car>
			</div>
		</div>    

5. 测试:跟刚才一样

07-29_010

二.顶部组件功能的实现

顶部组件没有什么功能,为了练习我们所学知识,我们可以把 我的 这两个字改成用户名,就是为了练习父组件向子组件如何传递数据

  1. 在父组件中定义一个数据,传递给顶部的子组件
//定义父组件
			Vue.component('my-car', {
				data:function(){
					return {
						username:'张三'
					}
				},
				template: `
				<div class="cart">
					<car-title :username="username"></car-title>
					<car-list></car-list>
					<car-total></car-total>
				</div>`,
				//注册子组件
				components: {
					'car-title': CarTitle,
					'car-list': CarList,
					'car-total': CarTotal
				}
			})
  1. 在 car-title 子组件中取出父组件传递过来的username,并展示
var CarTitle = {
				props:['username'], //获取父组件传递的数据
				template: `<div class="title">{{username}}商品</div>`,
			}

中间列表所需要的数据,我们在父组件中提供一下,然后传递给中间列表组件和底部总价组件

//定义父组件
			Vue.component('my-car', {
				data: function() {
					return {
						username: '张三',
						list: [{
							id: 1,
							name: 'TCL彩电',
							price: 1000,
							num: 1,
							img: 'img/a.jpg'
						}, {
							id: 2,
							name: '机顶盒',
							price: 1000,
							num: 1,
							img: 'img/b.jpg'
						}, {
							id: 3,
							name: '海尔冰箱',
							price: 1000,
							num: 1,
							img: 'img/c.jpg'
						}, {
							id: 4,
							name: '小米手机',
							price: 1000,
							num: 1,
							img: 'img/d.jpg'
						}, {
							id: 5,
							name: 'PPTV电视',
							price: 1000,
							num: 2,
							img: 'img/e.jpg'
						}]
					}
				},
				template: `
				<div class="cart">
					<car-title :username="username"></car-title>
					<car-list  :list="list"></car-list>
					<car-total :list="list"></car-total>
				</div>`,
				//注册子组件
				components: {
					'car-title': CarTitle,
					'car-list': CarList,
					'car-total': CarTotal
				}
			})

三.底部总价组件功能的实现

在底部总价组件中,接收父组件传递过来的数据,计算商品的总价,计算总价,我们使用计算属性来完成,因为商品的数量是变化的。

              //底部组件
			var CarTotal = {
				props: ['list'], //获取父组件传递的数据
				template: `<div class="total">
						<span>总价:{{total}}</span>
						<button>结算</button>
					</div>`,
			    computed: {
			    	total: function() {
			    		var sum = 0;
			    		this.list.forEach(function(item) {
			    			sum += (item.num) * item.price;
			    		});
			    		return sum;
			    	}
			    }		
			}

07-29_011

四.中间列表组件的功能实现

列表数据的动态显示

在父组件中将list数据传递给子组件,在组件中进行遍历展示

<car-list  :list="list"></car-list>

子组件接收list数据,并遍历list展示数据

   var CarList = {
				props: ['list'], //获取父组件传递的数据
				template: `<div>
						<div class="item" v-for="(item,index) in list" :key="index">
							<img :src="item.img" />
							<div class="name">{{item.name}}</div>
							<div class="change">
								<a href="">-</a>
								<input type="text" value="" class="num"/>
								<a href="">+</a>
							</div>
							<div class="del">×</div>
						</div>
					</div>`,
			}

注意: src前面加上冒号,注意模板要有一个根标签

07-30_001

五.删除按钮的实现

子组件的删除操作,要让父组件去做,因为数据是在父组件中绑定的,所以我们在子组件中,把当前要删除的条目的索引传递给父组件

  1. 给删除按钮绑定点击事件
<div class="del"  @click="delItme(index)">×</div>
  1. 在methods中处理删除操作
                methods: {
					delItme(index) {
						//把当前条目的索引传递给父组件
						this.$emit('delete-cart', index);
					}
				}
  1. 父组件中,绑定自定义事件 delete-cart ,进行删除
             <car-list @delete-cart="delEle"  :list="list"></car-list>
             methods:{
					delEle(index){
						this.list.splice(index,1);
					}	
				}                
六.商品数量的变更
  1. 在列表组件中,先把默认的商品数量展示出来,那这里我们不用 v-model 来绑定 而是 绑定 value属性,因为商品的数量是从父组件的传过来的,子组件不直接修改传递过来的数据,只做展示
                           <div class="change">
								<a href=""></a>
								<input type="text" :value="item.num" class="num" $ref="in"/>
								<a href=""></a>
							</div>

07-30_002

  1. 给输入框,绑定失去焦点事件,当用户在输入框输入了新的商品数量后,我们传递数据给父组件做数据更新。
<input @blur="inputNum(index,$event)" type="text" :value="item.num" class="num"/>
	              methods: {
					inputNum(index) {
						//alert(index);
						this.$emit('change-num', {
							index: index, //条目的索引
							num: this.$refs.in.value, //获取输入框输入的值
							type: 'change' //标记,表示是通过输入商品数量
						});
					}
				}	
  1. 在父组件中监听 change-num 自定义事件,根据子组件传递过来的数据,更新父组件中商品的数量
<car-list @delete-cart="delEle" @change-num="changeNum($event)"   :list="list"></car-list>
               changeNum(value) {
						//console.log(value);
						if (value.type == "change") {
                            //根据子组件传递过来的数据,更新父组件中商品的数量
							this.list.forEach(function(item, index) {
								if (index == value.index) {
									item.num = value.num;
								}
							});
						} 
					}                        
七.点击 + - 来改变商品数量

1.给 + - 绑定点击事件,改变商品的数量

                           <div class="change">
								<a href="" @click.prevent='subNum(index)'></a>
								<input @blur="inputNum(index)" type="text" :value="item.num" $ref="in" class="num"/>
								<a href="" @click.prevent='addNum(index)'></a>
							</div>         
               subNum(index){
						this.$emit('change-num', {
							index: index,
							num: this.$refs.in.value,
							type: 'sub' //表示是点击的-
						});
					},
					addNum(index){
						this.$emit('change-num', {
							index: index,
							num: this.$refs.in.value,
							type: 'add' //表示是点击的+
						});
					},
  1. 父组件中监听自定义事件,根据传递过来数据中 type 类型分别来处理
<car-list @delete-cart="delEle" @change-num="changeNum($event)" :list="list"></car-list>
          changeNum(value) {
						//console.log(value);
						if (value.type == "change") {
							this.list.forEach(function(item, index) {
								if (index == value.index) {
									item.num = value.num;
								}
							});
						} else if (value.type == "add") {
							this.list.forEach(function(item, index) {
								if (index == value.index) {
									item.num += 1;
								}
							});
						} else {
							this.list.forEach(function(item, index) {
								if (index == value.index) {
									item.num -= 1;
                                    if(item.num == 0){
                  						alert("亲,商品的数量不能为0哦~");
                  						item.num = 1;
                					}
								}
							});
						}
					}                    

四.最终代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style type="text/css">
    .container {
		padding: 0;
		margin: 0;
    }
    .container .cart {
      width: 300px;
      /*background-color: lightgreen;*/
      margin: auto;
    }
    .container .title {
      background-color: lightblue;
      height: 40px;
      line-height: 40px;
      text-align: center;
      /*color: #fff;*/  
    }
    .container .total {
      background-color: #FFCE46;
      height: 50px;
      line-height: 50px;
      text-align: right;
    }
    .container .total button {
      margin: 0 10px;
      background-color: #DC4C40;
      height: 35px;
      width: 80px;
      border: 0;
    }
    .container .total span {
      color: red;
      font-weight: bold;
    }
    .container .item {
      height: 55px;
      line-height: 55px;
      position: relative;
      border-top: 1px solid #ADD8E6;
    }
    .container .item img {
      width: 45px;
      height: 45px;
      margin: 5px;
    }
    .container .item .name {
      position: absolute;
      width: 90px;
      top: 0;left: 55px;
      font-size: 16px;
    }

    .container .item .change {
      width: 100px;
      position: absolute;
      top: 0;
      right: 50px;
    }
    .container .item .change a {
      font-size: 20px;
      width: 30px;
      text-decoration:none;
      background-color: lightgray;
      vertical-align: middle;
    }
    .container .item .change .num {
      width: 40px;
      height: 25px;
    }
    .container .item .del {
      position: absolute;
      top: 0;
      right: 0px;
      width: 40px;
      text-align: center;
      font-size: 40px;
      cursor: pointer;
      color: red;
    }
    .container .item .del:hover {
      background-color: orange;
    }
    #jiesuan:hover{
      cursor: pointer;
    }
  </style>
</head>
<body>
  <div id="app">
    <div class="container">
      <!-- 使用组件 -->
      <my-car></my-car>
    </div>
  </div>
  <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
    //定义三个子组件:一定要先定义三个子组件,这样后面的父组件就可以取到了
    var carTitle = {
      props:['username'],
      template: `<div class="title">{{username}}的商品</div>`,
    };
    var carList = {
      props: ['list'],
      template: `<div>
          <div class="item" v-for="(elem,index) in list" :key="index">
            <img :src="elem.img"/>
            <div class="name">{{elem.name}}</div>
            <div class="change">
              <a href="" @click.prevent="subNum(index)"></a>
              <input @blur="inputNum(index)" type="text" class="num" v-model="elem.num" ref="in"/>
              <a href="" @click.prevent="addNum(index)"></a>
            </div>
            <div class="del" @click="delItem(index)">×</div>
          </div>
        </div>`,
      methods: {
        delItem(index) {
          //把当前条目的索引传递给父组件
          this.$emit('delete-cart', index);
        },
        inputNum(index) {
          //alert(index);
          this.$emit('change-num', {
            index: index, //条目的索引
            num: this.$refs.in.value, //获取输入框输入的值
            type: 'change' //标记,表示是通过输入商品数量
          });
        },
        subNum(index){
          this.$emit('change-num', {
            index: index,
            num: this.$refs.in.value,
            type: 'sub' //表示是点击的-
          });
        },
        addNum(index){
          this.$emit('change-num', {
            index: index,
            num: this.$refs.in.value,
            type: 'add' //表示是点击的+
          });
        }
      }
    };

    var carTotal = {
      props: ['list'],
      template: `<div class="total">
						<span>总价:{{totalprice}}</span>
						<button id="jiesuan">结算</button>
					</div>`,
      computed:{
        totalprice(){
          var sum = 0;
          for(let i=0;i<this.list.length;i++){
            sum += this.list[i].num * this.list[i].price;
          }
          return sum;
        }
      }
    };

    //定义父组件:
    Vue.component('my-car', {
      data: function () {
        return {
          username: '张劲雷',
          list: [{
            id: 1,
            name: 'TCL彩电',
            price: 1000,
            num: 1,
            img: 'img/a.jpg'
          }, {
            id: 2,
            name: '机顶盒',
            price: 1000,
            num: 1,
            img: 'img/b.jpg'
          }, {
            id: 3,
            name: '海尔冰箱',
            price: 1000,
            num: 1,
            img: 'img/c.jpg'
          }, {
            id: 4,
            name: '小米手机',
            price: 1000,
            num: 1,
            img: 'img/d.jpg'
          }, {
            id: 5,
            name: 'PPTV电视',
            price: 1000,
            num: 2,
            img: 'img/e.jpg'
          }]
        }
      },
      template: `<div class="cart">
					<car-title :username="username"></car-title>
					<car-list @delete-cart="delEle" @change-num="changeNum($event)" :list="list"></car-list>
					<car-total :list="list"></car-total>
				</div>`,
      components: {
        'car-title': carTitle,
        'car-list': carList,
        'car-total': carTotal
      },
      methods: {
        delEle(index) {
          this.list.splice(index, 1);
        },
        changeNum(value) {
          //alert(value);
          if (value.type == "change") {
            //根据子组件传递过来的数据,更新父组件中商品的数量
            this.list.forEach(function (item, index) {
              if (index == value.index) {
                item.num = value.num;
              }
            });
          } else if (value.type == "add") {
            this.list.forEach(function (item, index) {
              if (index == value.index) {
                item.num += 1;
              }
            });
          } else {
            this.list.forEach(function (item, index) {
              if (index == value.index ) {
                item.num -= 1;
                if(item.num == 0){
                  alert("亲,商品的数量不能为0哦~");
                  item.num = 1;
                }
              }
            })
          }
        }
      }
    });

    var vm = new Vue({
      el: '#app',
      data: {
      }
    });

  </script>
</body>
</html>
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值