[Vue.js] 深入 -- 案例 - 购物车

案例 - 购物车

按照组件化方式实现业务需求
  • 根据业务功能进行组件化划分
    • 标题组件(展示文本)
    • 列表组件(列表展示、商品数量变更、商品删除)
    • 结算组件(计算商品总额)
功能实现步骤
  • 实现整体布局和样式效果
  • 划分独立的功能组件
  • 组合所有的子组件形成整体结构
  • 逐个实现各个组件功能
    • 标题组件
    • 列表组件
    • 结算组件
效果图展示

 

实现组件化布局
  • 先创建一个局部组件my-cart,在里面引入属性components,定义三个组件cart-title,cart-list,cart-total
var CartTitle = {
	template: `

	`
}
var CartList = {
	template: `

	`
}
var CartTotal = {
	template: `

	`
}

Vue.component('my-cart',{
	template: `
        
	`,
	components: {
		'cart-title': CartTitle,
		'cart-list': CartList,
		'cart-total': CartTotal
	}
});
  • 把页面的静态结构转化为组件化的方式
var CartTitle = {
	template: `
		<div class="title">我的商品</div>
	`
}
var CartList = {
	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 CartTotal = {
	template: `
		<div class="total">
          <span>总价:123</span>
          <button>结算</button>
       </div>
	`
}

Vue.component('my-cart',{
	template: `
		<div class='cart'>
          <cart-title></cart-title>
          <cart-list></cart-list>
          <cart-total></cart-total>
       </div>
	`,
	components: {
		'cart-title': CartTitle,
		'cart-list': CartList,
		'cart-total': CartTotal
	}
});
  • 在容器中利用<my-cart></my-cart>展示出效果
<div id="app">
	<div class="container">
		<my-cart></my-cart>
	</div>
</div>

 

实现标题和结算组件功能
  • 用父组件传递标题的数据
var CartTitle = {
	props: ['uname'],
	template: `
		<div class="title">{{uname}}的动物园拍卖会</div>
      `
}
Vue.component('my-cart',{
	data: function() {
		return {
			uname: 'DG',
		}
    },
	template: `
		<div class='cart'>
			<cart-title :uname='uname'></cart-title>
			<cart-list></cart-list>
			<cart-total></cart-total>
		</div>
	`,
}
  • 接下来需要有数据提供
Vue.component('my-cart',{
        data: function() {
            return {
                uname: 'DG',
                list: [{
                    id: 1,
                    name: '熊猫',
                    price: 1000,
                    num: 1,
                    img: 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3726570454,3633557931&fm=26&gp=0.jpg'
                },{
                    id: 2,
                    name: '猴子',
                    price: 1000,
                    num: 1,
                    img: 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3524667055,2282983046&fm=26&gp=0.jpg'
                },{
                    id: 3,
                    name: '兔子',
                    price: 1000,
                    num: 1,
                    img: 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1173496453,597070384&fm=26&gp=0.jpg'
                },{
                    id: 4,
                    name: '老鼠',
                    price: 1000,
                    num: 1,
                    img: 'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3570432866,2699104227&fm=26&gp=0.jpg'
                },{
                    id: 5,
                    name: '海豚',
                    price: 1000,
                    num: 2,
                    img: 'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1113954919,3492262937&fm=26&gp=0.jpg'
                }]
            }
        }
  • 之后把数据传递给cart-total
template: `
    <div class='cart'>
        <cart-title :uname='uname'></cart-title>
        <cart-list></cart-list>
        <cart-total :list='list'></cart-total>
    </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;
	}
}

 

实现列表组件删除功能
  • 传递和接受list

    • 传递
    template: `
    	<div class='cart'>
    		<cart-title :uname='uname'></cart-title>
    		<cart-list :list='list'></cart-list>
    		<cart-total :list='list'></cart-total>
    	</div>
    `
    
    • 接受
    var CartList = {
    	props: ['list'],
    	template: `
    		<div>
    			<div :key='item.id' v-for='item in list' class="item">
    				<img :src="item.img"/>
    				<div class="name">{{item.name}}</div>
    				<div class="change">
    					<a href="">-</a>
    					<input type="text" class="num" />
    					<a href="">+</a>
    				</div>
    				<div class="del" @click='del(item.id)'>×</div>
    			</div>
    		</div>
    	`
    }
    
  • 实现删除

methods: {
    del: function(id){
        // 把id传递给父组件
        this.$emit('cart-del', id);
    }
}
  • 找到对应的父组件
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>
`
  • 根据id删除list中对应的数据
methods: {
    delCart: function(id) {
        // 1、找到id所对应数据的索引
        var index = this.list.findIndex(item=>{
        return item.id == id;
        });
        // 2、根据索引删除对应数据
        this.list.splice(index, 1);
    }
}

 

实现组件更新数据功能
  • 利用属性绑定的方式实现num呈现在前端页面中
var CartList = {
	props: ['list'],
	template: `
		<div>
			<div :key='item.id' v-for='item in list' class="item">
				<img :src="item.img"/>
				<div class="name">{{item.name}}</div>
				<div class="change">
					<a href="">-</a>
					<input type="text" class="num" :value='item.num' @blur='changeNum(item.id, $event)'/>
					<a href="">+</a>
				</div>
				<div class="del" @click='del(item.id)'>×</div>
			</div>
		</div>
	`
}
  • 实现数字变更操作
methods: {
	changeNum: function(id, event){
		this.$emit('change-num', {
			id: id,
			num: event.target.value
			});
		},
		del: function(id){
            // 把id传递给父组件
            this.$emit('cart-del', id);
		}
	}
}
  • 在父组件中进行监听
template: `
    <div class='cart'>
        <cart-title :uname='uname'></cart-title>
        <cart-list :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list>
        <cart-total :list='list'></cart-total>
    </div>
`
  • 定义函数
methods: {
    changeNum: function(val) {
        // 根据子组件传递过来的数据,跟新list中对应的数据
    	this.list.some(item=>{
    		if(item.id == val.id) {
    			item.num = val.num;
    			// 终止遍历
    			return true;
    		}
    	});
	},
}
  • 实现加或减更改数值
template: `
	<div>
		<div :key='item.id' v-for='item in list' class="item">
			<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>
`
sub: function(id){
    this.$emit('change-num', {
    	id: id,
    	type: 'sub'
    });
},
add: function(id){
    this.$emit('change-num', {
    	id: id,
    	type: 'add'
	});
},
  • 输入域变更加号变更减号变更进行判断
methods: {
	changeNum: function(val) {
		// 分为三种情况:输入域变更、加号变更、减号变更
		if(val.type=='change') {
			// 根据子组件传递过来的数据,跟新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;
				}
			});
		}
	}
}

 

