开发思路
- 实现整体布局和样式效果
- 划分独立的功能组件
- 组合所有的子组件形成整体结构
- 逐个实现各个组件功能
①标题组件(展示文本)
②列表组件(列表展示、商品数量变更商品删除)
③结算组件(计算商品总额)
最终效果
可对商品进行加减、删除操作。
代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="./0.vue.js"></script>
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
.container {
width: 300px;
margin: 100px auto;
text-align: center;
}
a {
text-decoration: none;
}
.title {
background-color: #a9d2e1;
height: 40px;
line-height: 40px;
}
.item {
box-sizing: border-box;
height: 40px;
line-height: 40px;
border-bottom: 1px solid #a9d2e1;
}
.item .num {
padding-left: 10px;
box-sizing: border-box;
width: 30px;
height: 30px;
line-height: 30px;
position: relative;
bottom: 2px;
}
.item a {
display: inline-block;
width: 18px;
height: 25px;
background-color: #d3d3d3;
line-height: 25px;
}
.item .name {
float: left;
width: 100px;
}
.item .change {
float: left;
height: 40px;
width: 160px;
}
.item .del {
float: left;
width: 40px;
color: #fd2813;
font-size: 30px;
font-weight: 600;
cursor: pointer;
}
.item .del:hover {
background-color: orange;
}
.total {
margin-top: 2px;
background-color: #19bc9c;
height: 45px;
line-height: 45px;
color: white;
}
.total button {
width: 50px;
display: inline-block;
margin-left: 10px;
border: none;
height: 20px;
color: white;
background-color: #c03a2b;
}
</style>
</head>
<body>
<div id="app">
<div class="container">
<my-cart></my-cart>
</div>
</div>
<script>
let CartTitle = {
props: ['name'],
template: `
<div class="title">{{name}}的商品</div>`
};
let CartList = {
props: ['list'],
template: `
<div>
<div class="item" :key='item.id' v-for='item in list'>
<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(id) {
// 把id传递给父组件
this.$emit('cart-del', id);
},
changeNum(id, event) {
this.$emit('change-num', {
id: id,
num: event.target.value,
type: 'change'
})
},
add(id) {
this.$emit('change-num', {
id: id,
type: 'add'
});
},
sub(id) {
this.$emit('change-num', {
id: id,
type: 'sub'
});
}
},
};
let CartTotal = {
props: ['list'],
template: `
<div class="total">
<span>总价:{{total}}</span>
<button>结算</button>
</div>
`,
computed: {
total() {
// 计算商品总价
let t = 0;
this.list.forEach(item => {
t += item.price * item.num;
})
return t;
}
}
};
Vue.component('my-cart', {
data() {
return {
uname: '张三',
list: [{
id: 1,
name: 'TCL彩电',
price: 1000,
num: 1
}, {
id: 2,
name: '机顶盒',
price: 200,
num: 1,
}, {
id: 3,
name: '海尔冰箱',
price: 2010,
num: 1,
}, {
id: 4,
name: '小米手机',
price: 999,
num: 1,
}, {
id: 5,
name: '乐视TV',
price: 888,
num: 1
}
],
}
},
template: `
<div class='cart'>
<cart-title :name='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(id) {
// 根据id删除list中对应的数据
// 1.根据id找到对应数据的索引
var index = this.list.findIndex(item => {
return item.id == id;
})
// 2.根据索引删除对应的数据
this.list.splice(index, 1);
},
changeNum(val) {
// 根据子组件传递过来的数据,更新list数据
// 分为3种情况:输入域变更,加号变更,减号变更
if (val.type == 'change') {
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) {
if (item.num > 0) {
item.num--;
} else {
item.num = 0;
}
return true;
}
});
} else if (val.type == 'add') {
this.list.some(item => {
if (item.id == val.id) {
item.num++;
return true;
}
});
}
}
}
});
new Vue({
el: "#app",
data: {
},
methods: {
},
})
</script>
</body>
</html>
难点
难点在于子组件和父组件之间的通信。