需求:
案例展示:
代码展示:
HTML部分:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div class="container">
<my-cart></my-cart>
</div>
</div>
<script src="./js/vue.js"></script>
<script>
//插入自己写的js
</script>
</body>
css部分:(以后写css样式尽量写层级选择器,他们之间有权重关系)
<style>
.container {
width: 100%;
height: 100%;
}
.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: 0px;
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 {
user-select: none;
/* 双击不选中 */
position: absolute;
top: 0px;
right: 0px;
width: 40px;
text-align: center;
font-size: 40px;
cursor: pointer;
color: red;
}
.container .item .del:hover {
background-color: orange;
}
</style>
js部分:(重点分析)
<script>
//局部定义的变量组件一定要放在 全局component前面!!!!
var CartTitle = {
props: ['uname'],
template: `
<div class="title">{{uname}}的购物车</div>
`
}
var CartList = {
props: ['lists'],
template: `
<div>
<div class="item" v-for="item in lists" :key="item.id">
<img :src="item.img"/>
<!-- 不能写成{{item.img}} 直接写item.img-->
<div class="name">{{item.name}}</div>
<div class="change">
<a href="" @click.prevent='sub(item.id)'> - </a>
<input name="" type="text" class="num" :value='item.num' @blur='changeNum(item.id,$event)'></input>
<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
})
},
//按+ -得到的数据也传递到changeNum
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: ['lists'],
template: `
<div class="total">
<span>总价:{{total}}</span>
<button>结算</button>
</div>
`,
computed: {
//利用计算属性来计算总价 要有return值
total: function() {
//计算商品总价
var sum = 0;
this.lists.forEach(item => {
//foeEach遍历出数据
sum += item.price * item.num;
//总价=单价x数量
})
return sum;
}
},
}
Vue.component('my-cart', {
data() {
return {
uname: 'XXW',
lists: [{
id: 1,
name: 'TCL彩电',
price: 1200,
num: 1,
img: 'img/a.jpg',
}, {
id: 2,
name: '机顶盒',
price: 800,
num: 1,
img: 'img/b.jpg',
}, {
id: 3,
name: '海尔冰箱',
price: 2200,
num: 1,
img: 'img/c.jpg',
}, {
id: 4,
name: '小米手机',
price: 1799,
num: 1,
img: 'img/d.jpg',
}, {
id: 5,
name: 'PPTV电视',
price: 1888,
num: 1,
img: 'img/e.jpg',
}, ]
}
},
template: `
<div class="cart">
<cart-title :uname="uname"></cart-title>
<cart-list :lists="lists" @change-num='changeNum($event)' @cart-del='delCart($event)'></cart-list>
<cart-total :lists="lists"></cart-total>
</div>
`,
components: {
//第一局部组件 定义太多全局组件容易起冲突
'cart-title': CartTitle,
'cart-list': CartList,
'cart-total': CartTotal
},
methods: {
changeNum: function(val) {
var reg = /^[0-9]{1,5}$/; //正则匹配不能输入中文
//分三种情况:输入域变更,加号变更,减号变更
if (val.type == 'change') {
if (reg.test(val.num)) {
console.log(reg.test(val.num)); //true
console.log(typeof val.num); //隐式迭代 变成string型
//根据子组件传递过来的数据,跟新list中对应的数据
this.lists.some(item => {
if (item.id == val.id) {
if (item.num > 0) {
//输入框不能为负值
item.num = val.num;
} else {
item.num = 0;
}
//终止遍历
return true;
}
});
} else {
console.log(reg.test(val.num)); //false
this.lists.some(item => {
if (item.id == val.id) {
item.num = 0;
//终止遍历
return true;
}
});
}
} else if (val.type == 'sub') {
//减一操作
this.lists.some(item => {
//不能减到0以下
if (item.id == val.id) {
if (item.num >= 1) {
item.num = item.num * 1 - 1;
//我的想法是给item.num*1 这样就又转number类型 直接把item.num 写成 item.Number(num)没有用 显示undefined
} else {
item.num = 0;
}
//终止遍历
return true;
}
});
} else if (val.type == 'add') {
//加一操作
this.lists.some(item => {
if (item.id == val.id) {
item.num = item.num * 1 + 1;
//终止遍历
return true;
}
})
}
},
delCart: function(id) {
this.lists = this.lists.filter(function(item) { //把筛选后的结果重新赋值给books
return item.id != id //返回那些id 不相等的对象 形成一个新的数组
})
}
},
});
var vm = new Vue({
el: '#app',
data: {}
})
</script>
js代码分析:
1. 局部定义的变量组件一定要放在全局component前面!!!
2.<img :src="item.img"/>
不能写成{{item.img}} !! 直接写item.img
3.利用computed
计算属性要有返回值return
4.利用正则表达式匹配,让输入框只能输入数字,是中文直接归零。
注意!!!:正则表达式匹配后的num的数据类型发生了改变(隐式迭代)number=>string,导致后面的加减号按键是字符相加
解决方法:
后面加减按键的增加减少的代码改成如下
//加
item.num = item.num * 1 - 1;
//减
item.num = item.num * 1 + 1;
我的想法是给 item.num*1 这样就又转number类型 直接把item.num 写成 item.Number(num)没有用 结果是undefined
5. (细节处理)减号按键的操作不能让他<0,利用条件判断。
6.删除按钮的两种写法:
第一种:(分析看注释)
delCart: function(id) {
this.lists = this.lists.filter(function(item) { //把筛选后的结果重新赋值给books
return item.id != id //返回那些id 不相等的对象 形成一个新的数组
})
}
第二种(分析看注释)
delCart: function(id) {
// 根据id删除list中对应的数据
// 1、找到id所对应数据的索引
var index = this.list.findIndex(item => {
return item.id == id;
});
// 2、根据索引删除对应数据
this.list.splice(index, 1);
}