整体代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
    <style type="text/css">
        .container {
        }
        .container .cart {
            width: 300px;
            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">
        <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 :key='item.id' v-for='item in list' class="item">
                    <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: {
            changeNum: function(id, event){
                this.$emit('change-num', {
                    id: id,
                    type: 'change',
                    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'
                });
            },
            del: function(id){
                // 把id传递给父组件
                this.$emit('cart-del', id);
            }
        }
    }
    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: 'DG',
                list: [{
                    id: 1,
                    name: '熊猫',
                    price: 1000,
                    num: 1,
                    img: 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3726570454,3633557931&fm=26&gp=0.jpg'
                },{
                    id: 2,
                    name: '猴子',
                    price: 1000,
                    num: 1,
                    img: 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3524667055,2282983046&fm=26&gp=0.jpg'
                },{
                    id: 3,
                    name: '兔子',
                    price: 1000,
                    num: 1,
                    img: 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1173496453,597070384&fm=26&gp=0.jpg'
                },{
                    id: 4,
                    name: '老鼠',
                    price: 1000,
                    num: 1,
                    img: 'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3570432866,2699104227&fm=26&gp=0.jpg'
                },{
                    id: 5,
                    name: '海豚',
                    price: 1000,
                    num: 2,
                    img: 'https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1113954919,3492262937&fm=26&gp=0.jpg'
                }]
            }
        },
        template: `
            <div class='cart'>
                <cart-title :uname='uname'></cart-title>
                <cart-list :list='list' @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list>
                <cart-total :list='list'></cart-total>
            </div>
        `,
        components: {
            'cart-title': CartTitle,
            'cart-list': CartList,
            'cart-total': CartTotal
        },
        methods: {
            changeNum: function(val) {
                // 分为三种情况:输入域变更、加号变更、减号变更
                if(val.type=='change') {
                    // 根据子组件传递过来的数据,跟新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;
                        }
                    });
                }
            },
            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>
</body>
</html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值