Vue学习笔记——基于组件的购物车结算案例

Vue学习笔记——基于组件的购物车结算案例

效果图片:
在这里插入图片描述


需求分析:

  1. 按照组件化方式实现业务需求
  • 根据业务功能进行组件化划分
    ① 标题组件(展示文本)
    ② 列表组件(列表展示、商品数量变更、商品删除)
    ③ 结算组件(计算商品总额)

实现步骤:

  • 实现整体布局和样式效果
  • 划分独立的功能组件
  • 组合所有的子组件形成整体结构
  • 逐个实现各个组件功能(标题、列表、结算)
1. 实现组件化布局
  • 把静态页面转换成组件化模式
  • 把组件渲染到页面上
<div id="app">
	<div class="container">
		<my-cart></my-cart>
	</div>
</div>
 <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">
  //标题组件
  var CartTitle = {
  	props:['uname'],
  	template:`
			<div class="title" >{{uname}}的商品</div>
  	`
  }
  //商品列表组件
  var CartList = {
  	props:['list'],
  	template:`
			<div>
				<div class="item" :key='item.id' v-for='item in list'>
					<img :src="item.img">
					<div class="name">{{item.name}}</div>
					<div class="change">
						<a href="">-</a>
						<input type="text" class="num" :value='item.num'>
						<a href="">+</a>
					</div>
					<div class="del">×</div>
				</div>				
			</div>
  	`
  }
  //商品结算组件
  var CartTotal = {
  	props:['list'],
  	template:`
			<div class="total">
				<span>总价:{{total}}</span>
				<button>结算</button>
			</div>
  	`,
  	computed:{
  		total:function(){
  			//计算商品的总价
  			var t = 0;
  			//对数组进行遍历
  			this.list.forEach(item=>{
  				t += item.price * item.num;
  			});
  			return t;
  		}
  	}
  }
  // 定义一个全局组件 my-cart
  Vue.component('my-cart',{
  	data:function(){
  		return {
  			uname:'行行',
           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">
			<cart-title :uname='uname'></cart-title>
			<cart-list :list='list' ></cart-list>
			<cart-total :list='list'></cart-total>
		</div>
  	`,
  	//注册子组件
  	components:{
  		'cart-title':CartTitle,
  		'cart-list':CartList,
  		'cart-total':CartTotal,
  	}
  })
  var vm = new Vue({
  	el:'#app',
  	data:{

  	}
  })
  </script>

my-cart父组件里边包含三个子组件,定义为局部的组件,这样会更好一些,因为它们都隶属于my-cart父组件。
定义为全局就不太好了,因为名称可能存在冲突。所以定义为局部组件。

2. 实现 标题和结算功能组件

标题组件:
“我的商品” 改成一个动态的用户名
那么数据要从哪里传递呢?
从父组件传递给子组件,然后把传递过来的数据渲染到页面上

结算组件:

  • 从父组件把商品列表list 数据传递过来
  • 把传递过来的数据计算最终价格渲染到页面上
    使用计算属性。因为存在计算的变更,使用计算属性更方便

代码见↑

3. 实现列表组件删除功能
  • 从父组件把商品列表list 数据传递过来 即 父向子组件传值
  • 把传递过来的数据渲染到页面上 (v-for 注意:里头的属性,是属性的填充,所以要改成绑定)
  • 点击删除按钮的时候删除对应的数据
    (要在父组件中进行处理,所以要删除的元素id 子组件要通过自定义事件的方式传递给父组件)
    & 他的父组件是谁要搞清楚 (好像是声明他的自己)
 var CartList = {
  	props:['list'],
  	template:`
			<div>
				<div class="item" :key='item.id' v-for='item in list'>
					<img :src="item.img">
					<div class="name">{{item.name}}</div>
					<div class="change">
						<a href="">-</a>
						<input type="text" class="num" :value='item.num'>
						<a href="">+</a>
					</div>
					<div class="del" @click='del(item.id)'>×</div>
				</div>				
			</div>
  	`,
  	methods:{
  		del:function(id){
  			//但是这个删除操作应该在父组件内进行处理。因为不推荐在子组件中直接去操作props
  			//所以,他的父组件是谁?
  			this.$emit('cart-del',id)
  		}
  	}
  }

//父
Vue.component('my-cart',{
  	data:function(){
  		return {
  			uname:'行行',
           list: [{
            省略
          }],
  		}
  	},
  	template:`
		<div class="cart">
			<cart-title :uname='uname'></cart-title>
			<cart-list :list='list' @cart-del='delCart($event)'></cart-list>
			<cart-total :list='list'></cart-total>
		</div>
  	`,
  	components:{
  		'cart-title':CartTitle,
  		'cart-list':CartList,
  		'cart-total':CartTotal,
  	},
  	methods:{
  		delCart:function(id){
  			//根据id删除list中对应的数据
  			//1.找到id所对应数据的索引
  			var index = this.list.findIndex(item=>{
  				return item.id==id;
  			});
  			//2.根据索引删除对应数据
  			this.list.splice(index,1);
  		}
  	}
  })
  var vm = new Vue({
  	el:'#app',
  	data:{

  	}
  })
  </script>
4. 实现组件的输入框更新数组功能
  • 可在输入框更改数据
  • 输入框失去焦点的时候 更改商品的数量

如何得到当前输入域最新的值? 显示的把事件传递过来

<input type="text" class="num" :value='item.num' @blur='ChangeNum(item.id,$event)'>

  		ChangeNum:function(id,event){
  			//同样,也不是在子组件中进行操作,而是子组件把数据传递给父组件 让父组件处理这些数据
  			//
  			//如何得到当前输入域最新的值?显示的把事件传递过来
  			this.$emit('change-num',{
  				id:id,
  				num:event.target.value
  			});
  		}
5.实现组件的 - + 按钮更新数组功能
  • 绑定按钮的点击事件
  • 用同一个事件,你还需要知道到底点击的是加号还是减号,给个标志符
		add:function(id){
		  			//用同一个事件,你还需要知道到底点击的是加号还是减号,给个标志位
		  	this.$emit('change-num',{
		  		id:id,
		  		type:'add',
		  	})
		}

完整代码:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title></title>
	<style type="text/css">
		.container .cart{
			width: 300px;
			margin: auto;
		}
		.container .title{
			background-color: lightblue;
			height: 40px;
			line-height: 40px;
			text-align: center;
		}
		.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;
	    	text-align: center;
	    }
	    .container .item .change .num{
	    	width: 40px;
	    	height: 25px;
	    }
	    .container .item .del{
	    	position: absolute;
	    	top: 0;
	    	right: 0;
	    	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">
		<my-cart></my-cart>
	</div>
</div>
 <script type="text/javascript" src="js/vue.js"></script>
  <script type="text/javascript">

  var CartTitle = {
  	props:['uname'],
  	template:`
			<div class="title" >{{uname}}的商品</div>
  	`
  }
  var CartList = {
  	props:['list'],
  	template:`
			<div>
				<div class="item" :key='item.id' v-for='item in list'>
					<img :src="item.img">
					<div class="name">{{item.name}}</div>
					<div class="change">
						<a href="" @click.prevent="sub(item.id)">-</a>
						<input type="text" class="num" :value='item.num' @blur='ChangeNum(item.id,$event)'>
						<a href="" @click.prevent="add(item.id)">+</a>
					</div>
					<div class="del" @click='del(item.id)'>×</div>
				</div>				
			</div>
  	`,
  	methods:{
  		del:function(id){
  			//但是这个删除操作应该在父组件内进行处理。因为不推荐在子组件中直接去操作props
  			//所以,他的父组件是谁?
  			this.$emit('cart-del',id);
  		},
  		ChangeNum:function(id,event){
  			//同样,也不是在子组件中进行操作,而是子组件把数据传递给父组件 让父组件处理这些数据
  			//
  			//如何得到当前输入域最新的值?显示的把事件传递过来
  			this.$emit('change-num',{
  				id:id,
  				type:'text',
  				num:event.target.value
  			});
  		},
  		sub:function(id){
  			//你还需要知道到底点击的是加号还是减号,给个标志位
  			this.$emit('change-num',{
  				id:id,
  				type:'sub',
  			})
  		},
		add:function(id){
		  			//用同一个事件,你还需要知道到底点击的是加号还是减号,给个标志位
		  	this.$emit('change-num',{
		  		id:id,
		  		type:'add',
		  	})
		}
  	}
  }
  var CartTotal = {
  	props:['list'],
  	template:`
			<div class="total">
				<span>总价:{{total}}</span>
				<button>结算</button>
			</div>
  	`,
  	computed:{
  		total:function(){
  			//计算商品的总价
  			var t = 0;
  			//对数组进行遍历
  			this.list.forEach(item=>{
  				t += item.price * item.num;
  			});
  			return t;
  		}
  	}
  }
  Vue.component('my-cart',{
  	data:function(){
  		return {
  			uname:'行行',
           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">
			<cart-title :uname='uname'></cart-title>
			<cart-list :list='list' @cart-del='delCart($event)' @change-num='changeNum($event)'></cart-list>
			<cart-total :list='list'></cart-total>
		</div>
  	`,
  	components:{
  		'cart-title':CartTitle,
  		'cart-list':CartList,
  		'cart-total':CartTotal,
  	},
  	methods:{
  		delCart:function(id){
  			//根据id删除list中对应的数据
  			//1.找到id所对应数据的索引
  			var index = this.list.findIndex(item=>{
  				return item.id==id;
  			});
  			//2.根据索引删除对应数据
  			this.list.splice(index,1);
  		},
  		changeNum:function(val){
  			if(val.type=='text'){
  			//根据子组件传递过来的数据,更新list中对应的数据
  			this.list.some(item=>{
  				if(item.id==val.id){
  					item.num = val.num;
  					//终止遍历 
  					return true;
  				}
  			});
  			}else if(val.type=='sub'){
  				//仍然是要判断你点击的是那个元素哦!!
  			this.list.some(item=>{
  				if(item.id==val.id){
  					item.num -= 1;
  					//终止遍历 
  					return true;
  				}
  			});
  			}else if(val.type=='add'){
  			this.list.some(item=>{
  				if(item.id==val.id){
  					item.num += 1;
  					//终止遍历 
  					return true;
  				}
  			});
  			}

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值